hermes - ✅(Solved) Fix [Bug]: `hermes mcp add` silently launches chat instead of registering MCP server [2 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#19785Fetched 2026-05-05 06:05:12
View on GitHub
Comments
2
Participants
2
Timeline
8
Reactions
0
Timeline (top)
labeled ×4commented ×2cross-referenced ×2

Error Message

hermes mcp add <name> --url <url> does not register an MCP server. It silently falls through into an interactive Hermes chat session, with no error and no indication that the subcommand was not dispatched. The --help for the subcommand works correctly, which makes the bug look like a runtime/network problem rather than a dispatch problem.

Root Cause

There is an argparse dest collision between the top-level subparser and the mcp add subparser:

  1. hermes_cli/_parser.py registers the top-level subparsers with dest="command" so the dispatcher in hermes_cli/main.py can route on args.command.
  2. hermes_cli/main.py:9610 registers mcp_add_p.add_argument("--command", ...) for the stdio command. Without an explicit dest=, argparse derives dest="command" from the flag name.
  3. When the user runs hermes mcp add ... --url ... (no --command), argparse still writes args.command = None for that subparser flag, overwriting the top-level value of "mcp".
  4. The dispatcher at hermes_cli/main.py:10455 then sees args.command is None and falls through to cmd_chat. cmd_mcp_add is never reached.

Fix Action

Fix / Workaround

hermes mcp add <name> --url <url> does not register an MCP server. It silently falls through into an interactive Hermes chat session, with no error and no indication that the subcommand was not dispatched. The --help for the subcommand works correctly, which makes the bug look like a runtime/network problem rather than a dispatch problem.

  • CLI (interactive chat) / MCP subcommand dispatch
  1. hermes_cli/_parser.py registers the top-level subparsers with dest="command" so the dispatcher in hermes_cli/main.py can route on args.command.
  2. hermes_cli/main.py:9610 registers mcp_add_p.add_argument("--command", ...) for the stdio command. Without an explicit dest=, argparse derives dest="command" from the flag name.
  3. When the user runs hermes mcp add ... --url ... (no --command), argparse still writes args.command = None for that subparser flag, overwriting the top-level value of "mcp".
  4. The dispatcher at hermes_cli/main.py:10455 then sees args.command is None and falls through to cmd_chat. cmd_mcp_add is never reached.

PR fix notes

PR #19787: fix(mcp): give 'mcp add --command' a distinct argparse dest

Description (problem / solution / changelog)

What does this PR do?

hermes mcp add <name> --url <url> silently launches an interactive chat session instead of registering an MCP server, because of an argparse dest collision. This PR fixes the collision and adds a regression test at the parser layer.

The top-level subparsers in hermes_cli/_parser.py use dest="command" so the dispatcher in main.py can route on args.command. The mcp add subparser registers a --command flag (the stdio command for an MCP server) without an explicit dest=, so argparse derives dest="command" from the flag name. Whenever --command is omitted (e.g. on the documented --url install path), the subparser still writes args.command = None, clobbering the top-level "mcp". The dispatcher then sees args.command is None and falls through to cmd_chat. cmd_mcp_add is never reached — there is no error, no warning, no log.

The fix gives the flag an explicit, non-colliding dest. The user-facing CLI flag --command is unchanged; only the in-memory namespace attribute moves.

Related Issue

Fixes #19785.

Type of Change

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

Changes Made

  • hermes_cli/main.py — declare mcp_add_p.add_argument("--command", dest="mcp_command", ...) so the flag no longer overwrites args.command. Comment explains the constraint so a future cleanup pass does not re-introduce the collision.
  • hermes_cli/mcp_config.pycmd_mcp_add reads getattr(args, "mcp_command", None) instead of args.command. Comment cross-references the parser.
  • tests/hermes_cli/test_mcp_config.py_make_args helper now defaults mcp_command=None (was command=None); the four call-sites that asserted stdio-server behavior pass mcp_command="npx" / mcp_command="uvx" accordingly. Pure mechanical rename, no logic change.
  • tests/hermes_cli/test_mcp_add_command_dest.py (new) — three parser-level regression tests modelled on test_argparse_flag_propagation.py and test_subparser_routing_fallback.py. They build a minimal replica of the parent + mcp add subparser, parse representative argv vectors, and assert (a) args.command stays "mcp", (b) --command npx populates args.mcp_command (not args.command), (c) the bare-mcp add form also preserves the top-level dest.

How to Test

  1. Reproduce on main at cfd86dc:

    hermes mcp add example --url 'https://example.com/mcp'

    Observe an interactive chat prompt opens; hermes mcp list shows no example entry.

  2. Apply this PR. Repeat the command. The MCP server is now registered; hermes mcp list shows example.

  3. Run the new regression test:

    pytest tests/hermes_cli/test_mcp_add_command_dest.py -v

    All three tests should pass. Reverting the dest="mcp_command" change makes test_url_invocation_preserves_top_level_command and test_bare_mcp_add_does_not_clobber_command fail with args.command == None.

  4. The existing tests/hermes_cli/test_mcp_config.py suite (covering stdio-add, env vars, presets, etc.) continues to pass after the helper rename.

Notes on scope

  • No fallback read on the old args.command attribute. A naïve fallback (getattr(args, "mcp_command", None) or getattr(args, "command", None)) would silently return "mcp" whenever cmd_mcp_add is invoked through the real dispatcher (because argparse sets args.command = "mcp" from the top-level subparser), making cmd_mcp_add think the user passed --command mcp. The clean rename is safer.
  • The --command CLI flag name is preserved; this is purely an internal namespace change. No config-file format changes, no behaviour change for any path other than the previously broken one.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs — no duplicate
  • My PR contains only changes related to this fix
  • I've run pytest tests/ -q and all tests pass — I ran tests/hermes_cli/test_mcp_add_command_dest.py in isolation (3/3 pass) and verified Python syntax of the four touched files. I was not able to run the full pytest suite in my dev environment; happy to address any CI failures.
  • I've added tests for my changes
  • I've tested on my platform: Windows 11, Python 3.12

Documentation & Housekeeping

  • I've updated relevant documentation — N/A (no user-facing docs reference the internal dest name; the --command CLI flag is unchanged)
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact — argparse behaviour is platform-independent
  • I've updated tool descriptions/schemas if I changed tool behavior — N/A

Changed files

  • hermes_cli/main.py (modified, +9/-1)
  • hermes_cli/mcp_config.py (modified, +4/-1)
  • tests/hermes_cli/test_mcp_add_command_dest.py (added, +87/-0)
  • tests/hermes_cli/test_mcp_config.py (modified, +5/-5)

PR #19827: fix(cli): prevent argparse dest collision shadowing 'mcp add'

Description (problem / solution / changelog)

Summary

hermes mcp add NAME --url URL was silently launching interactive chat instead of registering an MCP server. Argparse derives dest from the flag name when no explicit dest= is given, so mcp_add_p.add_argument("--command", ...) at hermes_cli/main.py:9610 quietly targeted args.command — the same attribute the top-level subparser uses (hermes_cli/_parser.py:226, dest="command") to record which subcommand was selected.

When the user runs hermes mcp add foo --url ... (no --command), argparse writes --command's default of None into args.command after the top-level parser has already set it to "mcp". The dispatcher in main() then sees args.command is None and falls through to interactive chat — independent of URL, of MCP, and of any network state.

The fix gives the stdio --command flag an explicit dest="mcp_command" so it cannot shadow the top-level dest. The user-facing CLI surface is unchanged: --command npx ... still works exactly as documented. The only consumer that read the old attribute (cmd_mcp_add at hermes_cli/mcp_config.py:224) is updated to read args.mcp_command instead.

Fixes #19785.

Test plan

  • New regression test tests/hermes_cli/test_mcp_add_argparse_dest.py exercises a minimal parser replica (same convention as test_subparser_routing_fallback.py and test_argparse_flag_propagation.py) and asserts that both the URL form and the stdio form preserve args.command == "mcp" and populate args.mcp_command correctly.
  • Negative control: reverting the dest="mcp_command" change makes test_url_form_preserves_top_level_command fail with assert None == 'mcp' — confirming the test catches the original bug.
  • Existing tests/hermes_cli/test_mcp_config.py updated to use the new attribute name in its _make_args fixture and four call sites; full file passes (32/32).
  • Full pytest tests/hermes_cli/ → 3639 passed. The 13 failures are pre-existing on main (systemd / launchd / WSL environment-dependent) and unrelated to this change — confirmed by re-running them on a clean checkout of main.
  • Manual round-trip on macOS: hermes mcp add testbug --url 'https://example.invalid/mcp' now reaches cmd_mcp_add (prompts for auth, attempts connection, fails on DNS as expected). hermes mcp add teststdio --command npx --args @modelcontextprotocol/server-everything connects to the stdio server and presents the tool-selection flow.
  • Tested on macOS (Darwin 24.6.0), Python 3.11. The bug and fix are in argparse construction, which is platform-independent — the reporter confirmed reproduction on Linux, macOS, and Windows.

Notes

Why rename the internal dest rather than the user-facing flag? Renaming --command to e.g. --cmd would be a breaking change to the documented CLI (the help text and the existing examples in mcp_config.py all reference --command). The dest is a private implementation detail of the parser→consumer contract, so changing it is invisible outside the four files in this PR.

Why mcp_command rather than something like stdio_command? It mirrors the existing namespacing convention in this file (mcp_action, gateway_command, cron_command, auth_action, etc.). The dest is scoped to the mcp add Namespace, so the prefix unambiguously communicates ownership without inviting confusion with the top-level dest.

The four affected _make_args(command="npx", ...) callsites in test_mcp_config.py are internal test plumbing (they construct argparse.Namespace directly, bypassing the parser); updating them to mcp_command="npx" keeps the tests aligned with what the parser actually produces.

Changed files

  • hermes_cli/main.py (modified, +5/-1)
  • hermes_cli/mcp_config.py (modified, +1/-1)
  • tests/hermes_cli/test_mcp_add_argparse_dest.py (added, +66/-0)
  • tests/hermes_cli/test_mcp_config.py (modified, +5/-5)

Code Example

hermes mcp add example --url 'https://example.com/mcp'

---

import argparse
parser = argparse.ArgumentParser()
sub = parser.add_subparsers(dest='command')
mcp = sub.add_parser('mcp'); mcp_sub = mcp.add_subparsers(dest='mcp_action')
add = mcp_sub.add_parser('add'); add.add_argument('name'); add.add_argument('--url'); add.add_argument('--command')
args = parser.parse_args(['mcp', 'add', 'foo', '--url', 'https://example.com/mcp'])
print(args.command)   # → None  (should be 'mcp')

---

- mcp_add_p.add_argument("--command", help="Stdio command (e.g. npx)")
+ mcp_add_p.add_argument("--command", dest="mcp_command", help="Stdio command (e.g. npx)")

---

- command = getattr(args, "command", None)
+ command = getattr(args, "mcp_command", None)
RAW_BUFFERClick to expand / collapse

Bug Description

hermes mcp add <name> --url <url> does not register an MCP server. It silently falls through into an interactive Hermes chat session, with no error and no indication that the subcommand was not dispatched. The --help for the subcommand works correctly, which makes the bug look like a runtime/network problem rather than a dispatch problem.

Steps to Reproduce

hermes mcp add example --url 'https://example.com/mcp'

Expected Behavior

The MCP server example is added to the configuration; hermes mcp list shows it.

Actual Behavior

The command opens an interactive Hermes chat prompt. hermes mcp list shows no new entry. The bug is independent of the URL, of MCP, and of any network state — any invocation of hermes mcp add exhibits it (including the stdio form, since the trigger is the absence of --command, not the chosen transport).

Affected Component

  • CLI (interactive chat) / MCP subcommand dispatch

Operating System

Reproduces on Linux, macOS, and Windows. Does not depend on platform.

Python Version

3.11+ (the bug is in argparse construction; behavior is identical on every supported Python).

Hermes Version

Reproduces on current main at commit cfd86dc (also reported against v0.12.0).

Debug Report

Not applicable — this is a static argparse construction bug. It can be reproduced in three lines without invoking Hermes' runtime, network, config, or MCP layers; hermes debug share would not surface useful state. Minimal repro:

import argparse
parser = argparse.ArgumentParser()
sub = parser.add_subparsers(dest='command')
mcp = sub.add_parser('mcp'); mcp_sub = mcp.add_subparsers(dest='mcp_action')
add = mcp_sub.add_parser('add'); add.add_argument('name'); add.add_argument('--url'); add.add_argument('--command')
args = parser.parse_args(['mcp', 'add', 'foo', '--url', 'https://example.com/mcp'])
print(args.command)   # → None  (should be 'mcp')

Root Cause

There is an argparse dest collision between the top-level subparser and the mcp add subparser:

  1. hermes_cli/_parser.py registers the top-level subparsers with dest="command" so the dispatcher in hermes_cli/main.py can route on args.command.
  2. hermes_cli/main.py:9610 registers mcp_add_p.add_argument("--command", ...) for the stdio command. Without an explicit dest=, argparse derives dest="command" from the flag name.
  3. When the user runs hermes mcp add ... --url ... (no --command), argparse still writes args.command = None for that subparser flag, overwriting the top-level value of "mcp".
  4. The dispatcher at hermes_cli/main.py:10455 then sees args.command is None and falls through to cmd_chat. cmd_mcp_add is never reached.

Proposed Fix

Give the mcp add --command flag an explicit, non-colliding dest:

- mcp_add_p.add_argument("--command", help="Stdio command (e.g. npx)")
+ mcp_add_p.add_argument("--command", dest="mcp_command", help="Stdio command (e.g. npx)")

…and update cmd_mcp_add in hermes_cli/mcp_config.py to read from the new dest:

- command = getattr(args, "command", None)
+ command = getattr(args, "mcp_command", None)

The user-facing CLI flag --command is unchanged; only the in-memory namespace attribute moves.

PR with the fix and a parser regression test attached.

PR Ready

I'd like to fix this myself and submit a PR.

extent analysis

TL;DR

The issue can be fixed by giving the mcp add --command flag an explicit, non-colliding dest to avoid overwriting the top-level command value.

Guidance

  • Identify the argparse dest collision between the top-level subparser and the mcp add subparser.
  • Update the mcp add --command flag with an explicit dest to avoid the collision.
  • Verify the fix by running the hermes mcp add command with the --url option and checking if the MCP server is added to the configuration.
  • Test the fix with different scenarios, including the stdio form, to ensure the bug is fully resolved.

Example

mcp_add_p.add_argument("--command", dest="mcp_command", help="Stdio command (e.g. npx)")

Notes

The fix involves updating the hermes_cli/_parser.py and hermes_cli/mcp_config.py files to use the new dest attribute. The user-facing CLI flag --command remains unchanged.

Recommendation

Apply the proposed fix by updating the dest attribute of the mcp add --command flag to avoid the collision, as this directly addresses the root cause of the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix [Bug]: `hermes mcp add` silently launches chat instead of registering MCP server [2 pull requests, 2 comments, 2 participants]