Skip to content

Commit 7a554e7

Browse files
feat: Add CORS configuration for browser-based MCP clients
- Add CORSMiddleware to streamable HTTP example servers - Configure minimal CORS with Mcp-Session-Id exposed - Add CORS documentation section to README 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
1 parent 7942184 commit 7a554e7

File tree

3 files changed

+39
-0
lines changed
  • examples/servers
    • simple-streamablehttp-stateless/mcp_simple_streamablehttp_stateless
    • simple-streamablehttp/mcp_simple_streamablehttp

3 files changed

+39
-0
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,27 @@ The streamable HTTP transport supports:
713713
- JSON or SSE response formats
714714
- Better scalability for multi-node deployments
715715

716+
#### CORS Configuration for Browser-Based Clients
717+
718+
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:
719+
720+
```python
721+
from starlette.middleware.cors import CORSMiddleware
722+
723+
# Add CORS middleware to your Starlette app
724+
app.add_middleware(
725+
CORSMiddleware,
726+
allow_origins=["*"], # Configure appropriately for production
727+
allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
728+
expose_headers=["Mcp-Session-Id"],
729+
)
730+
```
731+
732+
This configuration is necessary because:
733+
- The MCP streamable HTTP transport uses the `Mcp-Session-Id` header for session management
734+
- Browsers restrict access to response headers unless explicitly exposed via CORS
735+
- Without this configuration, browser-based clients won't be able to read the session ID from initialization responses
736+
716737
### Mounting to an Existing ASGI Server
717738

718739
> **Note**: SSE transport is being superseded by [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http).

examples/servers/simple-streamablehttp-stateless/mcp_simple_streamablehttp_stateless/server.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from mcp.server.lowlevel import Server
99
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
1010
from starlette.applications import Starlette
11+
from starlette.middleware.cors import CORSMiddleware
1112
from starlette.routing import Mount
1213
from starlette.types import Receive, Scope, Send
1314

@@ -131,6 +132,14 @@ async def lifespan(app: Starlette) -> AsyncIterator[None]:
131132
],
132133
lifespan=lifespan,
133134
)
135+
136+
# Add CORS middleware to expose Mcp-Session-Id header for browser-based clients
137+
starlette_app.add_middleware(
138+
CORSMiddleware,
139+
allow_origins=["*"], # Allow all origins - adjust as needed for production
140+
allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
141+
expose_headers=["Mcp-Session-Id"],
142+
)
134143

135144
import uvicorn
136145

examples/servers/simple-streamablehttp/mcp_simple_streamablehttp/server.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
1010
from pydantic import AnyUrl
1111
from starlette.applications import Starlette
12+
from starlette.middleware.cors import CORSMiddleware
1213
from starlette.routing import Mount
1314
from starlette.types import Receive, Scope, Send
1415

@@ -159,6 +160,14 @@ async def lifespan(app: Starlette) -> AsyncIterator[None]:
159160
],
160161
lifespan=lifespan,
161162
)
163+
164+
# Add CORS middleware to expose Mcp-Session-Id header for browser-based clients
165+
starlette_app.add_middleware(
166+
CORSMiddleware,
167+
allow_origins=["*"], # Allow all origins - adjust as needed for production
168+
allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
169+
expose_headers=["Mcp-Session-Id"],
170+
)
162171

163172
import uvicorn
164173

0 commit comments

Comments
 (0)