Repository navigation
Conversation
…dalone SSE writer terminate() closes every request stream, including the standalone GET stream, while standalone_sse_writer is still iterating it. The resulting anyio.ClosedResourceError fell into the writer's generic `except Exception: logger.exception(...)`, so every normal client-initiated session close (DELETE) left an ERROR traceback in the server log. Catch ClosedResourceError separately and log it at DEBUG: a stream closed under the writer during termination is a normal end. All other exceptions keep the existing ERROR report. Regression test drives a GET request straight into the transport, calls terminate() and asserts no ERROR record from mcp.server.streamable_http (reproduces the traceback and fails on unpatched source). Focused transport subset: 13 passed; ruff check/format clean.
|
This PR has been closed automatically. This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue, and you aren't currently assigned to #3833. If a maintainer assigns you to #3833, this PR reopens on its own and there's nothing more you need to do here. Assignment is a maintainer call based on capacity; comments that only ask to be assigned don't factor in. What does help is engaging on the issue itself by confirming the repro, explaining why it matters for your use case, or describing the approach you'd take. You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned, and realistically most auto-closed PRs stay closed. There's no need to open a new PR either way. CONTRIBUTING.md has the full reasoning, but in short:
Maintainers: reopen, remove |
Fixes #3833
Problem
On the v1.x line, closing a stateful streamable-HTTP session the normal way (the client's
streamablehttp_clientcontext exits and sendsDELETE) logs anERRORwith a full traceback:StreamableHTTPServerTransport.terminate()closes every request stream, including the standaloneGET stream (
GET_STREAM_KEY), whilestandalone_sse_writeris still iterating it. The resultinganyio.ClosedResourceErroris caught by the writer's genericexcept Exception: logger.exception(...),so every successfully-closed session leaves an ERROR traceback in the server log and buries real
errors. Nothing actually fails — the calls succeed and the server keeps serving.
Fix
Catch
anyio.ClosedResourceErrorseparately instandalone_sse_writerand log it atDEBUG: astream closed under the writer during a client-initiated termination is a normal end of the stream.
All other exceptions keep the existing
logger.exception("Error in standalone SSE writer").Tests
New in-process regression test in
tests/shared/test_streamable_http.py:test_termination_does_not_log_an_error_in_the_standalone_sse_writerIt drives a GET request straight into a
StreamableHTTPServerTransport(no server process needed),waits for the standalone GET stream to register, calls
terminate()(exactly what aDELETEdoes)and asserts that
mcp.server.streamable_httpemitted no record atERRORor above.On the unpatched v1.x source the test reproduces the issue and fails with the traceback above; with
the fix it passes. Validation on this machine (Windows, Python 3.12):
AI assistance
Tool(s) used: DeepSeek Harness (coding agent). The agent reproduced the exact traceback through the
transport's public ASGI path, added the regression test first (red on unpatched source), split the
ClosedResourceErrorhandling out of the generic branch and ran the focused suite plus ruff. Ireviewed the diff, the log-level choice and the red/green output, and take responsibility for the
change.