|  | 
| 1 |  | -import asyncio | 
|  | 1 | +"""Test for issue #552: stdio_client hangs on Windows.""" | 
|  | 2 | + | 
| 2 | 3 | import sys | 
|  | 4 | +from textwrap import dedent | 
| 3 | 5 | 
 | 
|  | 6 | +import anyio | 
| 4 | 7 | import pytest | 
| 5 | 8 | 
 | 
| 6 | 9 | from mcp import ClientSession, StdioServerParameters | 
| 7 | 10 | from mcp.client.stdio import stdio_client | 
| 8 | 11 | 
 | 
| 9 |  | -# @pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific test") | 
| 10 |  | -@pytest.mark.skipif(sys.version_info < (3, 11), reason="asyncio.timeout in 3.11+") | 
| 11 |  | -@pytest.mark.parametrize( | 
| 12 |  | -    "args", | 
| 13 |  | -    [ | 
| 14 |  | -        ["/C", "echo", '{"jsonrpc": "2.0", "id": 1, "result": null}'], | 
| 15 |  | -        ["dfghfgh"], | 
| 16 |  | -        ["/C", "echo"], | 
| 17 |  | -    ], | 
| 18 |  | -) | 
|  | 12 | + | 
|  | 13 | +@pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific test") | 
| 19 | 14 | @pytest.mark.anyio | 
| 20 |  | -async def test_windows_process_creation(args): | 
|  | 15 | +async def test_windows_stdio_client_with_session(): | 
| 21 | 16 |     """ | 
| 22 |  | -    Test that directly tests the process creation function that was fixed in issue #552. | 
| 23 |  | -    This simpler test verifies that Windows process creation works without hanging. | 
|  | 17 | +    Test the exact scenario from issue #552: Using ClientSession with stdio_client. | 
|  | 18 | +
 | 
|  | 19 | +    This reproduces the original bug report where stdio_client hangs on Windows 11 | 
|  | 20 | +    when used with ClientSession. | 
| 24 | 21 |     """ | 
| 25 |  | -    # Use a simple command that should complete quickly on Windows | 
|  | 22 | +    # Create a minimal MCP server that responds to initialization | 
|  | 23 | +    server_script = dedent(""" | 
|  | 24 | +        import json | 
|  | 25 | +        import sys | 
|  | 26 | +
 | 
|  | 27 | +        # Read initialization request | 
|  | 28 | +        line = sys.stdin.readline() | 
|  | 29 | +
 | 
|  | 30 | +        # Send initialization response | 
|  | 31 | +        response = { | 
|  | 32 | +            "jsonrpc": "2.0", | 
|  | 33 | +            "id": 1, | 
|  | 34 | +            "result": { | 
|  | 35 | +                "protocolVersion": "1.0", | 
|  | 36 | +                "capabilities": {}, | 
|  | 37 | +                "serverInfo": {"name": "test-server", "version": "1.0"} | 
|  | 38 | +            } | 
|  | 39 | +        } | 
|  | 40 | +        print(json.dumps(response)) | 
|  | 41 | +        sys.stdout.flush() | 
|  | 42 | +
 | 
|  | 43 | +        # Exit after a short delay | 
|  | 44 | +        import time | 
|  | 45 | +        time.sleep(0.1) | 
|  | 46 | +        sys.exit(0) | 
|  | 47 | +    """).strip() | 
|  | 48 | + | 
| 26 | 49 |     params = StdioServerParameters( | 
| 27 |  | -        command="cmd", | 
| 28 |  | -        # Echo a valid JSON-RPC response message that will be parsed correctly | 
| 29 |  | -        args=args, | 
|  | 50 | +        command=sys.executable, | 
|  | 51 | +        args=["-c", server_script], | 
| 30 | 52 |     ) | 
| 31 | 53 | 
 | 
| 32 |  | -    # Directly test the fixed function that was causing the hanging issue | 
| 33 |  | -    try: | 
| 34 |  | -        # Set a timeout to prevent hanging | 
| 35 |  | -        async with asyncio.timeout(5): | 
| 36 |  | -            # Test the actual process creation function that was fixed | 
|  | 54 | +    # This is the exact pattern from the bug report | 
|  | 55 | +    with anyio.fail_after(10): | 
|  | 56 | +        try: | 
| 37 | 57 |             async with stdio_client(params) as (read, write): | 
| 38 |  | -                print("inside client") | 
| 39 |  | -                async with ClientSession(read, write) as c: | 
| 40 |  | -                    print("inside ClientSession") | 
| 41 |  | -                    await c.initialize() | 
| 42 |  | - | 
| 43 |  | -    except asyncio.TimeoutError: | 
| 44 |  | -        pytest.xfail("Process creation timed out, indicating a hang issue") | 
| 45 |  | -    except ProcessLookupError: | 
| 46 |  | -        pytest.xfail("Process creation failed with ProcessLookupError") | 
| 47 |  | -    except Exception as e: | 
| 48 |  | -        assert "ExceptionGroup" in repr(e), f"Unexpected error: {e}" | 
| 49 |  | -        assert "ProcessLookupError" in repr(e), f"Unexpected error: {e}" | 
| 50 |  | -        pytest.xfail(f"Expected error: {e}") | 
|  | 58 | +                async with ClientSession(read, write) as session: | 
|  | 59 | +                    await session.initialize() | 
|  | 60 | +                # Should exit ClientSession without hanging | 
|  | 61 | +            # Should exit stdio_client without hanging | 
|  | 62 | +        except Exception: | 
|  | 63 | +            # Connection errors are expected when process exits | 
|  | 64 | +            pass | 
0 commit comments