From f218474552b28afcfc53f001603bbde0f105d010 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Tue, 21 Jul 2026 07:09:35 -0700 Subject: [PATCH] fix(tui): gate the shared-owner MCP discovery wait on the stdio TUI flag The TUI retry-allowance follow-up made tui_gateway.entry.wait_for_mcp_discovery delegate to hermes_cli.mcp_startup unconditionally when no entry-local thread exists. But server._make_agent already calls the startup wait directly for dashboard /api/ws sessions, so every non-stdio agent build paid the bounded wait twice (caught by test_make_agent_waits_for_shared_mcp_discovery). Gate the retry-spawn AND the delegated wait on _mcp_discovery_enabled, which only the stdio TUI arms in main(). --- tests/test_tui_entry_mcp_owner.py | 7 +++++++ tui_gateway/entry.py | 26 ++++++++++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/tests/test_tui_entry_mcp_owner.py b/tests/test_tui_entry_mcp_owner.py index 1022fc4fbf5b..cf5b8c86bf18 100644 --- a/tests/test_tui_entry_mcp_owner.py +++ b/tests/test_tui_entry_mcp_owner.py @@ -16,6 +16,13 @@ from tui_gateway import entry def test_wait_falls_through_to_shared_owner(monkeypatch): monkeypatch.setattr(entry, "_mcp_discovery_thread", None) + # The fall-through to the shared owner only exists for the stdio TUI, + # which arms this flag in main(); other surfaces call the startup wait + # directly from _make_agent and must NOT be waited twice. + monkeypatch.setattr(entry, "_mcp_discovery_enabled", True) + monkeypatch.setattr( + mcp_startup, "start_background_mcp_discovery", lambda **kw: None + ) thread = threading.Thread(target=lambda: time.sleep(0.05), daemon=True) thread.start() monkeypatch.setattr(mcp_startup, "_mcp_discovery_thread", thread) diff --git a/tui_gateway/entry.py b/tui_gateway/entry.py index a81c0238eb9d..c585cca158fd 100644 --- a/tui_gateway/entry.py +++ b/tui_gateway/entry.py @@ -249,17 +249,23 @@ def wait_for_mcp_discovery(timeout: "float | None" = None) -> None: # connected servers, start_background_mcp_discovery's # retry-after-zero-connected allowance kicks off a fresh discovery run # here instead of leaving the TUI latched MCP-less for the session. - if _mcp_discovery_enabled: - try: - from hermes_cli.mcp_startup import start_background_mcp_discovery + # Only the stdio TUI (which spawned discovery through the shared owner) + # should delegate to the startup wait here — for every other surface + # (dashboard /api/ws) _make_agent already calls + # hermes_cli.mcp_startup.wait_for_mcp_discovery directly, and delegating + # unconditionally would make that bounded wait run twice per agent build. + if not _mcp_discovery_enabled: + return + try: + from hermes_cli.mcp_startup import start_background_mcp_discovery - start_background_mcp_discovery( - logger=logger, thread_name="tui-mcp-discovery" - ) - except Exception: - logger.debug( - "TUI MCP discovery retry-spawn failed", exc_info=True - ) + start_background_mcp_discovery( + logger=logger, thread_name="tui-mcp-discovery" + ) + except Exception: + logger.debug( + "TUI MCP discovery retry-spawn failed", exc_info=True + ) try: from hermes_cli.mcp_startup import ( wait_for_mcp_discovery as _startup_wait,