fix(anthropic-oauth): registry-aware mcp_ prefix on both ends of round-trip#31700
Merged
Conversation
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.
Contributor
🔎 Lint report:
|
| 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.
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 theirmcp_prefix mangled on the round trip, causingTool '<name>' does not existerrors on follow-up calls.Root cause
Two asymmetric prefix-handling paths in the OAuth code:
agent/transports/anthropic.py): unconditionally strippedmcp_from every tool_use block whenstrip_tool_prefix=True.agent/anthropic_adapter.pyblock Architecture planning #3): unconditionally addedmcp_to every tool name in the tools array.agent/anthropic_adapter.pyblock Fix terminal interactivity #4): already had astartswithguard — only addedmcp_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
startswithguard from block Fix terminal interactivity #4 so already-prefixed native MCP names are not double-prefixed.Changes
mcp_composio_Xmcp_mcp_composio_Xon wiremcp_composio_X(left alone)read_file(OAuth)mcp_read_filemcp_read_file(unchanged)mcp_composio_X(in registry)composio_X(registry miss)mcp_composio_X(registry hit)mcp_read_file(onlyread_filein registry)read_fileread_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_HOMEand livebuild_anthropic_kwargs/normalize_responsecalls — native MCP names preserve round-trip, OAuth bare names strip correctly.Closes #25255.
Infographic