fix(mcp): run MCP discovery synchronously for quiet-mode sessions - #36882
fix(mcp): run MCP discovery synchronously for quiet-mode sessions#36882vanhoof wants to merge 2 commits into
Conversation
da5d66d to
965fa4e
Compare
|
The kanban dispatcher spawns workers with -q (--query), not -Q (--quiet), they're different argparse flags. The original check only gated on args.quiet, so kanban workers and cron jobs still got the 750ms background timeout. Added args.query to the condition and split the test into two functions so each flag gets its own clean fixture lifecycle. |
|
Thanks for tracing the Problems
Suggested changes
This is an automated hermes-sweeper review. |
When `chat -q` is used (kanban workers, cron jobs, scripted invocations), MCP server registration must complete before `get_tool_definitions()` builds the tool schema. The background MCP discovery introduced in 0c6e133 uses a 750ms join timeout that is too short for slow-starting servers (e.g. mcp-atlassian via uvx takes 12+ seconds to register). This causes a race condition where late-registering MCP tools are invisible to the agent -- not in the direct tool schema and not behind tool_search. The agent literally cannot call them. The fix exempts quiet-mode sessions from background MCP startup. There is no interactive prompt to rush to, so synchronous discovery has zero UX cost and guarantees all configured MCP servers are registered before the first agent turn. Interactive sessions (chat without -q) keep the background optimization since users benefit from the faster prompt display. Signed-off-by: Chris van Hoof <vanhoof@ouwish.com>
…on tests Address hermes-sweeper review feedback on PR NousResearch#36882: - Remove args.quiet from the sync-discovery gate. -Q is output suppression for interactive sessions, not a non-interactive signal. - Replace raw discover_mcp_tools() fallthrough with background discovery + join_mcp_discovery(timeout=30s) so a dead server cannot hang the process. - Add integration tests: delayed server tools visible after startup, blocked server bounded by timeout cap, -Q keeps background optimization. Signed-off-by: Chris van Hoof <vanhoof@ouwish.com>
965fa4e to
2745c43
Compare
|
Addressed all three points from the review: 1. 2. Bounded deadline on the query-mode path. Query mode ( 3. Delayed-registration and bounded-timeout integration tests added:
Also cleaned up dead code: removed the now-redundant 9 tests pass (5 existing + 4 new/updated). |
…teractive sessions Non-interactive sessions (hermes chat -q, hermes -z) snapshot the tool registry at AIAgent construction time. If background MCP discovery hasn't finished, MCP tools are invisible for the entire session — and unlike interactive mode, there is no between-turns late-binding refresh to recover. Root cause: wait_for_mcp_discovery() only joins an already-created discovery thread, so it no-ops if a direct/single-query path reaches agent construction before MCP startup created that thread. Oneshot._run_agent() didn't call it at all. Fix: - Add ensure_mcp_discovery_before_agent_build() helper to mcp_startup.py: idempotently starts discovery if needed + bounded wait. Fail-open on errors. - Add single_query parameter to _resolve_discovery_timeout/wait_for_mcp_discovery: uses mcp_single_query_discovery_timeout (default 15s) instead of the interactive mcp_discovery_timeout (1.5s) because one-shot sessions have no second turn to recover. - Wire into CLI _init_agent (single_query from _single_query_mode flag set in cli.py's single-query path) and oneshot._run_agent (single_query=True). - Interactive sessions unchanged: keep 1.5s bound (between-turns refresh covers). Closes #38448, #51316, #37013, #68137 Composite salvage of #60017 (chrishart0), #51322 (Bartok9), #38620 (buptwz), #43544 (halonke), #36882 (vanhoof).
|
Closing as superseded upstream. This query-mode / kanban-worker MCP discovery race is resolved on main by #75933 (ensure MCP discovery completes before agent build in non-interactive sessions). Verified against current main during a runtime rebase — the bounded-timeout approach proposed here is no longer needed. |
…teractive sessions Non-interactive sessions (hermes chat -q, hermes -z) snapshot the tool registry at AIAgent construction time. If background MCP discovery hasn't finished, MCP tools are invisible for the entire session — and unlike interactive mode, there is no between-turns late-binding refresh to recover. Root cause: wait_for_mcp_discovery() only joins an already-created discovery thread, so it no-ops if a direct/single-query path reaches agent construction before MCP startup created that thread. Oneshot._run_agent() didn't call it at all. Fix: - Add ensure_mcp_discovery_before_agent_build() helper to mcp_startup.py: idempotently starts discovery if needed + bounded wait. Fail-open on errors. - Add single_query parameter to _resolve_discovery_timeout/wait_for_mcp_discovery: uses mcp_single_query_discovery_timeout (default 15s) instead of the interactive mcp_discovery_timeout (1.5s) because one-shot sessions have no second turn to recover. - Wire into CLI _init_agent (single_query from _single_query_mode flag set in cli.py's single-query path) and oneshot._run_agent (single_query=True). - Interactive sessions unchanged: keep 1.5s bound (between-turns refresh covers). Closes NousResearch#38448, NousResearch#51316, NousResearch#37013, NousResearch#68137 Composite salvage of NousResearch#60017 (chrishart0), NousResearch#51322 (Bartok9), NousResearch#38620 (buptwz), NousResearch#43544 (halonke), NousResearch#36882 (vanhoof).
…teractive sessions Non-interactive sessions (hermes chat -q, hermes -z) snapshot the tool registry at AIAgent construction time. If background MCP discovery hasn't finished, MCP tools are invisible for the entire session — and unlike interactive mode, there is no between-turns late-binding refresh to recover. Root cause: wait_for_mcp_discovery() only joins an already-created discovery thread, so it no-ops if a direct/single-query path reaches agent construction before MCP startup created that thread. Oneshot._run_agent() didn't call it at all. Fix: - Add ensure_mcp_discovery_before_agent_build() helper to mcp_startup.py: idempotently starts discovery if needed + bounded wait. Fail-open on errors. - Add single_query parameter to _resolve_discovery_timeout/wait_for_mcp_discovery: uses mcp_single_query_discovery_timeout (default 15s) instead of the interactive mcp_discovery_timeout (1.5s) because one-shot sessions have no second turn to recover. - Wire into CLI _init_agent (single_query from _single_query_mode flag set in cli.py's single-query path) and oneshot._run_agent (single_query=True). - Interactive sessions unchanged: keep 1.5s bound (between-turns refresh covers). Closes NousResearch#38448, NousResearch#51316, NousResearch#37013, NousResearch#68137 Composite salvage of NousResearch#60017 (chrishart0), NousResearch#51322 (Bartok9), NousResearch#38620 (buptwz), NousResearch#43544 (halonke), NousResearch#36882 (vanhoof).
Problem
When
chat -q "prompt"is used (kanban workers, cron jobs, scripted invocations), MCP tools from slow-starting servers are invisible to the agent.The background MCP discovery introduced in 0c6e133 ("perf: stop eager MCP discovery from blocking agent-capable startup") uses a 750ms join timeout via
wait_for_mcp_discovery(). Fast MCP servers (rover, slack, cp_strategy_coach) register in ~35ms, but slower ones likemcp-atlassianvia uvx take 12+ seconds due to subprocess startup + HTTPS handshake.get_tool_definitions()runs after the 750ms timeout expires, so only the tools from fast servers are present. Late-arriving tools are not in the direct schema and not behindtool_search-- they are completely invisible to the model.Kanban workers are the primary victim
The kanban dispatcher spawns workers as:
Note:
-qis--query(the prompt text), not-Q/--quiet(the quiet-mode flag). The existing_should_background_mcp_startup()had no check for query mode, so kanban workers always got the background optimization with the 750ms timeout -- exactly the sessions that can least afford it, since they build the tool list once at startup and never rebuild it.Timeline from a real kanban worker session:
The worker could not post a Jira comment because all 53
mcp_mcp_atlassian_jira_*tools were missing from both the direct schema and tool_search.Fix
Exempt both quiet mode (
-Q/--quiet) and query mode (-q/--query) from background MCP startup. These are non-interactive sessions (kanban workers, cron jobs, scripted invocations) -- there is no user waiting at a prompt, so synchronous discovery has zero UX cost.Interactive sessions (
chatwithout-qor-Q) keep the background optimization.Changes
hermes_cli/main.py:_should_background_mcp_startup()returnsFalsewhenargs.quietorargs.queryis settests/hermes_cli/test_mcp_startup.py: regression test covering both-Q(quiet) and-q "prompt"(query) modesTesting
All 5
test_mcp_startuptests pass (4 existing + 1 new with 2 sub-cases).