From 8a43e213b4d0d1341d724b60d95b9276f2c02d95 Mon Sep 17 00:00:00 2001 From: voidborne-d Date: Sun, 15 Mar 2026 21:11:53 +0000 Subject: [PATCH] fix(proxy): avoid 307 redirect on POST /mcp (fixes #23688) Starlette's Router redirects /mcp -> /mcp/ with 307 Temporary Redirect when the MCP sub-app is mounted at /mcp. Many HTTP clients (and MCP clients like Claude Code) do not follow redirects for POST requests, causing the request body to be dropped and the connection to fail. Add a lightweight pure-ASGI middleware that internally rewrites /mcp to /mcp/ before routing, so the mounted sub-app handles the request directly. Using a raw ASGI middleware (instead of BaseHTTPMiddleware) preserves SSE streaming used by the MCP transport layer. --- litellm/proxy/proxy_server.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 9c29927c5cb..7f8b94d0b8f 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -13399,3 +13399,23 @@ async def dynamic_mcp_route(mcp_server_name: str, request: Request): app.mount(path=BASE_MCP_ROUTE, app=mcp_app) app.include_router(mcp_rest_endpoints_router) app.include_router(mcp_discoverable_endpoints_router) + + +# Rewrite /mcp -> /mcp/ internally to avoid 307 redirect from Starlette's +# mounted sub-application routing. Many MCP clients (e.g. Claude Code) send +# POST /mcp and do not follow redirects for POST, which drops the request body. +# Using a pure ASGI middleware (not BaseHTTPMiddleware) keeps SSE streaming intact. +class _MCPTrailingSlashMiddleware: + """Transparently append '/' to the MCP mount path so Starlette serves it + directly instead of responding with 307 Temporary Redirect.""" + + def __init__(self, app: Any) -> None: # noqa: F811 + self.app = app + + async def __call__(self, scope: Any, receive: Any, send: Any) -> None: + if scope["type"] == "http" and scope.get("path") == BASE_MCP_ROUTE: + scope = dict(scope, path=BASE_MCP_ROUTE + "/") + await self.app(scope, receive, send) + + +app.add_middleware(_MCPTrailingSlashMiddleware)