hermes - ✅(Solved) Fix Anthropic MCP PascalCase tool responses do not normalize to registered tool names [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#16817Fetched 2026-04-29 06:38:54
View on GitHub
Comments
0
Participants
1
Timeline
8
Reactions
0
Participants
Timeline (top)
labeled ×6cross-referenced ×2

Fix Action

Fix / Workaround

Anthropic tool responses that use Claude Code-shaped MCP tool names can fail Hermes tool dispatch when strip_tool_prefix=True.

That misses the registered terminal tool and can break dispatch.

Local patch/regression was verified in Hermes:

PR fix notes

PR #16820: fix: normalize Anthropic PascalCase MCP tool names

Description (problem / solution / changelog)

Summary

Fixes Anthropic transport tool-name normalization for Claude Code-shaped MCP response names.

When an Anthropic/OAuth route returns a tool-use name like mcp_Terminal, Hermes strips the mcp_ prefix but currently leaves Terminal, which does not match the registered Hermes tool name terminal.

This PR lowercases the first character after stripping mcp_ when needed, preserving existing lowercase behavior while supporting Claude Code-shaped PascalCase tool names.

Fixes #16817

Test Plan

  • python -m py_compile agent/transports/anthropic.py tests/agent/test_anthropic_adapter.py
  • python -m pytest -q tests/agent/test_anthropic_adapter.py::TestAnthropicTransportToolNormalization tests/agent/test_anthropic_adapter.py::TestToolChoice
  • Ran the full tests/agent/test_anthropic_adapter.py locally; it exposed pre-existing environment/config failures unrelated to this change (local macOS Claude keychain credentials leaking into tests and an existing beta-header expectation mismatch), while the new focused regression passed.

Notes

This PR does not promote or install any admin/OAuth route. It only hardens response normalization for Claude Code-shaped mcp_ tool names.

Changed files

  • agent/transports/anthropic.py (modified, +2/-0)
  • tests/agent/test_anthropic_adapter.py (modified, +17/-0)

PR #16967: fix(agent): normalize Anthropic MCP tool names

Description (problem / solution / changelog)

Summary

  • normalize Anthropic mcp_ tool-use names back to registered Hermes tool names during response normalization
  • handle Claude Code-shaped PascalCase names like mcp_Terminal
  • add focused regression coverage for both lowercase and PascalCase MCP tool names

Root cause

AnthropicTransport.normalize_response() was passing tool_use.name through unchanged. When Anthropic returned Claude Code-shaped names such as mcp_Terminal, Hermes kept the mcp_ prefix and PascalCase spelling, so dispatch missed the registered terminal tool.

Fix

  • add a small _normalize_tool_name() helper in agent/transports/anthropic.py
  • when strip_tool_prefix=True, strip the mcp_ prefix and downcase the first remaining character if needed
  • preserve existing non-MCP names unchanged

Regression coverage

  • new test file tests/agent/test_anthropic_transport_mcp_tool_names.py
  • covers both mcp_terminal -> terminal and mcp_Terminal -> terminal
  • re-ran nearby Anthropic message conversion tests to confirm no tool-call regression

Testing

  • scripts/run_tests.sh tests/agent/test_anthropic_transport_mcp_tool_names.py
  • scripts/run_tests.sh tests/agent/test_anthropic_transport_mcp_tool_names.py tests/agent/test_anthropic_adapter.py::TestConvertMessages::test_converts_tool_calls tests/agent/test_anthropic_adapter.py::TestConvertMessages::test_converts_tool_results tests/agent/test_anthropic_adapter.py::TestConvertMessages::test_merges_consecutive_tool_results tests/agent/test_anthropic_adapter.py::TestConvertMessages::test_strips_orphaned_tool_use tests/agent/test_anthropic_adapter.py::TestConvertMessages::test_strips_orphaned_tool_result

Closes #16817

Changed files

  • agent/transports/anthropic.py (modified, +15/-1)
  • tests/agent/test_anthropic_transport_mcp_tool_names.py (added, +33/-0)

Code Example

from types import SimpleNamespace
from agent.transports.anthropic import AnthropicTransport

block = SimpleNamespace(
    type="tool_use",
    id="toolu_terminal",
    name="mcp_Terminal",
    input={"command": "printf OPUS_TOOL_OK"},
)
response = SimpleNamespace(content=[block], stop_reason="tool_use")

normalized = AnthropicTransport().normalize_response(response, strip_tool_prefix=True)
assert normalized.tool_calls[0].name == "terminal"

---

terminal

---

Terminal

---

mcp_Terminal -> Terminal -> terminal
RAW_BUFFERClick to expand / collapse

Bug Description

Anthropic tool responses that use Claude Code-shaped MCP tool names can fail Hermes tool dispatch when strip_tool_prefix=True.

A Claude Code / OAuth-shaped Anthropic route may return tool names like mcp_Terminal after outgoing Hermes tools are rewritten from lowercase MCP names such as mcp_terminal to PascalCase names such as mcp_Terminal.

Hermes currently strips the mcp_ prefix, but leaves the rest unchanged. That turns mcp_Terminal into Terminal, which does not match the registered Hermes tool name terminal.

Steps to Reproduce

Using AnthropicTransport.normalize_response(..., strip_tool_prefix=True) with a tool-use block named mcp_Terminal:

from types import SimpleNamespace
from agent.transports.anthropic import AnthropicTransport

block = SimpleNamespace(
    type="tool_use",
    id="toolu_terminal",
    name="mcp_Terminal",
    input={"command": "printf OPUS_TOOL_OK"},
)
response = SimpleNamespace(content=[block], stop_reason="tool_use")

normalized = AnthropicTransport().normalize_response(response, strip_tool_prefix=True)
assert normalized.tool_calls[0].name == "terminal"

Expected Behavior

mcp_Terminal should normalize to the registered Hermes tool name:

terminal

Likewise, PascalCase Claude Code-shaped MCP names should map back to the corresponding Hermes registered tool names after stripping the mcp_ prefix.

Actual Behavior

Before the local fix, mcp_Terminal normalized to:

Terminal

That misses the registered terminal tool and can break dispatch.

Proposed Fix

After stripping the mcp_ prefix in AnthropicTransport.normalize_response(..., strip_tool_prefix=True), lowercase the first remaining character if it is uppercase.

Example:

mcp_Terminal -> Terminal -> terminal

This preserves existing lowercase behavior while supporting Claude Code-shaped PascalCase tool names.

Local Verification

Local patch/regression was verified in Hermes:

  • Regression: mcp_Terminal normalizes to terminal.
  • Tool-shape matrix: 11/11 PascalCase mcp_ response names normalized back to registered Hermes tools.
  • Focused tests: 22 passed locally.

Related Note

This does not promote or install any admin/OAuth route. It only hardens Anthropic response normalization for Claude Code-shaped mcp_ tool names.

extent analysis

TL;DR

The issue can be fixed by modifying the AnthropicTransport.normalize_response method to lowercase the first character of the tool name after stripping the mcp_ prefix when strip_tool_prefix=True.

Guidance

  • Verify that the AnthropicTransport.normalize_response method is correctly stripping the mcp_ prefix from tool names.
  • Check that the method is not already lowercasing the entire tool name, which could interfere with the proposed fix.
  • Modify the AnthropicTransport.normalize_response method to lowercase the first character of the tool name after stripping the mcp_ prefix, as described in the proposed fix.
  • Test the modified method with a variety of tool names, including PascalCase and lowercase names, to ensure it produces the expected results.

Example

def normalize_response(self, response, strip_tool_prefix=True):
    # ...
    if strip_tool_prefix and block.name.startswith('mcp_'):
        name = block.name[4:]  # strip 'mcp_'
        name = name[0].lower() + name[1:]  # lowercase first character
        # ...

Notes

This fix assumes that the AnthropicTransport.normalize_response method is the only place where tool name normalization is performed. If there are other places where normalization occurs, they may also need to be modified.

Recommendation

Apply the proposed workaround by modifying the AnthropicTransport.normalize_response method to correctly handle PascalCase tool names. This will ensure that tool names are normalized correctly and can be dispatched by Hermes.

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