For AI agents

AI systems make decisions. InvariantDB proves what they knew, when they knew it, and why they changed their mind.

Your agent recalls a customer risk score from Tuesday. On Wednesday the underlying evidence is retracted. On Thursday an auditor asks: what did the agent actually see when it approved the loan? InvariantDB answers with one Cypher line — because valid time and recorded time are first-class query verbs, and every belief carries lineage.

MCP · protocolVersion 2024-11-05 · 14 tools · JSON-RPC 2.0

01 · Knew?

What did the agent believe?

Every belief is stored as an addressable node with a claim, a confidence, and a pointer to the evidence that produced it. Not a log line. A query target.

evidence evidence belief
02 · When?

Two clocks, first-class.

Valid time is when the fact held in the world. Recorded time is when the engine learned about it. Attach AT VALID or AT RECORDED to any MATCH pattern.

VALID RECORDED
03 · Why-changed?

Revision, not overwrite.

New evidence arrives. Instead of updating the belief in place, the engine records a successor with a reason. Old and new coexist. The chain is walkable.

belief v1 REVISES belief v2
Cypher-native MCP-native (14 tools) Bitemporal · two clocks, first-class Belief revision + lineage Crypto-shred erasure with receipts GDPR Art. 17 + 20 primitives
MCP quickstart

Point your agent at a graph. Speak Cypher.

Every graph exposes a JSON-RPC endpoint. Any Model Context Protocol client — Claude Desktop, an SDK, your own agent — can call it. The engine advertises fourteen tools on tools/list. The workhorse is cypher_query; every other tool is a shortcut around the equivalent CALL db.* procedure.

A · Endpoint

One URL per graph.

JSON-RPC 2.0 over HTTPS. Bearer auth. The transport announces itself as invariantdb-mcp and speaks protocol version 2024-11-05.

# Graph-scoped endpoint POST https://your-tenant.invariantdb.com/graphs/{name}/mcp Authorization: Bearer $INVARIANTDB_TOKEN Content-Type: application/json
B · Claude Desktop config

Drop into mcp.json.

The invariantdb-mcp npm package speaks JSON-RPC on stdio and forwards to the graph endpoint. Set the endpoint + token via env.

{ "mcpServers": { "invariantdb": { "command": "npx", "args": ["invariantdb-mcp"], "env": { "INVARIANTDB_ENDPOINT": "https://your-tenant.invariantdb.com/graphs/agent/mcp", "INVARIANTDB_TOKEN": "..." } } } }
C · The fourteen tools

One line of purpose each.

Every proc-wrapper tool dispatches to the equivalent CALL db.* form, so callers can drop to raw Cypher at any point.

cypher_query Run any Cypher against the bound graph. Returns columns + rows.
search_fulltext BM25 search over a (label, property). Top-k hits with score.
search_vector Vector search over a (label, property). Top-k hits with score.
get_node_by_str Fetch one node by its string id.
get_edge_by_str Fetch one edge by its string id.
engine_info Metadata for the bound graph: current version + auth summary.
record_episode Append one event to an agent’s session (episodic memory).
replay_episodes Replay a session in time order — last N events.
belief_lineage Walk every version of a belief back through its evidence.
property_provenance Full history + provenance for one node property.
derived_from Follow derivation edges to trace a value to its sources.
acl_events Replay ACL audit events — who tried, what, when, denied?
subject_export GDPR Art. 20 per-subject export — every node/edge/version.
destroy_subject_key GDPR Art. 17 crypto-shred with a signed audit-chain receipt.
Heads up: cypher_query rejects an atTime argument on purpose. Time travel belongs inside the Cypher body — put AT VALID or AT RECORDED on the MATCH pattern.
Belief revision

Record. Revise. Explain.

Four Cypher lines, in order. The columns match the runtime procedure catalog exactly — every one of these snippets is copy-runnable against a fresh graph.

  1. Record — the agent forms a belief.

    Confidence and evidence are stamped at record time. The engine mints a belief id and returns it.

    CALL db.recordBelief( "belief-99", "customer:c-77 risk = LOW", 0.82, ["episode-482", "episode-491"] ) YIELD beliefId RETURN beliefId;
  2. Revise — contradicting evidence arrives.

    The old belief is superseded, not overwritten. The successor gets its own id; the chain is intact. Note the yield columns: oldBeliefId, newBeliefId, revisedAt.

    CALL db.reviseBelief( "belief-99", "customer:c-77 risk = HIGH", 0.91, "address-mismatch document arrived 2026-03-04" ) YIELD oldBeliefId, newBeliefId, revisedAt RETURN oldBeliefId, newBeliefId, revisedAt;
  3. Lineage — walk backward.

    db.beliefLineage takes a belief id (head or any historical version) and walks the chain, deepest first. Every row carries claim, confidence, when it was created, when (if ever) it was superseded, depth, and the evidence ids that produced it.

    CALL db.beliefLineage("belief-99", 3) YIELD beliefId, claim, confidence, createdAt, supersededAt, depth, evidence RETURN beliefId, claim, confidence, createdAt, supersededAt, depth ORDER BY depth DESC;
  4. The auditor question — one line.

    Filter the lineage by createdAt and you have proof of what the agent could have known on any given day.

    // What did the agent know about customer c-77 on March 5? CALL db.beliefLineage("belief-99", 3) YIELD beliefId, claim, createdAt WHERE createdAt <= datetime("2026-03-05T00:00:00Z") RETURN beliefId, claim, createdAt ORDER BY createdAt DESC;
Hybrid recall

Two search modes. One graph.

Agents fetch context by keyword and by embedding — then follow edges from whatever they found. BM25 for lexical hits, vector search for semantic proximity, and Cypher expansion for the neighbourhood around each hit.

