Agent Studio - Beta

MCP streamable-HTTP client reuses a terminated mcp-session-id on reconnect, aborting agent-to-agent delegation
Summary When an Agent Studio agent (acting as an MCP client) finishes one round of MCP work against a sibling server, tears the session down with DELETE, and then reconnects to issue a follow-up call, the reconnect initialize request carries the session id of the session it just terminated. The server rejects it (-32600 "Session has been terminated"; observed as HTTP 400/404 at the client). The rejection surfaces as an unhandled ExceptionGroup ("unhandled errors in a TaskGroup"), which aborts the entire agent turn before the intended tool call is made. Net effect: the coordinator agent (artemis-oracle) can connect to and discover the tools of its sibling agents, but can never complete an actual delegation (tools/call) — every delegation turn dies on the reconnect. Environment Component Version anaconda-agent-runtime 0.1.0 mcp (Python SDK) 1.28.1 pydantic-ai-slim 1.106.0 httpx 0.28.1 openai 2.48.0 Python 3.14.6 Transport streamable_http (agent tools/mcp.json) Topology client agent → Studio backend proxy (127.0.0.1:54321) → server agent Impact Severity: high for multi-agent setups. Any agent that delegates to a sibling (the entire Oracle→specialist orchestration model) fails. Discovery (initialize + tools/list) works; only the reconnect for a follow-up call fails, so the failure looks intermittent and is easily mistaken for a model problem. The error reaches the caller as a generic Error: unhandled errors in a TaskGroup (1 sub-exception) with no indication that a stale session id is the cause. Observed sequence (from artemis-oracle/agent-serve.log) DELETE http://127.0.0.1:54321/api/agents/artemis-packrat/mcp "HTTP/1.1 200 OK" # session terminated mcp.client.streamable_http: Connecting to StreamableHTTP endpoint: .../artemis-packrat/mcp mcp.client.streamable_http: Sending client message: JSONRPCRequest(method='initialize', ...) POST http://127.0.0.1:54321/api/agents/artemis-packrat/mcp "HTTP/1.1 400 Bad Request" # reconnect rejected The reconnect initialize is sent immediately after the DELETE, and the client does not appear to clear the mcp-session-id request header it was using for the now-terminated session. Minimal reproduction (no LLM required) Against a running streamable-HTTP MCP server (here, a Studio agent via the backend proxy): BASE= http://127.0.0.1:54321/api/agents/artemis-packrat/mcp INIT='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"repro","version":"1"}}}' 1) initialize -> 200, capture the assigned session id SID=$(curl -s -D - -o /dev/null -X POST "$BASE" \ -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \ -d "$INIT" | awk 'BEGIN{IGNORECASE=1}/^mcp-session-id:/{gsub(/\r/,"");print $2}') 2) activate + terminate the session -> 200 curl -s -o /dev/null -X POST "$BASE" -H "mcp-session-id: $SID" \ -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' curl -s -o /dev/null -X DELETE "$BASE" -H "mcp-session-id: $SID" # 200 3) BUG: re-initialize while still carrying the terminated session id curl -s -X POST "$BASE" -H "mcp-session-id: $SID" \ -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \ -d "$INIT" -> HTTP 404, {"jsonrpc":"2.0","id":"server-error", "error":{"code":-32600,"message":"Not Found: Session has been terminated"}} 4) CONTROL: re-initialize with NO session-id header (correct client behavior) curl -s -o /dev/null -X POST "$BASE" \ -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \ -d "$INIT" -w '%{http_code}\n' -> 200 Step 3 reproduces the failure; step 4 shows the correct behavior. (In-runtime the client-visible status is 400; the isolated proxy repro returns 404 with the JSON-RPC -32600 "Session has been terminated" body. Both are the server rejecting a reconnect that carries a terminated session id.) Expected vs actual Expected: After DELETE-ing a session, the client should treat the next connection as fresh — send initialize with no mcp-session-id header and adopt the new id the server returns. Actual: The client sends initialize still carrying the terminated mcp-session-id; the server returns a 4xx -32600 and the client raises inside its TaskGroup, aborting the agent turn. Root cause (hypothesis) In the streamable_http client (mcp 1.28.1) session lifecycle, the stored mcp-session-id is not cleared when a session is terminated (or the client reuses a transport/header set across the terminate→reconnect boundary). The reconnect initialize therefore inherits the dead id. Per MCP spec, initialize starts a new session and must not be scoped to a prior (especially terminated) session id. Suggested fixes Client: clear mcp-session-id on DELETE/terminate, and never attach a session-id header to an initialize request. (Primary fix.) Runtime (anaconda_agent_runtime): when the MCP client raises during a sibling call, surface the underlying JSON-RPC error (e.g. "Session has been terminated") instead of the opaque unhandled errors in a TaskGroup, so the failure is diagnosable. Server/proxy (defensive): treat an initialize that carries an unknown or terminated session id as a fresh session (ignore the stale header) rather than rejecting — improves interop with clients that mis-handle the header. Notes / non-causes ruled out during investigation Not the model provider (exo): /v1/responses and /v1/chat/completions both return clean tool calls in isolation. Not endpoint choice: fixed a separate 405 by moving agent transports from /mcp/sse to /mcp; session DELETE then returns 200 and only the reconnect initialize fails. Not protocol version or benign session reuse: re-initialize against an active (non-terminated) session returns 200; only a terminated session id triggers the rejection.
0
·
Bugs
·
under review