Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/mcp/server/sse.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ async def response_wrapper(scope: Scope, receive: Receive, send: Send):
)
await read_stream_writer.aclose()
await write_stream_reader.aclose()
self._read_stream_writers.pop(session_id, None)
logging.debug(f"Client session disconnected {session_id}")

logger.debug("Starting SSE response task")
Expand Down
27 changes: 27 additions & 0 deletions tests/shared/test_sse.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,3 +611,30 @@ async def mock_aiter_sse() -> AsyncGenerator[ServerSentEvent, None]:
assert not isinstance(msg, Exception)
assert isinstance(msg.message, types.JSONRPCResponse)
assert msg.message.id == 1


@pytest.mark.anyio
async def test_sse_session_cleanup_on_disconnect(server: None, server_url: str) -> None:
"""Regression test for https://github.com/modelcontextprotocol/python-sdk/issues/1227

When a client disconnects, the server should remove the session from
_read_stream_writers. Without this cleanup, stale sessions accumulate and
POST requests to disconnected sessions return 202 Accepted followed by a
ClosedResourceError when the server tries to write to the dead stream.
"""
captured: list[str] = []

# Connect a client session, then disconnect
async with sse_client(server_url + "/sse", on_session_created=captured.append) as streams:
async with ClientSession(*streams) as session:
await session.initialize()

# After disconnect, POST to the stale session should return 404
# (not 202 as it did before the fix)
async with httpx.AsyncClient() as client:
response = await client.post(
f"{server_url}/messages/?session_id={captured[0]}",
json={"jsonrpc": "2.0", "method": "ping", "id": 99},
headers={"Content-Type": "application/json"},
)
assert response.status_code == 404