For AI agents

InvariantDB for AI agents

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-native · 28 tools Bitemporal · two clocks, first-class Belief revision + lineage Write provenance
The three questions

Querying what an agent knew

A belief is stored as a node with a claim, a confidence and links to its evidence. A revision creates a new belief linked to the old one. Temporal clauses on MATCH read the graph as of a past valid time or recorded time.

01
Knew?
Every belief is a queryable node with claim, confidence, and evidence pointers — not a log line.
02
When?
Two clocks, first-class. AT VALID for the world; AT RECORDED for the engine's knowledge.
03
Why changed?
New evidence records a successor with a reason. Old and new coexist; the chain is walkable.
MCP quickstart

Connect over MCP

Each graph has a JSON-RPC endpoint that Model Context Protocol clients can call. The engine lists 28 tools on tools/list. cypher_query runs any Cypher statement, including CALL db.* procedures. Setup for each client is on the MCP page.

invariantdb / mcp.quickstart protocol 2025-06-18
A · Graph endpoint
# One URL per graph. JSON-RPC 2.0 over HTTPS.
# Self-hosted: use your own server address.
POST https://invariantdb.com/graphs/{graph}/mcp
Authorization: Bearer $INVARIANTDB_TOKEN
Content-Type: application/json

# The server identifies itself as
# "invariantdb-mcp" on the initialize call.
B · MCP client config (HTTP transport)
{
  "mcpServers": {
    "invariantdb": {
      "type": "http",
      "url": "https://invariantdb.com/graphs/YOUR_GRAPH/mcp",
      "headers": {
        "Authorization": "Bearer gq_YOUR_API_KEY"
      }
    }
  }
}
Reading past states. Pass atTime to cypher_query to read the graph as of a past valid time; the query needs at least one top-level MATCH. For recorded time, or both, put AT VALID and AT RECORDED on the MATCH in the query instead; the two forms cannot be combined in one call. MCP clients that support OAuth can use OAuth sign-in instead of an API key.

Tools

Several tools run a fixed CALL db.* procedure; you can run the same procedure through cypher_query when you need other columns. Arguments and errors for each tool are on the MCP page.

cypher_queryRun a Cypher statement and return columns and rows. Accepts parameters, atTime, format and provenance.
get_node_by_strFetch a node by its string id.
get_edge_by_strFetch a relationship by its string id.
schema_infoList labels and relationship types with counts, and the source and target labels of each relationship type. Property names are not included.
list_indexesList single-property (range) indexes and their state.
list_graphsList the graphs you can access.
engine_infoReturn the graph’s current sequence number (it increases as saved changes become visible), the calling credential’s subject, tenant, roles and capabilities and, from 1.4.2, the name of the graph the connection is bound to.
search_fulltextKeyword (BM25) search over one text property of a label. Returns up to k hits as {node_id, score}. From 1.4.3, when a full-text index is declared on the label and property, it answers with phrases, Boolean and proximity connectors, exact totals, pages and snippets.
search_vectorNearest-neighbour search over a vector property, from query text or a precomputed vector. Returns up to k hits as {node_id, score}.
recallSearch saved memories by keyword and by meaning, best matches first, with document passages, saved facts and, from 1.4.3.1, memories linked through saved facts.
recall_as_ofSearch or list memories as they stood at a past valid time.
save_documentSave a document under a stable uri as its current version; earlier versions stay in history. Extracts facts when extraction is enabled on the server.
get_documentReturn the current version of a saved document, or the version current at a past time.
list_document_revisionsList the saved versions of a document, oldest first.
list_documentsList saved documents, most recently saved first (from 1.4.2.4).
find_documentFind saved documents whose title or uri contains a text, ignoring case (from 1.4.2.4).
set_ontologySet the categories and relationship types extraction uses on a graph.
describe_memoryShow the categories and relationship types set up for a graph and how many items each holds.
record_episodeAppend an entry to a session’s log.
replay_episodesReturn a session’s events oldest first, up to limit (default 50).
rememberSave one fact, decision or preference as a memory.
forgetNot available in this release; returns error -32006.
belief_lineageTrace a belief back through its revisions, one row per version.
property_provenanceShow each version of a property with its recorded attribution.
derived_fromFollow DERIVED_FROM relationships from a node to its sources.
acl_eventsList field reads that access rules denied in Cypher, and searches and node creates they refused.
subject_exportExport one data subject’s node, its relationships and its property versions (GDPR Article 20).
destroy_subject_keyDestroy one data subject’s encryption key (crypto-shred; in development).

