From 2993570503501c2e9cc6e690924b5fea03af19e5 Mon Sep 17 00:00:00 2001 From: Jeremy Schoemaker Date: Sun, 23 Aug 2026 10:40:35 -0500 Subject: [PATCH] fix: allow mcp-protocol-version request header in CORS preflight The cors() options already expose mcp-protocol-version via exposedHeaders, but allowedHeaders omitted it. The official StreamableHTTPClientTransport sends mcp-protocol-version on every request after initialize, so any browser-based client permitted via ALLOWED_ORIGINS failed the CORS preflight once initialization completed. Add the header to allowedHeaders in the server and in the mirrored transport test fixture, and add a preflight regression test. --- src/http.config.test.ts | 24 ++++++++++++++++++++++++ src/http.ts | 2 +- src/transport.test.ts | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/http.config.test.ts b/src/http.config.test.ts index a0d6205..117b249 100644 --- a/src/http.config.test.ts +++ b/src/http.config.test.ts @@ -107,6 +107,30 @@ describe("HTTP transport configuration", () => { ); }); + it("allows the mcp-protocol-version request header in preflight", async () => { + await start({ allowedOrigins: ["https://app.example"] }); + + // The official StreamableHTTPClientTransport sends mcp-protocol-version + // on every request after initialize, so browser preflights ask for it. + const preflight = await fetch(`${baseUrl}/mcp`, { + method: "OPTIONS", + headers: { + Origin: "https://app.example", + "Access-Control-Request-Method": "POST", + "Access-Control-Request-Headers": + "content-type,mcp-session-id,mcp-protocol-version", + }, + }); + + expect(preflight.headers.get("access-control-allow-origin")).toBe( + "https://app.example", + ); + const allowHeaders = ( + preflight.headers.get("access-control-allow-headers") ?? "" + ).toLowerCase(); + expect(allowHeaders).toContain("mcp-protocol-version"); + }); + it("does not allow a non-allowlisted origin even when others are allowlisted", async () => { await start({ allowedOrigins: ["https://app.example"] }); diff --git a/src/http.ts b/src/http.ts index 8c96715..511a4a1 100644 --- a/src/http.ts +++ b/src/http.ts @@ -91,7 +91,7 @@ export function createHttpApp(options: HttpAppOptions): Express { return callback(new CorsOriginNotAllowedError(origin)); }, exposedHeaders: ["Mcp-Session-Id", "mcp-protocol-version"], - allowedHeaders: ["Content-Type", "mcp-session-id"], + allowedHeaders: ["Content-Type", "mcp-session-id", "mcp-protocol-version"], }), ); diff --git a/src/transport.test.ts b/src/transport.test.ts index f6ef72f..5bd276b 100644 --- a/src/transport.test.ts +++ b/src/transport.test.ts @@ -76,7 +76,7 @@ describe("Transport Integration Tests", () => { app.use(cors({ origin: "*", exposedHeaders: ["Mcp-Session-Id", "mcp-protocol-version"], - allowedHeaders: ["Content-Type", "mcp-session-id"], + allowedHeaders: ["Content-Type", "mcp-session-id", "mcp-protocol-version"], })); app.use(express.json()); });