🐞 Bug Summary
CORS preflight OPTIONS requests to /servers/{id}/mcp endpoints return 401 Unauthorized, causing browser-based MCP clients to fail with CORS errors.
The streamable_http_auth function in mcpgateway/transports/streamablehttp_transport.py does not exempt OPTIONS requests from authentication. Per RFC 7231 Section 4.3.7, OPTIONS requests (used for CORS preflight) cannot carry Authorization headers and must be allowed through for CORS to function.
🧩 Affected Component
🔁 Steps to Reproduce
- Start the MCP Gateway with CORS enabled and
ALLOWED_ORIGINS configured to include the client origin (e.g., http://localhost:3000)
- From a browser-based MCP client (e.g., mcp-use inspector at
http://localhost:3000), attempt to connect to http://localhost:4444/servers/{server_uuid}/mcp/
- Browser sends OPTIONS preflight request
- Gateway returns 401 Unauthorized
- Browser blocks the actual request due to CORS failure
🤔 Expected Behavior
OPTIONS preflight requests should be allowed through without authentication, returning appropriate CORS headers (Access-Control-Allow-Origin, etc.). The actual POST/GET requests that follow should still require authentication.
📓 Logs / Error Output
Gateway logs show:
[http_gateway] Request completed: OPTIONS /servers/2e44c8eec1024d4b838d2b80594e9f92/mcp - 401
Browser console shows:
Access to fetch at 'http://localhost:4444/servers/.../mcp' from origin 'http://localhost:3000'
has been blocked by CORS policy: Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
🧠 Environment Info
| Key |
Value |
| Version or commit |
main branch |
| Runtime |
Python 3.11, Granian |
| Platform / OS |
macOS (Darwin 25.1.0) |
| Container |
Docker Compose |
🧩 Additional Context
The fix should add an OPTIONS method check at the beginning of the streamable_http_auth function:
# Skip auth for OPTIONS preflight requests (CORS requires this per RFC 7231)
method = scope.get("method", "")
if method == "OPTIONS":
return True
This is consistent with how DocsAuthMiddleware already handles OPTIONS requests (line 1347-1349 in main.py).
Note: The CORSMiddleware is added first in the middleware stack but runs last due to how Starlette middleware ordering works. Since MCPPathRewriteMiddleware calls streamable_http_auth directly before the request reaches CORSMiddleware, the preflight fails.
🐞 Bug Summary
CORS preflight OPTIONS requests to
/servers/{id}/mcpendpoints return 401 Unauthorized, causing browser-based MCP clients to fail with CORS errors.The
streamable_http_authfunction inmcpgateway/transports/streamablehttp_transport.pydoes not exempt OPTIONS requests from authentication. Per RFC 7231 Section 4.3.7, OPTIONS requests (used for CORS preflight) cannot carry Authorization headers and must be allowed through for CORS to function.🧩 Affected Component
mcpgateway- API🔁 Steps to Reproduce
ALLOWED_ORIGINSconfigured to include the client origin (e.g.,http://localhost:3000)http://localhost:3000), attempt to connect tohttp://localhost:4444/servers/{server_uuid}/mcp/🤔 Expected Behavior
OPTIONS preflight requests should be allowed through without authentication, returning appropriate CORS headers (Access-Control-Allow-Origin, etc.). The actual POST/GET requests that follow should still require authentication.
📓 Logs / Error Output
Gateway logs show:
Browser console shows:
🧠 Environment Info
🧩 Additional Context
The fix should add an OPTIONS method check at the beginning of the
streamable_http_authfunction:This is consistent with how
DocsAuthMiddlewarealready handles OPTIONS requests (line 1347-1349 in main.py).Note: The
CORSMiddlewareis added first in the middleware stack but runs last due to how Starlette middleware ordering works. SinceMCPPathRewriteMiddlewarecallsstreamable_http_authdirectly before the request reachesCORSMiddleware, the preflight fails.