| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
…not see a second response start Starlette wraps function endpoints in request_response. After EventSourceResponse has already finalized the SSE stream, that wrapper sent a second http.response.start (empty Response), which BaseHTTPMiddleware asserts against on client disconnect (modelcontextprotocol#883).
|
Thanks for the contribution. This repository only keeps pull requests open when they're linked to an issue that a maintainer has assigned to the author — CONTRIBUTING.md explains why and how we work. This PR has been closed for now because you aren't currently assigned to #883. If a maintainer would like this change as a PR from you, they'll assign you to #883 and this PR will reopen automatically — there's nothing more you need to do. (If you opened the issue, this PR already shows up on its timeline.) There's no need to open a new PR — this one will be reopened. While it's closed, please push any updates as new commits rather than force-pushing, since GitHub can't reopen a PR whose branch has been rewritten. Maintainers: reopening this PR, removing the missing-issue-link label, or adding bypass-issue-check bypasses the check. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Fixes #883.
What I hit
I reproduced this on current main (56af447) by mounting MCPServer.sse_app(host="0.0.0.0") under a Starlette app that uses a no-op BaseHTTPMiddleware, opening /sse, reading the first endpoint event, then closing the client. Same AssertionError:
AssertionError: Unexpected message: {'type': 'http.response.start', 'status': 200, 'headers': [(b'content-length', b'0')]}Stashing only src/mcp/server/mcpserver/server.py after this change makes test_sse_app_under_base_http_middleware_survives_client_disconnect_without_second_response fail with that same error.
Why
sse_app() registered the unauth SSE GET as a Starlette function endpoint. Starlette wraps those in request_response. EventSourceResponse already finalized the stream; the wrapper then sent a second http.response.start (empty Response()). BaseHTTPMiddleware asserts it only sees http.response.body after the first start. Kludex already called this on the issue: the SDK must not send two starts.
The auth path already passed a class instance (RequireAuthMiddleware) to Route, which Starlette treats as raw ASGI. The unauth path did not.
Fix
No new public API. Route shape is unchanged (one Route at /sse, one Mount at /messages).
Tests
uv run --frozen pytest tests/shared/test_sse.py tests/server/test_sse_security.py tests/interaction/transports/test_sse.py tests/server/mcpserver/test_server.py → 227 passed.
uv run --frozen pyright on the three changed files → 0 errors.
Notes
#2016 / #2029 tried a similar ASGI-callable approach and were closed unmerged. #1222 is still an open draft. This is a fresh patch against current main, not a rebase of those.
AI-assisted implementation (Claude Code). I reproduced the failure on this tree, reviewed the diff, and reran the SSE / MCPServer tests before opening this.
I have not asked to be assigned. If the auto-close bot parks this until a maintainer wants an outside PR, that is understood.