modelcontextprotocol / modelcontextprotocol/typescript-sdk
StreamableHTTPClientTransport doesn't handle 404 per spec (no session clear + re-init)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 4
Description
The MCP spec (2025-03-26), Session Management point 4 says:
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 newInitializeRequestwithout a session ID attached.
Neither send() (POST) nor _startOrAuthSse() (GET) handle 404. Both paths just throw StreamableHTTPError. There's no session ID clearing and no re-initialization.
send() has retry logic for 401 (auth) and 403 (insufficient_scope), but 404 falls through to:
throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${text}`);
Since _sessionId isn't cleared before the throw, subsequent send() calls keep sending the stale session ID and keep getting 404. It's not a transient failure - the client is stuck until it's destroyed and recreated.
This means servers that correctly return 404 for expired/unknown session IDs (as #389 asks them to) permanently break SDK-based clients instead of triggering a graceful reconnect.
Expected behavior: On 404, clear _sessionId and either re-initialize the session automatically or surface an event that lets the Client layer handle re-initialization (since InitializeRequest involves capability negotiation that the transport doesn't own).
Ref: #389 tracks the server-side of this (examples returning 400 instead of 404). This is the client-side counterpart. Affects v1.26.0 and v1.27.1.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading send() and _startOrAuthSse(), focusing on their existing 401 and 403 handling and the StreamableHTTPError path. Trace the Client initialization flow to decide whether a 404 should trigger re-initialization there or expose a transport event; done means stale session IDs no longer leave POST or GET requests permanently stuck.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100