Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions tests/tools/test_mcp_stdio_fastfail_reconnect.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,15 @@ async def _hanging_call(*a, **kw):
mcp_tool, "srv-midcall", _hanging_call, children_dead=lambda: False
)

async def _watch_children():
return # children die immediately → watcher resolves first
watch_calls = {"n": 0}

def _watch_children():
watch_calls["n"] += 1

async def _child_exit():
return # children die immediately → watcher resolves first

return _child_exit()

server._watch_stdio_children = _watch_children
mcp_tool._ensure_mcp_loop()
Expand All @@ -122,5 +129,6 @@ async def _watch_children():
assert "error" in parsed, parsed
assert "exited mid-call" in parsed["error"], parsed
assert server._reconnect_event.set_calls == 1
assert watch_calls["n"] == 1, "watcher coroutine must be created exactly once"
finally:
_cleanup(mcp_tool, "srv-midcall")
11 changes: 5 additions & 6 deletions tools/mcp_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -6184,11 +6184,10 @@ async def _call():
)
_call_coro = server.session.call_tool(tool_name, arguments=args)
_watch_children = getattr(server, "_watch_stdio_children", None)
_watch_ok = (
_watch_children is not None
and inspect.isawaitable(_watch_children())
and asyncio.iscoroutine(_call_coro)
)
_watch_coro = None
if callable(_watch_children) and asyncio.iscoroutine(_call_coro):
_watch_coro = _watch_children()
_watch_ok = inspect.isawaitable(_watch_coro)
if not _watch_ok:
# Stubbed sessions (MagicMock in tests) return a
# non-awaitable, or there is no child-watcher to race
Expand All @@ -6205,7 +6204,7 @@ async def _call():
# the call immediately instead of riding out the full
# tool timeout.
rpc_task = asyncio.ensure_future(_call_coro)
watch_task = asyncio.ensure_future(_watch_children())
watch_task = asyncio.ensure_future(_watch_coro)
try:
done, _pending = await asyncio.wait(
{rpc_task, watch_task},
Expand Down