MCP tools
The 15 ergo_* tools the stdio adapter exposes, one per HTTP endpoint.
The Ergo MCP adapter exposes 15 tools, each a 1:1 mapping to an HTTP endpoint. It's a thin stdio HTTP client — it never opens the SQLite file directly. Your agent calls these tools; you don't write glue code.
The tools cover the full data surface (write, read, and ops). The /admin/*
tenant-provisioning routes are deliberately not exposed as MCP tools — they're
an operator concern, reached over HTTP with the superadmin token (see
Multi-tenancy).
Registering the tools
Ergo runs both as a hosted MCP server (streamable HTTP, no install) and as a stdio adapter you run yourself against a self-hosted engine. Same 15 tools either way.
Hosted — point any MCP client at the endpoint with your API key:
claude mcp add --transport http ergo https://api.ergomem.com/mcp --header "Authorization: Bearer YOUR_API_KEY"See Quickstart for the Codex CLI and Cursor equivalents. On the
hosted endpoint, scope defaults are who = whatever your token was minted for and
project = "default" (still overridable per call); org_id is resolved from your
key server-side, so you never send it.
Self-hosted stdio — the adapter speaks MCP over stdio, so a host launches it as a subprocess and passes the engine location through environment variables:
ERGO_API_URL=http://127.0.0.1:8788 \
ERGO_API_TOKEN=your-secret-token \
python -m ergo.mcp.serverEvery tool is scoped to an (org_id, project). The stdio adapter's defaults are:
org_id="claude-code"project= the git-repo basename of the current working directorywho="claude-code"
All three are overridable per call. See Recipes for a full
.mcp.json registration entry.
Write tools (contradiction-guarded)
ergo_remember
Record a decision, constraint, rejection, or convention. Runs the full
contradiction gate (normalize → structural → NLI → judge). Always include a
reason — a reason-less claim can never win an ergo_why query.
statement— the claim, in plain declarative prose (required)reason— why it holds (strongly recommended; required in spirit for decisions)org_id,project,who— scope/authorship overridesforce_exception— a justification string to store anyway despite a conflict
A detected contradiction comes back as a structured conflict (the conflicting
claim, tier, and reason) — not an error. Resolve it: cancel, ergo_supersede,
or retry with force_exception.
ergo_learn
Record a belief formed from a source — the bridge between raw ergo_ingest
and a fully-guarded ergo_remember. Runs the same contradiction gate and folds
the provenance into the stored reason.
statement— the belief (required)source— where it came from: file:line, doc, URL (required)reason,org_id,project,who,force_exception
ergo_supersede
Replace an active claim with a revised one — "I changed my mind, and here's
why." The old row becomes superseded and links to the new one, preserving the
why-chain. The new claim is contradiction-checked against all other active
claims first, so it can still be refused with a conflict.
existing_id— the claim being replaced (required)statement— the revised claim (required)reason,org_id,project,who
ergo_retract
Remove a claim that was wrong at birth — a typo, test junk, or a mis-scoped
write. Flips it to retracted and drops it from the active set; it stays in
ergo_history. No contradiction gate. Removal is audit-loud, so a reason is
required.
claim_id— the claim to retract (required)reason— why it's being removed (required)org_id,project,who
Read tools
ergo_recall
Hybrid semantic search over active claims — sqlite-vec cosine similarity
blended with keyword overlap. Returns score, vec_sim, and kw per result so
you can tell a semantic-only match from a keyword hit.
query— natural-language query (required)limit— max results (clamped to[1, 50])org_id,project
ergo_why
Like ergo_recall, but reasoned-only and single-answer: returns the best
reasoned belief for the query plus its reason and full supersede history. A
reason-less claim can never win here. Returns null when nothing reasoned
matches at all.
A query that finds a reasoned claim but scores below the relevance floor
comes back suppressed instead of a low-confidence guess: why: null plus
an additive suppressed: {reason: "below_relevance_floor", score, floor}
object. That means "ask a more specific question," not "nothing is decided
here" — the underlying claim still exists; call ergo_recall directly if you
want to see what was actually nearby.
query— natural-language query (required)org_id,project
ergo_active
List active claims in a scope — a bounded page, not the whole set. Truncation is loud, never silent: the response always carries the scope's true total alongside whatever page came back.
org_id,projectlimit— page size; clamped to[1, 500], default 50offset— rows to skip, oldest-first; page withoffset=next_offsetto walk the whole set with no gaps or duplicatesprojection—"full"(default, every column) or"summary"(each claim reduced to{id, statement},reasonomitted); prefer"summary"when you just need to browse what a project believes, not why
Returns count (the scope's TRUE total, regardless of limit), returned
(claims in this page), truncated, truncated_reason ("row_limit" — more
rows remain, raising limit helps; or "byte_budget" — a ~50 KB served-JSON
size cap cut the page short of limit, and raising limit won't help; null
when not truncated), and next_offset. state (project_missing / empty /
has_active_claims) describes the scope, not the page — a project with
170 claims read at offset=500 still reports has_active_claims with an
empty claims list.
ergo_history
Full claim history for a scope — active plus superseded plus retracted, ordered by claim sequence. Use this to walk how beliefs evolved.
org_id,project
ergo_audit
Self-serve audit trail: your own org's writes + conflict_events rows — every
guarded write and every conflict it triggered. Built for a compliance or security
need where you want a record without going through support. Same tenant isolation
as ergo_active/ergo_history — you can only ever read your own org's rows.
org_id,projectlimit— caps each stream (writes, conflicts) independently; clamped to[1, 1000], defaults to 200
Returns {"writes": [...], "conflict_events": [...]}, each ordered oldest → newest.
Data portability
ergo_export
Download every active claim in the caller's org as a single JSON document —
no embedding vectors (they're embedder/dimension-specific and always
recomputed on import). Open to every plan, including Free — export is
never gated; only ergo_import is.
org_id,project— scope; omitprojectto export every project in the org
ergo_import
Upload claims — typically a document a prior ergo_export produced — and merge
them back in. Each incoming claim runs through the same contradiction gate
ergo_remember/ergo_learn use: an exact-text duplicate is skipped, a claim
that contradicts something already active is reported and skipped, never
silently overwritten, and everything else is stored. Pro/Team only: a
free-plan token gets a structured 403 (error.error: "import_requires_pro_or_team") instead of an error, unlike ergo_export
above. Import is exempt from the monthly write cap (a restore shouldn't burn
your authoring month) but is still bounded by the plan's import_max_claims
size limit and the tenant's active-claim cap.
Async for real tenants. For a real (provisioned) tenant, this call
enqueues a background job and returns {job_id, status: "pending", claims_count} immediately — the merge itself runs later via the engine's own
cron drain. Poll ergo_import_status (below) with the returned job_id for
progress and the final result. A legacy/superadmin caller is unaffected: it
still gets the old synchronous result shape directly, with no job to poll.
claims— the list of claim objects to merge in (required)org_id,target_project— scope;target_projectremaps every claim into one project, overriding each claim's ownproject
Real tenant → {job_id, status: "pending", claims_count}. Legacy/superadmin
caller → the old synchronous {imported, skipped_duplicate, conflicts: [...], conflict_count, cap_reached} shape directly. Once a job's status is
"succeeded", ergo_import_status reports that same synchronous shape.
cap_reached: true means the active-claim cap was hit partway through and the
import stopped gracefully (never a 500).
ergo_import_status
Poll a background import job started by ergo_import. Tenant-scoped — a
caller can only ever see its own jobs; a job belonging to another tenant, or
an unknown job_id, both come back as the same structured 404.
job_id— the id returned byergo_import's pending response (required)org_id,project
Returns {job_id, org_id, status, claims_count, created_at, last_attempt_at, last_error, imported, skipped_duplicate, conflicts, conflict_count, cap_reached}. status is one of "pending", "running", "succeeded", or
"failed" — the result counters (imported/skipped_duplicate/conflicts/
conflict_count/cap_reached) stay null until status is "succeeded"; a
"failed" job's last_error carries the reason instead.
Reference & ops tools
ergo_ingest
Bulk-load reference content (a runbook, README, or design doc) for later recall. Skips the contradiction gate — facts upsert freely — chunks the text word-safely, embeds it, and stores it. Use this for material you want recall-able, not for decisions you want guarded. It's the fast path: much cheaper than a guarded write.
text— the content to ingest (required)source— provenance string appended to each chunkorg_id,project,who
ergo_diagnose
Report-only ops view of the running engine: live counts, conflict telemetry, version drift, and anti-zombie warnings (missing owner/backup, drift). It never mutates beliefs — any fix still requires an explicit write.
(No parameters.)
ergo_health
Liveness probe (no auth). status: ok with retrieval: true means the whole API
— including recall, why, and ingest — is available. Call it once before relying
on the store.
(No parameters.)