Skip to content

fix(mcp): raise headless discovery timeout and add HERMES_MCP_DISCOVERY_WAIT override - #37301

Open
Kewe63 wants to merge 2 commits into
NousResearch:mainfrom
Kewe63:fix/mcp-headless-discovery-timeout
Open

fix(mcp): raise headless discovery timeout and add HERMES_MCP_DISCOVERY_WAIT override#37301
Kewe63 wants to merge 2 commits into
NousResearch:mainfrom
Kewe63:fix/mcp-headless-discovery-timeout

Conversation

@Kewe63

@Kewe63 Kewe63 commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Description

Problem

When running in hermes -z (headless/oneshot) mode, Model Context Protocol (MCP) servers that take longer than 0.75 seconds to connect are silently dropped. Container- and stdio-backed servers requiring a real process cold-start (typically 1–2 seconds) consistently miss this narrow initialization window. Because the toolset is frozen at session startup and never refreshed, the agent operates throughout the entire session as if these tools do not exist.

Root Cause

The wait_for_mcp_discovery() function utilized a hardcoded 0.75-second default timeout, offering no mechanism for overrides via environment variables or configuration files. Furthermore, the headless execution path in cli.py invoked this function without arguments, leaving deployments without any escape hatch to handle slower server spin-ups.

Solution

  • hermes_cli/mcp_startup.py: Refactored wait_for_mcp_discovery() to resolve the effective timeout dynamically using the following order of precedence:
    1. HERMES_MCP_DISCOVERY_WAIT environment variable (float, in seconds)
    2. mcp.discovery_wait key defined in config.yaml
    3. The explicit timeout argument provided by the caller
  • tui_gateway/entry.py: Applied the identical resolution logic to the duplicate implementation found here.
  • cli.py: Updated both headless call sites to pass timeout=5.0 as the new, safer default (which remains fully overridable via env/config).

Technical Changes

  • Eliminated hardcoded timeout values in the MCP discovery phase.
  • Introduced flexible configuration options for operators managing slow-starting MCP servers.

Testing Handled

Added 5 new regression tests in tests/hermes_cli/test_mcp_discovery_timeout.py to validate the fallback hierarchy and behaviors:

  • Environment Variable Override: Verifies that setting the env var properly extends the timeout.
  • Immediate Skip: Confirms that setting the env var to 0 skips the wait duration immediately.
  • Fallback on Invalid Input: Ensures an invalid env var value safely falls back to the caller's default.
  • Default Behavior: Validates that the caller's default is respected when no env var is present.
  • Headless Default: Confirms the headless caller correctly passes the new 5.0-second default.

Test Results: All 13 tests passed successfully (8 existing + 5 new).


Impact

  • Container and stdio-backed MCP servers with ~1–2 second startup times are now reliably included in the headless toolset.
  • Operational Flexibility: Operators requiring longer initialization windows can now easily set HERMES_MCP_DISCOVERY_WAIT=10 or append mcp: {discovery_wait: 10} to config.yaml without modifying the codebase.

Fixes #37013

…RY_WAIT override — fixes NousResearch#37013

In hermes -z (headless/oneshot) mode, wait_for_mcp_discovery() used a
hardcoded 0.75 s timeout. Container- and stdio-backed MCP servers with
a real process cold-start (typically 1-2 s) consistently missed this
window, leaving the agent with a frozen partial toolset for the entire
session.

Changes:
- hermes_cli/mcp_startup.py: wait_for_mcp_discovery() now resolves the
  effective timeout in order: HERMES_MCP_DISCOVERY_WAIT env var →
  mcp.discovery_wait config.yaml key → caller argument.
- tui_gateway/entry.py: same resolution logic applied to the duplicate
  implementation there.
- cli.py: both headless call sites now pass timeout=5.0 as the default
  (overridable via env/config) instead of the 0.75 s legacy default.

Tests: 5 new regression tests in test_mcp_discovery_timeout.py,
13/13 passed (8 existing + 5 new).
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) tool/mcp MCP client and OAuth labels Jun 2, 2026

@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 real slow-MCP discovery failure reported in #37013.

Problems

  • Current main already centralizes this setting as mcp_discovery_timeout in hermes_cli/mcp_startup.py:53-71, and tui_gateway/entry.py:226-232 delegates to that resolver. The proposed HERMES_MCP_DISCOVERY_WAIT and duplicated mcp.discovery_wait logic would diverge from that design; it also adds a non-secret behavioral environment variable contrary to AGENTS.md.
  • The cli.py _init_agent hunk no longer targets current code: it moved to hermes_cli/cli_agent_setup_mixin.py:218-239 in 094aa85c3.
  • tests/hermes_cli/test_mcp_discovery_timeout.py:90 simulates the desired call by directly invoking the mocked helper; it does not verify a real hermes -z caller.

Suggested changes

  • Re-scope onto the existing mcp_discovery_timeout resolver and add a real one-shot startup-path regression test. Commit b6e2a54a deliberately reduced the global default to 1.5s after adding cache-safe between-turn refresh, so a 5s one-shot-specific policy needs to be made explicitly against the current architecture.

Automated hermes-sweeper review.


monkeypatch.setattr(mcp_startup, "wait_for_mcp_discovery", _fake_wait)

# Simulate what cli.py headless path does

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.

This only calls the monkeypatched helper directly, so it cannot detect a regression in either real headless caller. Please test the actual hermes -z startup/agent-build path with an in-flight discovery thread.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 13, 2026
…meout test

Teknium's review noted that test_headless_caller_passes_5s was calling the
monkeypatched helper directly, so a regression in the actual cli.py headless
caller would go undetected. Now the test drives cli.get_tool_definitions()
with an in-flight discovery thread, confirming the real caller passes timeout=5.0.
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 comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) 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.

[Bug]: -z/headless oneshots silently drop slow-connecting MCP servers (0.75 s discovery wait too short for container/stdio-backed servers)

3 participants