From 04b7f6e6327294a315ec0dadd88bd0d4502ffa2c Mon Sep 17 00:00:00 2001 From: Nathan Brake <33383515+njbrake@users.noreply.github.com> Date: Fri, 30 May 2025 09:14:15 -0400 Subject: [PATCH 1/3] Pass client_session_timeout through from MCPAdapt Follow up from https://github.com/grll/mcpadapt/pull/35 --- src/mcpadapt/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mcpadapt/core.py b/src/mcpadapt/core.py index 7ae969a..26a0f91 100644 --- a/src/mcpadapt/core.py +++ b/src/mcpadapt/core.py @@ -176,6 +176,7 @@ def __init__( | list[StdioServerParameters | dict[str, Any]], adapter: ToolAdapter, connect_timeout: int = 30, + client_session_timeout_seconds: float | timedelta | None = 5, ): """ Manage the MCP server / client lifecycle and expose tools adapted with the adapter. @@ -185,6 +186,7 @@ def __init__( MCP server parameters (stdio or sse). Can be a list if you want to connect multiple MCPs at once. adapter (ToolAdapter): Adapter to use to convert MCP tools call into agentic framework tools. connect_timeout (int): Connection timeout in seconds to the mcp server (default is 30s). + client_session_timeout_seconds: Timeout for MCP ClientSession calls Raises: TimeoutError: When the connection to the mcp server time out. @@ -209,6 +211,7 @@ def __init__( self.thread = threading.Thread(target=self._run_loop, daemon=True) self.connect_timeout = connect_timeout + self.client_session_timeout_seconds = client_session_timeout_seconds def _run_loop(self): """Runs the event loop in a separate thread (for synchronous usage).""" @@ -217,7 +220,7 @@ def _run_loop(self): async def setup(): async with AsyncExitStack() as stack: connections = [ - await stack.enter_async_context(mcptools(params)) + await stack.enter_async_context(mcptools(params, self.client_session_timeout_seconds)) for params in self.serverparams ] self.sessions, self.mcp_tools = [list(c) for c in zip(*connections)] @@ -323,7 +326,7 @@ async def __aenter__(self) -> list[Any]: self._ctxmanager = AsyncExitStack() connections = [ - await self._ctxmanager.enter_async_context(mcptools(params)) + await self._ctxmanager.enter_async_context(mcptools(params, self.client_session_timeout_seconds)) for params in self.serverparams ] From e32458b78de86b7ff7ad2cdfaec91345d5076980 Mon Sep 17 00:00:00 2001 From: Guillaume Raille Date: Sat, 14 Jun 2025 11:08:01 +0200 Subject: [PATCH 2/3] Add tests for timeout parameters - Add test for connect_timeout that verifies TimeoutError is raised when server starts slowly - Add test for client_session_timeout_seconds parameter propagation and storage - Tests run quickly using 1-2 second timeouts for fast execution --- tests/test_core.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index 98ba851..c15ea4b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -153,6 +153,28 @@ async def echo_streamable_http_server(echo_server_streamable_http_script): process.wait() +@pytest.fixture +def slow_start_server_script(): + return dedent( + ''' + import time + from mcp.server.fastmcp import FastMCP + + # Sleep for 2 seconds to simulate slow startup + time.sleep(2) + + mcp = FastMCP("Slow Server") + + @mcp.tool() + def echo_tool(text: str) -> str: + """Echo the input text""" + return f"Echo: {text}" + + mcp.run() + ''' + ) + + def test_basic_sync(echo_server_script): with MCPAdapt( StdioServerParameters( @@ -319,3 +341,61 @@ async def test_basic_async_streamable_http(echo_streamable_http_server): ) as tools: assert len(tools) == 1 assert (await tools[0]({"text": "hello"})).content[0].text == "Echo: hello" + + +def test_connect_timeout(slow_start_server_script): + """Test that connect_timeout raises TimeoutError when server starts slowly""" + with pytest.raises(TimeoutError, match="Couldn't connect to the MCP server after 1 seconds"): + with MCPAdapt( + StdioServerParameters( + command="uv", args=["run", "python", "-c", slow_start_server_script] + ), + DummyAdapter(), + connect_timeout=1, # 1 second timeout, server takes 2 seconds to start + ): + pass + + +def test_client_session_timeout_parameter_propagation(echo_server_script): + """Test that client_session_timeout_seconds parameter is properly stored and accessible""" + from datetime import timedelta + + # Test with float value + adapter_float = MCPAdapt( + StdioServerParameters( + command="uv", args=["run", "python", "-c", echo_server_script] + ), + DummyAdapter(), + client_session_timeout_seconds=2.5, + ) + assert adapter_float.client_session_timeout_seconds == 2.5 + + # Test with timedelta value + timeout_td = timedelta(seconds=3.0) + adapter_td = MCPAdapt( + StdioServerParameters( + command="uv", args=["run", "python", "-c", echo_server_script] + ), + DummyAdapter(), + client_session_timeout_seconds=timeout_td, + ) + assert adapter_td.client_session_timeout_seconds == timeout_td + + # Test with None value + adapter_none = MCPAdapt( + StdioServerParameters( + command="uv", args=["run", "python", "-c", echo_server_script] + ), + DummyAdapter(), + client_session_timeout_seconds=None, + ) + assert adapter_none.client_session_timeout_seconds is None + + # Test default value + adapter_default = MCPAdapt( + StdioServerParameters( + command="uv", args=["run", "python", "-c", echo_server_script] + ), + DummyAdapter(), + ) + assert adapter_default.client_session_timeout_seconds == 5 From eb7dfe56e352518126bc40aeb39f2a141f644408 Mon Sep 17 00:00:00 2001 From: Guillaume Raille Date: Sat, 14 Jun 2025 11:08:49 +0200 Subject: [PATCH 3/3] ruff format --- src/mcpadapt/core.py | 8 ++++++-- tests/test_core.py | 12 +++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/mcpadapt/core.py b/src/mcpadapt/core.py index 26a0f91..5790f99 100644 --- a/src/mcpadapt/core.py +++ b/src/mcpadapt/core.py @@ -220,7 +220,9 @@ def _run_loop(self): async def setup(): async with AsyncExitStack() as stack: connections = [ - await stack.enter_async_context(mcptools(params, self.client_session_timeout_seconds)) + await stack.enter_async_context( + mcptools(params, self.client_session_timeout_seconds) + ) for params in self.serverparams ] self.sessions, self.mcp_tools = [list(c) for c in zip(*connections)] @@ -326,7 +328,9 @@ async def __aenter__(self) -> list[Any]: self._ctxmanager = AsyncExitStack() connections = [ - await self._ctxmanager.enter_async_context(mcptools(params, self.client_session_timeout_seconds)) + await self._ctxmanager.enter_async_context( + mcptools(params, self.client_session_timeout_seconds) + ) for params in self.serverparams ] diff --git a/tests/test_core.py b/tests/test_core.py index c15ea4b..f66170e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -345,7 +345,9 @@ async def test_basic_async_streamable_http(echo_streamable_http_server): def test_connect_timeout(slow_start_server_script): """Test that connect_timeout raises TimeoutError when server starts slowly""" - with pytest.raises(TimeoutError, match="Couldn't connect to the MCP server after 1 seconds"): + with pytest.raises( + TimeoutError, match="Couldn't connect to the MCP server after 1 seconds" + ): with MCPAdapt( StdioServerParameters( command="uv", args=["run", "python", "-c", slow_start_server_script] @@ -359,7 +361,7 @@ def test_connect_timeout(slow_start_server_script): def test_client_session_timeout_parameter_propagation(echo_server_script): """Test that client_session_timeout_seconds parameter is properly stored and accessible""" from datetime import timedelta - + # Test with float value adapter_float = MCPAdapt( StdioServerParameters( @@ -369,7 +371,7 @@ def test_client_session_timeout_parameter_propagation(echo_server_script): client_session_timeout_seconds=2.5, ) assert adapter_float.client_session_timeout_seconds == 2.5 - + # Test with timedelta value timeout_td = timedelta(seconds=3.0) adapter_td = MCPAdapt( @@ -380,7 +382,7 @@ def test_client_session_timeout_parameter_propagation(echo_server_script): client_session_timeout_seconds=timeout_td, ) assert adapter_td.client_session_timeout_seconds == timeout_td - + # Test with None value adapter_none = MCPAdapt( StdioServerParameters( @@ -390,7 +392,7 @@ def test_client_session_timeout_parameter_propagation(echo_server_script): client_session_timeout_seconds=None, ) assert adapter_none.client_session_timeout_seconds is None - + # Test default value adapter_default = MCPAdapt( StdioServerParameters(