MCP Streamable HTTP client: surface session_terminated to model instead of auto-reinitializing per spec

Status Fixed / completed
Maintainer reply None cached
Activity 3 comments · opened May 5, 2026 · closed May 6, 2026

Symptom

When the user's MCP server sleeps, redeploys, or returns 404 mcp_session_terminated for any other reason, claude-code surfaces the error to the model as a tool result instead of auto-reinitialising the session and replaying the call. The model sees something like:

Streamable HTTP error: Error POSTing to endpoint: {"type":"error","error":{"type":"not_found_error","message":"MCP session has been terminated or no longer exists on the server.","details":{"error_code":"mcp_session_terminated"}},"request_id":"req_011Caj9XocsEiGmk1EK83q7T"}

…in the middle of an otherwise-successful conversation. The next tool call usually succeeds (the client does re-init implicitly on the next request — it just leaks the failure of the one that triggered it).

Why it's a bug, not a feature

Per the MCP Streamable HTTP spec, §Session Management point 4:

When a client receives HTTP 404 in response to a request containing an MCP-Session-Id, it MUST start a new session by sending a new InitializeRequest without a session ID attached.

The spec is explicit that 404 mcp_session_terminated is a normal lifecycle event the client must transparently handle — not an error to surface to the model. claude-code's current behaviour is non-spec-compliant.

Repro

Any production MCP server that drops in-memory sessions across process boundaries reproduces this. Common shapes:

  • Fly.io / GCP Cloud Run / AWS ECS auto-stop: the server's machine sleeps after idle, wakes on the next request as a fresh process with empty session state.
  • Server redeploy: same effect.
  • Horizontal scaling without sticky sessions: a different replica gets the request than handled the initialize.
  • Network blip that the upstream proxy translates to a session-loss.

The 2026 MCP roadmap calls this out explicitly as a top problem ("stateful sessions fight with load balancers") and the canonical persistence pattern is still unresolved upstream (modelcontextprotocol/python-sdk#880). Servers can mitigate (we have — see "Workaround" below), but every server doing it independently doesn't scale; the spec puts the responsibility on the client and the client is the right layer to fix this in once for everyone.

Desired behaviour

When the Streamable HTTP transport gets back a 404 with the structured mcp_session_terminated shape (or any 404 to a request bearing an mcp-session-id):

  1. Discard the local mcp-session-id cookie.
  2. POST a fresh InitializeRequest to the same endpoint without a session-id, capture the new id from the response header.
  3. Re-issue the original tool call against the new session.
  4. Return the tool's actual result to the model. Surface the error only if step 2 or 3 also fails (a real outage rather than a stale session).

This is the spec-prescribed behaviour and the only way I'm aware of to deliver consistent UX across surfaces (Code, Desktop, mobile, web) without each MCP server independently implementing server-side workarounds.

Workaround we shipped (alfred-shell)

Until claude-code lands the spec-compliant client behaviour, our MCP server now lazy-recreates sessions: on a request with an unknown mcp-session-id and a valid auth token, it transparently creates a fresh transport bound to that exact id rather than 404'ing. Source if useful as a reference: zulven/alfred@3e16e1e — alfred-shell/src/http.ts.

This works around the symptom for our users but doesn't fix it for the broader MCP ecosystem — most third-party MCP servers will still 404, and end-users on Code / Desktop / mobile will still see the error mid-conversation.

Severity

Medium. Not a hard failure — the next tool call usually succeeds, so it's recoverable. But it leaks an internal protocol detail to the model (which then often "narrates" the failure to the user, training in a "retry" pattern), and reduces trust in the MCP layer. Easy fix at exactly one layer.

Happy to provide more detail or test fixes against alfred-shell if useful.

View original on GitHub ↗

3 Comments

github-actions[bot] · 5 months ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/40106

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

zulven · 5 months ago

Not a duplicate of #40106 — that's a plugin-MCP-never-initializes bug (sessions never created in the first place; tools don't appear in /mcp at all).

This issue is the post-handshake spec-compliance gap: server-issued sessions get terminated mid-conversation (server restart, Fly auto-stop, horizontal scale-out, etc.) and the client should re-init per §Session Management point 4 instead of leaking the error to the model.

The closer match is #27142 — same root cause, same proposed fix. It's currently closed-as-stale despite the bug still occurring as of 2026-05-05 across production MCP deployments. Filing fresh because (a) #27142 cites the 2025-11-05 spec; the current 2025-11-25 spec made the MUST language explicit, (b) the wire-format error shape evolved to the structured mcp_session_terminated error_code, and (c) #27142 is closed, so reopening visibility via a new issue with current refs seemed more useful than commenting on a stale-closed thread.

Happy to consolidate either direction if maintainers prefer — close this and reopen #27142, or vice versa. 👎'ing the auto-dupe here to prevent the 3-day auto-close.

github-actions[bot] · 3 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.