Skip to content

fix: replace mapName heuristic fallback with explicit built-in tool whitelist#4

Open
fujunchao wants to merge 2 commits intoffflyZzz:mainfrom
fujunchao:fix/mapname-whitelist-strategy
Open

fix: replace mapName heuristic fallback with explicit built-in tool whitelist#4
fujunchao wants to merge 2 commits intoffflyZzz:mainfrom
fujunchao:fix/mapname-whitelist-strategy

Conversation

@fujunchao
Copy link
Copy Markdown

问题

mapName() 函数中存在一个危险的回退逻辑,会将所有不在 NAME_MAP 中的工具名首字母大写:

return name.charAt(0).toUpperCase() + name.slice(1);

这导致 MCP 工具名被错误修改,例如:

  • grep_app_searchGitHubGrep_app_searchGitHub
  • context7_resolve-library-idContext7_resolve-library-id

MCP 工具名已经包含正确的大小写,盲目转换会导致 AnyRouter 无法匹配工具,工具调用直接失败。

根因分析

OpenCode 中有两类工具:

  1. 内置工具:通过 Tool.define("name", ...) 注册,名称全小写(如 todowritebashread
  2. MCP 工具:通过 sanitizedClientName + "_" + sanitizedToolName 注册,保留原始大小写(如 grep_app_searchGitHubChrome-devtools_click

旧的启发式回退无法区分这两类工具,将 MCP 工具误当作内置工具处理。

修复方案:白名单策略

用显式白名单替代启发式回退。参照 OpenCode 源码 中注册的全部 22 个内置工具:

src/index.ts 变更

  • NAME_MAP 从 3 条扩展到 22 条,覆盖所有 OpenCode 内置工具
  • 移除危险的首字母大写回退逻辑
  • 未知工具名(MCP 工具等)现在原样透传,不做任何转换
  • 导出 NAME_MAPmapName 供测试使用

src/index.test.ts 新增

9 个 vitest 回归测试,覆盖:

  • 内置工具特殊映射转换(todowriteTodoWrite
  • 单词内置工具首字母大写(bashBash
  • MCP 工具名保持不变(grep_app_searchGitHubgrep_app_searchGitHub
  • 边界情况(null/undefined/空字符串/未知工具名)
  • 白名单完整性检查(确保所有已知内置工具都在 NAME_MAP 中)

README.md / README_zh.md 文档更新

更新工具名转换表:

  • 移除 "其他名称 | 首字母大写" 的旧描述
  • 新增内置工具示例行(readbashedit 等)
  • 新增 MCP 工具说明(保持不变)

package.json

  • 新增 "test": "vitest run" 脚本

完整白名单(22 个内置工具)

工具名 转换结果 类型
todowrite TodoWrite 特殊映射
todoread TodoRead 特殊映射
webfetch WebFetch 特殊映射
google_search Google_Search 特殊映射
apply_patch Apply_patch 特殊映射
bash Bash 首字母大写
batch Batch 首字母大写
codesearch Codesearch 首字母大写
edit Edit 首字母大写
glob Glob 首字母大写
grep Grep 首字母大写
invalid Invalid 首字母大写
ls Ls 首字母大写
lsp Lsp 首字母大写
multiedit Multiedit 首字母大写
plan Plan 首字母大写
question Question 首字母大写
read Read 首字母大写
skill Skill 首字母大写
task Task 首字母大写
websearch Websearch 首字母大写
write Write 首字母大写

验证

  • ✅ 构建通过:bun run builddist/index.js 6.28 KB
  • ✅ 9/9 vitest 测试通过
  • ✅ LSP 诊断:0 错误
  • ✅ 手动验证 34 个测试用例全部通过(22 个内置工具 + 12 个 MCP 工具)

…hitelist

The previous mapName() had a dangerous fallback that capitalized the
first letter of ALL tool names not in NAME_MAP:
  return name.charAt(0).toUpperCase() + name.slice(1);

This broke MCP tools with mixed-case names (e.g. grep_app_searchGitHub
became Grep_app_searchGitHub), causing tool call failures.

Changes:
- Expand NAME_MAP from 3 to 22 entries covering all OpenCode built-in tools
- Remove heuristic fallback: unknown names now pass through as-is
- Add vitest regression tests (9 cases) for built-in and MCP tool names
- Add whitelist completeness check against known built-in tool list
- Update README.md and README_zh.md to reflect new behavior
- Add test script to package.json
- Transform headers: CLI User-Agent, Accept, anthropic-beta, x-app, remove x-api-key
- Transform URL: append ?beta=true query parameter
- Transform body: billing header injection, system normalization to 3 elements,
  thinking → adaptive, max_tokens → 64000, metadata.user_id generation
- Add normalizeSystemBlocks() with billing-first strategy
- Add isMessagesEndpoint() with URL.pathname precision matching
- Add patchFetch() idempotency guard (__anyrouter_patched)
- Add generateUserId() with crypto.getRandomValues()
- Update tests to 36 cases covering all new transforms
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant