MCP Tools
Quipu exposes its API as MCP (Model Context Protocol) tools for agent
integration, from its own MCP server: quipu mcp --db <path> over stdio, or
quipu-server at /mcp over HTTP. (Bobbin embeds Quipu but serves its own
knowledge_* tools, not these.)
The registry (tool_definitions()) exposes 77 tools in a default build, or
79 when built with the owl feature (which adds quipu_load_ontology and quipu_explain).
(The counts are pinned by tests in src/mcp/tests.rs, which also check this
page and the README against the manifest.)
Connect directly
The server includes the native mcp feature. Build the CLI and its companion:
cargo build --release --features full --bin quipu --bin quipu-server
claude mcp add quipu -- /absolute/path/to/target/release/quipu mcp --db /absolute/path/to/store.db
quipu mcp starts the sibling quipu-server --mcp-stdio with the same configuration
and store initialization as HTTP mode. It opens no network listener. On Unix it
replaces the CLI process, so signals and EOF reach the server directly. Install
both binaries together. Protocol output uses stdout; diagnostics use stderr.
For an already running server, register its /mcp URL as streamable HTTP. Calls
are stateless: a server restart does not leave stale MCP sessions. The HTTP caller
supplies the same service-specific bearer used for REST writes. For stdio with
protected writes, pass --mcp-token-file /absolute/path/to/private-token; the file
must be regular, at most 4096 bytes, and private (0600 or 0400 on Unix). Credentials
are never accepted in tool arguments or forwarded from a claimed actor field.
Unconfigured local stores retain the CLI’s existing local authority; configuring
write authentication applies to stdio too. Read-only mode refuses writes even
with a valid credential. No existing shared credential or open REST read changes.
Every tool dispatches through the existing REST application in process, sharing
its authentication, dataset selection, read pools, validation, and committed
transaction attribution. The schemas come from tool_definitions(); the native
transport does not maintain a second schema catalogue. quipu_graph_list preserves
its query filters. Tool failures are MCP error results with the REST error body.
Natural-language quipu_search still requires the configured embedding provider;
a precomputed embedding works without one. The transport does not download models.
Browser MCP requests require an Origin explicitly present in
quipu.server.cors_allowed_origins; no Origin is normal for native agent clients.
The REST origin policy remains unchanged. Tool requests are limited to 64 MiB.
Bobbin’s knowledge_* tools and existing Homelab quipu_* proxies remain supported
compatibility surfaces. Native MCP lets an installation use Quipu without either
proxy. The Homelab proxy continues to call REST; Bobbin keeps its existing library
integration. Register the intended server explicitly to avoid ambiguous names;
this addition does not remove an existing MCP entry or its credentials.
Run just mcp test for HTTP and stdio protocol acceptance against isolated stores,
including concurrent named/shared credentials and read-only refusal. Set
QUIPU_MCP_TEST_MODEL_DIR to a model directory containing onnx/model.onnx and
tokenizer.json, plus ORT_DYLIB_PATH as needed, to test natural-language search
with a real local embedding provider. Otherwise the test checks vector search and
the explicit missing-provider error separately.
Tool Reference
quipu_query
Execute a SPARQL SELECT query.
| Parameter | Required | Description |
|---|---|---|
query | Yes | SPARQL query string |
valid_at | No | ISO-8601 timestamp for time-travel |
tx | No | Transaction ID for time-travel |
graph | No | Named-graph IRI or dataset name scoping the default graph (unknown IRI → empty default graph, never ROOT) |
fork | No | Fork name to read (see quipu fork); unknown/dropped forks are refused; mutually exclusive with graph |
include_kinds | No | dataKind tokens (e.g. ["archive"]) that widen the default graph set with every graph declaring one of them. Absent/empty = unchanged scope; malformed tokens are refused; mutually exclusive with fork; a FROM in the query text still overrides |
verbose | No | Return full IRIs instead of the default CURIE-compacted values |
Query results use prefixes declared by the loaded shape sets for compact CURIE values by default. IRIs in unknown namespaces remain full IRIs.
quipu_rule_query
Read one grounded claim using independent positive and explicit-negative evidence. The result preserves supported, refuted, both and neither, with a separate admitted judgment disposition. Failed, capped or incomplete reads return UNKNOWN; answers never grant action authority. See rule answers for strict request fields, evidence scope and retained judgment requirements.
| Parameter | Required | Description |
|---|---|---|
subject | Yes | Absolute subject IRI |
positive_predicate | Yes | Absolute predicate for explicit positive support |
negative_predicate | Yes | Distinct absolute predicate for explicit negative support |
object | Yes | One strict IRI or plain-string value object |
judgment_policy | Yes | Expected admitted judgment policy |
witness_limit | No | Evidence row bound per side, 1–256 (default 64) |
quipu_export
Export deterministic RDF from ROOT, a named graph, an episode provenance group, or a SPARQL graph query. Scope parameters are mutually exclusive.
| Parameter | Required | Description |
|---|---|---|
graph | No | Named-graph IRI to export (omit for ROOT; unknown IRI is an error) |
group_id | No | Export ROOT entities attributed to this episode group |
construct | No | SPARQL CONSTRUCT or DESCRIBE query whose graph is exported |
format | No | turtle (default) or ntriples |
quipu_align_propose
READ. Proposes candidate cross-graph alignments between two named graphs as a
scored SSSOM mapping set, and returns the expected_version that
quipu_align_apply requires.
Refuses an unknown graph IRI rather than returning zero candidates: align::enumerate
returns an empty enumeration for an IRI it cannot look up, so a typo would otherwise
come back as 0 candidates — indistinguishable from two graphs that genuinely share
nothing.
quipu_align_decide
READ. Applies operator accept / negate verdicts to a proposed mapping set.
Touches no store. Returns the decided set and the expected_version to carry into
quipu_align_apply.
quipu_align_apply
WRITE. Materialises decided alignments as owl:sameAs / quipu:distinctFrom in an
alignment graph derived from the two source graphs, creating that graph if needed.
expected_version is required and must be carried from the decision being applied.
It is not computed here: set_version hashes the mapping set itself, so deriving the
version at apply time would hash the set about to be written, always match, and silently
discard a concurrent operator’s decision.
These are three separate tools rather than one with a mode because an MCP client judges
a tool by its annotation. A moded tool would carry a single, necessarily destructive
annotation, and every read-only call — including propose, the entry point — would be
refused under a no-approval policy.
quipu_merge_decisions
READ. Lists the conflicts of merging an incoming share into ROOT: one row per slot
with the base / ours / theirs values, the slot’s max_count, and each side’s
provenance. The rows are bound to ROOT’s graph hash and the incoming share id. With
propose: true, each row also gets a mechanical proposal (choose plus evidence).
It never sets a decision and writes nothing.
incoming and base are shares inline, in the shape /import takes (manifest,
export_ntriples, shapes_turtle). Both are verified by hash, and base must be the
incoming share’s parent_share. A server path is never accepted. REST:
POST /merge/decisions. CLI: quipu merge <share-dir> --emit-decisions <file> [--propose].
quipu_merge_apply
WRITE. Finishes the merge from the decisions file with each row’s decision set
({"choose": "ours"|"theirs"|"base"} or {"values": [<N-Triples terms>]}). It commits
the clean part of the merge plus every decided slot in one transaction. The
transaction’s source records both parents, the reviewer, and the SHA-256 of the
decisions.
It refuses, and writes nothing, on an undecided row, stale decisions (ROOT or the share
moved since they were emitted), more values than the slot’s sh:maxCount, or a value
that is not an RDF term. It also refuses a field merge-decisions/v1 does not
define. dry_run: true runs every check and returns the counts it would write,
without writing. REST: POST /merge/apply. CLI: quipu merge <share-dir> --decisions <file> --reviewer <who> [--dry-run].
quipu_knot
Assert facts from Turtle data, with optional SHACL validation.
| Parameter | Required | Description |
|---|---|---|
turtle | Yes | RDF Turtle data |
blank_node_scope | No | Explicit document identity for blank nodes; same ID shares only byte-identical input across graphs, distinct IDs separate repeat loads |
timestamp | No | Transaction-time: when this store came to believe the facts (defaults to now) |
valid_from | No | Valid-time: when the facts became true of the world. RFC 3339, normalised to UTC Z. Omit to reuse timestamp |
actor | No | Who is asserting |
source | No | Where the facts came from |
shapes | No | SHACL Turtle for validation gate |
graph | No | Registered committed-graph IRI to write into; unknown IRIs error, overlays refused; omit for ROOT |
replace_snapshot | No | Replace this producer’s prior facts (diffed), scoped to the target graph |
snapshot | No | Stable producer key required by replace_snapshot |
Returns: transaction ID, fact count, the normalised valid_from actually
stored, and whether validation passed.
timestamp and valid_from are the store’s two time axes and they answer
different questions — valid_from is queried with valid_at, timestamp with
tx/as_of_tx. A commit authored in March and ingested tonight wants
valid_from in March and timestamp tonight; passing only timestamp
collapses both, which is what this surface did before aegis-sb8of5.
Valid-time is compared as text, so valid_from is normalised to
YYYY-MM-DDTHH:MM:SSZ: a UTC offset is applied (git’s %aI emits the author’s
local offset, and 2026-09-07T00:30:00+01:00 would otherwise sort after
2026-09-06T23:45:00Z, though it is earlier) and sub-second precision is
dropped (.5Z sorts before Z). A malformed value is refused before the
vocabulary gate, the SHACL pass, or any transaction — nothing is written.
replace_snapshot retractions are not back-dated: a replaced fact’s
valid_to is the transaction stamp, because “these stopped being true in March”
is a different claim from “we replaced them tonight”.
When replacement deletes an entity, a reference from another producer preserves
its label, but not its stale type membership. Code snapshot keys
code:<repo>:<partition> share a producer boundary with code:<repo> and all
partitions of that repository. Their sibling references do not preserve a deleted
label: a promote replaces keys in separate transactions, so sibling references
can still be stale. References from another repository or producer still preserve
the label. Other snapshot keys retain exact-key ownership semantics.
quipu_cord
List entities with optional filtering.
| Parameter | Required | Description |
|---|---|---|
type | No | Filter by rdf:type IRI |
predicate | No | Filter by relationship |
limit | No | Max results (default: 100) |
quipu_unravel
Time-travel query: view facts at a past state.
| Parameter | Required | Description |
|---|---|---|
tx | No | Transaction ID |
valid_at | No | ISO-8601 timestamp |
At least one of tx or valid_at must be provided.
quipu_validate
Dry-run SHACL validation without writing.
| Parameter | Required | Description |
|---|---|---|
shapes | Yes | SHACL shapes as Turtle |
data | Yes | Data to validate as Turtle |
Returns: conforms boolean, plus arrays of violations, warnings, and informational issues.
quipu_shapes
Manage persistent SHACL shapes that auto-validate writes.
| Parameter | Required | Description |
|---|---|---|
action | Yes | load, list, or remove |
name | For load/remove | Shape set identifier |
turtle | For load | SHACL Turtle content |
timestamp | No | Timestamp for load |
quipu_retract
Retract ROOT/default-graph facts for an entity. Named-graph facts are untouched;
supplying graph, including null, is refused before mutation.
| Parameter | Required | Description |
|---|---|---|
entity | Yes | Entity IRI to retract |
predicate | No | Only retract this predicate |
timestamp | No | Retraction timestamp |
actor | No | Who is retracting |
quipu_set
Atomically set (entity, predicate) to exactly one value: retracts every
current object on that predicate and asserts the new one in a single
transaction — the supersede primitive. Single-value semantics: to add without
removing, assert via quipu_knot.
This operation affects only ROOT/default-graph facts. Named-graph facts are
untouched, and supplying graph, including null, is refused before mutation.
| Parameter | Required | Description |
|---|---|---|
entity | Yes | IRI of the entity (must exist) |
predicate | Yes | Predicate IRI to set (may be new) |
value | Yes | Bare string = literal; {"iri": …} for an edge; typed forms for int/float/bool/lang/datatype |
timestamp | No | ISO-8601 valid-time for the supersede |
actor | No | Who is performing the set |
quipu_retract_episode
Episode-scoped logical retraction (POST /episode/retract). Retracts the
facts an episode’s ingest contributed (activity node, entities, edges, reified
statements) by closing valid_to — logical, not physical, so time-travel history
is preserved. Entities and other episodes’ facts (even about shared IRIs) are
untouched. Idempotent.
By default (on_orphan: "preserve") it does not retract every currently-active
fact: it keeps rdfs:label / rdf:type alive for nodes that other episodes still
reference, so scope retraction cannot leave a node visible to predicate queries but
invisible to label/type scans.
| Parameter | Required | Description |
|---|---|---|
episode | Yes | Episode name to retract (aliases: episode_id, name) |
timestamp | No | Retraction timestamp |
actor | No | Who is retracting |
on_orphan | No | preserve (default) | refuse (reject if it would orphan identity) | allow (retract everything). Alias: orphan_policy |
Response: tx_id, retracted, episode, statements, plus identity accounting —
on_orphan, identity_preserved (+identity_preserved_statements), and
identity_orphans (+identity_orphan_entities).
Retraction is a more sensitive write than assertion. The endpoint honours read-only mode and bearer auth today; when per-principal scopes (hq-azs) and crew identity (hq-otm) land it should require an authorized principal.
quipu_retract_source
Identical facts asserted by different sources retain independent ownership.
Repair removes only the selected source’s claims in the selected graph; a fact
remains visible until its last owner retracts it. Repeating an assertion under
the same source is idempotent. The reported retracted count is the number of
source-owned statements processed, not the number that disappeared from the
logical graph. Historical assertions remain in the audit log.
The ownership fix is forward-only for newly recorded claims. Older versions skipped identical assertions from later producers, so those historical claims cannot be reconstructed. Reassert all relevant producer snapshots before retracting old shared facts; this release does not backfill ownership.
Retract-only repair of facts owned by a legacy transaction source
(POST /retract/source).
Retraction in the store is source-scoped: it closes every currently-live fact
whose transaction source equals a given string. quipu_knot composes its own
tag as snapshot:<key>, so a producer can only ever clear what it wrote under
that scheme — facts written under any other source string (a free-form producer
string, a hand-run CLI promote) were unreachable by any retraction, permanently.
This tool names the source explicitly and clears it.
It is deliberately not a raw source-tag input on quipu_knot. There, one tag
stamps a transaction that carries both retractions and new assertions, so a raw
tag would let any caller write facts attributed to any producer. Here there is no
turtle parameter at all, so the transaction cannot carry an assertion and
impersonation is impossible by construction rather than by discipline.
| Parameter | Required | Description |
|---|---|---|
source | Yes | EXACT transaction source string to retract — matched literally, never by prefix or pattern |
repair | Yes | Ticket or reason; stamped on the retraction transaction as repair:<ticket>, never the source being cleared |
apply | No | Default false = plan only, nothing is written |
expect | With apply | The planned count you are confirming; a mismatch is refused |
graph | No | Registered committed-graph IRI; absent targets ROOT (same rules as quipu_knot) |
timestamp | No | Retraction timestamp |
actor | No | Who is performing the repair |
Response: source, graph, planned, entities, applied, tx_id,
retracted, repair_source, sample (+ sample_truncated), and — on an
applied call — remaining, a fresh read of the post-state rather than an
echo of the request.
Two properties worth knowing before using it:
planned: 0is a real answer. It means the named source owns no live facts.quipu_knotreportsreplaced: true, count: 0both for a retraction that deletes nothing and for one that empties a graph. Readplannedto distinguish those cases.- Re-keying order is retract FIRST, then re-promote. The store dedups an identical triple to one row carrying one source, and the existence check ignores the transaction source — so asserting canonically first is skipped as a duplicate, every row keeps its legacy source, and the retraction then removes everything.
quipu_episode
Ingest structured agent knowledge as an episode.
| Parameter | Required | Description |
|---|---|---|
name | Yes | Episode identifier |
episode_body | No | Natural language description |
source | No | Source agent/system |
group_id | No | Provenance label for the episode (not an isolation boundary — see Episodes) |
nodes | No | Array of {name, type, description, properties} |
edges | No | Array of {source, target, relation} |
quipu_search
Semantic vector search over entity embeddings. Supply either a natural-language
query (auto-embedded when an EmbeddingProvider is attached) or a pre-computed
embedding vector. At least one is required.
| Parameter | Required | Description |
|---|---|---|
query | No | Natural-language query (auto-embedded; alternative to embedding) |
mode | No | semantic (default) or opt-in keyword (SQLite FTS5 BM25; requires query text, enabled and backfilled index; no embedding provider) |
embedding | No | Float array (query vector); takes precedence over query |
limit | No | Max results (default: 10) |
ranking | No | semantic (default) preserves cosine order; opt-in content demotes contentless repository artifacts |
valid_at | No | Temporal filter |
graph | No | Registered graph IRI, or all; omitted scope searches ROOT |
graphs | No | Nonempty list of registered graph IRIs; search their union |
all_graphs | No | Search ROOT and all registered named graphs, excluding graph metadata |
verbose | No | Return full entity IRIs instead of the default CURIE-compacted values |
Requires an embedding provider when called with query and no embedding;
without one it errors naming the missing [quipu.embedding] configuration.
The response carries an embeddings block (configured, embedded_entities)
so zero results are distinguishable from an unembedded store — see
Embeddings and Semantic Search.
| group_ids | No | Best-effort filter to entities from these provenance groups (episode-scoped label, not an isolation boundary; /knot facts are ungrouped and dropped from a group scope) |
| entity_type | No | Restrict to entities of this rdf:type IRI |
Results include raw similarity, adjusted score, and ranking_reason.
Graph selectors are mutually exclusive; unknown graphs are refused. Explicit
scope results carry graph and graphs. Semantic scope selects entity
membership before the result limit on the built-in SQLite backend. Other
vector backends use bounded best-effort oversampling. Embeddings remain per entity: ROOT text
is retained for entities with ROOT facts; other entities combine their
named-graph facts. This is membership scoping, not per-graph embedding text.
Explicit graph scope supports semantic and keyword ranking; ROOT content and
anchor reranking are refused with it.
Explicit graph selection in semantic and hybrid search requires
[quipu.search] named_graphs = true. The default is false. Prepare named-only
vectors with bounded backfill before enabling it; the flag also gates automatic
named-only embedding text. Keyword mode does not need it: the lexical index
records each assertion’s graph, so keyword search accepts an explicit scope
whenever keyword = true and the index is complete. Hybrid with alpha = 0
is pure keyword ranking and follows the keyword rule.
Keyword mode returns score (positive relevance, higher first), raw SQLite
bm25 (lower first), and ranking_reason: "keyword". It indexes literal terms
and quoted phrases from labels, alternate labels, descriptions including full
episode bodies, other literal attributes, type names, and entity IRI local-name
tokens. Phrase/term conjunctions currently match one fact document; structured
cross-attribute expressions belong to the structured-query stage. The default
semantic response and scores are unchanged. Keyword mode rejects embeddings,
anchors and content ranking rather than silently ignoring them.
Activate [quipu.search] keyword = true, then run explicit bounded
quipu search-index backfill --batch-size 500 --db <path> calls until status
reports complete: true. Search refuses an incomplete index. Each call commits
one batch and releases the writer; the CLI refuses UTC minutes 10 through 20
to protect scheduled ingestion. See keyword index.
See search ranking for content criteria, exact-name
exceptions, temporal behavior, and bounded candidate recall.
Anchored search (anchor, max_hops, anchor_mode, decay, via,
direction, explain) roots the search on one entity and ranks by hop
distance. It is off unless the server sets [quipu.search] anchored = true. See
anchored search.
quipu_hybrid_search
Combined SPARQL filtering + vector ranking. Supply either a natural-language
query (auto-embedded) or a pre-computed embedding; the sparql pre-filter is
optional.
| Parameter | Required | Description |
|---|---|---|
query | No | Natural-language query (auto-embedded; alternative to embedding) |
embedding | No | Float array (query vector); takes precedence over query |
sparql | No | SPARQL pre-filter query (enables predicate pushdown) |
limit | No | Max results (default: 10) |
valid_at | No | Temporal filter |
Requires an embedding provider when called with query and no embedding;
without one it errors naming the missing [quipu.embedding] configuration.
The response carries an embeddings block (configured, embedded_entities)
so zero results are distinguishable from an unembedded store — see
Embeddings and Semantic Search.
For the built-in SQLite backend, an explicit SPARQL scope is scored before selecting the top results. Narrow scopes to at most 1,000 candidate rows, resolved entity IDs, and eligible embedding rows; larger scopes return an error. Historical ranking returns the text from the exact embedding version scored. Query and stored vectors must contain finite values with matching dimensions; the SQLite scoped path accepts up to 16,384 dimensions. An empty eligible scope returns no results. SPARQL candidates use the requested valid time and a bounded query budget. Unscoped ranking and delegated backend ranking retain their existing implementation; candidate-before-top-K is a SQLite guarantee. Scoring reads the local SQLite vector table, including rows keyed by composed alias IDs; it does not scan vector tables in attachments. The candidate, scoring, and text reads do not provide one snapshot across concurrent writes.
quipu_graph
Project the knowledge graph into a render-ready node-link payload in one response: nodes (IRI, label, type, degree), index-addressed edges, and a type census. Episode/provenance scaffolding is excluded by default; nodes are ranked by degree and capped, and the response states what was dropped.
| Parameter | Required | Description |
|---|---|---|
limit | No | Max nodes, ranked by degree (default 250, hard max 2000) |
type | No | Restrict to nodes of this rdf:type IRI |
include_episodes | No | Include prov:Activity episode nodes (default false) |
quipu_project
Graph projection and algorithms.
| Parameter | Required | Description |
|---|---|---|
algorithm | No | stats, in_degree, pagerank/ppr, components, louvain, or shortest_path (default: stats) |
type | No | Restrict projection to this rdf:type IRI |
predicate | No | Restrict projection to edges with this predicate IRI |
graph | No | Project one named graph’s own facts instead of ROOT — cheap against a small derived layer even when the episode log is large |
limit | No | Max results for in_degree/pagerank (default: 20) |
seeds | No | Seed entity IRIs for personalized PageRank (non-empty switches pagerank to PPR) |
damping | No | PageRank damping factor (default: 0.85) |
max_iters | No | PageRank max iterations (default: 100) |
tolerance | No | PageRank convergence tolerance (default: 1e-6) |
from / to | No | Source/target entity IRIs for shortest_path |
persist | No | louvain: persist quipu:memberOfCommunity facts; pagerank (global runs only — a seeded run refuses): persist quipu:pageRank scores. Both supersede any prior derivation (default: false). Communities are emergent clustering, not an access boundary. |
The louvain algorithm runs deterministic modularity-based community detection
and returns { communities: [{ community, entities, size }], modularity }.
Read-only unless persist: true.
quipu_context
Unified knowledge context pipeline.
| Parameter | Required | Description |
|---|---|---|
query | Yes | Search query string |
max_entities | No | Max entities (default from pipeline config) |
expand_links | No | Follow relationships to linked entities |
ppr_rerank | No | Re-order candidates by Personalized PageRank seeded at the direct hits before truncation (default: false) |
The summary includes an embeddings block (configured,
embedded_entities) reporting whether semantic retrieval was possible.
quipu_report
Live graph report — graphify’s GRAPH_REPORT.md equivalent, but queryable.
Read-only.
| Parameter | Required | Description |
|---|---|---|
type | No | Restrict the projection to this rdf:type IRI |
predicate | No | Restrict the projection to edges with this predicate IRI |
hubs | No | Number of top hubs to return (default: 10) |
surprises | No | Number of surprising connections to return (default: 10) |
questions | No | Number of suggested questions to return (default: 8) |
Returns three sections:
hubs— “god-nodes”: the most central entities by PageRank, each with itsin_degreeas a secondary signal.surprising_connections— low-prior edges that bridge two otherwise-separate Louvain communities. Rarer bridges (fewer edges crossing between the same two communities —bridge_rarity) rank first; ties break toward bridges touching higher-PageRank endpoints.suggested_questions— deterministic, template-generated prompts seeded by the hubs and bridges above.
Plus a graph summary (nodes, edges, communities, modularity).
Communities here are emergent clustering for surfacing, not an access
boundary.
quipu_policy_check
Committed-tier evaluation of a governance Policy over the graph of record.
Evaluates the policy’s aegis:claim (a SPARQL ASK, optionally with a $target
placeholder) and returns a Verdict — outcome ∈ satisfied | unsatisfied | unknown bound to a reproducible evidence_hash. Deterministic: any verifier
re-running the same ASK over the same committed evidence gets the same verdict
(checked, not trusted). The verdict is returned unsigned unless the store
has a signing identity attached.
Stored claim and evidenceProbe properties are read under both vocabulary
namespaces. Identical alias values collapse; conflicting values are refused.
The stored query text, target identity, and signature inputs are preserved.
| Parameter | Required | Description |
|---|---|---|
policy | One of policy/claim | Policy IRI whose aegis:claim to evaluate |
claim | One of policy/claim | Inline SPARQL ASK claim |
target | Yes | Target IRI bound to the $target placeholder |
predicate_id | No | Predicate identifier recorded in the verdict (inline claims; default inline) |
evidence_probe | No | Inline ASK for “does the evidence exist?” — false yields unknown |
valid_at | No | ISO-8601 point-in-time for valid-time evaluation |
quipu_verdict_verify
Verify a signed Verdict against the Phase-0 root of trust: the signature must
be valid under the verifier’s registered public key, and the verifier must
be authorized to attest the predicate. trusted is the conjunction — the
property a consumer should gate on.
Registration classes and verifier/key predicates are read under both vocabulary namespaces. Conflicting registered keys are refused, rather than choosing one; duplicate aliases carrying the same key are accepted.
| Parameter | Required | Description |
|---|---|---|
predicate_id | Yes | Predicate the verdict attests |
target_ref | Yes | Target the verdict is about |
outcome | Yes | Verdict outcome |
evidence_hash | Yes | Evidence hash the signature seals |
tier | No | Evidence tier (default: committed) |
verifier | Yes | Verifier IRI whose registered key verifies the signature |
signature | Yes | Signature over the verdict message: hex for ed25519; base64url assertion signature for webauthn-*; the armored SSH SIGNATURE for sshsig-sk-ed25519 |
verdict | No | IRI of the stored verdict carrying this signature: verify as of when the store recorded it (use this for trust decisions) |
signed_at | No | Explicit valid-time instant (a what-if query; default now) |
tx | No | Explicit transaction to verify as of (a what-if query; default latest) |
scheme | No | ed25519 (default), webauthn-es256, webauthn-eddsa, or sshsig-sk-ed25519 |
authenticator_data | WebAuthn | base64url authenticatorData |
client_data_json | WebAuthn | base64url clientDataJSON |
The registry is read as of the signature (signing-plane S1): a key that
has since been rotated still verifies what it signed while registered, and
cannot verify anything recorded after it was closed. The result’s
as_of.basis is recorded, caller-supplied or now. A caller-supplied
instant is a what-if: trusted is then always false, and the answer is in
would_verify_as_of_supplied_instant. Otherwise naming a transaction from
before a revocation would make a revoked key read as trusted.
Hardware schemes are off by default ([quipu.governance] hardware_verdict_schemes); while off, a verdict naming one is refused. A
hardware verdict verifies only against a registration declaring the same
aegis:signatureScheme, and is trusted only when that same registration
attests the predicate. WebAuthn registrations carry the base64url COSE key in
aegis:publicKey plus aegis:webauthnRpId and aegis:webauthnOrigin; SSHSIG
registrations carry an OpenSSH sk-ssh-ed25519@openssh.com public key line.
The WebAuthn challenge is derived by quipu from the verdict message, never
taken from the caller, and SSHSIG must use namespace quipu-verdict. The
response’s sign_count is the authenticator counter to record as the
registration’s aegis:signCount. Once a nonzero counter is recorded, a
signature whose counter does not exceed it (including 0) is refused as a
possible clone; a credential that has only ever reported 0 is accepted.
WebAuthn requires user verification (UV); SSHSIG requires user presence and
reports UV without requiring it.
See src/verdict_schemes/mod.rs for the full rule list.
The as-of rule binds every scheme: a hardware verdict is checked against
the registrations, grants and recorded aegis:signCount in effect at the
same instant, and a caller-supplied instant leaves it untrusted too.
quipu_verifier_authorized
Check the Phase-0 verifier registry: may this verifier attest this predicate? The discovery half of the governance gate. Registration classes and each verifier/attests predicate may independently use the legacy or Quechua vocabulary.
| Parameter | Required | Description |
|---|---|---|
verifier | Yes | Verifier IRI |
predicate | Yes | Predicate IRI to attest |
signed_at | No | Valid-time instant to check at (default now) |
tx | No | Transaction to check as of (default latest) |
quipu_cooccurrence
Deterministic, auditable work-item co-occurrence: given a work-item (Bead)
IRI, returns the other work-items that share at least one touched code entity
via the provenance chain Bead ←implements− GitCommit −modifies→ entity.
A graph query over typed provenance edges, ordered by overlap strength. Each
implements/modifies edge may use the legacy or Quechua vocabulary independently;
duplicate aliases do not inflate shared-entity counts.
| Parameter | Required | Description |
|---|---|---|
work_item | Yes | Work-item (Bead) IRI |
valid_at | No | ISO-8601 point-in-time for valid-time filtering |
tx | No | Maximum transaction ID to consider |
quipu_overlay_create
Register an overlay-class named graph bound (bind-once) to a committed parent branch. Overlays are scratch layers over the committed graph: write hypotheses into an overlay, read the composed view, and the committed layer stays untouched.
| Parameter | Required | Description |
|---|---|---|
overlay | Yes | Overlay graph IRI to register |
parent_branch | No | Committed parent-branch IRI (omit for ROOT) |
quipu_overlay_write
Write one overlay primitive: assert, retract, or tombstone a triple in an
overlay graph. Tombstone masks the parent branch’s fact in the composed view
without touching the committed layer.
| Parameter | Required | Description |
|---|---|---|
overlay | Yes | Overlay graph IRI |
op | Yes | assert, retract, or tombstone |
subject | Yes | Subject IRI |
predicate | Yes | Predicate IRI |
object | Yes | Object value (IRI string, literal, or typed JSON value) |
timestamp | No | ISO-8601 valid-time (default: now) |
quipu_overlay_compose
Resolve an overlay’s composed view over [overlay > parent-branch-root].
Read-only. Two precedence modes: nearest (default, the scratch-layer read —
asserted-and-not-tombstoned, nearest wins) and governed (the
quarantine-plane read — the parent’s facts always win: an overlay value on a
same-subject-same-predicate slot the parent claims is suppressed, and an
overlay tombstone cannot mask a parent fact; it only masks the overlay’s own
contributions).
| Parameter | Required | Description |
|---|---|---|
overlay | Yes | Overlay graph IRI |
precedence | No | nearest (default) or governed |
quipu_search_nodes
Search for entities by natural-language query (text matching on names, labels,
and values). Replaces Graphiti’s search_nodes.
| Parameter | Required | Description |
|---|---|---|
query | Yes | Natural-language search query |
group_ids | No | Best-effort filter to entities from these provenance groups (episode-scoped label; /knot facts are ungrouped) |
max_results | No | Max results (default: 10) |
entity_type_filter | No | Filter by rdf:type IRI |
verbose | No | Return full IRIs instead of the default CURIE-compacted values |
quipu_search_facts
Search for relationships/edges by natural-language query (matches predicate or
value). Replaces Graphiti’s search_memory_facts.
| Parameter | Required | Description |
|---|---|---|
query | Yes | Natural-language search query |
group_ids | No | Best-effort filter to facts from these provenance groups (episode-scoped label; /knot facts are ungrouped) |
max_results | No | Max results (default: 10) |
verbose | No | Return full IRIs instead of the default CURIE-compacted values |
quipu_episodes_complete
Graphiti-compatible flat episode ingestion: accepts name, body text, group, and source, then converts to a Quipu episode and ingests.
| Parameter | Required | Description |
|---|---|---|
name | Yes | Episode name/identifier |
episode_body | No | Natural-language body of the episode |
group_id | No | Provenance label for the episode (not an isolation boundary — see Episodes) |
source_description | No | Who/what produced this episode |
timestamp | No | ISO-8601 timestamp |
quipu_impact
Impact analysis: walk downstream from an entity. With remove=true,
speculatively retracts the entity first (counterfactual). The store is never
mutated.
| Parameter | Required | Description |
|---|---|---|
entity | Yes | Entity IRI to analyse |
remove | No | Speculatively retract before walking (default: false) |
hops | No | Max edge hops to follow (default: 5) |
predicates | No | Restrict walk to these predicate IRIs (empty = all) |
rank_by_ppr | No | Order the reached set by Personalized PageRank seeded at the root — each entry gains a ppr score (default: false) |
timestamp | No | Timestamp for the speculative retraction (used when remove=true) |
quipu_path_cone
Golden paths: compute the provenance cone of a trajectory — which steps did
its falsifier-gated verified result depend on? Per-step verdicts are
in-cone (load-bearing; pruning needs a human Decision), out-of-cone
(mechanically prunable), or cannot-evaluate (no derivation edges recorded —
never silently prunable). Refuses trajectories with no steps or no
falsifier-gated verification. See the
golden-paths design.
| Parameter | Required | Description |
|---|---|---|
trajectory | Yes | IRI of the Trajectory to analyse |
via | No | Derivation predicate IRIs to walk, in addition to verifiedBy (always followed) |
hops | No | Depth bound for the derivation walk (default: 8) |
base_ns | No | Vocabulary namespace override (default: the store’s base_ns) |
quipu_path_backtest
Golden paths: backtest a pruned candidate (exemplar trajectory minus omitted
steps) over recorded history — which past trajectories with a shared
work-item topic would have conformed under gp-grammar/1, and how did their
work items close? Distinguishes 0 matches from cannot-evaluate, and refuses a
pattern it cannot compile.
| Parameter | Required | Description |
|---|---|---|
exemplar | Yes | IRI of the exemplar Trajectory |
omit | No | Step IRIs the candidate omits |
base_ns | No | Vocabulary namespace override (default: the store’s base_ns) |
quipu_unified_search
Unified knowledge search for Bobbin integration: combines text and optional
vector search, returning results tagged source="knowledge" with normalized
0–1 scores.
| Parameter | Required | Description |
|---|---|---|
query | Yes | Natural-language search query |
embedding | No | Pre-computed query embedding (else auto-embedded when provider attached) |
limit | No | Max results (default: 10) |
expand_links | No | Expand results via graph links (default: true) |
max_facts_per_entity | No | Max facts per entity (default: 10) |
quipu_ask
Run a curated, parameterized named query by name instead of hand-writing
SPARQL. The catalog is self-describing: call with no name (or name="list")
to list every query, its parameters, and their types.
| Parameter | Required | Description |
|---|---|---|
name | No | Named query to run; omit (or "list") to list the catalog |
params | No | Parameter map for the named query (names/types from the catalog) |
Catalog:
| Query | Parameters | Returns |
|---|---|---|
entity_facts | entity (iri), limit (int, 100) | All facts asserted about an entity |
service_deps | entity (iri), limit (int, 50) | Outgoing entity references (dependencies / links) |
references_to | entity (iri), limit (int, 50) | Entities that reference the given entity (incoming) |
entities_of_type | type (iri), limit (int, 100) | All entities of a given rdf:type |
labeled_like | text (text), limit (int, 50) | Entities whose rdfs:label contains text (case-insensitive) |
The provenance queries brief_ground, brief_related, entity_work, and
cochanged_with read both legacy vocabulary predicates and their Quechua
counterparts under https://scbrown.github.io/quechua/ns#. Each edge can use
either spelling independently. Duplicate aliases do not inflate returned paths
or shared-item counts. Entity IRIs and dataset selection stay unchanged; these
queries do not require an equivalence reasoner.
The group scope in quipu_search, quipu_search_nodes, and
quipu_search_facts accepts either vocabulary’s groupId predicate on the
provenance episode. Duplicate aliases do not duplicate search results. Other
groups, unrelated predicates with the same local name, and nodes without an
episode remain outside a requested group scope.
Parameters are validated and escaped by type before substitution, so values are
safe against SPARQL injection. The response includes the resolved sparql, the
result columns, and rows.
Example — service dependencies of an entity:
{ "name": "service_deps", "params": { "entity": "http://example.org/traefik" } }
quipu_queries
Manage stored named queries — competency questions a consumer ships with its
domain, callable through quipu_ask alongside the compiled-in catalog.
Definitions are validated at load and versioned (re-loading a name closes the
prior version rather than overwriting it).
| Parameter | Required | Description |
|---|---|---|
action | No | load, list (default), get, or remove |
name | For load/get/remove | Query name |
description | For load | What the query answers |
template | For load | SPARQL template with {param} placeholders |
dataset | No | Dataset IRI this query is scoped to |
params | No | Ordered param specs {name, type, required, default, description} |
timestamp | No | ISO-8601 timestamp |
quipu_graph_list
List registered named graphs with class, source, storage lifecycle, and labels
(freshness / durability / trust / policy / kind). The read half of the
graph-kinds surface, and the consumer capability probe: a store that does
not serve this tool (or GET /graphs) predates the kind axis, which a
consumer must treat as “cannot tell” — never as “no graphs”.
| Parameter | Required | Description |
|---|---|---|
graph | No | Exact registered absolute IRI; excludes kind/lifecycle; no materialization enrichment. Same count/graphs envelope, absent count zero, failures are errors. Attached source must support creator index v1. |
kind | No | Only graphs declaring this dataKind token (e.g. operational, archive) |
lifecycle | No | Only graphs in this storage lifecycle state (frozen) |
quipu_graph_freeze
Deep-freeze a named graph: export its full history (retracted rows and
transactions included) into a read-only archive pack, verify the copy by
content hash, delete the local rows, and re-attach the pack — the graph stays
addressable at the same IRI. Compose frozen graphs back in with FROM <iri>,
FROM <urn:quipu:dataset:frozen>, or include_kinds: ["archive"]. Known
cost: as_of_tx time travel is refused while any archive is attached
(pre-existing rule for attachments); valid-time queries survive.
| Parameter | Required | Description |
|---|---|---|
graph | Yes | IRI of the committed graph to freeze |
out_dir | No | Directory for the archive pack (default: beside the store file) |
timestamp | Yes | ISO-8601 timestamp |
actor | No | Who is freezing |
quipu_graph_thaw
Thaw a frozen graph: verify its archive pack, detach it, restore the full history into the local store under the same IRI, and reopen the graph for writes. The pack file is kept on disk; the freeze registry row is closed, never deleted.
| Parameter | Required | Description |
|---|---|---|
graph | Yes | IRI of the frozen graph |
timestamp | Yes | ISO-8601 timestamp |
actor | No | Who is thawing |
quipu_datasets
Manage named datasets — a reusable name for an arbitrary set of graphs, so it
can be labelled, governed and handed to another agent. FROM <dataset-iri>
then means FROM over its members.
| Parameter | Required | Description |
|---|---|---|
action | No | create, list (default), show, or remove |
name | For create/show/remove | Dataset IRI |
members | For create | Graph IRIs, or {"graph": …, "ord": N} for a declared ordering |
timestamp | No | ISO-8601 timestamp |
actor | No | Who is creating the dataset |
quipu_propose_schema_change
Submit a schema-evolution proposal (shape, class, property, or ontology change). Proposals require explicit acceptance before taking effect.
| Parameter | Required | Description |
|---|---|---|
kind | Yes | shape, ontology, class, or property |
target | Yes | Shape name, class IRI, or property IRI being changed |
diff | Yes | Turtle fragment or JSON patch describing the change |
proposer | Yes | Identity of the proposing agent |
rationale | No | Why this change is needed |
trigger_ref | No | Validation-failure ref or bead id that triggered this |
timestamp | No | ISO-8601 timestamp |
quipu_list_proposals
List schema-evolution proposals, optionally filtered by status.
| Parameter | Required | Description |
|---|---|---|
status | No | pending, accepted, or rejected (default: all) |
quipu_accept_proposal
Accept a pending schema proposal. Shape proposals are validated before writing.
| Parameter | Required | Description |
|---|---|---|
id | Yes | Proposal ID to accept |
decided_by | Yes | Identity of the approver |
note | No | Optional acceptance note |
timestamp | No | ISO-8601 timestamp |
quipu_reject_proposal
Reject a pending schema proposal with a reason.
| Parameter | Required | Description |
|---|---|---|
id | Yes | Proposal ID to reject |
note | Yes | Reason for rejection |
decided_by | Yes | Identity of the rejector |
timestamp | No | ISO-8601 timestamp |
quipu_resolve_entity
Check for existing near-duplicate entities before writing, using vector similarity and canonical-name matching (Jaro-Winkler). Returns candidates with similarity scores and match explanations.
| Parameter | Required | Description |
|---|---|---|
name | Yes | Canonical name of the proposed entity |
properties | No | Key-value properties (used for embedding context) |
top_k | No | Max candidates to return (default: 3) |
threshold | No | Similarity threshold 0.0–1.0 (default: 0.85) |
quipu_load_ontology (requires owl feature)
Manage OWL ontologies: load (parse + materialize entailments), list, or
remove. Only registered when Quipu is built with the owl feature.
| Parameter | Required | Description |
|---|---|---|
action | No | load, list, or remove (default: list) |
name | For load/remove | Ontology name |
turtle | For load | OWL ontology in Turtle format |
timestamp | No | ISO-8601 timestamp |
Hybrid fusion parameters on quipu_search: mode: "hybrid", alpha (semantic
weight, 0..1), fusion (weighted or rrf), positive rrf_k, and explain.
Server activation and the ready lexical index are required for intermediate
weights. Alpha endpoints retain exact pure responses. See the REST search
reference for candidate bounds, scope compatibility, snippets and rollback.
quipu_graph_create
quipu_graph_create takes graph (required), optional RFC 3339 timestamp
(default current UTC), and optional non-empty actor. It registers a committed
named graph and returns graph, g, created, and creation with timestamp
and actor. Repeats preserve original provenance. An actor is a declaration,
not authenticated identity; missing historical attribution is null.
quipu_graph_label
quipu_graph_label takes required graph and timestamp, optional actor,
trust: {iri, chain, rank}, freshness, durability, policy tokens, kind,
and valid_to. At least one label axis must be supplied. Register the graph
first. Label declarations are versioned. Invalid axes, empty declarations and
unregistered graphs are refused.
quipu_graph_list includes each graph’s creation alongside class and labels.
Follow the complete MCP-only named graph recipe
for create, label, new-class shapes, write and scoped query.
Portable graph workflows
These tools call the existing guarded REST handlers. Bearer authentication,
read-only mode, graph selectors and validation have the same authority. New
workflow tools reject unknown or wrong-typed top-level fields; pass an exact
scope to shares. None accepts a server-local file path.
quipu_share
Produce a bounded portable share payload, including canonical manifest and member bytes. Read-only; no server-local output path. Pass the exact scope. Internal-only export exemptions are not accepted over this transport.
Fields: scope (required), shapes, no_shapes, queries, alignments_turtle, parent_share, turtle_view, max_bytes.
quipu_import
Stage a portable share with hash, vocabulary and attestation gates. Copy member contents from quipu_share; this is not a server file path. Exact-name merges default off; review before promotion.
Fields: manifest (required), export_ntriples (required), shapes_turtle (required), queries_turtle, alignments_turtle, query_namespace, replace_queries, source (required), actor, accept_exact, attestation.
quipu_import_promote
Promote a staged share only when existing promotion gates permit it. Does not bypass quarantine or resolution review.
Fields: share_id (required), actor.
quipu_reason
Run stored or inline Datalog rules to fixpoint and persist derivations. Omitted graph means ROOT; pass graph to scope premises. Derivations persist in that graph’s reserved companion inferred graph, not alongside asserted facts.
Fields: rules, prefix, graph, timestamp.
quipu_update
Execute SPARQL 1.1 Update through the guarded update endpoint. Use explicit GRAPH clauses for named graphs; absent scope has standard SPARQL default-graph semantics. Actor/source are declared provenance, not authorization.
Fields: update (required), actor, source.
quipu_events
Read a bounded event batch without committing a consumer cursor. since overrides consumer. types is a comma-separated event-type filter; group is an episode group, not a graph selector.
Fields: since, limit, types, group, consumer.
quipu_events_commit
Durably set a consumer cursor. Lower offsets explicitly rewind; delivery alone never commits.
Fields: consumer_id (required), offset (required).
quipu_changes
Read whole transaction change pages. graph scopes an exact graph; use next_tx for the next page. No cursor write.
Fields: since, limit, capture, graph.
quipu_subscriptions
Manage persistent push subscriptions (create/list/delete). Creates may deliver externally; list is still conservatively write-classified like REST.
Fields: consumer_id, webhook_url, sparql_ask, action, types, mode, batch_size, batch_window_s.
quipu_entity
Dereference an exact IRI as JSON-LD. IRIs are encoded as query values, including slashes and fragments; expanded includes label expansion.
Fields: iri (required), expanded.
quipu_entity_history
Read the existing entity-history endpoint for an exact IRI. Its current MAIN-history semantics do not certify composed/named-graph audit completeness.
Fields: iri (required).
quipu_transactions
Read bounded transaction metadata after since; does not change a cursor.
Fields: since, limit.
quipu_acquire
Acquire complete bounded registered named graphs from an expected MAIN store lineage. Unknown graphs, unsupported attachments/pins and overflow refuse.
Fields: source_store_id (required), graphs (required), tx, max_bytes.
quipu_acquire_composed
Acquire qualified MAIN graphs with a fenced attachment census. expected_generation is required with an explicit main tx; provider clocks are never a global transaction.
Fields: source_store_id (required), graphs (required), expected_generation, tx, max_bytes.
quipu_embed_backfill
Backfill missing ROOT entity embeddings using the configured provider. May consume external embedding resources and persist vectors.
Fields: none.
quipu_embed_backfill_graph
Backfill a bounded number of named-graph entity embeddings; does not rewrite ROOT entity text.
Fields: graph (required), max_entities.
quipu_search_query
Fuse ranked search seeds with a SPARQL query. Existing mode-specific validation and query budgets apply.
Fields: mode, query, seed_variable, sparql, valid_at, entity_type, embedding, seed_limit, query_options, group_ids.
quipu_fragments
Read a bounded triple-pattern fragment. page and pageSize select a page; absent terms are wildcards.
Fields: subject, predicate, object, page, pageSize.
quipu_reconcile
Get the reconciliation service manifest or resolve keyed query objects without merging entities.
Fields: queries.
quipu_spotlight
Annotate text with known entities, read-only. A busy cold cache may return no annotations; that is not an absence proof.
Fields: text (required), confidence.
quipu_knot_stage
Stage one immutable numbered snapshot part. Supply exact sha256 hashes and upload identity; replay is idempotent, changed bytes refuse. No file paths.
Fields: upload_id (required), snapshot (required), content_hash (required), part_hash (required), payload (required), timestamp, actor, source, graph, total_parts (required), total_bytes (required), part_number (required).
quipu_knot_promote
Start or read back atomic promotion of a staged snapshot upload. pending=true is not completion; poll the same upload_id. Existing validation gates remain authoritative.
Fields: upload_id (required).
quipu_workflow_capabilities
Read the direct-workflow enforcement capability before guarded updates. No
arguments. A successful read reports enabled, signature version and scope;
it does not grant write authority.
To move a portable share, call quipu_share with
{"scope":{"kind":"graph","value":"urn:example:graph"}}. Its result includes
manifest and files. Pass that manifest, files["export.nt"] as
export_ntriples, files["shapes.ttl"] as shapes_turtle, and a provenance
source to quipu_import on the destination. Include queries_turtle and
files["alignments.ttl"] as alignments_turtle when present; alignments are
sealed evidence, never applied mappings. Review quarantine/resolution output before quipu_import_promote.
An import result or pending upload result is not promotion acceptance.
For quipu_update, provide a SPARQL update string, with explicit GRAPH
clauses for named scope. The adapter form-encodes the exact string and optional
actor/source; the existing update handler applies structural, vocabulary,
workflow and attribution gates. A declared actor is not authenticated identity.
quipu_events returns next_offset without committing. Commit only after
processing with quipu_events_commit. quipu_changes returns next_tx and
supports exact graph filtering. Both are bounded pull APIs; they do not
promise a persistent SSE connection. Subscription creation can trigger external
webhook delivery and retains the REST write classification even for list.
quipu_stats
Read serving-store statistics with no arguments. Unlike CLI stats --db, this
operates on the server’s store, not a caller-selected local path.
quipu_health
Read server health with no arguments. A positive liveness control does not certify target coverage or graph completeness.
quipu_version
Read serving version, source commit and dirty-build identity without REST. No arguments; version identity does not replace behavioral acceptance.