Skip to content

StreamableHTTPServerTransport destroys sessions on TCP keepalive timeout (regression in 1.25+) #1852

@cdunda-perchwell

Description

@cdunda-perchwell

Description

MCP sessions served by StreamableHTTPServerTransport are destroyed when the underlying TCP connection closes due to the Node.js default keepAliveTimeout (5 seconds). Subsequent requests with a valid Mcp-Session-Id receive a 400 "Session not found" error.

This is a regression introduced between SDK versions 1.24.x and 1.27.x. Sessions should survive across independent HTTP requests — the Mcp-Session-Id header is the continuity mechanism, not a persistent TCP connection.

Reproduction

Using mcp-proxy (npm) as the server wrapper:

# Works — sessions survive 8s idle
npx -y mcp-proxy@6.4.3 --port 9999 -- npx -y @modelcontextprotocol/server-everything
# mcp-proxy 6.4.3 uses @modelcontextprotocol/sdk ^1.24.3

# Broken — sessions die after 5s idle
npx -y mcp-proxy@6.4.4 --port 9999 -- npx -y @modelcontextprotocol/server-everything
# mcp-proxy 6.4.4 uses @modelcontextprotocol/sdk ^1.27.1

Test sequence:

  1. POST /mcp with initialize — 200, get Mcp-Session-Id
  2. Wait 8 seconds (past Node.js default keepAliveTimeout of 5s)
  3. POST /mcp with tools/list and the session ID — 400 "Session not found"

With SDK 1.24.x, step 3 succeeds. With SDK 1.27.x, it fails.

If a client keeps an SSE GET stream open to /mcp during the idle period, the session survives. This confirms the session is being destroyed when the TCP connection closes, not on an application-level timeout.

Impact

This breaks any proxy or gateway that uses HTTP/1.1 connection pooling to talk to a Streamable HTTP MCP server. The gateway opens a connection, sends initialize + tools/list, the connection goes idle, Node.js closes it after 5 seconds, and the session is destroyed. The next tools/call fails.

Affected setups:

  • AgentGateway (agentgateway.dev) proxying to mcp-proxy-wrapped servers
  • Any HTTP reverse proxy with connection pooling in front of an SDK-based MCP server

Expected behavior

Sessions should persist independently of TCP connection lifetime. The Mcp-Session-Id header identifies the session across requests. A session should only be destroyed by an explicit DELETE request or a server-side session timeout policy, not by TCP keepalive.

Environment

  • @modelcontextprotocol/sdk: regression between 1.24.x and 1.27.x
  • mcp-proxy: 6.4.3 (works) vs 6.4.4 (broken, only change was SDK bump)
  • Node.js: tested on v22 and v25
  • server.keepAliveTimeout: default 5000ms

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions