From e5f6524eb621de4fbee94bffb60ed16910c1d102 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Mon, 21 Apr 2025 11:34:55 -0400 Subject: [PATCH 1/7] Fix support for streamable-http connections. * In server/index.js - add get/post handlers for /mcp - amend console log on SSE connect, with deprecation message - add /stdio GET handler and refactored /sse GET handler to not also do stdio. Each transport has its own handler now - add appropriate headers to streamable-http request * In /client/src/lib/hooks/useConnection.ts - in connect function - create server url properly based on new transport type. --- client/src/lib/hooks/useConnection.ts | 29 ++++-- server/src/index.ts | 121 ++++++++++++++++++++++++-- 2 files changed, 135 insertions(+), 15 deletions(-) diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index abbeb7c3..2577a417 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -278,15 +278,26 @@ export function useConnection({ setConnectionStatus("error-connecting-to-proxy"); return; } - const mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`); - mcpProxyServerUrl.searchParams.append("transportType", transportType); - if (transportType === "stdio") { - mcpProxyServerUrl.searchParams.append("command", command); - mcpProxyServerUrl.searchParams.append("args", args); - mcpProxyServerUrl.searchParams.append("env", JSON.stringify(env)); - } else { - mcpProxyServerUrl.searchParams.append("url", sseUrl); + let mcpProxyServerUrl; + switch (transportType) { + case "stdio": + mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/stdio`); + mcpProxyServerUrl.searchParams.append("command", command); + mcpProxyServerUrl.searchParams.append("args", args); + mcpProxyServerUrl.searchParams.append("env", JSON.stringify(env)); + break; + case "sse": + mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`); + mcpProxyServerUrl.searchParams.append("url", sseUrl); + break; + + case "streamable-http": + mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/mcp`); + mcpProxyServerUrl.searchParams.append("url", sseUrl); + break; } + (mcpProxyServerUrl as URL).searchParams.append("transportType", transportType); + try { // Inject auth manually instead of using SSEClientTransport, because we're @@ -304,7 +315,7 @@ export function useConnection({ headers[authHeaderName] = `Bearer ${token}`; } - const clientTransport = new SSEClientTransport(mcpProxyServerUrl, { + const clientTransport = new SSEClientTransport(mcpProxyServerUrl as URL, { eventSourceInit: { fetch: (url, init) => fetch(url, { ...init, headers }), }, diff --git a/server/src/index.ts b/server/src/index.ts index e966910d..08a88e77 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -97,7 +97,9 @@ const createTransport = async (req: express.Request): Promise => { console.log("Connected to SSE transport"); return transport; } else if (transportType === "streamable-http") { - const headers: HeadersInit = {}; + const headers: HeadersInit = { + Accept: "text/event-stream, application/json" + }; for (const key of STREAMABLE_HTTP_HEADERS_PASSTHROUGH) { if (req.headers[key] === undefined) { @@ -127,9 +129,10 @@ const createTransport = async (req: express.Request): Promise => { let backingServerTransport: Transport | undefined; -app.get("/sse", async (req, res) => { + +app.get("/mcp", async (req, res) => { try { - console.log("New SSE connection"); + console.log("New streamable-http connection"); try { await backingServerTransport?.close(); @@ -149,9 +152,7 @@ app.get("/sse", async (req, res) => { console.log("Connected MCP client to backing server transport"); - const webAppTransport = new SSEServerTransport("/message", res); - console.log("Created web app transport"); - + const webAppTransport = new SSEServerTransport("/mcp", res); webAppTransports.push(webAppTransport); console.log("Created web app transport"); @@ -181,6 +182,114 @@ app.get("/sse", async (req, res) => { } }); +app.post("/mcp", async (req, res) => { + try { + const sessionId = req.query.sessionId; + console.log(`Received message for sessionId ${sessionId}`); + + const transport = webAppTransports.find((t) => t.sessionId === sessionId); + if (!transport) { + res.status(404).end("Session not found"); + return; + } + await transport.handlePostMessage(req, res); + } catch (error) { + console.error("Error in /mcp route:", error); + res.status(500).json(error); + } +}); + +app.get("/stdio", async (req, res) => { + try { + console.log("New connection"); + + try { + await backingServerTransport?.close(); + backingServerTransport = await createTransport(req); + } catch (error) { + if (error instanceof SseError && error.code === 401) { + console.error( + "Received 401 Unauthorized from MCP server:", + error.message, + ); + res.status(401).json(error); + return; + } + + throw error; + } + + console.log("Connected MCP client to backing server transport"); + + const webAppTransport = new SSEServerTransport("/message", res); + webAppTransports.push(webAppTransport); + + console.log("Created web app transport"); + + await webAppTransport.start(); + (backingServerTransport as StdioClientTransport).stderr!.on("data", (chunk) => { + webAppTransport.send({ + jsonrpc: "2.0", + method: "notifications/stderr", + params: { + content: chunk.toString(), + }, + }); + }); + + mcpProxy({ + transportToClient: webAppTransport, + transportToServer: backingServerTransport, + }); + + console.log("Set up MCP proxy"); + } catch (error) { + console.error("Error in /stdio route:", error); + res.status(500).json(error); + } +}); + +app.get("/sse", async (req, res) => { + try { + console.log("New SSE connection. NOTE: The sse transport is deprecated and has been replaced by streamable-http"); + + try { + await backingServerTransport?.close(); + backingServerTransport = await createTransport(req); + } catch (error) { + if (error instanceof SseError && error.code === 401) { + console.error( + "Received 401 Unauthorized from MCP server:", + error.message, + ); + res.status(401).json(error); + return; + } + + throw error; + } + + console.log("Connected MCP client to backing server transport"); + + const webAppTransport = new SSEServerTransport("/message", res); + webAppTransports.push(webAppTransport); + + console.log("Created web app transport"); + + await webAppTransport.start(); + + mcpProxy({ + transportToClient: webAppTransport, + transportToServer: backingServerTransport, + }); + + console.log("Set up MCP proxy"); + } catch (error) { + console.error("Error in /sse route:", error); + res.status(500).json(error); + } +}); + app.post("/message", async (req, res) => { try { const sessionId = req.query.sessionId; From 3a2e2485273a4dd1bfb5186ceee73fab8f8b8b9a Mon Sep 17 00:00:00 2001 From: cliffhall Date: Mon, 21 Apr 2025 11:40:01 -0400 Subject: [PATCH 2/7] Prettier --- client/src/lib/hooks/useConnection.ts | 6 ++++-- server/src/index.ts | 14 +++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index 2577a417..1fe93ad3 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -296,8 +296,10 @@ export function useConnection({ mcpProxyServerUrl.searchParams.append("url", sseUrl); break; } - (mcpProxyServerUrl as URL).searchParams.append("transportType", transportType); - + (mcpProxyServerUrl as URL).searchParams.append( + "transportType", + transportType, + ); try { // Inject auth manually instead of using SSEClientTransport, because we're diff --git a/server/src/index.ts b/server/src/index.ts index 08a88e77..cb016098 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -98,7 +98,7 @@ const createTransport = async (req: express.Request): Promise => { return transport; } else if (transportType === "streamable-http") { const headers: HeadersInit = { - Accept: "text/event-stream, application/json" + Accept: "text/event-stream, application/json", }; for (const key of STREAMABLE_HTTP_HEADERS_PASSTHROUGH) { @@ -129,7 +129,6 @@ const createTransport = async (req: express.Request): Promise => { let backingServerTransport: Transport | undefined; - app.get("/mcp", async (req, res) => { try { console.log("New streamable-http connection"); @@ -227,7 +226,9 @@ app.get("/stdio", async (req, res) => { console.log("Created web app transport"); await webAppTransport.start(); - (backingServerTransport as StdioClientTransport).stderr!.on("data", (chunk) => { + (backingServerTransport as StdioClientTransport).stderr!.on( + "data", + (chunk) => { webAppTransport.send({ jsonrpc: "2.0", method: "notifications/stderr", @@ -235,7 +236,8 @@ app.get("/stdio", async (req, res) => { content: chunk.toString(), }, }); - }); + }, + ); mcpProxy({ transportToClient: webAppTransport, @@ -251,7 +253,9 @@ app.get("/stdio", async (req, res) => { app.get("/sse", async (req, res) => { try { - console.log("New SSE connection. NOTE: The sse transport is deprecated and has been replaced by streamable-http"); + console.log( + "New SSE connection. NOTE: The sse transport is deprecated and has been replaced by streamable-http", + ); try { await backingServerTransport?.close(); From 6e4dcd6120b374ab85b1852ed4f94dcbb5ba6d4c Mon Sep 17 00:00:00 2001 From: cliffhall Date: Tue, 22 Apr 2025 18:25:47 -0400 Subject: [PATCH 3/7] WIP: Attempting to proxy streamable-http connections. Inspector still works fine with STDIO and SSE servers. * In index.ts, - refactor transport webAppTransports to be a map with the session id as key and transport as value. * Implement /mcp GET and POST endpoints using StreamableHTTPServerTransport and doing the new session in the POST (opposite from SSE) handler. * In package.json - update the SDK to 1.10.2 * In useConnection.ts - import StreamableHTTPClientTransport - NOTE: while we NEED to do this, it causes useConnection.test.ts to fail with " ReferenceError: TransformStream is not defined" - in connect method - instantiate the appropriate transport --- client/src/lib/hooks/useConnection.ts | 18 +++- package-lock.json | 14 +-- package.json | 2 +- server/src/index.ts | 145 +++++++++++++++----------- 4 files changed, 106 insertions(+), 73 deletions(-) diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index 1fe93ad3..27bc11a2 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -3,6 +3,7 @@ import { SSEClientTransport, SseError, } from "@modelcontextprotocol/sdk/client/sse.js"; +import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; import { ClientNotification, ClientRequest, @@ -286,6 +287,7 @@ export function useConnection({ mcpProxyServerUrl.searchParams.append("args", args); mcpProxyServerUrl.searchParams.append("env", JSON.stringify(env)); break; + case "sse": mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`); mcpProxyServerUrl.searchParams.append("url", sseUrl); @@ -317,14 +319,24 @@ export function useConnection({ headers[authHeaderName] = `Bearer ${token}`; } - const clientTransport = new SSEClientTransport(mcpProxyServerUrl as URL, { + // Create appropriate transport + const transportOptions = { eventSourceInit: { - fetch: (url, init) => fetch(url, { ...init, headers }), + fetch: ( + url: string | URL | globalThis.Request, + init: RequestInit | undefined, + ) => fetch(url, { ...init, headers }), }, requestInit: { headers, }, - }); + }; + const clientTransport = + transportType === "streamable-http" + ? new StreamableHTTPClientTransport(mcpProxyServerUrl as URL, { + sessionId: undefined, + }) + : new SSEClientTransport(mcpProxyServerUrl as URL, transportOptions); if (onNotification) { [ diff --git a/package-lock.json b/package-lock.json index a8a025ce..f8461b33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@modelcontextprotocol/inspector-cli": "^0.10.2", "@modelcontextprotocol/inspector-client": "^0.10.2", "@modelcontextprotocol/inspector-server": "^0.10.2", - "@modelcontextprotocol/sdk": "^1.10.0", + "@modelcontextprotocol/sdk": "^1.10.2", "concurrently": "^9.0.1", "shell-quote": "^1.8.2", "spawn-rx": "^5.1.2", @@ -37,7 +37,7 @@ }, "cli": { "name": "@modelcontextprotocol/inspector-cli", - "version": "0.10.1", + "version": "0.10.2", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.10.0", @@ -58,7 +58,7 @@ }, "client": { "name": "@modelcontextprotocol/inspector-client", - "version": "0.10.1", + "version": "0.10.2", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.10.0", @@ -1399,9 +1399,9 @@ "link": true }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.10.0.tgz", - "integrity": "sha512-wijOavYZfSOADbVM0LA7mrQ17N4IKNdFcfezknCCsZ1Y1KstVWlkDZ5ebcxuQJmqTTxsNjBHLc7it1SV0TBiPg==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.10.2.tgz", + "integrity": "sha512-rb6AMp2DR4SN+kc6L1ta2NCpApyA9WYNx3CrTSZvGxq9wH71bRur+zRqPfg0vQ9mjywR7qZdX2RGHOPq3ss+tA==", "license": "MIT", "dependencies": { "content-type": "^1.0.5", @@ -8550,7 +8550,7 @@ }, "server": { "name": "@modelcontextprotocol/inspector-server", - "version": "0.10.1", + "version": "0.10.2", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.10.0", diff --git a/package.json b/package.json index f20af506..d127d138 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@modelcontextprotocol/inspector-cli": "^0.10.2", "@modelcontextprotocol/inspector-client": "^0.10.2", "@modelcontextprotocol/inspector-server": "^0.10.2", - "@modelcontextprotocol/sdk": "^1.10.0", + "@modelcontextprotocol/sdk": "^1.10.2", "concurrently": "^9.0.1", "shell-quote": "^1.8.2", "spawn-rx": "^5.1.2", diff --git a/server/src/index.ts b/server/src/index.ts index cb016098..b294cfe7 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -12,15 +12,17 @@ import { StdioClientTransport, getDefaultEnvironment, } from "@modelcontextprotocol/sdk/client/stdio.js"; -import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; -import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; +import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import express from "express"; import { findActualExecutable } from "spawn-rx"; import mcpProxy from "./mcpProxy.js"; +import { randomUUID } from "node:crypto"; const SSE_HEADERS_PASSTHROUGH = ["authorization"]; -const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = ["authorization"]; +const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = ["authorization", "mcp-session-id"]; const defaultEnvironment = { ...getDefaultEnvironment(), @@ -38,7 +40,7 @@ const { values } = parseArgs({ const app = express(); app.use(cors()); -let webAppTransports: SSEServerTransport[] = []; +const webAppTransports: Map = new Map(); // Transports by sessionId const createTransport = async (req: express.Request): Promise => { const query = req.query; @@ -130,71 +132,89 @@ const createTransport = async (req: express.Request): Promise => { let backingServerTransport: Transport | undefined; app.get("/mcp", async (req, res) => { + const sessionId = req.headers["mcp-session-id"] as string; + console.log(`Received GET message for sessionId ${sessionId}`); try { - console.log("New streamable-http connection"); + const transport = webAppTransports.get( + sessionId, + ) as StreamableHTTPServerTransport; + if (!transport) { + res.status(404).end("Session not found"); + return; + } else { + await transport.handleRequest(req, res); + } + } catch (error) { + console.error("Error in /mcp route:", error); + res.status(500).json(error); + } +}); +app.post("/mcp", async (req, res) => { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + console.log(`Received POST message for sessionId ${sessionId}`); + if (!sessionId) { try { - await backingServerTransport?.close(); - backingServerTransport = await createTransport(req); - } catch (error) { - if (error instanceof SseError && error.code === 401) { - console.error( - "Received 401 Unauthorized from MCP server:", - error.message, - ); - res.status(401).json(error); - return; + console.log("New streamable-http connection"); + try { + await backingServerTransport?.close(); + backingServerTransport = await createTransport(req); + } catch (error) { + if (error instanceof SseError && error.code === 401) { + console.error( + "Received 401 Unauthorized from MCP server:", + error.message, + ); + res.status(401).json(error); + return; + } + + throw error; } - throw error; - } - - console.log("Connected MCP client to backing server transport"); - - const webAppTransport = new SSEServerTransport("/mcp", res); - webAppTransports.push(webAppTransport); - console.log("Created web app transport"); - - await webAppTransport.start(); + console.log("Connected MCP client to backing server transport"); - if (backingServerTransport instanceof StdioClientTransport) { - backingServerTransport.stderr!.on("data", (chunk) => { - webAppTransport.send({ - jsonrpc: "2.0", - method: "notifications/stderr", - params: { - content: chunk.toString(), - }, - }); + const webAppTransport = new StreamableHTTPServerTransport({ + sessionIdGenerator: randomUUID, + onsessioninitialized: (sessionId) => { + webAppTransports.set(sessionId, webAppTransport); + console.log("Created streamable web app transport " + sessionId); + }, }); - } - - mcpProxy({ - transportToClient: webAppTransport, - transportToServer: backingServerTransport, - }); - console.log("Set up MCP proxy"); - } catch (error) { - console.error("Error in /sse route:", error); - res.status(500).json(error); - } -}); + await webAppTransport.start(); -app.post("/mcp", async (req, res) => { - try { - const sessionId = req.query.sessionId; - console.log(`Received message for sessionId ${sessionId}`); + mcpProxy({ + transportToClient: webAppTransport, + transportToServer: backingServerTransport, + }); - const transport = webAppTransports.find((t) => t.sessionId === sessionId); - if (!transport) { - res.status(404).end("Session not found"); - return; + await (webAppTransport as StreamableHTTPServerTransport).handleRequest( + req, + res, + req.body, + ); + } catch (error) { + console.error("Error in /mcp POST route:", error); + res.status(500).json(error); + } + } else { + try { + const transport = webAppTransports.get( + sessionId, + ) as StreamableHTTPServerTransport; + if (!transport) { + res.status(404).end("Transport not found for sessionId " + sessionId); + } else { + await (transport as StreamableHTTPServerTransport).handleRequest( + req, + res, + ); + } + } catch (error) { + console.error("Error in /mcp route:", error); + res.status(500).json(error); } - await transport.handlePostMessage(req, res); - } catch (error) { - console.error("Error in /mcp route:", error); - res.status(500).json(error); } }); @@ -221,7 +241,7 @@ app.get("/stdio", async (req, res) => { console.log("Connected MCP client to backing server transport"); const webAppTransport = new SSEServerTransport("/message", res); - webAppTransports.push(webAppTransport); + webAppTransports.set(webAppTransport.sessionId, webAppTransport); console.log("Created web app transport"); @@ -276,8 +296,7 @@ app.get("/sse", async (req, res) => { console.log("Connected MCP client to backing server transport"); const webAppTransport = new SSEServerTransport("/message", res); - webAppTransports.push(webAppTransport); - + webAppTransports.set(webAppTransport.sessionId, webAppTransport); console.log("Created web app transport"); await webAppTransport.start(); @@ -299,7 +318,9 @@ app.post("/message", async (req, res) => { const sessionId = req.query.sessionId; console.log(`Received message for sessionId ${sessionId}`); - const transport = webAppTransports.find((t) => t.sessionId === sessionId); + const transport = webAppTransports.get( + sessionId as string, + ) as SSEServerTransport; if (!transport) { res.status(404).end("Session not found"); return; From 1ec4e3b556e4fc8d426d5b4ca51d4d936719b752 Mon Sep 17 00:00:00 2001 From: Shiv Deepak Muddada Date: Wed, 23 Apr 2025 01:43:49 -0700 Subject: [PATCH 4/7] fix cors issue with accessing mcp-session-id header --- server/src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/index.ts b/server/src/index.ts index b294cfe7..ed79ae90 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -39,6 +39,10 @@ const { values } = parseArgs({ const app = express(); app.use(cors()); +app.use((req, res, next) => { + res.header("Access-Control-Expose-Headers", "mcp-session-id"); + next(); +}); const webAppTransports: Map = new Map(); // Transports by sessionId From bf1026b6ec357a0e4a9337a51db58642881e2374 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Wed, 23 Apr 2025 17:52:17 -0400 Subject: [PATCH 5/7] Fix failing unit tests * This PR caused the Sidebar.test.ts file tests to fail because TransformStream is not found. * TransformStream exists in the Node version I'm testing with (20), but it still isn't found by Jest * Turns out it is a problem with Jest, and the workaround is the simple package jest-fixed-jsdom, which subclasses JSDOMEnvironment testing environment, placing this and several other dependencies in its global object. * In package.json - add jest-fixed-jsdom as a devDependency * In jest.config.cjs - change testEnvironment to jest-fixed-jsdom --- client/jest.config.cjs | 2 +- package-lock.json | 14 ++++++++++++++ package.json | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/client/jest.config.cjs b/client/jest.config.cjs index c360e72b..b4d81cfc 100644 --- a/client/jest.config.cjs +++ b/client/jest.config.cjs @@ -1,6 +1,6 @@ module.exports = { preset: "ts-jest", - testEnvironment: "jsdom", + testEnvironment: "jest-fixed-jsdom", moduleNameMapper: { "^@/(.*)$": "/src/$1", "\\.css$": "/src/__mocks__/styleMock.js", diff --git a/package-lock.json b/package-lock.json index f8461b33..973ca62c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "@types/jest": "^29.5.14", "@types/node": "^22.7.5", "@types/shell-quote": "^1.7.5", + "jest-fixed-jsdom": "^0.0.9", "prettier": "3.3.3", "typescript": "^5.4.2" } @@ -5358,6 +5359,19 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-fixed-jsdom": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/jest-fixed-jsdom/-/jest-fixed-jsdom-0.0.9.tgz", + "integrity": "sha512-KPfqh2+sn5q2B+7LZktwDcwhCpOpUSue8a1I+BcixWLOQoEVyAjAGfH+IYZGoxZsziNojoHGRTC8xRbB1wDD4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "jest-environment-jsdom": ">=28.0.0" + } + }, "node_modules/jest-get-type": { "version": "29.6.3", "dev": true, diff --git a/package.json b/package.json index d127d138..bba280cd 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@types/jest": "^29.5.14", "@types/node": "^22.7.5", "@types/shell-quote": "^1.7.5", + "jest-fixed-jsdom": "^0.0.9", "prettier": "3.3.3", "typescript": "^5.4.2" } From 7b9cd1e74d05c5036b019053f35ec34dc2794f0c Mon Sep 17 00:00:00 2001 From: cliffhall Date: Thu, 24 Apr 2025 11:39:48 -0400 Subject: [PATCH 6/7] In server/index.ts - Add last-event-id to STREAMABLE_HTTP_HEADERS_PASSTHROUGH. --- server/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/index.ts b/server/src/index.ts index ed79ae90..4bc78378 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -22,7 +22,7 @@ import mcpProxy from "./mcpProxy.js"; import { randomUUID } from "node:crypto"; const SSE_HEADERS_PASSTHROUGH = ["authorization"]; -const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = ["authorization", "mcp-session-id"]; +const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = ["authorization", "mcp-session-id", "last-event-id"]; const defaultEnvironment = { ...getDefaultEnvironment(), From 5d0c3c48f67815549641df0d287912c909d08dd5 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Thu, 24 Apr 2025 12:01:02 -0400 Subject: [PATCH 7/7] Prettier --- server/src/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/index.ts b/server/src/index.ts index 4bc78378..c967b60c 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -22,7 +22,11 @@ import mcpProxy from "./mcpProxy.js"; import { randomUUID } from "node:crypto"; const SSE_HEADERS_PASSTHROUGH = ["authorization"]; -const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = ["authorization", "mcp-session-id", "last-event-id"]; +const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = [ + "authorization", + "mcp-session-id", + "last-event-id", +]; const defaultEnvironment = { ...getDefaultEnvironment(),