fix(mcp_client): sanitize tool names + preserve original (#153) #8

Closed
mik-tf wants to merge 1 commit from development_mik_mcp_sanitizer into development
Owner

Summary

Sanitize MCP tool names so they pass the LLM tool-name regex ^[a-zA-Z0-9_-]+$. Per-domain OSIS tools (e.g. hero_osis_business.contact.list) contain dots which OpenAI/Anthropic/Gemini all reject. Without this, the entire MCP tool list is silently rejected and the LLM sees zero tools.

Fix

  • sanitize_tool_name(raw) helper — .__, other illegal chars → _.
  • McpTool::original_name: Option<String> so calls back to the MCP server use the real name.
  • parse_tools_response populates sanitized + original; call_tool resolves either form.

Demo state

Verified live on herodemo.gent01.grid.tf. hero_agent discovers tools across 17 per-domain OSIS servers; the LLM accepts the tool list and search_hero_docs plus the per-domain RPC tools all dispatch correctly.

Risk

  • Blast radius: 1 file (hero_agent/src/mcp_client.rs)
  • Rollback: trivial revert
  • New struct field is Option<String> with #[serde(default)] so wire-compat is preserved.

Closes (refs) lhumina_code/home#153

Signed-off-by: mik-tf

## Summary Sanitize MCP tool names so they pass the LLM tool-name regex `^[a-zA-Z0-9_-]+$`. Per-domain OSIS tools (e.g. `hero_osis_business.contact.list`) contain dots which OpenAI/Anthropic/Gemini all reject. Without this, the entire MCP tool list is silently rejected and the LLM sees zero tools. ## Fix - `sanitize_tool_name(raw)` helper — `.` → `__`, other illegal chars → `_`. - `McpTool::original_name: Option<String>` so calls back to the MCP server use the real name. - `parse_tools_response` populates sanitized + original; `call_tool` resolves either form. ## Demo state Verified live on herodemo.gent01.grid.tf. hero_agent discovers tools across 17 per-domain OSIS servers; the LLM accepts the tool list and `search_hero_docs` plus the per-domain RPC tools all dispatch correctly. ## Risk - Blast radius: 1 file (`hero_agent/src/mcp_client.rs`) - Rollback: trivial revert - New struct field is `Option<String>` with `#[serde(default)]` so wire-compat is preserved. Closes (refs) https://forge.ourworld.tf/lhumina_code/home/issues/153 Signed-off-by: mik-tf
fix(mcp_client): sanitize tool names + preserve original via OnceLock
Some checks failed
Build and Test / build (push) Failing after 12s
Build and Test / build (pull_request) Failing after 44s
15df173a9a
OpenAI/Anthropic/Gemini all reject tool names containing dots — the OpenRPC
naming convention used by hero_osis ("hero_osis_business.contact.list") fails
the LLM tool-name regex `^[a-zA-Z0-9_-]+$`. Without this, hero_agent's MCP
discovery silently produces an unusable tool list and the LLM sees no tools.

Fix:
- Add `sanitize_tool_name(raw)` — replaces `.` with `__`, drops other
  illegal characters to `_`. Reversible if needed.
- McpTool gains `original_name: Option<String>` so calls back to the MCP
  server use the real name (the LLM-facing name is sanitized, the
  server-facing name is preserved).
- `parse_tools_response` populates both names; `call_tool` resolves either
  form (sanitized OR original) to the right server.

Verified: hero_agent on herodemo discovers 165+ MCP tools across 17
per-domain OSIS servers without any tool-list rejection from the LLM.
search_hero_docs and per-domain RPC tools both reach the LLM correctly.

Closes (refs) lhumina_code/home#153

Signed-off-by: mik-tf
mik-tf closed this pull request 2026-04-25 15:13:24 +00:00
Some checks failed
Build and Test / build (push) Failing after 12s
Build and Test / build (pull_request) Failing after 44s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_agent!8
No description provided.