Let Client take StdioServerParameters directly (#3321)
This commit is contained in:
parent
fb443cc4b2
commit
e473cca6a1
2 changed files with 51 additions and 27 deletions
|
|
@ -3,6 +3,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import contextvars
|
||||
import sys
|
||||
from collections.abc import AsyncIterator, Iterator
|
||||
from contextlib import asynccontextmanager, contextmanager
|
||||
from unittest.mock import patch
|
||||
|
|
@ -35,7 +36,7 @@ from mcp_types import (
|
|||
from mcp_types.version import LATEST_HANDSHAKE_VERSION
|
||||
from pydantic import FileUrl
|
||||
|
||||
from mcp import MCPDeprecationWarning, MCPError
|
||||
from mcp import MCPDeprecationWarning, MCPError, StdioServerParameters
|
||||
from mcp.client._memory import InMemoryTransport
|
||||
from mcp.client._transport import TransportStreams
|
||||
from mcp.client.client import Client
|
||||
|
|
@ -414,6 +415,24 @@ async def test_client_uses_transport_directly(app: MCPServer):
|
|||
)
|
||||
|
||||
|
||||
async def test_client_with_stdio_parameters_launches_the_server_as_a_subprocess() -> None:
|
||||
"""SDK-defined: `Client` routes a `StdioServerParameters` through `stdio_client`, so entering it
|
||||
spawns the command and negotiates over the child's stdin/stdout. The process boundary is the
|
||||
behaviour, hence a real child interpreter running a one-line `MCPServer`."""
|
||||
params = StdioServerParameters(
|
||||
command=sys.executable,
|
||||
args=["-c", "from mcp.server import MCPServer; MCPServer('stdio-demo').run()"],
|
||||
)
|
||||
# Wider than the standard 5: a cold interpreter start plus `import mcp.server` in the child takes
|
||||
# seconds on a loaded Windows runner, and exit may wait out stdio_client's terminate/kill
|
||||
# escalation (PROCESS_TERMINATION_TIMEOUT + FORCE_KILL_TIMEOUT + reap, ~6s) if the child is slow.
|
||||
with anyio.fail_after(20):
|
||||
async with Client(params) as client:
|
||||
assert client.server_info is not None
|
||||
assert client.server_info.name == "stdio-demo"
|
||||
assert (await client.list_tools()).tools == []
|
||||
|
||||
|
||||
_TEST_CONTEXTVAR = contextvars.ContextVar("test_var", default="initial")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
|
||||
from docs_src.client_transports import tutorial001, tutorial004
|
||||
from mcp import Client
|
||||
from mcp.client.stdio import get_default_environment, stdio_client
|
||||
from mcp.client.stdio import get_default_environment
|
||||
from mcp.client.streamable_http import streamable_http_client
|
||||
|
||||
# See test_index.py for why this is a per-module mark and not a conftest hook.
|
||||
|
|
@ -41,9 +41,9 @@ async def test_streamable_http_configuration_lives_on_the_httpx_client() -> None
|
|||
assert list(inspect.signature(streamable_http_client).parameters) == ["url", "http_client", "terminate_on_close"]
|
||||
|
||||
|
||||
async def test_stdio_parameters_are_wrapped_by_stdio_client() -> None:
|
||||
"""tutorial004: `stdio_client(params)` is the transport, and `Client` takes it like any other."""
|
||||
client = Client(stdio_client(tutorial004.server))
|
||||
async def test_stdio_parameters_go_straight_to_client() -> None:
|
||||
"""tutorial004: `Client` takes the `StdioServerParameters` directly, and nothing is spawned until you enter it."""
|
||||
client = Client(tutorial004.server)
|
||||
with pytest.raises(RuntimeError, match="Client must be used within an async context manager"):
|
||||
client.session
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue