From cdbdcd6432dfb83fa97d9d20b7d47f93c9a3741f Mon Sep 17 00:00:00 2001 From: nicha16 <256398740+nicha16@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:31:39 -0700 Subject: [PATCH] fix(mcp): re-register tools after a parked server is revived MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _discover_tools only filled self._tools; registry registration happened only in _discover_and_register_server (initial start) and _refresh_tools. After parking deregistered a server's tools, a revival rebuilt the transport but published zero tools — a phantom recovery. Register freshly discovered tools whenever _ready is set and the registry entry list is empty. Extracted from PR #54139 by @nicha16 (the remainder of that PR reverses the park design and is not taken). --- tools/mcp_tool.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/mcp_tool.py b/tools/mcp_tool.py index 2a4d485f8d1..fabc1a74240 100644 --- a/tools/mcp_tool.py +++ b/tools/mcp_tool.py @@ -2340,6 +2340,7 @@ class MCPServerTask: self.name, ) self._tools = [] + self._register_discovered_tools_if_needed() return async with self._rpc_lock: tools_result = await self.session.list_tools() @@ -2348,6 +2349,23 @@ class MCPServerTask: if hasattr(tools_result, "tools") else [] ) + self._register_discovered_tools_if_needed() + + def _register_discovered_tools_if_needed(self) -> None: + """Re-register tools after a post-ready reconnect if needed. + + Initial registration is performed by ``_discover_and_register_server`` + after ``start()`` completes. During a later reconnect, however, + ``_ready`` remains set; if outage handling previously deregistered + stale tools (parking calls ``_deregister_tools``), a successful + revival must publish the freshly discovered tools again — otherwise + the transport comes back alive with zero registered tools. + """ + if not self._ready.is_set() or self._registered_tool_names: + return + self._registered_tool_names = _register_server_tools( + self.name, self, self._config + ) async def run(self, config: dict): """Long-lived coroutine: connect, discover tools, wait, disconnect.