fix(agent): repair mcp_ prefix drop in _repair_tool_call - #21696
Closed
real-wimpSquad wants to merge 1 commit into
Closed
fix(agent): repair mcp_ prefix drop in _repair_tool_call#21696real-wimpSquad wants to merge 1 commit into
real-wimpSquad wants to merge 1 commit into
Conversation
Models sometimes call MCP server tools without the mcp_ prefix that Hermes adds at registration time — e.g. APE_search instead of mcp_APE_search, or discord_send instead of mcp_discord_send. difflib scores ~0.60 for this pattern (just below the 0.7 cutoff), so the existing fuzzy-match path misses it and the call fails with "Tool does not exist". Fix: add an explicit mcp_ prepend check before the candidate-set / fuzzy-match path. Three candidates are tried in order: mcp_<original> mcp_<lowered> mcp_<normalized> then a case-folded linear scan for mixed-case server names (e.g. APE_SEARCH -> mcp_APE_search). The check is unambiguous — only resolves on an exact match in valid_tool_names, so it cannot mis-route non-MCP tools. Adds TestMcpPrefixDrop (10 tests) to the existing repair test file, covering: all APE tool variants, other MCP servers, already-correct names, uppercase input, non-MCP tools unaffected, and the no-invent guard. All 28 tests pass; pre-existing test_concurrent_interrupt failures are unrelated and present on main before this change. Discovered while working with an APE MCP server where the model consistently emitted APE_meta / APE_search instead of the prefixed names.
yingliang-zhang
added a commit
to yingliang-zhang/hermes-agent
that referenced
this pull request
Jul 9, 2026
Models (notably GLM) sometimes emit MCP tool names without the full mcp__<server>__ prefix — e.g. list_memory_projects instead of mcp__basic_memory__list_memory_projects. The existing repair pipeline (case-folding, CamelCase, suffix stripping, fuzzy match at 0.7 cutoff) cannot map the bare suffix back to the full prefixed name, so the call fails with "Unknown tool" and the agent burns 3 retries before aborting with "Model generated invalid tool call". Add two repair paths in repair_tool_call() before the fuzzy-match fallback: 1. Bare suffix (no __ in emitted name): scan registered mcp__ tools and match on the tool-suffix portion. Skip when ambiguous (multiple servers expose the same suffix) to avoid non-deterministic routing. 2. Partial prefix (__ in emitted name but missing mcp__): prepend mcp__ and check for an exact match. The mcp__server__tool naming convention was introduced in NousResearch#52750 (merged 2026-07-05). Prior PRs NousResearch#21696, NousResearch#37100, NousResearch#33359 target the old single-underscore mcp_ format and do not apply to the new convention. 9 new tests in TestMcpPrefixRepair covering both cases, ambiguity guard, and non-MCP tool passthrough. All 38 tests in test_repair_tool_call_name.py pass.
yingliang-zhang
added a commit
to yingliang-zhang/hermes-agent
that referenced
this pull request
Jul 11, 2026
Models (notably GLM) sometimes emit MCP tool names without the full mcp__<server>__ prefix — e.g. list_memory_projects instead of mcp__basic_memory__list_memory_projects. The existing repair pipeline (case-folding, CamelCase, suffix stripping, fuzzy match at 0.7 cutoff) cannot map the bare suffix back to the full prefixed name, so the call fails with "Unknown tool" and the agent burns 3 retries before aborting with "Model generated invalid tool call". Add two repair paths in repair_tool_call() before the fuzzy-match fallback: 1. Bare suffix (no __ in emitted name): scan registered mcp__ tools and match on the tool-suffix portion. Skip when ambiguous (multiple servers expose the same suffix) to avoid non-deterministic routing. 2. Partial prefix (__ in emitted name but missing mcp__): prepend mcp__ and check for an exact match. The mcp__server__tool naming convention was introduced in NousResearch#52750 (merged 2026-07-05). Prior PRs NousResearch#21696, NousResearch#37100, NousResearch#33359 target the old single-underscore mcp_ format and do not apply to the new convention. 9 new tests in TestMcpPrefixRepair covering both cases, ambiguity guard, and non-MCP tool passthrough. All 38 tests in test_repair_tool_call_name.py pass.
6 tasks
swissly
added a commit
to swissly/hermes-agent
that referenced
this pull request
Jul 11, 2026
Structured stats collection for the existing tool-call repair pipeline. Records RepairEvent (pattern, tool, model, timestamp) at each repair pass in message_sanitization.py and model_tools.py coerce_tool_args. New module: agent/tool_repair_stats.py - RepairPattern enum (20 known failure patterns) - ToolRepairStats singleton: thread-safe, ring-buffer (10k events) - record_repair() convenience function - summary() for CLI display Hooks added (1-2 lines each, zero-overhead when unused): - message_sanitization.py: 6 hooks in _repair_tool_call_arguments (empty_args, none_literal, control_char_escape, trailing_comma, unrepairable) - model_tools.py: 2 hooks in coerce_tool_args (bare_string_wrap, bare_object_wrap) Design constraints: - No new model tools (zero API cost impact) - No prompt caching impact - No new config keys - Import failure → no-op (never breaks repair pipeline) - Thread-safe with threading.Lock - Bounded memory (ring buffer caps at 10k events) Tests: 19 new tests (stats, thread-safety, ring-buffer, resilience) Regression: 82 existing repair/coercion tests still pass Complementary to existing repair PRs (NousResearch#62578, NousResearch#56399, NousResearch#61550, NousResearch#59267, NousResearch#52747, NousResearch#55620, NousResearch#56557, NousResearch#21696) — adds observability, not repairs.
Contributor
|
Thanks for the focused reproduction and regression coverage. This automated hermes-sweeper review is closing this under the standing
Please re-scope any follow-up to the model/provider or MCP schema/prompt source that emits the incorrect tool name, rather than adding another core output-repair branch. Closed as not-planned per standing maintainer policy ( |
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.
What
Models sometimes call MCP server tools without the
mcp_prefix that Hermes adds at registration time — e.g.APE_searchinstead ofmcp_APE_search, ordiscord_sendinstead ofmcp_discord_send. The call fails withTool does not existand the agent burns retries before giving up.Why it happens
Hermes registers MCP tools as
mcp_{server}_{tool}(line ~995 inmcp_tool.py). The existing_repair_tool_callfuzzy matcher uses difflib with cutoff=0.7, butAPE_searchscores ~0.60 againstmcp_APE_search— just below the threshold. The fuzzy path misses it every time.Fix
Add an explicit
mcp_prepend check before the candidate-set / fuzzy-match path. Four candidates tried in order:mcp_<original>mcp_<lowered>mcp_<normalized>APE_SEARCH→mcp_APE_searchwhere server name has mixed case)The check is unambiguous — only resolves on an exact match in
valid_tool_names, cannot mis-route non-MCP tools.How to reproduce
APE)mcp_prefix (observed with several models including Gemma and Claude variants)APE_search→Tool 'APE_search' does not existTests
Added
TestMcpPrefixDrop(10 tests) to the existingtest_repair_tool_call_name.py:All 28 tests pass. The 2 pre-existing failures in
test_concurrent_interrupt.pyare present onmainbefore this change and unrelated.Platform
Tested on macOS 14 / Python 3.11.