Belief revision in Cypher

Belief revision in four Cypher statements, after a setup step that creates the evidence nodes. createdAt and revisedAt values are nanoseconds since the Unix epoch. The example data is fictional.

01 / RECORD

Form a belief.

The agent forms a claim; the engine stamps confidence and evidence pointers at record time and returns a belief id.

See the CALL →
02 / REVISE

Contradict without erasing.

Contradicting evidence arrives. The old belief is superseded, not overwritten. The successor gets its own id — the chain stays intact.

See the CALL →
03 / EXPLAIN

Answer the auditor.

Walk the lineage backward and filter by createdAt to list the evidence recorded for the agent up to a given day.

See the CALL →
invariantdb / belief-flow.cypher production
Setup — Evidence (fictional data)
// db.recordBelief refuses evidence ids that do not exist.
CREATE (:Episode {id: "episode-482", text: "KYC address check passed"}),
       (:Episode {id: "episode-491", text: "Payment history reviewed"});
Step 1 — Record
CALL db.recordBelief(
  "belief-99",
  "customer:c-77 risk = LOW",
  0.82,
  ["episode-482", "episode-491"]
)
YIELD beliefId
RETURN beliefId;
Step 2 — Revise
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;
Step 3 — Lineage
CALL db.beliefLineage("belief-99", 3)
YIELD beliefId, claim, confidence,
      createdAt, supersededAt, depth, evidence
RETURN beliefId, claim, confidence, depth
ORDER BY depth DESC;
Step 4 — The auditor question
// What did the agent know about c-77 on March 5?
CALL db.beliefLineage("belief-99", 3)
YIELD beliefId, claim, createdAt
WITH beliefId, claim, createdAt
WHERE createdAt <= 1772668800000000000  // 2026-03-05T00:00:00Z in nanoseconds
RETURN beliefId, claim, createdAt
ORDER BY createdAt DESC;
Receipts
belief-99 · LOW · 0.82r7a1…9cc2
recorded2026-02-28
revisionrev 1
evidenceep-482, ep-491
superseded2026-03-04
chain intact
belief-99 · HIGH · 0.91bd41…0f73
recorded2026-03-04
revisionrev 2
reasonaddress-mismatch doc
supersedesrev 1
both revisions remain in the history

Search and recall

Agents can find context by keyword (BM25) or by embedding (vector search), then follow relationships from each hit with Cypher.

Fulltext · BM25

Keyword recall, ranked.

Point gds.fulltextSearch at a (label, property) pair and a query string. Yield gives 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;
"Find every note where a bureau analyst flagged the address mismatch — then follow the edges to the beliefs it produced."
Vector · semantic

Semantic recall, ranked.

Pass the query vector to gds.vectorSearch. It yields nodeId, node, strId, score and, from 1.4.3.2, exact; the example uses nodeId to join back with MATCH.

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;
"Retrieve semantically-similar documents, then walk one hop to the customers who authored them."
Episodic memory

Session-scoped replay.

Each session has an event log. db.recordEpisode appends one event and creates the session on first use. db.replayEpisodes returns up to N events in time order, oldest first, with kind, timestamp, text and any payload the agent attached.

