Description
The MCPPathRewriteMiddleware in mcpgateway/main.py rewrites /servers/<id>/mcp to /mcp/ by modifying the ASGI scope["path"] internally. This rewrite does not survive a streaming POST cleanly — it surfaces as httpx.ReadError during initialize on the Streamable HTTP transport.
Root cause
The middleware calls streamable_http_auth() before the path rewrite, which may partially consume the ASGI receive() stream. For streaming POST requests (Streamable HTTP, SSE), after the path rewrite the downstream route handler expects to read the full request body, but the stream has already been partially consumed by the auth check. This results in corrupted or incomplete streaming data.
Regular JSON POST requests with complete bodies are typically buffered and survive this, but chunked/streaming POSTs do not.
Current workaround
Tests and clients send requests to /mcp/ directly (with trailing slash) to bypass the rewrite path entirely. This is documented in a comment in tests/unit/mcpgateway/transports/test_streamablehttp_transport.py:
# Trailing slash matters: ContextForge's MCPPathRewriteMiddleware rewrites
# /mcp to /mcp/, but the rewrite doesn't survive a streaming POST cleanly
# (surfaces as httpx.ReadError during initialize). Send /mcp/ directly.
Impact
- Clients hitting
/mcp (without trailing slash) on the Streamable HTTP transport may get httpx.ReadError during session initialization
- The
/servers/<id>/mcp virtual server path also goes through this rewrite and is likely affected
- The workaround (using
/mcp/ directly) is fragile and non-obvious
Affected code
MCPPathRewriteMiddleware in mcpgateway/main.py (~line 2890)
streamable_http_auth() in mcpgateway/transports/streamablehttp_transport.py (~line 4157)
Suggested fix
The middleware should either:
- Buffer or tee the
receive() stream so auth consumption doesn't affect downstream handlers
- Issue a proper HTTP 307 redirect for non-streaming requests and reject or buffer streaming requests appropriately
- Move authentication to happen after the path rewrite rather than before it
Description
The
MCPPathRewriteMiddlewareinmcpgateway/main.pyrewrites/servers/<id>/mcpto/mcp/by modifying the ASGIscope["path"]internally. This rewrite does not survive a streaming POST cleanly — it surfaces ashttpx.ReadErrorduringinitializeon the Streamable HTTP transport.Root cause
The middleware calls
streamable_http_auth()before the path rewrite, which may partially consume the ASGIreceive()stream. For streaming POST requests (Streamable HTTP, SSE), after the path rewrite the downstream route handler expects to read the full request body, but the stream has already been partially consumed by the auth check. This results in corrupted or incomplete streaming data.Regular JSON POST requests with complete bodies are typically buffered and survive this, but chunked/streaming POSTs do not.
Current workaround
Tests and clients send requests to
/mcp/directly (with trailing slash) to bypass the rewrite path entirely. This is documented in a comment intests/unit/mcpgateway/transports/test_streamablehttp_transport.py:Impact
/mcp(without trailing slash) on the Streamable HTTP transport may gethttpx.ReadErrorduring session initialization/servers/<id>/mcpvirtual server path also goes through this rewrite and is likely affected/mcp/directly) is fragile and non-obviousAffected code
MCPPathRewriteMiddlewareinmcpgateway/main.py(~line 2890)streamable_http_auth()inmcpgateway/transports/streamablehttp_transport.py(~line 4157)Suggested fix
The middleware should either:
receive()stream so auth consumption doesn't affect downstream handlers