Skip to content

Commit 09e3a05

Browse files
jerome3o-anthropicKludexfelixweinberger
authored
feat: Add CORS configuration for browser-based MCP clients (modelcontextprotocol#1059)
Co-authored-by: Marcelo Trylesinski <[email protected]> Co-authored-by: Felix Weinberger <[email protected]>
1 parent ff02c59 commit 09e3a05

File tree

3 files changed

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

3 files changed

+46
-0
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,32 @@ The streamable HTTP transport supports:
11651165
- JSON or SSE response formats
11661166
- Better scalability for multi-node deployments
11671167

1168+
#### CORS Configuration for Browser-Based Clients
1169+
1170+
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:
1171+
1172+
```python
1173+
from starlette.applications import Starlette
1174+
from starlette.middleware.cors import CORSMiddleware
1175+
1176+
# Create your Starlette app first
1177+
starlette_app = Starlette(routes=[...])
1178+
1179+
# Then wrap it with CORS middleware
1180+
starlette_app = CORSMiddleware(
1181+
starlette_app,
1182+
allow_origins=["*"], # Configure appropriately for production
1183+
allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
1184+
expose_headers=["Mcp-Session-Id"],
1185+
)
1186+
```
1187+
1188+
This configuration is necessary because:
1189+
1190+
- The MCP streamable HTTP transport uses the `Mcp-Session-Id` header for session management
1191+
- Browsers restrict access to response headers unless explicitly exposed via CORS
1192+
- Without this configuration, browser-based clients won't be able to read the session ID from initialization responses
1193+
11681194
### Mounting to an Existing ASGI Server
11691195

11701196
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.

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from mcp.server.lowlevel import Server
1010
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
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

@@ -123,6 +124,15 @@ async def lifespan(app: Starlette) -> AsyncIterator[None]:
123124
lifespan=lifespan,
124125
)
125126

127+
# Wrap ASGI application with CORS middleware to expose Mcp-Session-Id header
128+
# for browser-based clients (ensures 500 errors get proper CORS headers)
129+
starlette_app = CORSMiddleware(
130+
starlette_app,
131+
allow_origins=["*"], # Allow all origins - adjust as needed for production
132+
allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
133+
expose_headers=["Mcp-Session-Id"],
134+
)
135+
126136
import uvicorn
127137

128138
uvicorn.run(starlette_app, host="127.0.0.1", port=port)

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

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

@@ -148,6 +149,15 @@ async def lifespan(app: Starlette) -> AsyncIterator[None]:
148149
lifespan=lifespan,
149150
)
150151

152+
# Wrap ASGI application with CORS middleware to expose Mcp-Session-Id header
153+
# for browser-based clients (ensures 500 errors get proper CORS headers)
154+
starlette_app = CORSMiddleware(
155+
starlette_app,
156+
allow_origins=["*"], # Allow all origins - adjust as needed for production
157+
allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
158+
expose_headers=["Mcp-Session-Id"],
159+
)
160+
151161
import uvicorn
152162

153163
uvicorn.run(starlette_app, host="127.0.0.1", port=port)

0 commit comments

Comments
 (0)