Problem
Some MCP clients (e.g., OpenCode with type: "remote") do not preserve the mcp-session-id header across requests. The client correctly sends initialize, receives a session ID, but subsequent tools/list and tool call requests omit the header.
The current server rejects these with:
400 Bad Request: No valid session ID provided
Root Cause
@modelcontextprotocol/sdk v1.x requires sessionIdGenerator to be a function. Setting it to undefined throws a type error — stateless mode isn't available in v1.
Solution
The v2 SDK (@modelcontextprotocol/server@2.0.0-beta.4 + @modelcontextprotocol/node@2.0.0-beta.4) supports native stateless mode:
// v2: stateless — sessionIdGenerator: undefined disables session validation
const transport = new NodeStreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
This would accept any POST without an mcp-session-id header, solving the issue cleanly.
Migration Scope
The v2 SDK replaces the monolithic @modelcontextprotocol/sdk with three packages:
@modelcontextprotocol/server — McpServer, types
@modelcontextprotocol/node — NodeStreamableHTTPServerTransport
@modelcontextprotocol/express — createMcpExpressApp
This touches ~16 files across src/ and __tests__/. The import paths change:
@modelcontextprotocol/sdk/server/mcp.js → @modelcontextprotocol/server
@modelcontextprotocol/sdk/server/streamableHttp.js → @modelcontextprotocol/node
@modelcontextprotocol/sdk/server/stdio.js → @modelcontextprotocol/node
@modelcontextprotocol/sdk/types.js → @modelcontextprotocol/server
Also requires zod as a peer dependency.
References
Problem
Some MCP clients (e.g., OpenCode with
type: "remote") do not preserve themcp-session-idheader across requests. The client correctly sendsinitialize, receives a session ID, but subsequenttools/listand tool call requests omit the header.The current server rejects these with:
Root Cause
@modelcontextprotocol/sdkv1.x requiressessionIdGeneratorto be a function. Setting it toundefinedthrows a type error — stateless mode isn't available in v1.Solution
The v2 SDK (
@modelcontextprotocol/server@2.0.0-beta.4+@modelcontextprotocol/node@2.0.0-beta.4) supports native stateless mode:This would accept any POST without an
mcp-session-idheader, solving the issue cleanly.Migration Scope
The v2 SDK replaces the monolithic
@modelcontextprotocol/sdkwith three packages:@modelcontextprotocol/server—McpServer, types@modelcontextprotocol/node—NodeStreamableHTTPServerTransport@modelcontextprotocol/express—createMcpExpressAppThis touches ~16 files across
src/and__tests__/. The import paths change:@modelcontextprotocol/sdk/server/mcp.js→@modelcontextprotocol/server@modelcontextprotocol/sdk/server/streamableHttp.js→@modelcontextprotocol/node@modelcontextprotocol/sdk/server/stdio.js→@modelcontextprotocol/node@modelcontextprotocol/sdk/types.js→@modelcontextprotocol/serverAlso requires
zodas a peer dependency.References