Skip to content

Commit c5517c6

Browse files
committed
Fix stdio_client kill process after timeout
1 parent 6f43d1f commit c5517c6

File tree

2 files changed

+7
-28
lines changed

2 files changed

+7
-28
lines changed

src/mcp/client/stdio/__init__.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from .win32 import (
1717
create_windows_process,
1818
get_windows_executable_command,
19-
terminate_windows_process,
2019
)
2120

2221
# Environment variables to inherit by default
@@ -180,13 +179,15 @@ async def stdin_writer():
180179
finally:
181180
# Clean up process to prevent any dangling orphaned processes
182181
try:
183-
if sys.platform == "win32":
184-
await terminate_windows_process(process)
185-
else:
186-
process.terminate()
182+
process.terminate()
183+
with anyio.fail_after(2.0):
184+
await process.wait()
187185
except ProcessLookupError:
188186
# Process already exited, which is fine
189187
pass
188+
except TimeoutError:
189+
# Force kill if it doesn't terminate
190+
process.kill()
190191
await read_stream.aclose()
191192
await write_stream.aclose()
192193
await read_stream_writer.aclose()

src/mcp/client/stdio/win32.py

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
from pathlib import Path
99
from typing import BinaryIO, TextIO, cast
1010

11-
import anyio
1211
from anyio import to_thread
13-
from anyio.abc import Process
1412
from anyio.streams.file import FileReadStream, FileWriteStream
1513

1614

@@ -158,25 +156,5 @@ async def create_windows_process(
158156
cwd=cwd,
159157
bufsize=0,
160158
)
161-
return FallbackProcess(popen_obj)
162-
163-
164-
async def terminate_windows_process(process: Process | FallbackProcess):
165-
"""
166-
Terminate a Windows process.
167-
168-
Note: On Windows, terminating a process with process.terminate() doesn't
169-
always guarantee immediate process termination.
170-
So we give it 2s to exit, or we call process.kill()
171-
which sends a SIGKILL equivalent signal.
172159

173-
Args:
174-
process: The process to terminate
175-
"""
176-
try:
177-
process.terminate()
178-
with anyio.fail_after(2.0):
179-
await process.wait()
180-
except TimeoutError:
181-
# Force kill if it doesn't terminate
182-
process.kill()
160+
return FallbackProcess(popen_obj)

0 commit comments

Comments
 (0)