From bda811a95037b71dae55fdc2f2fc55660aaf486b Mon Sep 17 00:00:00 2001 From: Jerome Date: Mon, 30 Jun 2025 15:21:57 +0100 Subject: [PATCH] feat: Add CORS configuration for browser-based MCP clients - Add cors middleware to example servers with Mcp-Session-Id exposed - Add CORS documentation section to README - Configure minimal CORS settings (only expose required headers) This enables browser-based clients to connect to MCP servers by properly exposing the Mcp-Session-Id header required for session management. Reported-by: Jerome --- README.md | 20 +++++++++++++++++++ .../server/jsonResponseStreamableHttp.ts | 7 +++++++ .../server/simpleStatelessStreamableHttp.ts | 7 +++++++ .../sseAndStreamableHttpCompatibleServer.ts | 7 +++++++ 4 files changed, 41 insertions(+) diff --git a/README.md b/README.md index ac10e8cb..65189353 100644 --- a/README.md +++ b/README.md @@ -584,6 +584,26 @@ app.listen(3000); > ); > ``` + +#### CORS Configuration for Browser-Based Clients + +If you'd like your server to be accessible by browser-based MCP clients, you'll need to configure CORS headers. The `Mcp-Session-Id` header must be exposed for browser clients to access it: + +```typescript +import cors from 'cors'; + +// Add CORS middleware before your MCP routes +app.use(cors({ + origin: '*', // Configure appropriately for production + exposedHeaders: ['Mcp-Session-Id'] +})); +``` + +This configuration is necessary because: +- The MCP streamable HTTP transport uses the `Mcp-Session-Id` header for session management +- Browsers restrict access to response headers unless explicitly exposed via CORS +- Without this configuration, browser-based clients won't be able to read the session ID from initialization responses + #### Without Session Management (Stateless) For simpler use cases where session management isn't needed: diff --git a/src/examples/server/jsonResponseStreamableHttp.ts b/src/examples/server/jsonResponseStreamableHttp.ts index 02d8c2de..04b14470 100644 --- a/src/examples/server/jsonResponseStreamableHttp.ts +++ b/src/examples/server/jsonResponseStreamableHttp.ts @@ -4,6 +4,7 @@ import { McpServer } from '../../server/mcp.js'; import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js'; import { z } from 'zod'; import { CallToolResult, isInitializeRequest } from '../../types.js'; +import cors from 'cors'; // Create an MCP server with implementation details @@ -81,6 +82,12 @@ const getServer = () => { const app = express(); app.use(express.json()); +// Configure CORS to expose Mcp-Session-Id header for browser-based clients +app.use(cors({ + origin: '*', // Allow all origins - adjust as needed for production + exposedHeaders: ['Mcp-Session-Id'] +})); + // Map to store transports by session ID const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {}; diff --git a/src/examples/server/simpleStatelessStreamableHttp.ts b/src/examples/server/simpleStatelessStreamableHttp.ts index 6fb2ae83..d235265c 100644 --- a/src/examples/server/simpleStatelessStreamableHttp.ts +++ b/src/examples/server/simpleStatelessStreamableHttp.ts @@ -3,6 +3,7 @@ import { McpServer } from '../../server/mcp.js'; import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js'; import { z } from 'zod'; import { CallToolResult, GetPromptResult, ReadResourceResult } from '../../types.js'; +import cors from 'cors'; const getServer = () => { // Create an MCP server with implementation details @@ -96,6 +97,12 @@ const getServer = () => { const app = express(); app.use(express.json()); +// Configure CORS to expose Mcp-Session-Id header for browser-based clients +app.use(cors({ + origin: '*', // Allow all origins - adjust as needed for production + exposedHeaders: ['Mcp-Session-Id'] +})); + app.post('/mcp', async (req: Request, res: Response) => { const server = getServer(); try { diff --git a/src/examples/server/sseAndStreamableHttpCompatibleServer.ts b/src/examples/server/sseAndStreamableHttpCompatibleServer.ts index ded110a1..7b18578a 100644 --- a/src/examples/server/sseAndStreamableHttpCompatibleServer.ts +++ b/src/examples/server/sseAndStreamableHttpCompatibleServer.ts @@ -6,6 +6,7 @@ import { SSEServerTransport } from '../../server/sse.js'; import { z } from 'zod'; import { CallToolResult, isInitializeRequest } from '../../types.js'; import { InMemoryEventStore } from '../shared/inMemoryEventStore.js'; +import cors from 'cors'; /** * This example server demonstrates backwards compatibility with both: @@ -71,6 +72,12 @@ const getServer = () => { const app = express(); app.use(express.json()); +// Configure CORS to expose Mcp-Session-Id header for browser-based clients +app.use(cors({ + origin: '*', // Allow all origins - adjust as needed for production + exposedHeaders: ['Mcp-Session-Id'] +})); + // Store transports by session ID const transports: Record = {};