Serving over MCP¶
MCP is the one interface, in dev and in production. elbi mcp (or
elbi serve, which serves the same MCP endpoint alongside a chat UI)
is simply that interface served on your machine.
What gets exposed¶
For each derivation, the local server registers:
- a resource at
elbi://derivation/<name>, reading current context (thereadcapability); and - a tool
run_<name>, computing and returning the served output (theruncapability).
Each call builds a fresh runner, so the agent always sees a current computation against your local data bindings.
Shaping results for the model¶
LLM accuracy drops on large inline tables, so a table tool does not dump every
row into the model's context. It returns:
- a preview as text: the rows that fit the
maxCellsbudget (rows x columns, a portable proxy for a token budget), with a note of the total when trimmed; - the full rows in MCP
structuredContent, at zero token cost to the model and available to a capable host for rendering or processing; and - a
resource_linkto the derivation's resource, for an out-of-band read of the full result.
The resource (read) still returns the complete rendering. Tune the budget with
serve.table(max_cells=…); other formats return plain text unchanged.
The audit trail¶
The serving layer carries an AuditSink seam, off by default so a local server stays
zero-config. It records one AuditEvent per invocation -- the derivation, the
content-addressed version that produced the answer, the params, and the outcome -- with
keys aligned to the OpenTelemetry MCP semantic conventions.
NullAuditSink (the default) records nothing. JsonlAuditSink appends a durable
one-line-per-event trail. ChainedJsonlAuditSink adds tamper-evidence: each line hashes
the one before it, so editing, reordering or dropping a record breaks the chain. Verify
it with elbi certificate chain; see
Verification certificates.
A served, certified derivation also carries its signed
verification certificate alongside its result: the tool call's
structuredContent.certificate and the resource's _meta, so any client can trust and
export what it received without a separate fetch.
Connecting a client¶
elbi mcp serves streamable HTTP at http://localhost:7878/mcp by
default (elbi serve serves the same endpoint at
http://localhost:7700/mcp, alongside a chat UI). Point any MCP client at it:
- MCP Inspector:
npx @modelcontextprotocol/inspector, then connect to the URL. - Claude Desktop / Cursor / your own agent: add the URL as an MCP server.
Protocol revisions¶
Both the 2026-07-28 revision and the earlier handshake revision are served, with
nothing to configure on either side. A client announces which it speaks with the
MCP-Protocol-Version header; one that sends no header is served the handshake
revision, so an older client keeps working unchanged.
The 2026-07-28 revision has no initialize handshake and no session header: each
request carries its own protocol version and capabilities, and a client reads what the
server supports from server/discover. Nothing routes on per-connection state, so a
deployment can answer any request from any replica, with neither sticky routing nor a
shared session store. elbi mcp owns its socket and still keeps sessions for
handshake-era clients; the endpoint mounted by elbi serve does not.
Tasks is deliberately absent: a long tool reports through
run_code(background=true) with job_status and cancel_job instead.
ttlMs / cacheScope are left at the conservative defaults the SDK emits on a
resource read, ttlMs: 0 and cacheScope: "private", which tell a client to cache
nothing. The revision carries them on resource and list results only, and a
derivation's freshness is content-addressed rather than clock-based, so there is no
duration to publish in their place (see
Caching).
W3C trace context is honoured. A call carries traceparent in its own _meta rather
than in an HTTP header, so it survives a transport that is not HTTP, and the server
continues that trace instead of starting a new one: a tool call is a span inside the
caller's trace, not an unattributed gap in the middle of it. The spans leave this
process only when the deployment configures an OTLP endpoint.
The client features the revision deprecates are unused here, so a client needs neither roots nor sampling to connect.
Auth¶
Elbi runs as a single-user application: your machine, your data, no account. There is no
token to issue and no identity to check, and elbi mcp serves your own configured data
with nothing to manage. Per the MCP authorization spec, local transport should pull
identity from the environment rather than running a browser flow. Doing nothing is
how this obeys that.
Serving the endpoint to anyone but yourself is a different problem, and this runner does not try to solve it. Put it behind something that authenticates, or reach it over an SSH tunnel.