Skip to content

Commit 1095767

Browse files
wiggzzclaude
andcommitted
Revert sleep(0) approach — coverage was racy
The sleep(0) between terminate() and cancel_scope.cancel() was non-deterministic: sometimes the event loop ran exception handlers (covering the pragmas), sometimes the cancel won the race (leaving lines uncovered). This caused strict-no-cover to flip between "wrongly marked" and "missing coverage" across CI runs. Revert to the simple, deterministic approach: - Restore all original pragma: no cover annotations - Add pragma: no cover for the ClosedResourceError/_terminated path in the message router, which is unreachable with request-scoped task groups (Cancelled always wins over ClosedResourceError) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4116e02 commit 1095767

File tree

3 files changed

+6
-9
lines changed

3 files changed

+6
-9
lines changed

src/mcp/server/streamable_http.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -626,14 +626,14 @@ async def sse_writer(): # pragma: lax no cover
626626
# Then send the message to be processed by the server
627627
session_message = self._create_session_message(message, request, request_id, protocol_version)
628628
await writer.send(session_message)
629-
except Exception:
629+
except Exception: # pragma: no cover
630630
logger.exception("SSE response error")
631631
await sse_stream_writer.aclose()
632632
await self._clean_up_memory_streams(request_id)
633633
finally:
634634
await sse_stream_reader.aclose()
635635

636-
except Exception as err:
636+
except Exception as err: # pragma: no cover
637637
logger.exception("Error handling POST request")
638638
response = self._create_error_response(
639639
f"Error handling POST request: {err}",
@@ -816,7 +816,7 @@ async def _validate_request_headers(self, request: Request, send: Send) -> bool:
816816

817817
async def _validate_session(self, request: Request, send: Send) -> bool:
818818
"""Validate the session ID in the request."""
819-
if not self.mcp_session_id:
819+
if not self.mcp_session_id: # pragma: no cover
820820
# If we're not using session IDs, return True
821821
return True
822822

@@ -1025,7 +1025,7 @@ async def message_router():
10251025
might reconnect and replay."""
10261026
)
10271027
except anyio.ClosedResourceError:
1028-
if self._terminated:
1028+
if self._terminated: # pragma: no cover
10291029
logger.debug("Read stream closed by client")
10301030
else:
10311031
logger.exception("Unexpected closure of read stream in message router")

src/mcp/server/streamable_http_manager.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ async def run_stateless_server(*, task_status: TaskStatus[None] = anyio.TASK_STA
178178
self.app.create_initialization_options(),
179179
stateless=True,
180180
)
181-
except Exception:
181+
except Exception: # pragma: no cover
182182
logger.exception("Stateless session crashed")
183183

184184
# Use a request-scoped task group instead of the global one.
@@ -193,9 +193,6 @@ async def run_request_handler(*, task_status: TaskStatus[None] = anyio.TASK_STAT
193193
await http_transport.handle_request(scope, receive, send)
194194
# Terminate the transport after the request is handled
195195
await http_transport.terminate()
196-
# Yield one scheduling step so other tasks (e.g. the message
197-
# router) can observe the closed streams before we cancel.
198-
await anyio.sleep(0)
199196
# Cancel the request-scoped task group to stop the server task
200197
request_tg.cancel_scope.cancel()
201198

src/mcp/shared/session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ async def _receive_loop(self) -> None:
423423
try:
424424
await stream.send(JSONRPCError(jsonrpc="2.0", id=id, error=error))
425425
await stream.aclose()
426-
except Exception:
426+
except Exception: # pragma: no cover
427427
# Stream might already be closed
428428
pass
429429
self._response_streams.clear()

0 commit comments

Comments
 (0)