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 →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.
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.
AT VALID for the world; AT RECORDED for the engine's knowledge.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.
# 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.
{
"mcpServers": {
"invariantdb": {
"type": "http",
"url": "https://invariantdb.com/graphs/YOUR_GRAPH/mcp",
"headers": {
"Authorization": "Bearer gq_YOUR_API_KEY"
}
}
}
}
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.
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 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.
The agent forms a claim; the engine stamps confidence and evidence pointers at record time and returns a belief id.
See the CALL →Contradicting evidence arrives. The old belief is superseded, not overwritten. The successor gets its own id — the chain stays intact.
See the CALL →Walk the lineage backward and filter by createdAt to list the evidence recorded for the agent up to a given day.
See the CALL →// 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"});
CALL db.recordBelief( "belief-99", "customer:c-77 risk = LOW", 0.82, ["episode-482", "episode-491"] ) YIELD beliefId RETURN beliefId;
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;
CALL db.beliefLineage("belief-99", 3) YIELD beliefId, claim, confidence, createdAt, supersededAt, depth, evidence RETURN beliefId, claim, confidence, depth ORDER BY depth DESC;
// 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;
Agents can find context by keyword (BM25) or by embedding (vector search), then follow relationships from each hit with Cypher.
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;
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;
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;
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;
Every fact carries two intervals. Add a temporal clause to a MATCH and the pattern, including its relationships, is read as of that time.
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'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'// 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;
Procedures for GDPR Article 20 export and for checking the access-control log. Crypto-shred for Article 17 erasure is 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;
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;
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;
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;
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;
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;
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.
The free tier includes an MCP endpoint and a graph.