You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Streamable HTTP: Client mode="auto" fails on server/discover against MCPServer + uvicorn (RemoteProtocolError) #3588
With mcp 2.2.0, a Client in the default mode="auto" cannot talk to an MCPServer served over Streamable HTTP by uvicorn: the server/discover POST returns 200 OK, but reading the response body fails with httpx2.RemoteProtocolError('peer unexpectedly closed connection'). mode="legacy" against the same server works.
legacy OK 3
auto FAILED RemoteProtocolError('peer unexpectedly closed connection')
Client debug logging shows the failure on the very first request (server/discover, protocol version 2026-07-28): the server logs POST /mcp 200 OK, then the client errors while reading the body.
Possibly related observation
A hand-written curl POST of the same server/discover request (headers content-type: application/json, accept: application/json, text/event-stream, MCP-Protocol-Version: 2026-07-28) got HTTP/1.1 400 Bad Request with transfer-encoding: chunked, and curl failed with chunk hex-length char not a hex digit: 0x7b — i.e. a raw {-prefixed body under a chunked header. My curl request may itself have been malformed (hence the 400), but the body framing looks wrong regardless of status, and would explain the client-side error.
Hey team, on server/discover the Streamable HTTP ASGI handler in src/mcp/server/streamable_http.py sends transfer-encoding: chunked in http.response.start with raw JSON body bytes, causing uvicorn to skip chunk framing. Happy to open a PR fixing this!
I couldn't reproduce this on Windows with the versions you listed (mcp 2.2.0, uvicorn 0.53.0, starlette 1.6.0, httpx2 2.13.0, Python 3.13.15). Your script prints legacy OK 3 / auto OK 3, and a further 1,600 mode="auto" connections all succeed.
Your curl output is the useful clue. Every mode="auto" reply from the server has a content-length header and a matching body; nothing in the SDK or uvicorn sends transfer-encoding: chunked there. I do get exactly your two results (the RemoteProtocolError and curl's chunk hex-length char not a hex digit: 0x7b) when a proxy sits between the client and the server and relabels those replies as chunked without re-framing them. mode="legacy" survives that because its replies are already chunked streams.
So I think something on your machine is rewriting the responses. An HTTP_PROXY / ALL_PROXY environment variable or web-filtering software would both do it, and the client sends requests for 127.0.0.1 through a configured proxy too. Setting NO_PROXY=127.0.0.1,localhost should confirm it.
I'm closing this as not reproducible. If it still fails with NO_PROXY set, comment here with the output of curl -v --raw --noproxy "*" and I'll reopen it.
Activity
avinash-git-24 commentedon Sep 28, 2026
Hey team, on
server/discoverthe Streamable HTTP ASGI handler insrc/mcp/server/streamable_http.pysendstransfer-encoding: chunkedinhttp.response.startwith raw JSON body bytes, causinguvicornto skip chunk framing. Happy to open a PR fixing this!maxisbey commentedon Oct 1, 2026
I couldn't reproduce this on Windows with the versions you listed (mcp 2.2.0, uvicorn 0.53.0, starlette 1.6.0, httpx2 2.13.0, Python 3.13.15). Your script prints
legacy OK 3/auto OK 3, and a further 1,600mode="auto"connections all succeed.Your curl output is the useful clue. Every
mode="auto"reply from the server has acontent-lengthheader and a matching body; nothing in the SDK or uvicorn sendstransfer-encoding: chunkedthere. I do get exactly your two results (theRemoteProtocolErrorand curl'schunk hex-length char not a hex digit: 0x7b) when a proxy sits between the client and the server and relabels those replies as chunked without re-framing them.mode="legacy"survives that because its replies are already chunked streams.So I think something on your machine is rewriting the responses. An
HTTP_PROXY/ALL_PROXYenvironment variable or web-filtering software would both do it, and the client sends requests for 127.0.0.1 through a configured proxy too. SettingNO_PROXY=127.0.0.1,localhostshould confirm it.I'm closing this as not reproducible. If it still fails with
NO_PROXYset, comment here with the output ofcurl -v --raw --noproxy "*"and I'll reopen it.AI Disclaimer