Skip to content

Commit d297fcf

Browse files
committed
revert: use try/finally for sse and streamable_http stream cleanup
The async with form triggers coverage.py false-positive branch arcs on Python 3.14 — nested multi-CM async with creates WITH_EXCEPT_START handlers whose POP_JUMP_IF_TRUE branches (did __aexit__ suppress?) get attributed to inner async with lines via the exception table. Memory stream __aexit__ never suppresses, so those arcs are unreachable, but coverage.py's static analysis expects them. try/finally has no suppression branch (it's unconditional RERAISE), so it sidesteps the issue entirely. Keeping the explicit aclose() chain for these two files; websocket.py stays on the merged async with form since its single-level nesting is clean on all tested Python versions (3.10-3.14).
1 parent 906bcfc commit d297fcf

File tree

2 files changed

+12
-2
lines changed

2 files changed

+12
-2
lines changed

src/mcp/client/sse.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ async def sse_client(
6161
write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
6262

6363
async with anyio.create_task_group() as tg:
64-
async with read_stream_writer, read_stream, write_stream, write_stream_reader:
64+
try:
6565
logger.debug(f"Connecting to SSE endpoint: {remove_request_params(url)}")
6666
async with httpx_client_factory(
6767
headers=headers, auth=auth, timeout=httpx.Timeout(timeout, read=sse_read_timeout)
@@ -157,3 +157,8 @@ async def post_writer(endpoint_url: str):
157157
yield read_stream, write_stream
158158
finally:
159159
tg.cancel_scope.cancel()
160+
finally:
161+
await read_stream_writer.aclose()
162+
await write_stream.aclose()
163+
await read_stream.aclose()
164+
await write_stream_reader.aclose()

src/mcp/client/streamable_http.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ async def streamable_http_client(
547547
transport = StreamableHTTPTransport(url)
548548

549549
async with anyio.create_task_group() as tg:
550-
async with read_stream_writer, read_stream, write_stream, write_stream_reader:
550+
try:
551551
logger.debug(f"Connecting to StreamableHTTP endpoint: {url}")
552552

553553
async with contextlib.AsyncExitStack() as stack:
@@ -574,3 +574,8 @@ def start_get_stream() -> None:
574574
if transport.session_id and terminate_on_close:
575575
await transport.terminate_session(client)
576576
tg.cancel_scope.cancel()
577+
finally:
578+
await read_stream_writer.aclose()
579+
await write_stream.aclose()
580+
await read_stream.aclose()
581+
await write_stream_reader.aclose()

0 commit comments

Comments
 (0)