ergo
Sign inGet started

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.server

Every 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 directory
  • who = "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 overrides
  • force_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, project
  • limit — page size; clamped to [1, 500], default 50
  • offset — rows to skip, oldest-first; page with offset=next_offset to walk the whole set with no gaps or duplicates
  • projection"full" (default, every column) or "summary" (each claim reduced to {id, statement}, reason omitted); 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, project
  • limit — 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; omit project to 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_project remaps every claim into one project, overriding each claim's own project

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 by ergo_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 chunk
  • org_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.)