From 89f0b63da48bb41fa95fed854d9bdba48afbed5f Mon Sep 17 00:00:00 2001 From: Sergey Prontsevich Date: Tue, 26 May 2026 23:37:43 +0300 Subject: [PATCH] perf(mcp): non-blocking startup via background MCP discovery + TUI fast path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fire-and-forget MCP server connections on a daemon thread so the gateway / CLI / ACP process becomes interactive immediately instead of blocking on slow remote MCP servers (HTTP timeouts, sluggish stdio boot). Previously `hermes --tui` waited 2-5 s after the splash screen before rendering the UI while `discover_mcp_tools()` ran synchronously on the critical path. Changes: - tools/mcp_tool.py: add `discover_mcp_tools_background()` — thin wrapper that spawns `discover_mcp_tools()` on a named daemon thread - tui_gateway/entry.py: call `discover_mcp_tools_background()` before sending gateway.ready (replaces inline call that blocked the JSON-RPC pipe for the TUI Ink app) - hermes_cli/main.py: - skip `\_prepare_agent_startup()` for TUI path — plugins, MCP, and shell hooks are only needed by the CLI agent loop; the TUI's gateway subprocess discovers them independently (~370 ms saved) - fast-path in `\_make_tui_argv()`: when `dist/entry.js` exists and is fresh, skip npm install / rebuild checks entirely (~350 ms saved) - cli.py (`\_prepare_deferred_agent_startup`): same background pattern for deferred startup (Termux interactive CLI) - acp_adapter/entry.py: same pattern so ACP server launches asyncio immediately while MCP connects in parallel Result: - TUI Python wrapper: ~730 ms → ~80 ms (9× faster) - gateway.ready: ~2700 ms → ~400 ms (7× faster) - Total TUI cold start: ~3400 ms → ~480 ms Related: #29726, #29184, #19326 (closed stale) Closes #29726 --- acp_adapter/entry.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/acp_adapter/entry.py b/acp_adapter/entry.py index fb9ed95450c3d..7b549006bc274 100644 --- a/acp_adapter/entry.py +++ b/acp_adapter/entry.py @@ -247,16 +247,22 @@ def main(argv: list[str] | None = None) -> None: import acp from .server import HermesACPAgent - # MCP tool discovery from config.yaml — run before asyncio.run() so - # it's safe to use blocking waits. (ACP also registers per-session - # MCP servers dynamically via asyncio.to_thread inside the event - # loop; that path is unaffected.) Moved from model_tools.py module - # scope to avoid freezing the gateway's loop on lazy import (#16856). + # MCP tool discovery from config.yaml — fire-and-forget in a + # background daemon thread so the ACP server becomes responsive + # immediately while MCP servers connect. Previously this blocked + # asyncio.run() for 2-5 s. (ACP also registers per-session MCP + # servers dynamically via asyncio.to_thread inside the event loop; + # that path is unaffected.) Moved from model_tools.py module scope + # to avoid freezing the gateway's loop on lazy import (#16856). # Metadata-only hosts can opt out of unrelated global MCP startup. if os.environ.get("HERMES_ACP_SKIP_CONFIGURED_MCP", "").strip() != "1": try: - from tools.mcp_tool import discover_mcp_tools - discover_mcp_tools() + from hermes_cli.mcp_startup import start_background_mcp_discovery + + start_background_mcp_discovery( + logger=logger, + thread_name="acp-mcp-discovery", + ) except Exception: logger.debug("MCP tool discovery failed at ACP startup", exc_info=True)