diff --git a/package.json b/package.json index 08d053a..f86d228 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "zod": "^3.25.50" }, "peerDependencies": { - "@modelcontextprotocol/sdk": "1.25.2", + "@modelcontextprotocol/sdk": ">=1.25.2", "next": ">=13.0.0" }, "peerDependenciesMeta": { diff --git a/src/handler/mcp-api-handler.ts b/src/handler/mcp-api-handler.ts index 5dd92e2..1803617 100644 --- a/src/handler/mcp-api-handler.ts +++ b/src/handler/mcp-api-handler.ts @@ -277,11 +277,6 @@ export function initializeMcpApiHandler( let servers: McpServer[] = []; - let statelessServer: McpServer; - const statelessTransport = new StreamableHTTPServerTransport({ - sessionIdGenerator: sessionIdGenerator, - }); - // Start periodic cleanup if not already running if (!cleanupInterval) { cleanupInterval = setInterval(() => { @@ -363,11 +358,14 @@ export function initializeMcpApiHandler( config.onEvent ); - if (!statelessServer) { - statelessServer = new McpServer(serverInfo, mcpServerOptions); - await initializeServer(statelessServer); - await statelessServer.connect(statelessTransport); - } + // CVE-2026-25536: SDK >=1.26.0 requires a fresh transport+server per + // request in stateless mode to prevent cross-client data leaks. + const statelessTransport = new StreamableHTTPServerTransport({ + sessionIdGenerator: sessionIdGenerator, + }); + const statelessServer = new McpServer(serverInfo, mcpServerOptions); + await initializeServer(statelessServer); + await statelessServer.connect(statelessTransport); // Parse the request body let bodyContent: BodyType; diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts index 412ad00..e96f7cf 100644 --- a/tests/e2e.test.ts +++ b/tests/e2e.test.ts @@ -310,6 +310,47 @@ describe("e2e", () => { ); }); + // Regression test for CVE-2026-25536: the handler must create a fresh + // transport per request. Without the fix, the second client's initialize + // fails because the SDK rejects reuse of a stateless transport. + it("should handle multiple independent clients sequentially", async () => { + // First client — connects and calls a tool + const transport1 = new StreamableHTTPClientTransport( + new URL(`${endpoint}/mcp`) + ); + const client1 = new Client( + { name: "client-1", version: "1.0.0" }, + { capabilities: {} } + ); + await client1.connect(transport1); + const result1 = await client1.callTool( + { name: "echo", arguments: { message: "from client 1" } }, + undefined, + {} + ); + expect((result1.content as any)[0].text).toEqual( + "Tool echo: from client 1" + ); + + // Second client — must also succeed against the same server + const transport2 = new StreamableHTTPClientTransport( + new URL(`${endpoint}/mcp`) + ); + const client2 = new Client( + { name: "client-2", version: "1.0.0" }, + { capabilities: {} } + ); + await client2.connect(transport2); + const result2 = await client2.callTool( + { name: "echo", arguments: { message: "from client 2" } }, + undefined, + {} + ); + expect((result2.content as any)[0].text).toEqual( + "Tool echo: from client 2" + ); + }); + it("should return an invalid token error when verifyToken fails", async () => { const authenticatedTransport = new StreamableHTTPClientTransport( new URL(`${endpoint}/mcp`),