From 386fd9c7039fe7da126c3532ad4f0012dc20a31c Mon Sep 17 00:00:00 2001 From: Jay Hemnani <193022578+jayhemnani9910@users.noreply.github.com> Date: Fri, 2 Oct 2026 00:39:46 +0530 Subject: [PATCH] fix(everything): answer 404 for an unknown streamable HTTP session Requests carrying a session ID the server didn't know (never issued, or already terminated with DELETE) got 400 "No valid session ID provided". The transport spec requires 404 here so the client starts a new session; with 400 a client cannot recover, e.g. after a server restart. Return 404 "Session not found" (as the SDK transport does) for an unknown session ID, and keep 400 for a missing one. --- src/everything/transports/streamableHttp.ts | 35 ++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/everything/transports/streamableHttp.ts b/src/everything/transports/streamableHttp.ts index 2e79abc554..bdaea3a41b 100644 --- a/src/everything/transports/streamableHttp.ts +++ b/src/everything/transports/streamableHttp.ts @@ -56,6 +56,18 @@ const transports: Map = new Map< StreamableHTTPServerTransport >(); +// The spec requires 404 for a session ID the server does not know (or has terminated). +const sendSessionNotFound = (req: Request, res: Response) => { + res.status(404).json({ + jsonrpc: "2.0", + error: { + code: -32001, + message: "Session not found", + }, + id: req?.body?.id, + }); +}; + // Handle POST requests for client messages app.post("/mcp", async (req: Request, res: Response) => { console.log("Received MCP POST request"); @@ -102,15 +114,8 @@ app.post("/mcp", async (req: Request, res: Response) => { await transport.handleRequest(req, res); return; } else { - // Invalid request - no session ID or not initialization request - res.status(400).json({ - jsonrpc: "2.0", - error: { - code: -32000, - message: "Bad Request: No valid session ID provided", - }, - id: req?.body?.id, - }); + // Unknown or terminated session: 404 tells the client to re-initialize + sendSessionNotFound(req, res); return; } @@ -137,7 +142,7 @@ app.post("/mcp", async (req: Request, res: Response) => { app.get("/mcp", async (req: Request, res: Response) => { console.log("Received MCP GET request"); const sessionId = req.headers["mcp-session-id"] as string | undefined; - if (!sessionId || !transports.has(sessionId)) { + if (!sessionId) { res.status(400).json({ jsonrpc: "2.0", error: { @@ -148,6 +153,10 @@ app.get("/mcp", async (req: Request, res: Response) => { }); return; } + if (!transports.has(sessionId)) { + sendSessionNotFound(req, res); + return; + } // Check for Last-Event-ID header for resumability const lastEventId = req.headers["last-event-id"] as string | undefined; @@ -164,7 +173,7 @@ app.get("/mcp", async (req: Request, res: Response) => { // Handle DELETE requests for session termination app.delete("/mcp", async (req: Request, res: Response) => { const sessionId = req.headers["mcp-session-id"] as string | undefined; - if (!sessionId || !transports.has(sessionId)) { + if (!sessionId) { res.status(400).json({ jsonrpc: "2.0", error: { @@ -175,6 +184,10 @@ app.delete("/mcp", async (req: Request, res: Response) => { }); return; } + if (!transports.has(sessionId)) { + sendSessionNotFound(req, res); + return; + } console.log(`Received session termination request for session ${sessionId}`);