Transports
- stdio (local)
- streamable-HTTP (remote)
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 theAuthorization: Bearer header. The key format is frozen by the auth contract:
| Segment | Meaning |
|---|---|
dmbg | Fixed prefix (lets leaked keys be scanned and revoked) |
<env> | Deployment env tag (test by default, prod in production) |
<keyid> | Public, stable credential id (UUID4 hex). Used for lookup and audit attribution |
<secret> | High-entropy URL-safe token. Never stored — only sha256(salt + secret) is persisted |
Verification flow
TheTenantAuthMiddleware resolves every request:
- Extract the bearer token from the
Authorizationheader. - Split into
(prefix, env, key_id, secret)— the secret may contain_, so only the first three separators are split. - Look up the
key_idrow in theapi_keystable. - If the key is revoked (
revoked = 1), returnNone→ 401. - Compute
sha256(salt + secret)and compare with the stored hash usinghmac.compare_digest(constant-time). - On match, return
{tenant_id, scopes, key_id}→ thePrincipal.
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:| Scope | Required for |
|---|---|
data:read | Any tools/call or resource read/list request |
strategy:author | The propose_strategy tool |
runs:write | The run_skill tool |
runs:cancel | The cancel_run tool |
approvals:read | The list_approvals tool |
approvals:decide | The decide_approval tool (also requires a human actor) |
artifacts:read | The get_artifact tool |
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 aninitialize request. The Ithaca server responds with its capabilities and instructions:
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
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 MCPtools/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 thedoomberg:// scheme. These are tenant-scoped (HTTP mode requires data:read) and return credential-redacted JSON.
| URI | Description |
|---|---|
doomberg://skills/catalog | Compact skill catalog for progressive discovery |
doomberg://skills/{skill_id}/{version} | One versioned skill manifest |
doomberg://runs/{run_id} | Durable run with jobs, artifacts, and replayable trace events |
doomberg://policies/current | Current deterministic platform policy (trading disabled, execution limits, typed error codes) |
doomberg://accounts | Tenant-scoped account and provider connection summary (credentials redacted) |
doomberg://data/providers | Tenant-scoped data-provider and dataset snapshot summary |
Example: read the policies resource
Credential redaction
Resource responses never expose credential material. Provider connections returncredential_configured: true/false but never credential_ref. IBKR-sourced 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
TheToolset keys research sessions off (tenant_id, agent_id):
- In stdio mode,
agent_iddefaults to a per-process UUID. All calls in one process share a research run. - In HTTP mode,
agent_idis the verifiedkey_id. All calls from one credential share a research run. - If
session_contextis called with an explicitsession_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 atool_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.
| Event type | When |
|---|---|
session_open | First session_context call in a session |
reasoning | session_context with a prompt, or agent-narrated reasoning |
tool_call | Before a tool executes |
tool_result | After a tool returns |
strategy_proposed | propose_strategy succeeds |
run_transition | Run state changes (draft→queued→running→succeeded) |
denial | A money-path action is refused (agent tried to promote) |
error | A tool or transition fails |
Disconnect and replay
When the MCP session ends, the research run persists. The human can open app.doomberg.me → Sessions and replay the entire trace fromseq=0. SSE replay uses Last-Event-ID to resume from any point without gaps.
Related
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.