The MCP surface
The engine inside Ouroboros is named Sophia; every tool your agent sees is sophia.*, grouped into 19 capability families, with a sandboxed execute_code isolate for composing several reads into one round trip.
Verified 2026-07-09 @ 80a91241
What this is
The engine inside Ouroboros is named Sophia — State Operating Platform for
Human Intelligence Agents. The name is the architecture: humans use AI
agents to get things done; agents use Sophia. It sits between your agent
and the database as the MCP server they talk to, and everything an agent
can do here happens through it. Every tool your agent sees over that
connection is namespaced sophia.* — one flat list of typed calls,
discoverable at runtime, with no separate manifest to keep in sync. From
here on, sophia.* is just what the API is called.
Why it exists
An agent that has to guess a tool’s shape burns a round trip finding out
it guessed wrong. Sophia’s answer is to make the surface self-describing:
sophia.capability_catalog returns every tool grouped by capability with
a version hash, and sophia.get_sdk_types returns generated TypeScript
declarations for the same surface, so an agent can discover what it can
call instead of being handed a static list that drifts out of date. The
catalog returned by this daemon just now, captured live for this page,
counts 166 tools across 19 capability groups, tagged with
server_git_sha: "80a91241" — the exact build that produced the number.
That count is a live property of the running server, not a fixed spec:
it has moved before as tools shipped and merged, and it will move again.
Treat any number on this page as a snapshot, not a promise.
How it works
The 166 tools sort into 19 groups: orient (session bootloaders like
sophia.orient and sophia.panorama), coordination (inter-agent
messaging), system (schema/permission/execute_code plumbing), seven
read_* families (entities, finance, calendar, documents, knowledge,
wiki, code), read_meta (briefings, search, mutation history, skills),
preflight (evidence checks before a write), and seven mutate_* families
gated behind approval. Most day-to-day work lives in the read families —
writes are the minority of the surface by design.
Composing several reads into one call goes through sophia.execute_code:
a TypeScript snippet run inside a real V8 isolate (isolated-vm), hosted
in a spawned Node child process rather than in the Bun daemon’s own
process — an 8MB heap limit, a 10-second default timeout (30s max), and a
cap of 50 Sophia calls per run. Inside the isolate, sophia.* is a Proxy
that forwards each camelCase method name to its dotted MCP tool
(queryKnowledge → sophia.query_knowledge) through a mapping built by
reflection over the tool registry, not a hand-maintained list. The tool’s
own description states the intended default directly: “Use this by
default when you need 3+ Sophia reads or a read-modify-write sequence: it
composes calls with Promise.all and keeps intermediate data inside the
isolate instead of expanding every MCP result into the chat.”
Two smaller mechanisms cut round trips further. Every tool response —
not just execute_code’s — carries an _inbox_unread field, spliced in
by a shared response wrapper after the tool’s own result is built; a
nonzero count means another agent left you something before your next
call finishes. And several high-traffic read tools (query_knowledge,
search, search_documents among them) accept compact: true for a
curated ~5-key projection of each row, or fields: [...] for an exact
key list — an unknown key in fields returns a structured error listing
the tool’s valid keys instead of a generic failure, so a wrong guess costs
nothing.
Both are context-economics tools: a response that carries five keys instead of forty — or an isolate that keeps intermediate results out of the conversation entirely — defends the agent’s reasoning quality, not just its token bill.
sequenceDiagram
participant A as Your agent
participant D as Daemon (MCP)
participant I as V8 isolate (child process)
A->>D: sophia.execute_code({ code })
D->>I: spawn/reuse isolate, inject sophia.* proxy
par Promise.all
I->>D: queryKnowledge(...)
I->>D: capabilityCatalog(...)
I->>D: searchDocuments(...)
and
D-->>I: three results, scoped by connection
end
I-->>D: return value
D-->>A: result + _inbox_unread What your agent does with it
// Real response from this daemon, captured 2026-07-09:
const [briefing, catalog, docs] = await Promise.all([
sophia.orient({ goal: 'draft mcp-surface page' }),
sophia.capabilityCatalog({ brief: true }),
sophia.searchDocuments({ query: 'mcp', limit: 3, compact: true }),
]);
// → {
// sync: { entity_count: 31, document_count: 6576,
// knowledge_fact_count: 6519, unindexed_doc_count: 4526 },
// total_tools: 166,
// server_git_sha: '80a91241',
// doc_hits: { items: [ /* 3 hits, compact-projected */ ], total: 3,
// fusion: { method: 'rrf', k: 60, signals: ['bm25', 'dense'] } },
// }
// sophia_calls: 3, execution_time_ms: 3510 One execute_code call, three composed reads, one round trip — a session
bootstrap, a live catalog check, and a scoped document search, none of
which touched each other’s intermediate results outside the isolate. The
same pattern is how an agent stays current on the tool count itself:
sophia.capability_catalog is cheap enough to call at the start of a
session rather than trusting a number written down last month.
Boundaries
Read tools run under whatever entity_scope the connection carries; write
tools additionally depend on the connection’s profile. observer
connections can’t call write tools at all. assistant connections get a
per-call approval prompt on every write. full connections are
pre-approved for ordinary writes — no per-call prompt — with one fixed
exception: a small set of elevated tools (sophia.remember_fact,
sophia.create_entity, sophia.begin_import_session,
sophia.revert_mutation) always requires an explicit owner-approval
gesture, regardless of profile, because each one can poison memory, plant
false ground truth, bulk-write, or rewrite history. Portal connections —
the owner’s own browser session, authenticated by cookie rather than a
minted bearer — are exempt from that gate by design, not by oversight.
There is no OAuth-style interactive flow anywhere in this surface: no
redirect, no consent screen, no refresh-token dance. Auth is a static
bearer token checked against a connection row in the daemon’s ops
database on every call. That simplicity has one sharp edge — a stale or
revoked bearer doesn’t degrade gracefully, it fails outright with
401 Authentication required. Provide a valid Bearer token. (or, on the
REST-style path, invalid_or_revoked_bearer). If every call in a session
starts failing at once, check the bearer before you debug anything else.
How a connection actually gets minted, scoped, and approved is
Security Model; the wiring steps to get a
bearer into your agent’s config live at /connect/, not
here. What sophia.search and its ranking signals (BM25, dense, fusion,
rerank) actually do is Search & Retrieval.
The daemon these tools sit in front of, and the graph/vault/gate split
underneath them, is The State Layer; how a
mutation gets journaled and reverted is Time Machine;
how a claim gets grounded before it’s ever writable is
Truth; and the inter-agent messaging that
_inbox_unread is a side-channel for is Coordination.