fix(client): handle HTTP 410 Gone for expired/stale MCP sessions#1402
fix(client): handle HTTP 410 Gone for expired/stale MCP sessions#1402joseairosa wants to merge 3 commits intomodelcontextprotocol:mainfrom
Conversation
When a server returns HTTP 410 Gone (indicating the session no longer exists), the client now automatically clears the stale session ID and retries the request. This enables seamless reconnection after server restarts or session timeouts. Previously, receiving a 410 would throw an error but the client would retain the invalid session ID, causing all subsequent requests to fail with the same 410 error - breaking the connection permanently until the client was restarted. Changes: - Add 410 handling in send() method for POST requests - Add 410 handling in _startOrAuthSse() for SSE GET requests - Clear _sessionId before retrying to allow server to assign a new session - Add comprehensive tests for both POST and GET 410 scenarios Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: b4124d1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@modelcontextprotocol/client
@modelcontextprotocol/server
@modelcontextprotocol/express
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
felixweinberger
left a comment
There was a problem hiding this comment.
Thanks for this! A few thoughts:
-
Did you encounter this 410 in the wild with a real server? The spec says servers should return HTTP 404 for expired sessions and doesn't mention 410.
-
Should we add a guard against infinite recursion, similar to
_hasCompletedAuthFlowfor 401? While unlikely with a well-behaved server, it matches the existing defensive pattern. -
Per spec: "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 newInitializeRequest". Just retrying the original message (e.g., atools/call) won't work - should this trigger a full re-initialization flow instead?
Summary
This PR adds proper handling for HTTP 410 Gone responses in
StreamableHTTPClientTransport, enabling seamless reconnection when MCP sessions expire or servers restart.Problem
When an MCP server restarts or a session times out, the server returns HTTP 410 Gone to indicate the session ID is no longer valid. Currently, the client:
_sessionIdThis creates a poor user experience where MCP connections become permanently broken after server restarts, requiring users to manually restart their client applications.
Real-world scenario
Solution
Handle 410 responses by:
_sessionIdThis follows the same pattern already used for 401 (auth) and 403 (scope) responses.
After this fix
Changes
send()method: Added 410 handling that clears session ID and retries POST requests_startOrAuthSse()method: Added 410 handling that clears session ID and retries SSE GET connectionsTesting
All existing tests pass, plus 2 new tests:
should clear session ID and retry on 410 during POST requestshould clear session ID and retry on 410 during SSE GET requestRelated
This complements the server-side behavior where servers should return 410 Gone for expired sessions, as documented in the MCP Streamable HTTP transport specification.