Documentation
¶
Overview ¶
MCP subprocess client for mcp-neo4j-cypher (Pattern 3). Aura never links a native Go Neo4j driver (CLAUDE.md §Project scope discipline ban); every Cypher call rides a stdio JSON-RPC 2.0 channel to the subprocess, which holds the only bolt connection. Requests are serialized through mu since a single stdio pipe pair cannot interleave. D-06: a subprocess crash is unrecoverable — we surface a wrapped error and let the caller fail the Aura process; no restart, no graceful degrade.
Package knowledge is the graph + vector substrate: an MCP-subprocess Cypher client (no native Go Neo4j driver per CLAUDE.md §Project scope discipline ban), a Cypher migration runner that audits applied versions in Postgres aura.knowledge_migrations, and the embedding-sidecar dim self-test that makes the AURA_EMBED_DIMENSIONS=384 contract operational (Amendment #18 / Pitfall #5).
Config is pure data, populated by internal/config from the environment. It carries everything the MCP client and embed self-test need; no methods.
Read-only Neo4j graph-explorer normalizer (Phase 27, GRAPH-01/03/04). It compiles STRUCTURED query intents (never raw Cypher) into parameterized read-Cypher, runs them through the existing knowledge.Client.Read MCP path, and projects the resulting row maps into the flat {nodes,edges,paths,schema,query} JSON contract the REST layer (plan 02) and the cockpit (plans 03/04) consume.
All injection-safety lives here: values ride the param map (D-05, never fmt.Sprintf into the body), a belt-and-suspenders assertReadOnly write-verb guard backs the read-only-by-construction compilers, and the MCP-serialization quirks (list->NULL, node->property-dict) are absorbed by the explicit-field projection + normalizeRows. No native Go Neo4j driver is imported for reads (CLAUDE.md ban); the GraphReader seam exposes Read ONLY.
Belt-and-suspenders write-verb guard (Phase 27, GRAPH-04 / T-27-02). The intent compilers only ever emit read-Cypher, but every dispatched query is re-checked here before it reaches the reader — defense in depth on an authenticated REST endpoint, on top of the read-tx enforced at the mcp/bolt layer. String literals are stripped first so a write verb living in NODE-PROPERTY DATA (e.g. a note that says "please DELETE this") never trips the guard.
Structured graph-query intents → parameterized read-Cypher (Phase 27, GRAPH-01). Every intent value rides the returned param map ($seed/$session/$labels/ $rel_types/$node_cap/$edge_cap) — there is NO fmt.Sprintf of any value into the query body (D-05, T-27-01). Label/rel-type filters are bound as DATA in `WHERE x IN $list`, never interpolated as Cypher label syntax. Every projection uses explicit scalar fields + apoc.convert.toJson(labels(...)) so node labels + element-ids survive the mcp-neo4j-cypher serialization boundary (Pattern 1; a bare `RETURN n` or a bare labels() list would lose data).
Row-map → contract normalization (Phase 27, GRAPH-01/03). knowledge.Client.Read returns []map[string]any; each seed/expand row carries a SOURCE node (s_*), an optional NEIGHBOR node (n_*), and an optional relationship (r_*). This builds the de-duped node/edge sets, decodes labels from the apoc.convert.toJson STRING via json.Unmarshal (lists return NULL through the read tool — Pitfall 3), and derives GraphNode.Citations from the already-fetched edges (NO extra Cypher, D-09).
Cypher migration runner (Pattern 4). .cypher files numbered like SQL migrations are embedded into the binary; applied versions are audited in the Postgres aura.knowledge_migrations table (created by Slice 0.5), consumed here via the sqlc-generated bindings. New migrations execute statement-by-statement through the driver-backed SchemaExecutor (auto-commit — see schema.go for why MCP cannot run schema DDL); on success an audit row is written. Re-running is idempotent: already-applied versions are skipped, and a checksum drift on an applied version is a hard error (history corruption).
Health probes for the graph substrate (Pattern 5). Ping runs two checks: the MCP/Neo4j liveness call (dbms.components, asserts a 5.26.x kernel) and the embed-sidecar dim self-test. The dim self-test is the ONLY place the AURA_EMBED_DIMENSIONS=384 contract becomes operational (Amendment #18 / Pitfall #5): a sidecar returning the wrong dimension would silently corrupt the HNSW index, so we refuse to start on mismatch with a load-bearing error.
Connectivity probe for the Neo4j graph substrate, used by the serve daemon's /readyz readiness endpoint (O-05/AP-14). It dials bolt with the native driver, forces a real connection via VerifyConnectivity, and closes — no long-lived handle, no MCP subprocess. A connectivity failure is returned so the readiness endpoint can answer 503 when the required graph backend is unreachable.
Reset is the dev-only destructive teardown behind `aura neo4j reset --yes`. It drops the D-08 schema objects, deletes all nodes, clears the Postgres audit trail so versions re-apply, then re-runs Migrate to a clean baseline. Schema DROPs and the bulk delete both go through the driver-backed SchemaExecutor (auto-commit) for the same reason migrate does — MCP cannot run schema DDL. Never wire this into a non-dev path; the CLI dispatcher owns the --yes + AURA_RESET_YES guard.
Schema DDL executor backed by the official Neo4j Go driver. This is the CLAUDE.md-sanctioned "Go driver native (fallback se mcp-neo4j-cypher non sufficiente)" path, used ONLY for schema operations (CREATE/DROP CONSTRAINT|INDEX). The MCP subprocess (client.go) remains the LLM-facing runtime interface for data reads/writes.
Why a separate path: Neo4j forbids schema commands inside an explicit transaction, and mcp-neo4j-cypher's write tool always wraps queries in a managed write-tx — so it rejects schema DDL ("Only write queries are allowed"). The industrial pattern (golang-migrate's neo4j driver, Neo4j-Migrations) runs schema via the driver in AUTO-COMMIT mode: session.Run, never ExecuteWrite. D-06 spirit is preserved: a connectivity failure errors out (no restart, no graceful degrade).
Status reads the applied-Cypher-migration audit trail from Postgres (aura.knowledge_migrations) via the Slice 0.5 sqlc bindings, for `aura neo4j status` tabulation. Postgres is the source of truth for migration history; Neo4j Community has no migration tracker of its own.
Index ¶
- Constants
- func Migrate(ctx context.Context, schema *SchemaExecutor, pool *pgxpool.Pool) (int, error)
- func Reset(ctx context.Context, schema *SchemaExecutor, pool *pgxpool.Pool) error
- func VerifyConnectivity(ctx context.Context, cfg *Config) error
- type Client
- func (c *Client) Close() error
- func (c *Client) Cypher(ctx context.Context, query string, params map[string]any, write bool) (json.RawMessage, error)
- func (c *Client) Read(ctx context.Context, query string, params map[string]any) ([]map[string]any, error)
- func (c *Client) Write(ctx context.Context, query string, params map[string]any) ([]map[string]any, error)
- type Config
- type GraphEdge
- type GraphIntent
- type GraphNode
- type GraphPath
- type GraphReader
- type GraphResult
- type GraphSchema
- type GraphView
- type MigrationRow
- type PingResult
- type SchemaExecutor
Constants ¶
const ( OpSeed = "seed" OpExpand = "expand" OpSchemaOverview = "schema_overview" )
Op enum (GraphIntent.Op). Exported so the REST layer (Phase 27 plan 02) can enum-validate an inbound intent against the SAME constants the dispatcher switches on — one source of truth for the wire op set, no drift between the validator and the compiler.
const DefaultEmbedDimensions = 384
DefaultEmbedDimensions is the vector width emitted by the repo's default Granite embedding sidecar and encoded in the initial Neo4j HNSW index.
Variables ¶
This section is empty.
Functions ¶
func Migrate ¶
Migrate applies every embedded .cypher migration not yet recorded in aura.knowledge_migrations and returns the count newly applied. Idempotent.
func Reset ¶
Reset drops indexes/constraints + all nodes, clears the audit table, and re-applies migrations. Dev only.
func VerifyConnectivity ¶
VerifyConnectivity dials Neo4j over bolt and confirms the server is reachable, then releases the driver. The supplied ctx bounds the dial+verify (the caller passes a short readiness timeout). It does NOT assert a kernel version (that is the boot-time Ping's job, ping.go) — readiness is a liveness signal for traffic routing, not a version gate.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps the mcp-neo4j-cypher subprocess. The zero value is unusable; use Open.
func Open ¶
Open spawns mcp-neo4j-cypher in stdio transport mode against cfg's bolt endpoint. On spawn failure the error carries the literal install hint so an operator missing the binary on PATH gets an actionable message.
func (*Client) Cypher ¶
func (c *Client) Cypher(ctx context.Context, query string, params map[string]any, write bool) (json.RawMessage, error)
Cypher sends one read or write Cypher call and returns the raw MCP result. Serialized via mu. Send/recv failures wrap crashHint (D-06) with redacted stderr so a dead subprocess fails Aura with an actionable, secret-free error.
type Config ¶
type Config struct {
BoltURL string // bolt://host:7687 — passed to mcp-neo4j-cypher --db-url
User string // Neo4j auth user (default neo4j)
Password string // Neo4j auth password (NEO4J_PASSWORD; never logged unredacted)
Database string // Community edition is single-DB; always "neo4j" (Pitfall #8)
MCPBinary string // mcp-neo4j-cypher executable on PATH
ConnectTimeoutSec int // first-call connect/retry budget
EmbedURL string // OpenAI-compat embeddings base URL (sidecar)
EmbedDimensions int // contract dim; boot self-test refuses a mismatch
}
Config holds the Neo4j + mcp-neo4j-cypher + embed-sidecar wiring. Populated by internal/config.Load from NEO4J_*, AURA_NEO4J_*, AURA_MCP_NEO4J_*, AURA_EMBED_*.
type GraphEdge ¶ added in v1.0.0
type GraphEdge struct {
ID string `json:"id"`
Source string `json:"source"`
Target string `json:"target"`
RelType string `json:"rel_type,omitempty"`
}
GraphEdge is one relationship: Source/Target are elementId keys matching node IDs (attached via elementId(startNode(r))/elementId(endNode(r))).
type GraphIntent ¶ added in v1.0.0
type GraphIntent struct {
Op string `json:"op"` // "seed" | "expand" | "schema_overview"
SeedID string `json:"seed_id,omitempty"` // elementId anchor for "expand"
Session string `json:"session,omitempty"` // active conversation ThreadID for "seed"
Labels []string `json:"labels,omitempty"` // include-label filter (bound as data)
RelTypes []string `json:"rel_types,omitempty"` // include-rel filter (bound as data)
NodeCap int `json:"node_cap,omitempty"` // default 75, hard max 300
EdgeCap int `json:"edge_cap,omitempty"` // default 200, hard max 800
UserID string `json:"-"` // authenticated Aura identity; never client-supplied
}
GraphIntent is the structured payload the cockpit submits (D-05). The compilers turn it into parameterized read-Cypher; the cockpit never authors Cypher.
type GraphNode ¶ added in v1.0.0
type GraphNode struct {
ID string `json:"id"`
Caption string `json:"caption,omitempty"`
Labels []string `json:"labels,omitempty"`
EntityType string `json:"entity_type,omitempty"`
Degree int `json:"degree,omitempty"`
Props map[string]any `json:"props,omitempty"`
RefID string `json:"ref_id,omitempty"`
Citations []string `json:"citations,omitempty"`
}
GraphNode is one node in the contract. ID is the elementId (de-dupe + edge anchor). Labels arrives via apoc.convert.toJson(labels(n)) → json.Unmarshal. EntityType is the POLE+O sub-type carried on Entity.type — a SECOND color dimension (D-02). Citations is DERIVED in the normalizer from this node's neighbor edges whose target carries a :Document/:Source label (GRAPH-03 / D-09); it costs no extra Cypher and is empty when the node has no such neighbor.
type GraphPath ¶ added in v1.0.0
type GraphPath struct {
Steps []GraphEdge `json:"steps"`
}
GraphPath is an ordered run of edges (the path strip, D-10).
type GraphReader ¶ added in v1.0.0
type GraphReader interface {
Read(ctx context.Context, query string, params map[string]any) ([]map[string]any, error)
}
GraphReader is the narrow Cypher seam GraphView needs. *knowledge.Client satisfies it. It exposes Read ONLY — the read-only milestone never surfaces a Write path through this seam (mirror reasoningstore.GraphClient, minus Write).
type GraphResult ¶ added in v1.0.0
type GraphResult struct {
Nodes []GraphNode `json:"nodes"`
Edges []GraphEdge `json:"edges"`
Paths []GraphPath `json:"paths,omitempty"`
Schema GraphSchema `json:"schema"`
Query string `json:"query"`
}
GraphResult is the flat tagged contract a graph read emits (mirror display.Payload discipline: a struct, not an interface, with omitempty so decode(encode) is identity and the wire stays lean).
type GraphSchema ¶ added in v1.0.0
type GraphSchema struct {
Labels []string `json:"labels"`
RelTypes []string `json:"rel_types"`
PropertyKeys []string `json:"property_keys,omitempty"`
EntityTypes []string `json:"entity_types,omitempty"`
Counts map[string]int `json:"counts,omitempty"`
}
GraphSchema is the live introspection result (D-06): the label set + rel-types + property keys feed the left-panel filters + color legend + the schema-overview fallback. EntityTypes is the POLE+O second color dimension. Counts is optional.
type GraphView ¶ added in v1.0.0
type GraphView struct {
// contains filtered or unexported fields
}
GraphView wraps a GraphReader and serves the two read operations the REST layer needs: Schema (live label/rel-type overview) and Query (a structured intent → the flat contract). It holds no state beyond the reader.
func NewGraphView ¶ added in v1.0.0
func NewGraphView(r GraphReader) *GraphView
NewGraphView wraps a GraphReader (typically *knowledge.Client).
func (*GraphView) Query ¶ added in v1.0.0
func (v *GraphView) Query(ctx context.Context, in GraphIntent) (GraphResult, error)
Query dispatches a structured intent through the full compile → assertReadOnly → Read → normalize path. GraphResult.Query is set to the compiled (display) Cypher so the cockpit's read-only "Show Cypher" affordance (D-09) shows the exact query. On an empty seed footprint it falls back to the relationship overview (D-07/D-08) so the default open can still draw graphs written outside the older conversation-message footprint.
type MigrationRow ¶
MigrationRow is one applied-migration audit record for CLI display.
type PingResult ¶
PingResult carries what the CLI prints on a healthy ping.
type SchemaExecutor ¶
type SchemaExecutor struct {
// contains filtered or unexported fields
}
SchemaExecutor runs Cypher schema DDL via auto-commit driver sessions.
func OpenSchema ¶
func OpenSchema(ctx context.Context, cfg *Config) (*SchemaExecutor, error)
OpenSchema dials Neo4j over bolt and verifies connectivity. Caller must Close.