perf(mcp): non-blocking startup via background MCP discovery + TUI fast path

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
This commit is contained in:
Sergey Prontsevich 2026-05-26 23:37:43 +03:00 committed by kshitij
parent 9b1d8341b2
commit 89f0b63da4
1 changed files with 13 additions and 7 deletions

View File

@ -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)