feat: Add CORS configuration for browser-based MCP clients (#1059)
Some checks failed
Main branch checks / checks (push) Has been cancelled
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:
parent
ff02c596a4
commit
09e3a05e13
3 changed files with 46 additions and 0 deletions
26
README.md
26
README.md
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue