hermes - ✅(Solved) Fix Bug: MCP tools not available in ACP sessions — enabled_toolsets hardcoded to ["hermes-acp"] [2 pull requests, 1 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#14986Fetched 2026-04-24 10:43:46
View on GitHub
Comments
0
Participants
1
Timeline
8
Reactions
0
Participants
Timeline (top)
labeled ×4cross-referenced ×2closed ×1referenced ×1

MCP server tools (registered via mcp_servers in config.yaml or provided by ACP clients) are correctly discovered and registered at startup, but are not available to the LLM in ACP sessions. The tools appear in agent logs as registered, but the LLM never receives them in its tool schema.

This is a regression — MCP tools worked correctly in earlier versions before the ACP session toolset was hardcoded.

Root Cause

There are 5 interconnected gaps that prevent MCP tools from reaching the LLM:

Fix Action

Fix / Workaround

"hermes-acp": {
    "description": "Editor integration (VS Code, Zed, JetBrains) — ...",
    "tools": [
        "web_search", "web_extract", "terminal", "process",
        "read_file", "write_file", "patch", "search_files",
        ...
        "execute_code", "delegate_task",
    ],
    "includes": []              # ← no reference to MCP toolsets
},

PR fix notes

PR #14709: fix(acp): include MCP toolsets in ACP sessions

Description (problem / solution / changelog)

Why

ACP sessions hardcoded enabled_toolsets=["hermes-acp"], so MCP toolsets never reached the model tool schema unless they were manually added. This broke both config-defined MCP servers and ACP-provided per-session MCP servers.

Fixes #14986.

Files changed

  • acp_adapter/session.py — expand ACP session toolsets with configured MCP servers at agent creation
  • acp_adapter/server.py — preserve per-session MCP toolsets after ACP MCP registration and in /tools
  • tests/acp/test_session.py — cover config-defined MCP server inclusion
  • tests/acp/test_server.py — cover tool surface refresh with session MCP toolsets

Verification run

  • pytest tests/acp/test_session.py -q
  • pytest tests/acp/test_server.py -q
  • pytest tests/acp/test_mcp_e2e.py -q
  • independent diff review: passed

Risk level

Low. ACP-only scope. Change only expands explicit MCP toolsets already configured for that ACP session and adds regression tests.

Changed files

  • acp_adapter/server.py (modified, +9/-3)
  • acp_adapter/session.py (modified, +28/-1)
  • tests/acp/test_server.py (modified, +7/-1)
  • tests/acp/test_session.py (modified, +37/-0)

PR #15052: fix: P1 batch — Discord wildcard, ACP+MCP, NO_PROXY bypass, resume-after-compression

Description (problem / solution / changelog)

Four high-impact P1 fixes, salvaged with contributor attribution preserved via rebase-merge.

Fixes

  • #14920 — Discord wildcard "*" silently drops all messages Salvaged from @mrunmayee17's PR #14930 (allowed_channels). Extended to cover free_response_channels and ignored_channels which had the same "*" literal-in-set bug, and added regression tests for all three.
  • #14986 — MCP tools invisible in ACP sessions (hardcoded toolsets) Salvaged from @camaragon's PR #14709 unchanged. ACP sessions now expand enabled_toolsets with configured MCP servers at agent creation, and the tool-surface refresh after dynamic MCP server registration preserves the additions.
  • #14966 — _build_keepalive_http_client ignores NO_PROXY → 502 on local endpoints Salvaged from @shamork's PR #14546 unchanged. Added regression tests for _get_proxy_for_base_url() and the full keepalive-client path.
  • #15000 — --resume <id> loads empty chat after context compression New fix (no contributor PR existed). SessionDB.resolve_resume_session_id() walks the parent_session_id chain forward and redirects resume targets to the first descendant with messages. Wired into all three CLI resume entry points (_preload_resumed_session, _init_agent, /resume).

Also closed

  • #14933, #14938 — DeepSeek V4 reasoning_content issues closed as unactionable (both submitted with empty issue bodies; asked reporters to re-file with a reproducer).
  • PRs #14930, #14709, #14546 will be closed with credit referencing this PR after merge.
  • PR #14885 (duplicate NO_PROXY fix by @fqx) will be closed with credit — @shamork's implementation was preferred because it reuses stdlib urllib.request.proxy_bypass_environment() which correctly handles wildcards, leading dots, and CIDR-like patterns that #14885's custom matcher missed.

Attribution

FixContributorEmail → GH
#14920@mrunmayee17[email protected]
#14986@camaragonnoreply form
#14966@shamork[email protected]

All three added to scripts/release.py AUTHOR_MAP in the tail commit.

Validation

Test fileResult
tests/gateway/test_discord_allowed_channels.py15/15 ✓
tests/acp/test_session.py + tests/acp/test_server.py83/83 ✓
tests/run_agent/test_create_openai_client_proxy_env.py9/9 ✓ (4 new NO_PROXY tests)
tests/hermes_state/test_resolve_resume_session_id.py8/8 ✓ (new file)

E2E for #15000: reproduced the exact 6-session compression chain from the issue body (5 empty + 1 with 119 messages); resolve_resume_session_id correctly redirects to the 5th session from any of the 5 empty ones and returns the msg-bearing session unchanged.

Merge method

Rebase merge — each contributor's cherry-picked commit preserves their authorship. Squash would flatten all seven commits into one authored by us, losing credit.

Closes #14920, #14966, #14986, #15000 Supersedes #14546, #14709, #14885, #14930

Changed files

  • acp_adapter/server.py (modified, +9/-3)
  • acp_adapter/session.py (modified, +28/-1)
  • cli.py (modified, +49/-0)
  • gateway/platforms/discord.py (modified, +13/-4)
  • hermes_state.py (modified, +65/-0)
  • run_agent.py (modified, +26/-5)
  • scripts/release.py (modified, +4/-0)
  • tests/acp/test_server.py (modified, +7/-1)
  • tests/acp/test_session.py (modified, +37/-0)
  • tests/gateway/test_discord_allowed_channels.py (added, +104/-0)
  • tests/hermes_state/test_resolve_resume_session_id.py (added, +96/-0)
  • tests/run_agent/test_create_openai_client_proxy_env.py (modified, +76/-1)

Code Example

mcp_servers:
     olympus:
       command: /path/to/venv/bin/python
       args: [-m, olympus.server]
       env:
         AETHER_HOME: /path/to/Aether-Agents/home
         PYTHONPATH: /path/to/Aether-Agents/src
       enabled: true

---

MCP server 'olympus' (stdio): registered 6 tool(s): mcp_olympus_talk_to, mcp_olympus_discover, ...

---

kwargs = {
    "platform": "acp",
    "enabled_toolsets": ["hermes-acp"],          # ← hardcoded, no MCP toolsets
    "quiet_mode": True,
    "session_id": session_id,
    "model": model or default_model,
}

---

"hermes-acp": {
    "description": "Editor integration (VS Code, Zed, JetBrains) — ...",
    "tools": [
        "web_search", "web_extract", "terminal", "process",
        "read_file", "write_file", "patch", "search_files",
        ...
        "execute_code", "delegate_task",
    ],
    "includes": []              # ← no reference to MCP toolsets
},

---

enabled_toolsets = getattr(state.agent, "enabled_toolsets", None) or ["hermes-acp"]  # ← still no MCP
disabled_toolsets = getattr(state.agent, "disabled_toolsets", None)
state.agent.tools = get_tool_definitions(
    enabled_toolsets=enabled_toolsets,   # ← only "hermes-acp"
    disabled_toolsets=disabled_toolsets,
    quiet_mode=True,
)

---

if enabled_toolsets is not None:
    for toolset_name in enabled_toolsets:
        if validate_toolset(toolset_name):
            resolved = resolve_toolset(toolset_name)
            tools_to_include.update(resolved)

---

_SUBAGENT_TOOLSETS = sorted(
    name
    for name, defn in TOOLSETS.items()       # ← only static TOOLSETS, no dynamic MCP
    if name not in _EXCLUDED_TOOLSET_NAMES
    and not name.startswith("hermes-")
    and not all(t in DELEGATE_BLOCKED_TOOLS for t in defn.get("tools", []))
)

---

child_toolsets = _strip_blocked_tools(parent_enabled)  # ← inherits ["hermes-acp"]

---

from tools.registry import registry

mcp_toolsets = [ts for ts in registry.get_registered_toolset_names() if ts.startswith("mcp-")]
kwargs["enabled_toolsets"] = ["hermes-acp"] + mcp_toolsets

---

# In config.yaml or toolsets.py
hermes-acp:
  includes: ["mcp-*"]    # auto-include all MCP toolsets
RAW_BUFFERClick to expand / collapse

Bug: MCP tools not available in ACP sessions — enabled_toolsets hardcoded to ["hermes-acp"]

Summary

MCP server tools (registered via mcp_servers in config.yaml or provided by ACP clients) are correctly discovered and registered at startup, but are not available to the LLM in ACP sessions. The tools appear in agent logs as registered, but the LLM never receives them in its tool schema.

This is a regression — MCP tools worked correctly in earlier versions before the ACP session toolset was hardcoded.

Environment

  • hermes-agent version: 0.11.0 (v2026.4.23-26-g6fdbf2f2)
  • Platform: WSL2 (Ubuntu) on Windows
  • ACP client: VS Code (Continue) + custom ACP orchestrator (Aether Agents Olympus MCP)

Reproduction

  1. Configure an MCP server in ~/.hermes/config.yaml:

    mcp_servers:
      olympus:
        command: /path/to/venv/bin/python
        args: [-m, olympus.server]
        env:
          AETHER_HOME: /path/to/Aether-Agents/home
          PYTHONPATH: /path/to/Aether-Agents/src
        enabled: true
  2. Start Hermes with hermes acp or connect via an ACP client.

  3. Observe agent log — MCP tools are registered:

    MCP server 'olympus' (stdio): registered 6 tool(s): mcp_olympus_talk_to, mcp_olympus_discover, ...
  4. Send a prompt asking to use an MCP tool (e.g., mcp_olympus_talk_to).

  5. Result: The LLM does not recognize mcp_olympus_talk_to as a callable tool. The tool schema sent to the model does not include any mcp_* entries.

Root Cause

There are 5 interconnected gaps that prevent MCP tools from reaching the LLM:

Gap 1: ACP session hardcodes enabled_toolsets

File: acp_adapter/session.py — line 542

kwargs = {
    "platform": "acp",
    "enabled_toolsets": ["hermes-acp"],          # ← hardcoded, no MCP toolsets
    "quiet_mode": True,
    "session_id": session_id,
    "model": model or default_model,
}

The ACP agent is created with only the "hermes-acp" toolset. This static toolset definition does not include dynamically-registered MCP tools.

Gap 2: hermes-acp toolset is static, no MCP references

File: toolsets.py — lines 241-258

"hermes-acp": {
    "description": "Editor integration (VS Code, Zed, JetBrains) — ...",
    "tools": [
        "web_search", "web_extract", "terminal", "process",
        "read_file", "write_file", "patch", "search_files",
        ...
        "execute_code", "delegate_task",
    ],
    "includes": []              # ← no reference to MCP toolsets
},

MCP tools are registered dynamically into mcp-{server_name} toolsets at runtime, but hermes-acp has no mechanism to pull them in.

Gap 3: Tool surface refresh after MCP registration discards MCP tools

File: acp_adapter/server.py — lines 284-296

enabled_toolsets = getattr(state.agent, "enabled_toolsets", None) or ["hermes-acp"]  # ← still no MCP
disabled_toolsets = getattr(state.agent, "disabled_toolsets", None)
state.agent.tools = get_tool_definitions(
    enabled_toolsets=enabled_toolsets,   # ← only "hermes-acp"
    disabled_toolsets=disabled_toolsets,
    quiet_mode=True,
)

After registering MCP servers (line 270-287), the code refreshes the tool surface. But it rebuilds the tool list from enabled_toolsets=["hermes-acp"], which doesn't include MCP toolsets. The newly-registered MCP tools are discarded.

Gap 4: get_tool_definitions only resolves static toolsets

File: model_tools.py — lines 222-234

if enabled_toolsets is not None:
    for toolset_name in enabled_toolsets:
        if validate_toolset(toolset_name):
            resolved = resolve_toolset(toolset_name)
            tools_to_include.update(resolved)

When enabled_toolsets=["hermes-acp"], this only resolves the static tools in that toolset. Dynamically-registered MCP toolsets (mcp-olympus, etc.) are never iterated because they're not in the enabled_toolsets list.

Gap 5: Delegation inherits the gap

File: tools/delegate_tool.py

Lines 60-67_SUBAGENT_TOOLSETS is computed at module import time:

_SUBAGENT_TOOLSETS = sorted(
    name
    for name, defn in TOOLSETS.items()       # ← only static TOOLSETS, no dynamic MCP
    if name not in _EXCLUDED_TOOLSET_NAMES
    and not name.startswith("hermes-")
    and not all(t in DELEGATE_BLOCKED_TOOLS for t in defn.get("tools", []))
)

Lines 847 — children inherit parent's toolsets:

child_toolsets = _strip_blocked_tools(parent_enabled)  # ← inherits ["hermes-acp"]

The _preserve_parent_mcp_toolsets mechanism (from recent fix 7d8b2eee) only works if the parent has MCP toolsets in its enabled_toolsets. Since ACP sessions don't, children never get MCP tools either.

Impact

  • MCP tools are invisible to ACP sessions — this affects all ACP clients (VS Code, Zed, JetBrains, and custom orchestrators like Aether Agents' Olympus).
  • Delegation from ACP context also loses MCP tools — sub-agents spawned via delegate_task from an ACP session don't inherit MCP tools.
  • Existing MCP inheritance fix (7d8b2eee) doesn't cover this case — it only works when the parent already has MCP toolsets in enabled_toolsets.

Suggested Fix

The core issue is that MCP toolsets (mcp-{server}) are dynamic and need to be added to enabled_toolsets at runtime. Three possible approaches:

Option A: Dynamic toolset expansion in ACP session creation

In acp_adapter/session.py, after creating the agent, discover registered MCP toolsets and add them:

from tools.registry import registry

mcp_toolsets = [ts for ts in registry.get_registered_toolset_names() if ts.startswith("mcp-")]
kwargs["enabled_toolsets"] = ["hermes-acp"] + mcp_toolsets

Also apply the same expansion in acp_adapter/server.py:290 after MCP server registration.

Option B: Config-based toolset includes

Add an includes mechanism to toolset definitions that references dynamic toolset patterns:

# In config.yaml or toolsets.py
hermes-acp:
  includes: ["mcp-*"]    # auto-include all MCP toolsets

Then resolve_toolset("hermes-acp") would expand mcp-* against the registry at runtime.

Option C: Auto-include MCP toolsets in all toolset resolution

In model_tools.py:get_tool_definitions, after resolving explicit toolsets, always append MCP toolset tools when no explicit toolset filter excludes them, or when the calling context supports MCP (e.g., main agent loop, ACP sessions).


Option A is the smallest change and can be a targeted fix. Option B is a more general mechanism that benefits all toolsets. Option C is the most robust but requires careful design to avoid leaking MCP tools to contexts that shouldn't have them.

Happy to submit a PR for Option A as a quick fix, with Option B as a follow-up.

extent analysis

TL;DR

To fix the issue of MCP tools not being available in ACP sessions, modify the acp_adapter/session.py file to dynamically add registered MCP toolsets to the enabled_toolsets list.

Guidance

  1. Identify the hardcoded enabled_toolsets: In acp_adapter/session.py, find the line where enabled_toolsets is hardcoded to ["hermes-acp"].
  2. Discover registered MCP toolsets: Use the tools.registry module to get a list of registered MCP toolsets, filtering by names that start with "mcp-".
  3. Add MCP toolsets to enabled_toolsets: Modify the kwargs dictionary to include the discovered MCP toolsets in the enabled_toolsets list.
  4. Apply the same expansion in acp_adapter/server.py: After registering MCP servers, refresh the tool surface with the updated enabled_toolsets list.

Example

from tools.registry import registry

mcp_toolsets = [ts for ts in registry.get_registered_toolset_names() if ts.startswith("mcp-")]
kwargs["enabled_toolsets"] = ["hermes-acp"] + mcp_toolsets

Notes

This fix assumes that the tools.registry module is correctly populated with registered MCP toolsets. If this is not the case, additional debugging may be required.

Recommendation

Apply the suggested fix (Option A) to dynamically add registered MCP toolsets to the enabled_toolsets list, as it is the smallest change and can be a targeted fix.

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: MCP tools not available in ACP sessions — enabled_toolsets hardcoded to ["hermes-acp"] [2 pull requests, 1 participants]