Skip to main content
Ithaca exposes its tools over the Model Context Protocol (MCP). Agents connect using one of two transports — stdio for local, trusted execution or streamable-HTTP for remote, hosted connections. Every remote connection is authenticated with a per-tenant bearer token and scoped to the caller’s capabilities.

Transports

The default transport for local development. The MCP server runs as a child process of the agent, communicating over stdin/stdout. No network, no auth — the trust boundary is the local machine.
The agent launches this process and speaks MCP over its stdio pipes. This is the path used when you self-host or run the demo end-to-end script.
stdio mode has no authentication. It is intended for local, trusted use only. Never expose a stdio MCP server over the network.

Authentication

Remote connections authenticate with a per-tenant API key in the Authorization: Bearer header. The key format is frozen by the auth contract:

Verification flow

The TenantAuthMiddleware resolves every request:
  1. Extract the bearer token from the Authorization header.
  2. Split into (prefix, env, key_id, secret) — the secret may contain _, so only the first three separators are split.
  3. Look up the key_id row in the api_keys table.
  4. If the key is revoked (revoked = 1), return None → 401.
  5. Compute sha256(salt + secret) and compare with the stored hash using hmac.compare_digest (constant-time).
  6. On match, return {tenant_id, scopes, key_id} → the Principal.
The principal is stashed in a contextvars.ContextVar so the tool wrappers can pass it into the Toolset as _ctx. Every hosted call is then tenant-scoped and traced under that tenant.

Scope enforcement

Scopes are enforced per-request by inspecting the JSON-RPC body: Missing scopes return 403 missing scope: <scope>. Batch requests get the union of all required scopes.

The MCP initialize handshake

When the agent connects, the MCP client sends an initialize request. The Ithaca server responds with its capabilities and instructions:
The instructions field carries the canonical workflow (see Agent Workflow). The agent is expected to read and follow it. After initialize, the client sends notifications/initialized and the session is ready for tools/list, tools/call, resources/list, and resources/read.

Tool discovery

Ithaca exposes 60+ tools. Loading all of them into the agent’s context at once is wasteful and can overflow the context window. Ithaca supports progressive discovery:

search_skills — progressive discovery

The agent searches for skills matching a query, asset class, or intent, then calls describe_skill for the ones it wants to use. This keeps the context window small.

describe_skill — one skill’s manifest

tools/list — list all tools

The standard MCP tools/list method returns every registered tool. This is the full catalog — use it when you need the complete list, but prefer search_skills for progressive discovery in a research session.

Resource URIs

Ithaca exposes read-only resources under the doomberg:// scheme. These are tenant-scoped (HTTP mode requires data:read) and return credential-redacted JSON.

Example: read the policies resource

Credential redaction

Resource responses never expose credential material. Provider connections return credential_configured: true/false but never credential_ref. Connected-account data is sanitized: source_account, account_ref, host, quote_path, and order_path are stripped; the provider name is replaced with the public data-source label.

Session lifecycle

A Ithaca MCP session maps to a persistent research run in the platform store. The lifecycle:

Session correlation

The Toolset keys research sessions off (tenant_id, agent_id):
  • In stdio mode, agent_id defaults to a per-process UUID. All calls in one process share a research run.
  • In HTTP mode, agent_id is the verified key_id. All calls from one credential share a research run.
  • If session_context is called with an explicit session_id (a research-run UUID), subsequent calls attach to that run. This lets the web UI start a session and the agent join it.

Trace events

Every tool call emits a tool_call event (with the tool name and arguments) and a tool_result event (with a provenance summary). These are appended to the trace_event table with a gap-free monotonic seq per run_id. The web UI replays them via SSE.

Disconnect and replay

When the MCP session ends, the research run persists. The human can open app.doomberg.meSessions and replay the entire trace from seq=0. SSE replay uses Last-Event-ID to resume from any point without gaps.

Install an Agent

The one-liner installer that registers the hosted MCP endpoint with your agent.

Agent Workflow

The canonical research workflow and server instructions.

Security Model

Tenant isolation, API key security, and the glass-box principle.