Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 90f224d

Browse files
committedApr 7, 2025·
fix detection of failed process
1 parent be41b81 commit 90f224d

File tree

2 files changed

+18
-10
lines changed

2 files changed

+18
-10
lines changed
 

Diff for: ‎src/mcp/client/stdio/__init__.py

+18-8
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ async def stdio_client(server: StdioServerParameters, errlog: TextIO = sys.stder
127127
cwd=server.cwd,
128128
)
129129

130-
async def stdout_reader():
130+
async def stdout_reader(done_event: anyio.Event):
131131
assert process.stdout, "Opened process is missing stdout"
132132

133133
try:
@@ -151,6 +151,7 @@ async def stdout_reader():
151151
await read_stream_writer.send(message)
152152
except anyio.ClosedResourceError:
153153
await anyio.lowlevel.checkpoint()
154+
done_event.set()
154155

155156
async def stdin_writer():
156157
assert process.stdin, "Opened process is missing stdin"
@@ -174,21 +175,30 @@ async def stdin_writer():
174175
anyio.create_task_group() as tg,
175176
process,
176177
):
177-
tg.start_soon(stdout_reader)
178+
stdout_done_event = anyio.Event()
179+
tg.start_soon(stdout_reader, stdout_done_event)
178180
tg.start_soon(stdin_writer)
179-
# tg.start_soon(monitor_process, tg.cancel_scope)
180181
try:
181182
yield read_stream, write_stream
183+
if stdout_done_event.is_set():
184+
# The stdout reader exited before the calling code stopped listening
185+
# (e.g. because of process error)
186+
# Give the process a chance to exit if it was the reason for crashing
187+
# so we can get exit code
188+
with anyio.move_on_after(0.1) as scope:
189+
await process.wait()
190+
process_error = f"Process exited with code {process.returncode}."
191+
if scope.cancelled_caught:
192+
process_error = (
193+
"Stdout reader exited (process did not exit immediately)."
194+
)
182195
finally:
183196
await read_stream.aclose()
184197
await write_stream.aclose()
185198
await read_stream_writer.aclose()
186199
await write_stream_reader.aclose()
187-
188-
if process.returncode is not None and process.returncode != 0:
189-
process_error = f"Process exited with code {process.returncode}."
190-
else:
191-
# Clean up process to prevent any dangling orphaned processes
200+
# Clean up process to prevent any dangling orphaned processes
201+
if process.returncode is None:
192202
if sys.platform == "win32":
193203
await terminate_windows_process(process)
194204
else:

Diff for: ‎tests/client/test_stdio.py

-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,5 @@ async def test_stdio_client_bad_path():
7070
async for message in read_stream:
7171
if isinstance(message, Exception):
7272
raise message
73-
74-
pass
7573
except TimeoutError:
7674
pytest.fail("The connection hung.")

0 commit comments

Comments
 (0)
Please sign in to comment.