Skip to content

Commit bfd000f

Browse files
njbrakegrll
andauthored
Pass client_session_timeout through from MCPAdapt (#51)
* Pass client_session_timeout through from MCPAdapt Follow up from #35 * 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 * ruff format --------- Co-authored-by: Guillaume Raille <guillaume.raille@gmail.com>
1 parent 8b914ac commit bfd000f

File tree

2 files changed

+91
-2
lines changed

2 files changed

+91
-2
lines changed

src/mcpadapt/core.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ def __init__(
176176
| list[StdioServerParameters | dict[str, Any]],
177177
adapter: ToolAdapter,
178178
connect_timeout: int = 30,
179+
client_session_timeout_seconds: float | timedelta | None = 5,
179180
):
180181
"""
181182
Manage the MCP server / client lifecycle and expose tools adapted with the adapter.
@@ -185,6 +186,7 @@ def __init__(
185186
MCP server parameters (stdio or sse). Can be a list if you want to connect multiple MCPs at once.
186187
adapter (ToolAdapter): Adapter to use to convert MCP tools call into agentic framework tools.
187188
connect_timeout (int): Connection timeout in seconds to the mcp server (default is 30s).
189+
client_session_timeout_seconds: Timeout for MCP ClientSession calls
188190
189191
Raises:
190192
TimeoutError: When the connection to the mcp server time out.
@@ -209,6 +211,7 @@ def __init__(
209211
self.thread = threading.Thread(target=self._run_loop, daemon=True)
210212

211213
self.connect_timeout = connect_timeout
214+
self.client_session_timeout_seconds = client_session_timeout_seconds
212215

213216
def _run_loop(self):
214217
"""Runs the event loop in a separate thread (for synchronous usage)."""
@@ -217,7 +220,9 @@ def _run_loop(self):
217220
async def setup():
218221
async with AsyncExitStack() as stack:
219222
connections = [
220-
await stack.enter_async_context(mcptools(params))
223+
await stack.enter_async_context(
224+
mcptools(params, self.client_session_timeout_seconds)
225+
)
221226
for params in self.serverparams
222227
]
223228
self.sessions, self.mcp_tools = [list(c) for c in zip(*connections)]
@@ -323,7 +328,9 @@ async def __aenter__(self) -> list[Any]:
323328
self._ctxmanager = AsyncExitStack()
324329

325330
connections = [
326-
await self._ctxmanager.enter_async_context(mcptools(params))
331+
await self._ctxmanager.enter_async_context(
332+
mcptools(params, self.client_session_timeout_seconds)
333+
)
327334
for params in self.serverparams
328335
]
329336

tests/test_core.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,28 @@ async def echo_streamable_http_server(echo_server_streamable_http_script):
153153
process.wait()
154154

155155

156+
@pytest.fixture
157+
def slow_start_server_script():
158+
return dedent(
159+
'''
160+
import time
161+
from mcp.server.fastmcp import FastMCP
162+
163+
# Sleep for 2 seconds to simulate slow startup
164+
time.sleep(2)
165+
166+
mcp = FastMCP("Slow Server")
167+
168+
@mcp.tool()
169+
def echo_tool(text: str) -> str:
170+
"""Echo the input text"""
171+
return f"Echo: {text}"
172+
173+
mcp.run()
174+
'''
175+
)
176+
177+
156178
def test_basic_sync(echo_server_script):
157179
with MCPAdapt(
158180
StdioServerParameters(
@@ -319,3 +341,63 @@ async def test_basic_async_streamable_http(echo_streamable_http_server):
319341
) as tools:
320342
assert len(tools) == 1
321343
assert (await tools[0]({"text": "hello"})).content[0].text == "Echo: hello"
344+
345+
346+
def test_connect_timeout(slow_start_server_script):
347+
"""Test that connect_timeout raises TimeoutError when server starts slowly"""
348+
with pytest.raises(
349+
TimeoutError, match="Couldn't connect to the MCP server after 1 seconds"
350+
):
351+
with MCPAdapt(
352+
StdioServerParameters(
353+
command="uv", args=["run", "python", "-c", slow_start_server_script]
354+
),
355+
DummyAdapter(),
356+
connect_timeout=1, # 1 second timeout, server takes 2 seconds to start
357+
):
358+
pass
359+
360+
361+
def test_client_session_timeout_parameter_propagation(echo_server_script):
362+
"""Test that client_session_timeout_seconds parameter is properly stored and accessible"""
363+
from datetime import timedelta
364+
365+
# Test with float value
366+
adapter_float = MCPAdapt(
367+
StdioServerParameters(
368+
command="uv", args=["run", "python", "-c", echo_server_script]
369+
),
370+
DummyAdapter(),
371+
client_session_timeout_seconds=2.5,
372+
)
373+
assert adapter_float.client_session_timeout_seconds == 2.5
374+
375+
# Test with timedelta value
376+
timeout_td = timedelta(seconds=3.0)
377+
adapter_td = MCPAdapt(
378+
StdioServerParameters(
379+
command="uv", args=["run", "python", "-c", echo_server_script]
380+
),
381+
DummyAdapter(),
382+
client_session_timeout_seconds=timeout_td,
383+
)
384+
assert adapter_td.client_session_timeout_seconds == timeout_td
385+
386+
# Test with None value
387+
adapter_none = MCPAdapt(
388+
StdioServerParameters(
389+
command="uv", args=["run", "python", "-c", echo_server_script]
390+
),
391+
DummyAdapter(),
392+
client_session_timeout_seconds=None,
393+
)
394+
assert adapter_none.client_session_timeout_seconds is None
395+
396+
# Test default value
397+
adapter_default = MCPAdapt(
398+
StdioServerParameters(
399+
command="uv", args=["run", "python", "-c", echo_server_script]
400+
),
401+
DummyAdapter(),
402+
)
403+
assert adapter_default.client_session_timeout_seconds == 5

0 commit comments

Comments
 (0)