Skip to content

fix(cli): wait for background MCP discovery before building the one-shot agent (#68137) - #68162

Closed
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:fix/oneshot-wait-mcp-discovery
Closed

fix(cli): wait for background MCP discovery before building the one-shot agent (#68137)#68162
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:fix/oneshot-wait-mcp-discovery

Conversation

@PRATHAMESH75

Copy link
Copy Markdown
Contributor

What does this PR do?

hermes -z (one-shot) snapshots the tool registry before background MCP
discovery finishes. CLI startup kicks MCP discovery onto the
cli-mcp-discovery background thread for the one-shot command
(_should_background_mcp_startup), but hermes_cli/oneshot.py:_run_agent
built AIAgent — which snapshots the tool registry at construction — without
ever joining that thread.

Fast servers (a compiled binary like mcp-grafana) usually make it in; slower
stdio servers (a Python-based MCP that takes several seconds to import/boot)
are silently dropped — the agent runs without their tools, with no error
anywhere. Since the model never sees the tools, it sometimes refuses the task
and sometimes fabricates a plausible answer as if it had called the tool.

The interactive path already waits (cli_agent_setup_mixin calls
wait_for_mcp_discovery()); the one-shot path just never got the same
treatment. This PR adds that wait before constructing the agent.

Related Issue

Fixes #68137

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • hermes_cli/oneshot.py — in _run_agent, call
    wait_for_mcp_discovery() before building AIAgent. The wait is a no-op
    when no discovery thread is in flight and is bounded by
    mcp_discovery_timeout, so a dead server can't hang the one-shot; failures
    are swallowed (debug-logged) so they never abort the run.
  • tests/hermes_cli/test_oneshot_mcp_wait.py — new regression tests: the wait
    is invoked before the agent is constructed, and a wait that raises does
    not break the one-shot run.

How to Test

scripts/run_tests.sh tests/hermes_cli/test_oneshot_mcp_wait.py -q

Both tests pass. Verified the ordering test genuinely fails without the
source change (stashed the fix → test_waits_before_building_agent fails).
Preflight (windows-footguns, ruff, affected tests) is green.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run the affected tests and they pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) — or N/A (pure Python, no platform-specific code)
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Notes

The issue also flags a related quirk: under -p <profile>, load_config()
resolves the profile config, so a globally-set mcp_discovery_timeout may
not be honored by profile runs. That is a separate config-resolution concern
and is intentionally left out of this focused fix.

…hot agent

CLI startup kicks MCP tool discovery onto the cli-mcp-discovery background
thread for the one-shot command, but hermes_cli/oneshot.py:_run_agent built
AIAgent — which snapshots the tool registry at construction — without joining
that thread. Fast MCP servers usually made it in; slower stdio servers (a
Python module that takes several seconds to import/boot) were silently dropped,
so the model ran without their tools and would sometimes refuse or fabricate.

Mirror the interactive path (cli_agent_setup_mixin) by calling
wait_for_mcp_discovery() before constructing the agent. It is a no-op when no
discovery is in flight and is bounded by mcp_discovery_timeout, so a dead
server still can't hang the one-shot.

Fixes NousResearch#68137

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for targeting the direct one-shot construction path. Current main still has the pre-snapshot gap: hermes_cli/main.py:10376-10435 backgrounds discovery, while hermes_cli/oneshot.py:394-410 reaches AIAgent without a wait; agent/agent_init.py:1393-1397 then snapshots tools.

Problems

  • The new call remains bounded by mcp_discovery_timeout (hermes_cli/mcp_startup.py:151-164), whose default is 1.5 seconds (hermes_cli/config_defaults.py:433-446). For a one-shot, a server completing after that bound cannot arrive through the regular next-turn refresh (agent/turn_context.py:399-425), so the reported multi-second startup case remains unresolved.

Suggested changes

  • Define a one-shot completion policy that covers slow configured MCP servers, or narrow the scope to the configured bounded-wait behavior and test both sides of the timeout.
  • Add a delayed-discovery behavioral test asserting the constructed agent receives the registered MCP schema.

This is an automated hermes-sweeper review.

Comment thread hermes_cli/oneshot.py
from hermes_cli.mcp_startup import wait_for_mcp_discovery

wait_for_mcp_discovery()
except Exception:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait_for_mcp_discovery() is bounded by the 1.5-second default. Because one-shot has no later user turn for agent/turn_context.py's between-turn refresh, a configured MCP server completing after that bound is still absent from this run. Please either make the one-shot policy sufficient for that case or narrow and test the stated guarantee.

@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Jul 30, 2026
@PRATHAMESH75

Copy link
Copy Markdown
Contributor Author

Closing as superseded. Upstream commit 3572d4bca ("fix(mcp): ensure MCP discovery completes before agent build in non-interactive sessions", by @kshitijk4poor) landed on main and explicitly Closes #68137 — the same issue this PR targets. It wires ensure_mcp_discovery_before_agent_build(single_query=True) into oneshot._run_agent, exactly where this PR added its wait.

Its approach is also strictly broader than mine: this PR called wait_for_mcp_discovery(), which only joins an already-created discovery thread and no-ops if a direct/single-query path reaches agent construction before MCP startup ever created that thread. The merged helper idempotently starts discovery when needed and uses the larger single-query timeout (default 15s), covering that gap. So there's no remaining coverage for me to add on top.

Thanks @kshitijk4poor — closing in favor of the merged fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades tool/mcp MCP client and OAuth type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

One-shot mode (-z) builds the agent without waiting for background MCP discovery — slow stdio servers are silently dropped

3 participants