Conversation
|
Corroborating #96030 — independent confirmation of the coroutine-leak premise. I independently root-caused #96030 (reporter ilgo) to the same line: Your change to probe the function type ( One suggestion for review: |
|
Closing — both halves of this PR are now handled, with your coroutine-leak fix landing via salvage:
Thanks for the contribution — the leak half would have been easy to miss under the pile of polarity duplicates. |
…k probe The fast-fail gate probed the stdio child watcher by CALLING it — inspect.isawaitable(_watch_children()) — creating a fresh coroutine on every stdio MCP tool call that was never awaited (RuntimeWarning spam + gc churn). Inspect the function instead of invoking it. Salvaged (unique hunk only) from PR #96044; the bundled _stdio_children_dead polarity fix was already on main via #94339.
…function Follow-up to the salvaged #96044 hunk: drop the 'or callable(...)' arm — callable(MagicMock) is True, which would have flipped stubbed sessions into the fast-fail race the surrounding comment explicitly routes to the plain-await path. inspect.iscoroutinefunction alone reproduces the old isawaitable(call) split exactly (real async def / AsyncMock -> race, MagicMock -> plain await) without creating the leaked coroutine.
…k probe The fast-fail gate probed the stdio child watcher by CALLING it — inspect.isawaitable(_watch_children()) — creating a fresh coroutine on every stdio MCP tool call that was never awaited (RuntimeWarning spam + gc churn). Inspect the function instead of invoking it. Salvaged (unique hunk only) from PR NousResearch#96044; the bundled _stdio_children_dead polarity fix was already on main via NousResearch#94339.
…function Follow-up to the salvaged NousResearch#96044 hunk: drop the 'or callable(...)' arm — callable(MagicMock) is True, which would have flipped stubbed sessions into the fast-fail race the surrounding comment explicitly routes to the plain-await path. inspect.iscoroutinefunction alone reproduces the old isawaitable(call) split exactly (real async def / AsyncMock -> race, MagicMock -> plain await) without creating the leaked coroutine.
Summary
Fixes #96016, #96019, #96030
tools/mcp_tool.py(_stdio_children_dead):Truewhenpsutil.pid_exists(pid)was True (indicating the process is alive), which caused live stdio MCP subprocesses to be prematurely flagged as dead and killed withTimeoutError.Falseif any tracked child PID is alive, andTrueonly when all child processes have exited.tools/mcp_tool.py(_make_tool_handler):_watch_okcheck by avoiding calling_watch_children()directly during liveness inspection.tests/tools/test_mcp_stdio_children_dead.py:_stdio_children_deadbehavior on empty, alive, and dead PIDs.