modelcontextprotocol / modelcontextprotocol/typescript-sdk

StreamableHTTPClientTransport doesn't handle 404 per spec (no session clear + re-init)

Open
#1,708 8 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug fix proposed P2 ready for work
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 new InitializeRequest without 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.