CALL db.recordEpisode("session-1", "observation",
  "customer address 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;
"Replay the events in this agent session in the order they happened."
Property provenance

Who wrote each value

When a write includes a provenance object, each property version it writes is recorded with actor, source, reason, requestId and confidence, and the server adds principal, the credential that made the write. Send it as the provenance argument of the MCP cypher_query tool, or as a body field or X-Provenance-* headers on the HTTP Cypher endpoints. Writes without it have null values in the five columns the caller supplies. From 1.4.2 the server records principal for every write of an authenticated request, with or without a provenance object; on 1.4.1 and earlier, only when the write includes one.

CALL db.propertyProvenance("c-77", "risk")
YIELD value, validFrom, validTo, version, txnAt,
      actor, source, reason, requestId, confidence, principal
RETURN version, value, actor, reason, principal ORDER BY version;
"Who wrote this value, from which source, with what confidence, and with which credential, for each version."
Bitemporal recall

Reading past states

Every fact carries two intervals. Add a temporal clause to a MATCH and the pattern, including its relationships, is read as of that time.

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. Check training data for facts recorded after the cutoff.

AT RECORDED '2026-03-04T09:00:00Z'
invariantdb / auditor.cypher production
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?
// Fictional data: assumes Customer nodes created with an id
// property and linked to Belief nodes by HAS_BELIEF.
MATCH (c:Customer {id: "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;

// 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;
Receipt
c-77 · risk = LOWr7a1…9cc2
valid2026-03-05
recorded2026-03-04
revisionrev 1 · not yet superseded
evidence2 episodes
answer preserved exactly as the agent saw it
c-77 · risk = HIGHbd41…0f73
valid2026-03-05
recorded2026-06-01 (later)
revisionrev 2
reasonaddress-mismatch doc
today's answer; the earlier one stays in the history

Export, erasure and access audit

Procedures for GDPR Article 20 export and for checking the access-control log. Crypto-shred for Article 17 erasure is in development.

Art. 17 · erasure

Crypto-shred a subject (in development).

Crypto-shred (per-subject encryption keys and key destruction) is in development. On the current release, declaring an encrypted property is refused by default, and the call is refused without recording a receipt. Contact us before relying on it. destroyedAt is in nanoseconds since the Unix epoch.

CALL db.destroySubjectKey("customer:c-77")
YIELD subjectId, destroyedAt, receiptId
RETURN subjectId, destroyedAt, receiptId;
"Destroy this customer's encryption key and record a receipt."
Art. 17 · receipts

Read the erasure receipts (in development).

db.proveErasure returns the receipts recorded for a subject, each anchored to a history-log position and hash, and re-checks the receipt chain. The receipts are hash-chained, not signed. The call does not examine the graph’s data, so a clean result does not show that any value is unreadable.

CALL db.proveErasure("customer:c-77")
YIELD receiptId, destroyedAt,
      anchorHistorySeq, anchorHistoryHash,
      ownChainHash, chainStatus
RETURN receiptId, chainStatus;
"List the key-destruction receipts for this customer."
Art. 20 · export

Per-subject data export.

The subject’s node, its relationships and its property versions, one row each with a JSON payload. On a graph with no declared encrypted property it matches only the node whose string id equals the argument. A subject id held only in a property is not matched, and nodes created with Cypher CREATE or MERGE get a generated string id. An empty result does not show that the graph holds nothing about the subject.

CALL db.subjectExport("customer:c-77")
YIELD type, id, kind, payload
RETURN type, id, kind, payload;
"Export the data linked to this customer for their access request."
Audit · chain

Verify the ACL log.

Re-checks the hash chain of the ACL event log and reports the first event whose hash no longer matches. The log is not signed.

CALL db.verifyAclChain()
YIELD status, entries, brokenAtEventId, reason
RETURN status, entries, brokenAtEventId, reason;
"Has any entry in the ACL event log been changed? If yes, at which event id?"
Audit · access

Replay ACL events.

Field reads that access rules denied in Cypher, and the searches and node creates they refused, with the credential, property, time and reason. Allowed access is not logged. Filter by since-id, principal or limit.

CALL db.aclEvents(0, "apikey:key_123", 100)
YIELD eventId, ts, principal, label,
      property, action, denied, reason
RETURN ts, principal, action, denied
ORDER BY eventId DESC;
"Which field reads by this credential did access rules deny?"
Derivation

Follow the evidence.

Walk a value backwards through the DERIVED_FROM edges. Use it for citation chains and document lineage. The argument is the node’s string id (for a belief, belief: plus its belief id), a value of the node’s id property (from 1.4.2.2), or id(n); from 1.4.2.2 a reference that names no node is refused with LINEAGE_NOT_FOUND.

CALL db.derivedFrom("belief:belief-99", 5)
YIELD sourceStrId, depth, kind
RETURN sourceStrId, depth, kind ORDER BY depth;
"Which citations, documents, and sub-inferences produced this final claim — five hops deep, in order?"
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. /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.

MCP28 tools on tools/list. Protocol versions 2025-06-18, 2025-03-26 and 2024-11-05. JSON-RPC 2.0 over HTTPS.
Valid and recorded timeAT VALID and AT RECORDED clauses on MATCH.
Belief revisionA revision creates a new belief linked to the old one. Evidence ids are available through db.beliefLineage.
Subject exportArt. 20 per-subject export. Crypto-shred (per-subject encryption keys and key destruction) is in development.
Get started

Connect your agent

The free tier includes an MCP endpoint and a graph.