Skip to content

Bug: MCP HTTP server crashes on second request with "Stateless transport cannot be reused"#166

Open
Onyelaudochukwuka wants to merge 1 commit intotobi:mainfrom
Onyelaudochukwuka:main
Open

Bug: MCP HTTP server crashes on second request with "Stateless transport cannot be reused"#166
Onyelaudochukwuka wants to merge 1 commit intotobi:mainfrom
Onyelaudochukwuka:main

Conversation

@Onyelaudochukwuka
Copy link

Fixes #163

Description

The MCP HTTP server (qmd mcp --http) crashes when handling a second request with the following error:

Error: Stateless transport cannot be reused across requests. Create a new transport per request.

Stack trace:

at handleRequest (.../node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.js)
at fetch (qmd/src/mcp.ts:610)

Root Cause

In src/mcp.ts:556-558, a single WebStandardStreamableHTTPServerTransport is created at server startup and reused for all incoming HTTP requests:

const transport = new WebStandardStreamableHTTPServerTransport({
  enableJsonResponse: true,
});
await mcpServer.connect(transport);

When no sessionIdGenerator is provided, the transport operates in stateless mode. The MCP SDK's WebStandardStreamableHTTPServerTransport sets an internal _hasHandledRequest flag after the first call to handleRequest() and throws on all subsequent calls to prevent message ID collisions between requests.

This design is intentional for serverless/lambda deployments where each invocation creates a fresh transport, but breaks for persistent HTTP servers like Bun.serve().

Expected Behavior

The MCP HTTP server should handle multiple concurrent requests without crashing.

Actual Behavior

  • First request succeeds
  • Second request throws: "Stateless transport cannot be reused across requests"
  • Server becomes unusable

Reproduction

# Terminal 1: Start the HTTP server
qmd mcp --http --port 8181

# Terminal 2: Send two requests
curl -X POST http://localhost:8181/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'

# First request works, second request crashes:
curl -X POST http://localhost:8181/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

Solution

Add sessionIdGenerator to enable stateful mode, which is the correct pattern for persistent HTTP servers:

const transport = new WebStandardStreamableHTTPServerTransport({
  enableJsonResponse: true,
  sessionIdGenerator: () => crypto.randomUUID(),
});

Why this works:

  • The transport now manages sessions via the Mcp-Session-Id header (part of the MCP Streamable HTTP spec)
  • Multiple requests can be handled by routing them to the appropriate session
  • MCP clients (Claude Desktop, Cursor, etc.) automatically handle session headers
  • The stop() cleanup remains clean—just one transport to close

Files Changed

  • src/mcp.ts line 557 — add sessionIdGenerator: () => crypto.randomUUID()

Testing

After fix:

# Start server
qmd mcp --http --port 8181

# Multiple requests should all succeed
curl -X POST http://localhost:8181/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'

curl -X POST http://localhost:8181/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

curl -X POST http://localhost:8181/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"qmd_status"}}'

References

@kenaroik
Copy link

@Onyelaudochukwuka Thanks! This solves the error for me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

qmd mcp --http fails on second request: "Stateless transport cannot be reused"

2 participants