Neo4j logoAgents

Six Guesses Before the First Hop

Ask an agent a question about your graph — "which companies do our top account contacts work for?" — and watch what it has to decide before it can write a single line of Cypher.

Six Guesses Before the First Hop

Ask an agent a question about your graph — "which companies do our top account contacts work for?" — and watch what it has to decide before it can write a single line of Cypher.

Is the label Person, person, or Contact? Is the identifier email, emailAddress, or primaryEmail? Is the relationship WORKS_FOR, EMPLOYED_BY, or WORKS_AT? Does it run from person to company, or the other way? Does it only ever land on Company, or does the same relationship type sometimes point somewhere else? And if the agent is going to write anything back: which property is unique, so that a MERGE matches instead of forking the entity?

That is six guesses, and a wrong one does not throw. It returns a result. The query runs, rows come back, the agent narrates them fluently, and nothing anywhere reports that the answer is about a different graph than the one you have.

Sampling teaches the mode, not the rule

The standard mitigation is to let the agent look first — sample some nodes, read some property keys, maybe call apoc.meta.schema() and paste the output into context. This helps, and it has a ceiling that is easy to miss.

A sample tells you what is common. It cannot tell you what is intended. An agent that has seen two hundred Person nodes knows most of them have an email; it does not know that email is supposed to be unique, that status draws from a five-value controlled list, or that the OWNS relationship it is about to traverse started pointing at a second label six months ago. Those are statements about design, and design is exactly what a sample cannot contain.

The governed model does contain them, because a human put them there.

What an agent can consult

An assistant connects to https://coremodels.example.com/mcp over OAuth and consults before it composes. The tools served there are all read-only, Viewer-role tools; the integration tools validate their project identifier against a 32-character hex pattern, so a malformed argument fails fast rather than half-executing.

get_vendor_integration_status with graphProjectId and vendor: "neo4j" answers the freshness question: is this graph imported at all, when was the last import, what did it contain, and how many governed datasets exist. Called without vendor, it lists every registered connector with its capabilities and expected artifacts — which is how an agent discovers that neo4j supports Import, Audit and Generate and expects meta_schema and constraints, instead of assuming.

get_project_summary returns the labels and identifiers of the types, elements and taxonomies in the project, and search_nodes finds the governed node behind a concept. Between them, an agent can go from "the user said customer" to "here is the governed Type, its Elements, their declared types and their constraints" without touching your database once.

audit_vendor_project is the interesting one, because it lets an agent check its own footing. Give it graphProjectId, vendor: "neo4j" and artifacts (or artifactUrls, for payloads too large to inline) and it compares a current export against the governed model, returning coverage, drift and conformance findings plus counts and a markdown report. An agent can literally ask "is the model I am about to rely on still true of this graph?" and get a structured answer, with errorCount as the same signal a CI gate uses. Those URL fetches are guarded: https only, redirects disabled, hosts resolving to loopback, link-local or private ranges refused, response size capped.

generate_vendor_artifacts goes the other way — governed model to coremodels_constraints.cypher, returned as content for a human to review.

The facts that stop the guessing

What the model carries is specific enough to close each of the six guesses above.

The Type is the node label as it exists, not a normalization of it. Each property is an Element with a declared type — the sampled STRING, INTEGER, FLOAT, BOOLEAN and date-time families map to governed primitives, and where they cannot (points, durations, lists) the approximation is recorded rather than presented as fact. Uniqueness and existence are explicit: an agent that reads a Unique check knows which property to MERGE on, which is the difference between matching an entity and silently duplicating it.

Relationships are the part most worth dwelling on. Each outgoing typed relationship is a reference Element named after the relationship type, targeting the label it points at. Direction is not inferred, because only outgoing relationships are governed and incoming ones are deliberately not duplicated on the target side. And when a relationship targets more than one label, the model governs the first target and flags the rest — surfaced to any consumer as the finding polymorphic-relationship, with the target list in the detail. An agent reading that knows, before it writes the traversal, that this particular hop is ambiguous. That is a far better outcome than discovering the ambiguity in a wrong answer.

Grounding only works if the ground truth keeps its sharp edges. A model that hid its own approximations would be a brochure.

Read is wide, write is a door

The tool surface is asymmetric on purpose. Everything above is read-only. The tools that change governed material — node and relation writes, and import_vendor_project, which brings a vendor estate into the graph — require Editor or Admin membership and are served only on a separate admin MCP endpoint. Import is additive even there: existing governed nodes are never mutated, and the recommended follow-up is an audit to see what changed.

Ontology grounding follows the same shape. An agent can call an advisory lookup that returns ranked candidate terms from the public EMBL-EBI Ontology Lookup Service, and the response says in words that suggestions are advisory and nothing was written. Binding a chosen term is a separate, explicit, human-directed act.

And underneath all of it: we hold no Neo4j credentials and open no connection to your instance. There is no path — for us, for an agent, for a confused loop in someone's orchestration code — from "consulted the model" to "touched your graph".

The honest framing

If your agents query Neo4j today without a governed model, they are not operating without a schema. They are operating with one they invented from samples, that nobody reviewed, that drifts invisibly, and that can differ between two runs an hour apart.

The choice was never governed agents versus ungoverned agents. It is a schema someone owns versus a schema something guessed.

Our Neo4j quickstart lists the MCP tool calls beside the HTTP routes — the same governed facts, whichever way your agents prefer to ask.