fix(desktop): discover MCP tools for dashboard /api/ws backends (#44512)
The desktop chat surface talks to the dashboard's in-process /api/ws gateway, which builds agents through tui_gateway.server._make_agent. That path only snapshots the existing tool registry — MCP discovery is started by tui_gateway/entry.py (the stdio TUI), which the dashboard process never runs. So a profile's configured MCP servers never connect under the desktop app and sessions show no MCP tools. Start a shared background MCP discovery thread at dashboard startup (via hermes_cli.mcp_startup, bounded so a slow/dead server can't block boot), and have _make_agent briefly join that thread in addition to the existing entry-owned TUI thread before snapshotting tools. Carved out of #44478. Co-authored-by: AJ <yspdev@gmail.com>
This commit is contained in:
@@ -128,3 +128,45 @@ class TestUnifiedDashboardRouting:
|
||||
with pytest.raises((SystemExit, AttributeError, ImportError, TypeError)):
|
||||
main_mod.cmd_dashboard(_args(open_profile="worker_x"))
|
||||
assert execs == []
|
||||
|
||||
def test_dashboard_starts_mcp_discovery_for_ws_backend(self, main_mod, monkeypatch):
|
||||
"""The dashboard process serves the /api/ws gateway but never runs
|
||||
tui_gateway/entry.py, so it must kick off MCP discovery itself or
|
||||
desktop sessions never see a profile's MCP tools."""
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.profiles.get_active_profile_name", lambda: "default"
|
||||
)
|
||||
monkeypatch.delenv("HERMES_WEB_DIST", raising=False)
|
||||
monkeypatch.setattr(main_mod, "_sync_bundled_skills_quietly", lambda: None)
|
||||
monkeypatch.setattr(main_mod, "_build_web_ui", lambda *_a, **_k: True)
|
||||
monkeypatch.setitem(sys.modules, "fastapi", types.SimpleNamespace())
|
||||
monkeypatch.setitem(sys.modules, "uvicorn", types.SimpleNamespace())
|
||||
monkeypatch.setitem(
|
||||
sys.modules,
|
||||
"hermes_logging",
|
||||
types.SimpleNamespace(setup_logging=lambda **_k: None),
|
||||
)
|
||||
monkeypatch.setitem(
|
||||
sys.modules,
|
||||
"hermes_cli.plugins",
|
||||
types.SimpleNamespace(discover_plugins=lambda: None),
|
||||
)
|
||||
calls = []
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.mcp_startup.start_background_mcp_discovery",
|
||||
lambda **kwargs: calls.append(kwargs),
|
||||
)
|
||||
monkeypatch.setitem(
|
||||
sys.modules,
|
||||
"hermes_cli.web_server",
|
||||
types.SimpleNamespace(start_server=lambda **_kwargs: None),
|
||||
)
|
||||
|
||||
main_mod.cmd_dashboard(_args())
|
||||
|
||||
assert calls == [
|
||||
{
|
||||
"logger": main_mod.logger,
|
||||
"thread_name": "dashboard-mcp-discovery",
|
||||
}
|
||||
]
|
||||
|
||||
@@ -6096,6 +6096,24 @@ def test_make_agent_reads_nested_max_turns(monkeypatch):
|
||||
assert mock_agent.call_args.kwargs["max_iterations"] == 200
|
||||
|
||||
|
||||
def test_make_agent_waits_for_shared_mcp_discovery(monkeypatch):
|
||||
_setup_make_agent_mocks(monkeypatch, {})
|
||||
waited = []
|
||||
|
||||
from hermes_cli import mcp_startup
|
||||
|
||||
monkeypatch.setattr(
|
||||
mcp_startup,
|
||||
"wait_for_mcp_discovery",
|
||||
lambda timeout=0.75: waited.append(timeout),
|
||||
)
|
||||
|
||||
with patch("run_agent.AIAgent"):
|
||||
server._make_agent("sid1", "key1")
|
||||
|
||||
assert waited == [0.75]
|
||||
|
||||
|
||||
def test_make_agent_nested_max_turns_takes_priority(monkeypatch):
|
||||
_setup_make_agent_mocks(
|
||||
monkeypatch, {"agent": {"max_turns": 500}, "max_turns": 100}
|
||||
|
||||
Reference in New Issue
Block a user