Fulltext (BM25)

Keyword recall, ranked.

Point gds.fulltextSearch at a (label, property) pair and a query string. The yield gives you node id, score, the full node record, and the human-facing string id.

CALL gds.fulltextSearch( "Note", "body", "address mismatch", 20 ) YIELD nodeId, score, node, strId RETURN strId, score, node.body ORDER BY score DESC;
Vector search

Semantic recall, ranked.

Pass the embedded query vector to gds.vectorSearch. The yield is intentionally narrow — just node id and score — so you can pipe straight into a MATCH to enrich or expand.

CALL gds.vectorSearch( "Doc", "embedding", $queryVector, 10 ) YIELD nodeId, score MATCH (d:Doc) WHERE id(d) = nodeId RETURN d.title, score ORDER BY score DESC;
Episodic memory

Session-scoped replay.

Every agent has a session log. db.recordEpisode appends one event; db.replayEpisodes hands the last N back in time order — kind, timestamp, text, and any payload the agent attached.

CALL db.recordEpisode( "session-1", "observation", "customer address on file does not match KYC document" ) YIELD eventId, ts RETURN eventId, ts; CALL db.replayEpisodes("session-1", 50) YIELD eventId, kind, ts, text, payload RETURN kind, ts, text ORDER BY ts;
Property provenance

Every value carries its receipts.

Every write is stamped with the actor, source, reason, request id, and confidence. Ask any property for its story — you get the full version history, provenance attached.

MATCH (c:Customer {strId: "c-77"}) CALL db.propertyProvenance(id(c), "risk") YIELD value, validFrom, validTo, version, txnAt, actor, source, reason, requestId, confidence RETURN version, value, actor, reason, txnAt ORDER BY version;
Bitemporal recall

Two clocks. One MATCH clause.

Every fact carries two intervals. Attach a clock verb to the pattern and the whole query travels there — edges included, indexes included, deletes reversed if the delete came later.

Valid time · the world clock

When was the fact true out in reality? Reprice a portfolio at last quarter’s prices. Reread a customer’s permissions at consent grant.

AT VALID '2026-03-05T00:00:00Z'

Recorded time · the system clock

When did the engine learn the fact? Reconstruct what the agent could have known before a bug fix. Prove there was no future-info leakage into training.

AT RECORDED '2026-03-04T09:00:00Z'

The auditor query.

// What did the agent's memory look like at record-time March 4, // as of what the world knew at valid-time March 5? MATCH (c:Customer {strId: "c-77"})-[:HAS_BELIEF]->(b:Belief) AT VALID "2026-03-05T00:00:00Z" AT RECORDED "2026-03-04T09:00:00Z" RETURN b.claim, b.confidence, b.createdAt;

Windowed change stream.

Every mutation in a bitemporal window — useful for driving downstream projections or verifying that an agent’s writes stayed inside a policy budget.

CALL db.changes($fromNanos, $toNanos) YIELD kind, op, entity_id, label, txn_at, applied_seq RETURN kind, op, label, txn_at ORDER BY applied_seq;
Compliance

Erasure without corruption. Access with receipts.

GDPR Article 17 and Article 20 are query verbs, not tickets in a queue. Erase a subject in place and prove it happened; export everything a subject touched; verify the ACL audit chain hasn’t been tampered with.

Art. 17 · erasure

Crypto-shred a subject.

The subject’s encryption key is destroyed. Every ciphertext that mentioned them becomes unreadable in place. History structure survives.

CALL db.destroySubjectKey("customer:c-77") YIELD subjectId, destroyedAtNanos RETURN subjectId, destroyedAtNanos;
Art. 17 · proof

Prove the erasure ran.

A signed receipt with anchor hashes that lets an auditor confirm the destroy happened without ever seeing what was destroyed.

CALL db.proveErasure("customer:c-77") YIELD receiptId, destroyedAt, anchorHistorySeq, anchorHistoryHash, ownChainHash, chainStatus RETURN receiptId, chainStatus;
Art. 20 · export

Per-subject data export.

Every node, edge, and version history the subject appears in — typed and shaped for direct download or handoff to the requester.

CALL db.subjectExport("customer:c-77") YIELD type, id, kind, payload RETURN type, id, kind, payload;
Audit · chain

Verify the ACL log.

Tamper-evidence check on the ACL audit log. If any entry has been rewritten between event id A and event id B, the call flags it — and tells you where.

CALL db.verifyAclChain() YIELD status, entries, brokenAtEventId, reason RETURN status, entries, brokenAtEventId, reason;
Audit · access

Replay ACL events.

Who tried to read what, when, and whether the engine allowed it. Filter by principal, since-id, or limit.

CALL db.aclEvents(0, "agent-42", 100) YIELD eventId, ts, principal, label, property, action, denied, reason RETURN ts, principal, action, denied ORDER BY eventId DESC;
Derivation

Follow the evidence.

Walk a value backwards through the DERIVED_FROM edges. RAG citation chains, inference trees, document lineage — all in one traversal.

CALL db.derivedFrom("belief-99", 5) YIELD strId, depth, kind RETURN strId, depth, kind ORDER BY depth;
Machine-readable corpus

Two files for your crawler.

InvariantDB serves an llms.txt index and a full plain-text corpus. Both are stable, versioned, and safe to include in a training or retrieval pipeline — they contain only the capability-level narrative and public API surface, never internals.

Ready-to-index.

/llms.txt is the H1+bulleted section index. /llms-full.txt is the concatenated corpus of every page linked from it. Both are declared in <link rel="alternate" type="text/plain"> so crawlers can discover them from any page on the site.

Connect your agent in one afternoon.

Free tier includes an MCP endpoint, a graph, and enough audit chain to run the demos on this page against your own data.