mcp: CORS on the streamable-http app (answer OPTIONS preflight without auth)
OPTIONS /mcp returned 401 (auth layer rejecting the preflight) with no CORS headers, which blocks browser-based MCP clients / connector setup. Build the app explicitly and add CORSMiddleware (wildcard origin — bearer-token auth, no cookies) so preflight is answered 200 and Mcp-Session-Id/WWW-Authenticate are exposed.
This commit is contained in:
@@ -69,10 +69,25 @@ def start_server(mcp_instance: FastMCP, transport: TransportAliases) -> None:
|
|||||||
)
|
)
|
||||||
mcp_instance.run(transport="sse", mount_path=mount_path)
|
mcp_instance.run(transport="sse", mount_path=mount_path)
|
||||||
else: # STREAMABLE_HTTP
|
else: # STREAMABLE_HTTP
|
||||||
|
import uvicorn # noqa: PLC0415
|
||||||
|
from starlette.middleware.cors import CORSMiddleware # noqa: PLC0415
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Starting MCP server with Streamable HTTP transport at http://%s:%s%s.",
|
"Starting MCP server with Streamable HTTP transport at http://%s:%s%s.",
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
mcp_instance.settings.streamable_http_path,
|
mcp_instance.settings.streamable_http_path,
|
||||||
)
|
)
|
||||||
mcp_instance.run(transport="streamable-http")
|
app = mcp_instance.streamable_http_app()
|
||||||
|
# CORS so browser-based MCP clients / connector setup can reach /mcp — the
|
||||||
|
# preflight is what matters: CORSMiddleware answers OPTIONS directly (200)
|
||||||
|
# instead of the auth layer rejecting it (401). Bearer-token auth, no
|
||||||
|
# cookies, so a wildcard origin is safe; expose the headers clients read.
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
expose_headers=["Mcp-Session-Id", "WWW-Authenticate"],
|
||||||
|
)
|
||||||
|
uvicorn.run(app, host=host, port=port, log_level=os.getenv("FASTMCP_LOG_LEVEL", "info").lower())
|
||||||
|
|||||||
Reference in New Issue
Block a user