Skip to content

Commit dddff2b

Browse files
committed
Fix Windows compatibility for STDIO with async DummyProcess
1 parent 5815e6c commit dddff2b

File tree

3 files changed

+19
-11
lines changed

3 files changed

+19
-11
lines changed

src/mcp/client/stdio/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ async def stdio_client(server: StdioServerParameters, errlog: TextIO = sys.stder
108108
read_stream_writer, read_stream = anyio.create_memory_object_stream(0)
109109
write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
110110

111-
command = _get_executable_command(server.command)
111+
try:
112+
command = _get_executable_command(server.command)
112113

113114
# Open process with stderr piped for capture
114115
process = await _create_platform_compatible_process(

src/mcp/client/stdio/win32.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,8 @@ def __init__(self, popen_obj: subprocess.Popen[bytes]):
6262
self.stdout_raw = popen_obj.stdout # type: ignore[assignment]
6363
self.stderr = popen_obj.stderr # type: ignore[assignment]
6464

65-
self.stdin = (
66-
FileWriteStream(cast(BinaryIO, self.stdin_raw)) if self.stdin_raw else None
67-
)
68-
self.stdout = (
69-
FileReadStream(cast(BinaryIO, self.stdout_raw)) if self.stdout_raw else None
70-
)
65+
self.stdin = FileWriteStream(cast(BinaryIO, self.stdin_raw)) if self.stdin_raw else None
66+
self.stdout = FileReadStream(cast(BinaryIO, self.stdout_raw)) if self.stdout_raw else None
7167

7268
async def __aenter__(self):
7369
"""Support async context manager entry."""
@@ -91,6 +87,10 @@ def terminate(self):
9187
"""Terminate the subprocess immediately."""
9288
return self.popen.terminate()
9389

90+
def kill(self) -> None:
91+
"""Kill the subprocess immediately (alias for terminate)."""
92+
self.terminate()
93+
9494

9595
# ------------------------
9696
# Updated function
@@ -131,7 +131,7 @@ async def create_windows_process(
131131
env=env,
132132
cwd=cwd,
133133
bufsize=0, # Unbuffered output
134-
creationflags = getattr(subprocess, "CREATE_NO_WINDOW", 0),
134+
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
135135
)
136136
return DummyProcess(popen_obj)
137137

@@ -148,7 +148,8 @@ async def create_windows_process(
148148
)
149149
return DummyProcess(popen_obj)
150150

151-
async def terminate_windows_process(process: Process):
151+
152+
async def terminate_windows_process(process: Process | DummyProcess):
152153
"""
153154
Terminate a Windows process.
154155

tests/client/test_stdio.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@
22

33
import pytest
44

5-
from mcp.client.stdio import StdioServerParameters, stdio_client
5+
from mcp.client.session import ClientSession
6+
from mcp.client.stdio import (
7+
StdioServerParameters,
8+
stdio_client,
9+
)
10+
from mcp.shared.exceptions import McpError
611
from mcp.shared.message import SessionMessage
7-
from mcp.types import JSONRPCMessage, JSONRPCRequest, JSONRPCResponse
12+
from mcp.types import CONNECTION_CLOSED, JSONRPCMessage, JSONRPCRequest, JSONRPCResponse
813

914
tee: str = shutil.which("tee") # type: ignore
15+
python: str = shutil.which("python") # type: ignore
1016

1117

1218
@pytest.mark.anyio

0 commit comments

Comments
 (0)