feat: Add CORS configuration for browser-based MCP clients (#1059)
Some checks failed
Main branch checks / checks (push) Has been cancelled

Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
This commit is contained in:
Jerome 2025-08-21 14:34:45 +01:00 • committed by GitHub
commit 09e3a05e13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 46 additions and 0 deletions

View file

@ -1165,6 +1165,32 @@ The streamable HTTP transport supports:
- JSON or SSE response formats - JSON or SSE response formats
- Better scalability for multi-node deployments - Better scalability for multi-node deployments
#### 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:
```python
from starlette.applications import Starlette
from starlette.middleware.cors import CORSMiddleware
# Create your Starlette app first
starlette_app = Starlette(routes=[...])
# Then wrap it with CORS middleware
starlette_app = CORSMiddleware(
starlette_app,
allow_origins=["*"], # Configure appropriately for production
allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
expose_headers=["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
### Mounting to an Existing ASGI Server ### Mounting to an Existing ASGI Server
By default, SSE servers are mounted at `/sse` and Streamable HTTP servers are mounted at `/mcp`. You can customize these paths using the methods described below. By default, SSE servers are mounted at `/sse` and Streamable HTTP servers are mounted at `/mcp`. You can customize these paths using the methods described below.

View file

@ -9,6 +9,7 @@ import mcp.types as types
from mcp.server.lowlevel import Server from mcp.server.lowlevel import Server
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
from starlette.applications import Starlette from starlette.applications import Starlette
from starlette.middleware.cors import CORSMiddleware
from starlette.routing import Mount from starlette.routing import Mount
from starlette.types import Receive, Scope, Send from starlette.types import Receive, Scope, Send
@ -123,6 +124,15 @@ def main(
lifespan=lifespan, lifespan=lifespan,
) )
# Wrap ASGI application with CORS middleware to expose Mcp-Session-Id header
# for browser-based clients (ensures 500 errors get proper CORS headers)
starlette_app = CORSMiddleware(
starlette_app,
allow_origins=["*"], # Allow all origins - adjust as needed for production
allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
expose_headers=["Mcp-Session-Id"],
)
import uvicorn import uvicorn
uvicorn.run(starlette_app, host="127.0.0.1", port=port) uvicorn.run(starlette_app, host="127.0.0.1", port=port)

View file

@ -10,6 +10,7 @@ from mcp.server.lowlevel import Server
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
from pydantic import AnyUrl from pydantic import AnyUrl
from starlette.applications import Starlette from starlette.applications import Starlette
from starlette.middleware.cors import CORSMiddleware
from starlette.routing import Mount from starlette.routing import Mount
from starlette.types import Receive, Scope, Send from starlette.types import Receive, Scope, Send
@ -148,6 +149,15 @@ def main(
lifespan=lifespan, lifespan=lifespan,
) )
# Wrap ASGI application with CORS middleware to expose Mcp-Session-Id header
# for browser-based clients (ensures 500 errors get proper CORS headers)
starlette_app = CORSMiddleware(
starlette_app,
allow_origins=["*"], # Allow all origins - adjust as needed for production
allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
expose_headers=["Mcp-Session-Id"],
)
import uvicorn import uvicorn
uvicorn.run(starlette_app, host="127.0.0.1", port=port) uvicorn.run(starlette_app, host="127.0.0.1", port=port)