From 9575bce6ca95c0fe088e04f1abfaf4009a1d3e12 Mon Sep 17 00:00:00 2001 From: AJV20 Date: Wed, 15 Apr 2026 08:01:15 -0400 Subject: [PATCH] fix(mcp): clear stale thread interrupt before MCP discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #9930 When an agent session is interrupted (Ctrl+C or gateway timeout), the current thread's interrupt flag is set in _interrupted_threads. asyncio executor threads are pooled and reused across sessions, so a thread that carried an interrupt flag from a prior session will immediately cancel any new asyncio work dispatched to it — including MCP server discovery. Fix: in register_mcp_servers(), temporarily clear the interrupt flag on the current thread before running _discover_all(), then restore it afterward in a finally block so the original interrupt state is not lost. --- tools/mcp_tool.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tools/mcp_tool.py b/tools/mcp_tool.py index c3d88475f5..d5c6fc6a45 100644 --- a/tools/mcp_tool.py +++ b/tools/mcp_tool.py @@ -2922,7 +2922,19 @@ def register_mcp_servers(servers: Dict[str, dict]) -> List[str]: # Per-server timeouts are handled inside _discover_and_register_server. # The outer timeout is generous: 120s total for parallel discovery. - _run_on_mcp_loop(_discover_all(), timeout=120) + # + # Temporarily clear the interrupt flag on the current thread so that MCP + # discovery is never cancelled by a stale interrupt from a prior agent + # session (executor threads get reused and may carry old interrupt state). + from tools.interrupt import is_interrupted as _is_interrupted, set_interrupt as _set_interrupt + _was_interrupted = _is_interrupted() + if _was_interrupted: + _set_interrupt(False) + try: + _run_on_mcp_loop(_discover_all(), timeout=120) + finally: + if _was_interrupted: + _set_interrupt(True) # Log a summary so ACP callers get visibility into what was registered. with _lock: