工具系统与 MCP
大约 4 分钟
工具系统与 MCP
核心认知:工具描述就是提示词
模型选工具、填参数,完全依据工具的 name + description + schema。写工具描述 = 写给模型的提示词:
// ❌ 差的描述
{
"name": "search",
"description": "搜索"
}
// ✅ 好的描述(来自真实 harness 的风格)
{
"name": "Grep",
"description": "内容搜索工具,基于 ripgrep,支持正则。优先用本工具而非 Bash grep。output_mode: 'content' 返回匹配行,'files_with_matches' 只返回文件路径(默认)。当你在多个文件中查找某符号定义时用它。",
"input_schema": {
"type": "object",
"properties": {
"pattern": { "type": "string", "description": "正则表达式,注意转义字面量大括号" },
"glob": { "type": "string", "description": "文件过滤,如 '*.ts'" }
},
"required": ["pattern"]
}
}
描述里写什么:
- 何时用 / 何时不用(「优先用本工具而非 Bash grep」——路由指令)
- 参数语义与坑(转义、单位、默认值)
- 返回什么(帮模型决定要不要用)
一个编码 Harness 的标准工具集
| 类别 | 工具 | 设计要点 |
|---|---|---|
| 读 | Read / Glob / Grep | 只读可并行;Read 带行号范围防上下文爆炸 |
| 写 | Write / Edit / NotebookEdit | Edit 必须先 Read(防盲改);精确 old_string 匹配 |
| 执行 | Bash / PowerShell | 超时控制、输出截断、后台任务 |
| 采集 | WebFetch / WebSearch | 外部信息入口 |
| 代理 | Task(Agent)/ SendMessage | 派生子代理(见下篇) |
注意「专用工具优于 Bash」原则:
Grep工具比bash grep好——结果结构化、可被权限系统理解、不依赖模型记住 shell 语法。Bash 是万能逃生舱,不是首选。
权限模型:工具的闸门
工具调用在执行前过权限层(读 / 写 / 执行):
分级放行:
只读工具(Read/Grep/Glob) → 默认允许(低风险)
写操作(Edit/Write) → 依据会话权限模式放行或询问
危险命令(rm -rf、git push) → 显式询问用户
网络请求 → 域名白名单
用户可配置(.claude/settings.json):
{
"permissions": {
"allow": ["Bash(npm test:*)", "Read(./**)"],
"deny": ["Bash(curl:*)", "Read(.env)"]
}
}
被拒绝的调用:不抛异常,返回「用户拒绝了」作为工具结果 → 模型改道
MCP:工具的外部生态
解决什么问题
每家 harness × 每个数据源 = N×M 重复适配:
没有 MCP:每个 AI 应用各自为 GitHub/Slack/数据库写集成
有了 MCP:数据源方写一次 MCP Server,所有 MCP 客户端(Claude Code、
Cursor、自建 harness…)即插即用 ——「AI 的 USB-C 接口」
架构
┌────────────┐ MCP 协议 ┌──────────────┐
│ MCP Client │ ◀──────────▶ │ MCP Server │
│ (harness) │ stdio/HTTP │ (GitHub 等) │
└────────────┘ └──────────────┘
暴露三类原语:
Tools 可执行的动作(创建 issue、查 PR)
Resources 可读取的数据(文件、数据库记录)
Prompts 预置的提示模板
实际接入(以 Claude Code 为例)
# 添加一个 GitHub MCP server
claude mcp add github -- npx -y @modelcontextprotocol/server-github
# 配置后模型自动获得新工具(如 create_issue、list_prs)
# 可用 /mcp 查看已连接的 server 和工具列表
MCP 的上下文成本问题与解法
问题:每个 MCP server 的工具 schema 都要进上下文。
接 10 个 server × 平均 20 个工具 → 系统提示膨胀几十 k,
且大部分工具本轮用不上。
解法(现代 harness 的做法):
ToolSearch:默认只注入一个「搜索工具」的 meta-tool,
模型按需搜索、动态加载具体工具 schema。
上下文占用从 O(所有工具) 降到 O(1)。
设计你自己的工具:五条军规
1. 描述写给模型看,不是写给人看
写清楚「什么时候该用我」「参数怎么填」「我会返回什么」
2. 返回结论,不返回原始数据
❌ 返回 2000 行 JSON 让模型自己找
✅ 返回结构化摘要 + 支持按需取详情
3. 宁可多个小工具,不要一个万能大工具
「deploy」「rollback」「status」 优于 「admin(action: ...)」
4. 幂等与破坏性显式标注
描述里写明「此操作不可逆」,配合权限层询问
5. 错误信息要能指导模型自纠
❌ "Error: invalid path"
✅ "路径不存在。当前目录是 /app,可用文件:src/、tests/"
案例分析:为什么 Edit 工具长那样
真实 harness 的 Edit 工具要求:old_string 必须唯一匹配(否则报错)+ 编辑前必须先 Read 过该文件:
为什么这么「麻烦」?
1. old_string 唯一匹配 → 防止模型凭想象改错位置
2. 先 Read 后 Edit → 保证模型看到的是文件真实内容,
而不是几轮前可能过期的记忆
3. 匹配失败返回错误并附提示 → 模型重新 Read 再试,自愈
每个看似别扭的工具设计,背后都是对模型失败模式的理解。
本篇小结
- 工具描述 = 提示词,决定模型行为上限
- 专用工具优于 Bash 直调;读并行、写串行
- 权限层是工具执行前的闸门,拒绝结果作为反馈回注
- MCP 统一了工具生态,「ToolSearch 动态加载」解决了工具过多的上下文膨胀
下一篇:子代理与并行执行。
