From 22e2b3a9cf312f129ae1db16c2516bfde12a5017 Mon Sep 17 00:00:00 2001 From: Lion Yang Date: Tue, 22 Apr 2025 14:46:12 +0800 Subject: [PATCH 1/6] Fix the TaskGroup hanging on stdin_writer when stdio_client exiting Triggers a TaskGroup cancel after reaching the finally code block. --- src/mcp/client/stdio/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mcp/client/stdio/__init__.py b/src/mcp/client/stdio/__init__.py index 83de57a2..17ea94cb 100644 --- a/src/mcp/client/stdio/__init__.py +++ b/src/mcp/client/stdio/__init__.py @@ -177,6 +177,7 @@ async def stdin_writer(): await terminate_windows_process(process) else: process.terminate() + tg.cancel_scope.cancel() def _get_executable_command(command: str) -> str: From 277449646f92e39812b72428618f9cabf0826398 Mon Sep 17 00:00:00 2001 From: Lion Yang Date: Tue, 22 Apr 2025 15:45:07 +0800 Subject: [PATCH 2/6] Add test_stdio_cm for stdin_writer hanging --- tests/client/test_stdio_cm.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/client/test_stdio_cm.py diff --git a/tests/client/test_stdio_cm.py b/tests/client/test_stdio_cm.py new file mode 100644 index 00000000..f8fb9f7d --- /dev/null +++ b/tests/client/test_stdio_cm.py @@ -0,0 +1,18 @@ +import pytest + +from mcp import StdioServerParameters +from mcp.client.stdio import stdio_client + +MCP_SERVER = { + "command": "uvx", + "args": ["mcp-server-fetch"], +} + + +@pytest.mark.anyio +async def test_context_manager_exiting(): + async with stdio_client(StdioServerParameters(**MCP_SERVER)) as ( + read_stream, + write_stream, + ): + pass From 2591f806b1b6c49f0bf94c2b9f53c20865ed0ba0 Mon Sep 17 00:00:00 2001 From: Lion Yang Date: Tue, 22 Apr 2025 16:03:57 +0800 Subject: [PATCH 3/6] Test using simple tee instead of mcp-server-fetch --- tests/client/test_stdio_cm.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/client/test_stdio_cm.py b/tests/client/test_stdio_cm.py index f8fb9f7d..d5ceef9d 100644 --- a/tests/client/test_stdio_cm.py +++ b/tests/client/test_stdio_cm.py @@ -4,8 +4,7 @@ from mcp.client.stdio import stdio_client MCP_SERVER = { - "command": "uvx", - "args": ["mcp-server-fetch"], + "command": "tee", } From 474f0721422430c18f1074f27bdcbffc94e7f3b7 Mon Sep 17 00:00:00 2001 From: Lion Yang Date: Tue, 22 Apr 2025 16:05:36 +0800 Subject: [PATCH 4/6] Close write_stream_reader in finally block --- src/mcp/client/stdio/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/client/stdio/__init__.py b/src/mcp/client/stdio/__init__.py index 17ea94cb..eb61aee8 100644 --- a/src/mcp/client/stdio/__init__.py +++ b/src/mcp/client/stdio/__init__.py @@ -177,7 +177,7 @@ async def stdin_writer(): await terminate_windows_process(process) else: process.terminate() - tg.cancel_scope.cancel() + write_stream_reader.close() def _get_executable_command(command: str) -> str: From d2f1146d496d1183ac0d386b53cc69510561fc7a Mon Sep 17 00:00:00 2001 From: Lion Yang Date: Tue, 22 Apr 2025 16:08:41 +0800 Subject: [PATCH 5/6] Fix pyright warnings in test_stdio_cm.py --- tests/client/test_stdio_cm.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/client/test_stdio_cm.py b/tests/client/test_stdio_cm.py index d5ceef9d..524d7557 100644 --- a/tests/client/test_stdio_cm.py +++ b/tests/client/test_stdio_cm.py @@ -3,14 +3,10 @@ from mcp import StdioServerParameters from mcp.client.stdio import stdio_client -MCP_SERVER = { - "command": "tee", -} - @pytest.mark.anyio async def test_context_manager_exiting(): - async with stdio_client(StdioServerParameters(**MCP_SERVER)) as ( + async with stdio_client(StdioServerParameters(command="tee")) as ( read_stream, write_stream, ): From 12d4740adf523c672354f786e29f698d7b0dd1a5 Mon Sep 17 00:00:00 2001 From: Lion Yang Date: Tue, 22 Apr 2025 16:25:35 +0800 Subject: [PATCH 6/6] Fix read_stream stream leakage --- src/mcp/client/stdio/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mcp/client/stdio/__init__.py b/src/mcp/client/stdio/__init__.py index eb61aee8..fcc9037f 100644 --- a/src/mcp/client/stdio/__init__.py +++ b/src/mcp/client/stdio/__init__.py @@ -177,7 +177,8 @@ async def stdin_writer(): await terminate_windows_process(process) else: process.terminate() - write_stream_reader.close() + read_stream.close() + write_stream.close() def _get_executable_command(command: str) -> str: