Skip to content

Commit a9a52b6

Browse files
committed
fix(lowlevel): log Disconnect/Cancel exceptions at DEBUG, not ERROR
The low-level server's generic case-Exception handler logs any exception from the session stream at ERROR level. When the streamable HTTP transport sends ClientDisconnect through the writer (to unblock the inner session task), it was caught here and logged as ERROR — producing the same noise we're trying to eliminate. Add a guard clause matching Disconnect/Cancel exception types (by name, to keep the low-level server transport-agnostic) and log at DEBUG instead. Also skip sending 'Internal Server Error' to the client (there is none).
1 parent 55a042a commit a9a52b6

2 files changed

Lines changed: 6 additions & 2 deletions

File tree

src/mcp/server/lowlevel/server.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,9 @@ async def _handle_message(
703703
await self._handle_request(message, req, session, lifespan_context, raise_exceptions)
704704
case types.ClientNotification(root=notify):
705705
await self._handle_notification(notify)
706+
case Exception() if "Disconnect" in type(message).__name__ or "Cancel" in type(message).__name__: # pragma: no cover
707+
# Client went away (Disconnect) or task was cancelled — not a server error.
708+
logger.debug(f"Received {type(message).__name__} from stream")
706709
case Exception(): # pragma: no cover
707710
logger.error(f"Received exception from stream: {message}")
708711
await session.send_log_message(

src/mcp/server/streamable_http.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -654,8 +654,9 @@ async def sse_writer():
654654
if writer is not None:
655655
with suppress(Exception):
656656
await writer.send(ClientDisconnect())
657-
# 499 = Client Closed Request (nginx convention, not in stdlib HTTPStatus)
658-
response = self._create_json_response(None, 499) # type: ignore[arg-type]
657+
# 499 = Client Closed Request (nginx convention, not in stdlib HTTPStatus).
658+
# Build Response directly to avoid _create_json_response's HTTPStatus type hint.
659+
response = Response(content=b"", status_code=499, media_type=CONTENT_TYPE_JSON)
659660
with suppress(Exception):
660661
await response(scope, receive, send)
661662
return

0 commit comments

Comments
 (0)