ADR 0013: Console protocol: streaming turns, a stateless tool server and per-role run tokens
- Status
- Accepted
- Date
- Deciders
- Founder
Context
Phase 1 ran agents as single request/response jobs polled every two seconds, with the container reading only bootstrapped material and no way for an agent to call platform data. Phase 2 needs a chat console with streamed answers, approval gates that pause a run, and agents that read and propose through the platform under the user’s permissions. The managed agent runtime’s network mode is PUBLIC (verified in infra/terraform/modules/agentcore-runtime/main.tf), so containment cannot rely on the network; a managed MCP gateway with policy exists but its general availability is not stated.
Decision
- A second invocation mode:
startwithphase = console_turnreturns a streamed envelope (one SSE event per runtime event; the terminal event carries job state). Legacy phases keep the poll path; the worker falls back tostatuspolling if a stream ends early. agent_eventsstays the durable log; the API tails it withLISTEN/NOTIFYand servesGET P/threads/{thread_id}/streamas server-sent events withid = seqand a 15 s keepalive.- One platform tool server inside the API at
POST /api/v1/agent-tools/mcp, MCP Streamable HTTP run stateless (no session id), advertising revision 2025-06-18. It is the deterministic enforcement point: role and plan allowlists, live permission re-validation, approved-hash checks and budgets. - Six run tokens per run (console, evidence, plans, review, progress, reader): RS256 JWTs minted by the worker with a worker-only private key (
trovensa/<env>/worker-run-token-key); the API verifies withapp-run-token-jwks. The container holds no vendor credentials and its only configured endpoint is the tool server. - The
mcpPython dependency is pinned>=1.30,<2in api and agent (verified 2026-09-14:pip install mcpnow installs 2.x with a renamed server API; the stateless FastMCP options belong to the 1.x line); the client forces theMCP-Protocol-Versionheader to the contract revision and never keys state on a session id. - Vendor MCP servers are reference-only until their protocol revision and audit attribution are verified; integrations go through versioned REST APIs.
Consequences
- The runtime image, the worker and the API change together (contract section 10.7); a fake MCP server keeps container and API tests offline.
- The maximum duration of one streamed runtime response and session behaviour after
max_lifetimeare measured and recorded inCLAIMS-REGISTER.mdbefore R1b ships. - Retrieved text reaches only the reader role; write proposals are hashed at proposal time and verified at approval.
Alternatives considered
- Managed gateway with policy as the enforcement point: not stated as generally available; kept as R2 defence in depth by publishing the same OpenAPI to it.
- WebSocket end to end: the ingress and the API pods would need new configuration and sticky sessions; SSE over the existing ALB with reconnect from
Last-Event-IDis sufficient for interactive turns. - Direct API-to-runtime invocation for interactive turns: two invocation paths and a token issuer in the API pod; rejected for the pilot.
Follow-ups
- VPC network mode with an egress allow-list when available (R2).
- Publish the tool server’s OpenAPI to the managed gateway with mirrored policy rules (R2).
Last updated on