Skip to content

fix(anthropic-oauth): registry-aware mcp_ prefix on both ends of round-trip#31700

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-6b5d491e
May 24, 2026
Merged

fix(anthropic-oauth): registry-aware mcp_ prefix on both ends of round-trip#31700
teknium1 merged 2 commits into
mainfrom
hermes/hermes-6b5d491e

Conversation

@teknium1

@teknium1 teknium1 commented May 24, 2026

Copy link
Copy Markdown
Contributor

Salvages #25270 (cherry-picked from @hayka-pacha with authorship preserved) and adds the companion outgoing-side fix so the round-trip is symmetric.

Summary

Fixes #25255 — Anthropic OAuth + native MCP server tools (from mcp_servers: in ~/.hermes/config.yaml) were getting their mcp_ prefix mangled on the round trip, causing Tool '<name>' does not exist errors on follow-up calls.

Root cause

Two asymmetric prefix-handling paths in the OAuth code:

  1. Incoming (agent/transports/anthropic.py): unconditionally stripped mcp_ from every tool_use block when strip_tool_prefix=True.
  2. Outgoing schema (agent/anthropic_adapter.py block Architecture planning #3): unconditionally added mcp_ to every tool name in the tools array.
  3. Outgoing history (agent/anthropic_adapter.py block Fix terminal interactivity #4): already had a startswith guard — only added mcp_ if not already prefixed.

The schema/history mismatch let the bug hide on the first call (model copies schema name) and surface on the second (model copies history name, strip turns it into something the registry doesn't have).

Fix

Changes

Before After
Outgoing mcp_composio_X mcp_mcp_composio_X on wire mcp_composio_X (left alone)
Outgoing read_file (OAuth) mcp_read_file mcp_read_file (unchanged)
Incoming mcp_composio_X (in registry) composio_X (registry miss) mcp_composio_X (registry hit)
Incoming mcp_read_file (only read_file in registry) read_file read_file (unchanged)

Test plan

tests/agent/test_anthropic_mcp_prefix_strip.py — 11 tests total (7 incoming from #25270 + 4 outgoing added here).

Full suite for adapter + transports: 453/453 pass. tests/tools/test_mcp_tool.py: 196/196 pass.

E2E round-trip verified with isolated HERMES_HOME and live build_anthropic_kwargs / normalize_response calls — native MCP names preserve round-trip, OAuth bare names strip correctly.

Closes #25255.

Infographic

oauth-mcp-prefix-roundtrip

hayka-pacha and others added 2 commits May 24, 2026 15:15
When strip_tool_prefix=True (Anthropic OAuth path), normalize_response
unconditionally stripped the mcp_ prefix from ALL tool names starting
with mcp_. This broke Hermes-native MCP server tools (registered under
their full mcp_<server>_<tool> name in the registry) because the stripped
name doesn't match any registry entry.

Fix: check the tool registry before stripping. Only strip when:
- The stripped name EXISTS in the registry (OAuth-injected tool)
- The full name does NOT exist in the registry

This preserves backward compatibility for OAuth-injected tools while
protecting native MCP server tools from incorrect prefix removal.

7 new tests covering: OAuth strip, native preserve, no-flag, non-mcp,
unknown tools, mixed responses, and dual-registration edge case.

Signed-off-by: HKPA <hayka-pacha@users.noreply.github.com>
…y prefixed

Companion to the GH-25255 incoming-strip fix from @hayka-pacha. Without
this, build_anthropic_kwargs unconditionally added 'mcp_' to every tool
name in step 3, so a native MCP server tool registered as
'mcp_composio_X' was sent as 'mcp_mcp_composio_X' on the wire. The
incoming strip only removes ONE prefix, which still worked on first
call, but on subsequent calls the model pattern-matched the
single-prefixed form from message history and produced names that
stripped to 'composio_X' — registry miss, dispatch fail.

The history-rewrite block (#4) already has this guard. Apply the same
guard to the schema-rewrite block (#3) so round-trip is symmetric.

Added 4 outgoing-side tests. Existing 7 incoming-side tests still pass.

Author map: hayka-pacha added for PR #25270 salvage attribution.

Refs GH-25255.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-6b5d491e vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9094 on HEAD, 9093 on base (🆕 +1)

🆕 New issues (1):

Rule Count
unresolved-import 1
First entries
tests/agent/test_anthropic_mcp_prefix_strip.py:15: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`

✅ Fixed issues: none

Unchanged: 4843 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@teknium1 teknium1 merged commit eea9553 into main May 24, 2026
26 checks passed
@teknium1 teknium1 deleted the hermes/hermes-6b5d491e branch May 24, 2026 22:27
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/agent Core agent loop, run_agent.py, prompt builder provider/anthropic Anthropic native Messages API tool/mcp MCP client and OAuth labels May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder P1 High — major feature broken, no workaround provider/anthropic Anthropic native Messages API 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]: Anthropic OAuth strips mcp_ prefix from Hermes-native MCP tool names, breaking registry lookup

3 participants