ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Claude Code工具编排实战:从MCP膨胀到精准供给

Claude Code工具编排实战:从MCP膨胀到精准供给 1. 从“工具越多越强”说起我为什么给 Claude Code 挂了满身装备刚上手 Claude Code 那阵子我跟很多人一样陷入了一种“装备焦虑”。看到别人分享一个 MCP Server赶紧装刷到某个 skill 脚本立刻 clone 下来塞进配置目录听说 Playwright MCP 能让 AI 直接操控浏览器二话不说就接上。不到两周我的 Claude Code 配置里已经挂了三十多个工具——文件系统、浏览器自动化、数据库查询、终端复用、代码检索、甚至还有几个我自己都忘了是干嘛的 skill。结果呢响应变慢了工具调用经常选错有时候明明只是让它改个变量名它却绕了一大圈去调用某个八竿子打不着的 MCP 工具。最离谱的一次我让它读一个本地 JSON 文件它居然试图通过浏览器自动化去打开一个不存在的 URL。那一刻我才意识到“给 AI 减负”这个说法本身就是个伪命题——真正的问题不是工具太多而是工具的组织方式错了。这篇文章不聊虚的就聊我踩过的坑、试过的方案、以及最后沉淀下来的一套“工具编排”思路。如果你也在用 Claude Code、Codex 这类 AI CLI 工具或者正在折腾 MCP 协议、skill 脚本、终端复用这些东西那这篇内容应该能帮你少走不少弯路。核心关键词就几个Claude Code、MCP、skill、终端复用、工具编排。我会从整体设计思路讲到具体配置再到问题排查尽量把每个“为什么”都说清楚。先说结论工具不是越多越好但“减负”也不是简单地删工具。关键在于分层、分场景、分优先级。下面我按自己的实操顺序一层层拆开讲。2. 整体设计思路为什么“减负”是个伪命题2.1 工具膨胀的真实代价不是慢是“选择困难”很多人以为工具多了只是拖慢启动速度其实那是最次要的问题。真正的代价在于模型在工具选择上的认知负荷。Claude Code 这类工具在每次对话时会把所有可用工具的 schema 塞进上下文模型需要从中挑选最合适的一个或多个来完成任务。工具数量从 5 个涨到 30 个选择空间是指数级增长的。我做过一个粗略的对比测试同样一个“读取 package.json 并提取 version 字段”的任务在只挂文件系统工具时Claude Code 平均 1.2 秒完成一次工具调用搞定挂满 30 多个工具后平均要 3.5 秒而且有大约 15% 的概率会先调用一个无关工具“试探”一下。这个损耗在单次任务里不明显但一天几十次交互下来累积的时间浪费和 token 消耗相当可观。更麻烦的是误调用。比如你同时挂了 Playwright MCP 和文件系统 MCP让它“打开某个文件看看”它有概率理解成“用浏览器打开”。这种错误不是模型笨而是工具描述之间的语义边界模糊了。注意工具膨胀带来的最大问题不是性能而是“语义污染”——不同工具的功能描述在向量空间里互相干扰导致模型的选择准确率下降。2.2 我的分层思路核心层、场景层、备用层既然不能简单删那怎么办我的做法是按使用频率和场景相关性分三层核心层几乎每次对话都会用到的工具比如文件读写、终端执行、代码检索。这一层控制在 5 个以内常驻加载。场景层按当前项目类型动态加载。比如做前端项目时加载浏览器自动化工具做数据处理时加载数据库工具。这一层按需启用不常驻。备用层那些偶尔用一次的工具比如某个特定 API 的 MCP Server。这一层平时不加载需要时手动挂载。这个分层不是拍脑袋想的而是基于一个简单观察80% 的任务只需要 20% 的工具。把常驻工具压到 5 个以内模型的工具选择准确率能回到 95% 以上响应速度也明显回升。2.3 为什么不用“全自动动态加载”有人可能会问能不能让 AI 自己判断需要哪些工具自动加载我试过不靠谱。原因是动态加载本身需要一次“元决策”而这次决策同样受当前上下文影响容易陷入“我不知道需要什么工具所以我先加载所有工具看看”的死循环。更稳妥的方式是按项目目录预设配置比如在项目根目录放一个.claude/tools.yaml进入该项目时自动加载对应工具集。这样既避免了手动切换的麻烦又保证了工具集的确定性。3. 核心细节解析MCP、skill 与终端复用的实操要点3.1 MCP 协议到底解决了什么问题MCPModel Context Protocol本质上是给 AI 工具调用定的一套“标准接口”。在没有 MCP 之前每个工具都要自己写一套适配层Claude Code 要调 A 工具用一套方式调 B 工具用另一套方式。MCP 出现后所有工具只要实现这个协议就能被统一调用。但这里有个容易被忽略的点MCP 是软件协议不是硬件协议。我见过有人在群里问“MCP 是不是像 USB 那种硬件标准”其实不是。它更像是一种“函数签名约定”——告诉 AI“我这个工具叫什么名字、接受什么参数、返回什么格式”。理解这一点很重要因为它意味着 MCP Server 的质量参差不齐有的写得很规范有的参数设计一塌糊涂直接挂上去反而添乱。我自己的筛选标准是三条参数是否自解释、返回是否结构化、错误是否有明确提示。三条里有一条不满足我就不会把它放进核心层。3.2 skill 脚本的编写要点别写成“万能胶”skill 是 Claude Code 里另一个容易滥用的东西。很多人把 skill 当成“万能胶”什么功能都往里塞结果一个 skill 脚本几百行逻辑分支比迷宫还复杂。我的经验是一个 skill 只做一件事而且这件事要能用一句话说清楚。比如我写过一个extract-version的 skill功能就是从 package.json 里提取 version 字段并输出。就这么简单十行代码。但它比挂一个完整的文件系统 MCP 再让 AI 去解析要快得多也准得多。因为 skill 是确定性的不依赖模型的“理解”。写 skill 的时候有几个实操要点输入输出要严格定义用 JSON Schema 或者简单的类型标注别让 AI 猜。错误处理要明确失败时返回什么、成功时返回什么格式要统一。别在 skill 里做“智能判断”判断交给 AIskill 只负责执行。提示skill 脚本的命名很关键。用动词开头、语义明确的名称比如read-file、run-tests避免helper、utils这种模糊命名。模型对工具名的语义敏感度比你想象的高。3.3 终端复用为什么我最终选了 tmux 而不是 Tabby终端复用这块我折腾了很久。一开始用 Tabby界面好看配置也方便但用久了发现一个问题Tabby 的会话管理和 Claude Code 的终端调用之间有隔阂。Claude Code 执行命令时有时候会开一个新的终端会话而不是复用当前的导致上下文丢失。后来换到 tmux虽然界面朴素但胜在会话模型清晰。tmux 的 session、window、pane 三层结构正好对应我“项目-任务-命令”的组织方式。我现在的做法是每个项目一个 tmux session每个任务一个 windowClaude Code 在指定的 pane 里执行命令上下文不会乱。配置上我建议在.tmux.conf里加这几条# 设置前缀键为 Ctrla比默认的 Ctrlb 顺手 set -g prefix C-a bind C-a send-prefix # 开启鼠标支持方便切换 pane set -g mouse on # 设置窗口编号从 1 开始符合直觉 set -g base-index 1 setw -g pane-base-index 1 # 减少 ESC 延迟提升响应速度 set -sg escape-time 10这几条配置看起来简单但实际用起来差别很大。尤其是escape-time默认值 500ms 在频繁切换模式时会明显感觉卡顿改成 10ms 后流畅很多。3.4 工具描述的“语义边界”怎么划这是最容易被忽略但影响最大的一点。每个 MCP 工具或 skill 都有一段描述文字模型就是靠这段文字来判断“什么时候该用这个工具”。如果两个工具的描述语义重叠模型就会犯迷糊。我的做法是给每个工具写一句“排他性描述”明确它不做什么。比如文件读取工具的描述“读取本地文件内容。不用于网络请求不用于数据库查询。”浏览器工具的描述“操控浏览器进行网页交互。不用于读取本地文件不用于执行 shell 命令。”这种“正向功能 反向排除”的描述方式能显著降低误调用率。我实测下来误调用率从 15% 降到了 5% 以下。4. 实操过程从零搭建一套“分层工具编排”配置4.1 目录结构设计先说我现在的目录结构这是整套方案的基础~/.claude/ ├── config.yaml # 全局配置 ├── tools/ │ ├── core/ # 核心层工具配置 │ │ ├── filesystem.yaml │ │ ├── terminal.yaml │ │ └── search.yaml │ ├── scene/ # 场景层工具配置 │ │ ├── frontend.yaml │ │ ├── backend.yaml │ │ └── data.yaml │ └── backup/ # 备用层工具配置 │ └── ... └── skills/ ├── extract-version.sh ├── run-tests.sh └── ...核心层常驻场景层按项目类型加载备用层手动挂载。这个结构的好处是一目了然想调整哪一层直接改对应目录就行不用在一大堆配置里翻找。4.2 核心层配置5 个工具封顶核心层我只留 5 个工具配置如下# ~/.claude/tools/core/filesystem.yaml name: filesystem description: 读写本地文件。支持读取、写入、追加、删除。不用于网络请求。 commands: - read - write - append - delete - list # ~/.claude/tools/core/terminal.yaml name: terminal description: 执行 shell 命令并返回输出。不用于文件内容解析。 commands: - exec - exec_bg # ~/.claude/tools/core/search.yaml name: search description: 在代码库中搜索文本或正则表达式。不用于文件读写。 commands: - grep - find这 5 个工具覆盖了日常 80% 的操作。注意每个描述里都有“不用于”的排除句这是关键。4.3 场景层配置按项目类型动态加载场景层的加载逻辑我写了一个简单的 shell 脚本放在项目根目录的.claude/load-scene.sh#!/bin/bash # 根据项目类型加载对应的场景工具集 PROJECT_TYPE$(cat .claude/project-type 2/dev/null || echo default) case $PROJECT_TYPE in frontend) claude tools load ~/.claude/tools/scene/frontend.yaml ;; backend) claude tools load ~/.claude/tools/scene/backend.yaml ;; data) claude tools load ~/.claude/tools/scene/data.yaml ;; *) echo No scene tools loaded. ;; esac然后在项目根目录放一个.claude/project-type文件内容就一行比如frontend。进入项目时手动跑一下这个脚本或者把它加到 shell 的chpwd钩子里自动执行。前端场景的工具集大概长这样# ~/.claude/tools/scene/frontend.yaml tools: - name: playwright description: 操控浏览器进行网页交互和截图。不用于本地文件操作。 - name: chrome-devtools description: 访问 Chrome 开发者工具协议。不用于浏览器自动化。后端场景则是数据库和 API 测试工具# ~/.claude/tools/scene/backend.yaml tools: - name: postgres description: 执行 PostgreSQL 查询。不用于其他数据库。 - name: redis description: 执行 Redis 命令。不用于持久化存储。4.4 备用层手动挂载的正确姿势备用层的工具平时不加载需要时用一条命令挂上claude tools load ~/.claude/tools/backup/some-specific-tool.yaml用完记得卸载claude tools unload some-specific-tool这里有个小技巧给备用层工具加一个“过期时间”。比如加载时指定--ttl 30m30 分钟后自动卸载。这样即使忘了手动卸载也不会一直占着上下文。4.5 参数计算上下文预算怎么分配Claude Code 的上下文窗口是有限的工具 schema 占用的 token 直接影响到留给对话的空间。我粗略算过一笔账每个工具的 schema 平均占用 200-400 token30 个工具就是 6000-12000 token如果上下文窗口是 200K token看起来占比不大但实际对话中还要塞代码、文件内容、历史记录累积起来就很紧张了我的分配策略是工具 schema 占用不超过上下文窗口的 5%。按 200K 算就是 10000 token 以内。核心层 5 个工具约 1500 token场景层 3-5 个工具约 1500 token总共 3000 token 左右留足了余量。注意不同模型的 token 计算方式略有差异上面的数字是估算。实际配置时建议用claude tools list --verbose查看真实的 token 占用。4.6 实操现场一次完整的工具编排过程举个具体例子。我最近在做一个前端项目需要 Claude Code 帮我改一个 React 组件的样式同时用浏览器验证效果。第一步进入项目目录确认.claude/project-type内容是frontend。第二步跑load-scene.sh加载 Playwright 和 Chrome DevTools 工具。第三步启动 Claude Code此时可用工具是核心层 5 个 场景层 2 个 7 个。第四步给指令“把 Button 组件的背景色改成蓝色然后用浏览器打开 localhost:3000 截图确认。”Claude Code 的执行路径很清晰先用 filesystem 读取 Button 组件文件用 terminal 执行构建命令用 playwright 打开页面并截图。全程没有误调用一次通过。对比之前挂 30 多个工具的时候同样的任务它可能会先尝试用某个数据库工具“看看有没有相关数据”或者用某个 API 工具“检查一下接口”绕一大圈才回到正轨。5. 常见问题与排查技巧实录5.1 工具调用失败先查描述再查参数工具调用失败是最常见的问题。我的排查顺序是看工具描述是否清晰如果描述模糊模型可能传错参数。看参数格式是否匹配比如工具要求 JSON模型传了 YAML。看工具本身是否正常手动跑一下 MCP Server 或 skill 脚本确认不是工具本身的问题。我遇到过最坑的一次是某个 MCP Server 的返回格式不稳定有时候返回 JSON有时候返回纯文本。模型拿到纯文本后解析失败但错误提示又不明确导致它反复重试。后来我在工具配置里加了一层“返回格式校验”不合法就直接报错问题才解决。5.2 误调用频发用“排他性描述”和“优先级”双管齐下误调用的根源是语义重叠。除了前面说的“排他性描述”还可以给工具设优先级。比如文件读取工具的优先级设为high浏览器工具的优先级设为medium。当模型在两个工具之间犹豫时优先级高的会被优先选择。配置方式name: filesystem priority: high description: 读写本地文件。不用于网络请求。优先级不是万能的但在边界模糊的场景下能起到“最后一票”的作用。5.3 上下文爆炸定期清理不用的工具即使做了分层时间长了还是会积累一些“僵尸工具”——加载了但从来不用。我的做法是每周清理一次用claude tools list --usage查看每个工具的调用次数连续一周零调用的就移到备用层。这个习惯帮我省了不少上下文空间。有一次清理完发现30 多个工具里有 12 个是零调用的移走后响应速度明显提升。5.4 终端复用冲突tmux 会话命名要规范用 tmux 做终端复用时最容易出的问题是会话命名混乱。我的规范是session 名 项目名window 名 任务类型如edit、test、deploypane 名 具体命令如dev-server、test-runner这样 Claude Code 在执行命令时能明确知道该往哪个 pane 里发指令不会串台。5.5 常见问题速查表问题现象可能原因排查方法解决方案工具调用超时MCP Server 无响应手动运行 Server 看是否卡住重启 Server 或检查网络模型选错工具描述语义重叠查看工具描述是否有重复关键词加排他性描述或调整优先级参数格式错误工具 schema 不清晰检查 schema 定义补充类型标注和示例上下文占用过高工具数量过多claude tools list --verbose移除非核心工具到备用层终端命令串台tmux 会话命名混乱检查 session/window/pane 命名统一命名规范skill 执行失败输入输出格式不匹配手动跑 skill 脚本严格定义输入输出格式5.6 几个我踩过的坑坑一盲目追求“全自动”。一开始我想让 Claude Code 自己判断需要哪些工具结果它经常加载一堆用不上的。后来改成按项目预设稳定多了。坑二忽略工具描述的语言。工具描述用中文还是英文对模型的影响比想象中大。我的经验是跟主对话语言保持一致如果平时用中文跟 Claude Code 交流工具描述也用中文匹配度更高。坑三skill 脚本里做太多事。有个 skill 我一开始写了 200 行功能是从多个文件里提取信息并汇总。结果经常出错因为逻辑太复杂。后来拆成三个小 skill每个只做一件事稳定性大幅提升。坑四忘了卸载备用工具。有次加载了一个数据库工具查数据用完忘了卸载结果后面几次对话模型总是试图用它干扰很大。后来加了--ttl参数才解决。6. 工具编排的进阶思路从“减负”到“精准供给”6.1 按任务阶段动态调整工具集项目开发有不同的阶段编码、测试、部署、调试。每个阶段需要的工具不一样。我现在的做法是按阶段切换工具集而不是按项目类型一刀切。比如编码阶段只需要核心层 代码检索测试阶段加上测试运行器和浏览器工具部署阶段加上 CI/CD 相关工具。这样每个阶段的实际可用工具都控制在 7-8 个精准匹配当前需求。实现方式是在load-scene.sh里加一个阶段参数#!/bin/bash STAGE${1:-coding} case $STAGE in coding) claude tools load ~/.claude/tools/scene/coding.yaml ;; testing) claude tools load ~/.claude/tools/scene/testing.yaml ;; deploying) claude tools load ~/.claude/tools/scene/deploying.yaml ;; esac用的时候./load-scene.sh testing就行。6.2 工具组合的“化学反应”有些工具单独用效果一般但组合起来威力很大。比如文件系统 代码检索 终端执行这三个组合起来就能完成大部分代码修改任务。我的经验是优先打磨核心层的组合效率而不是不断往场景层加新工具。具体做法是给核心层工具写“组合示例”放在工具描述里。比如name: filesystem description: 读写本地文件。常与 search 组合使用先用 search 定位文件再用 filesystem 读取。不用于网络请求。这种“组合提示”能引导模型形成固定的工作流减少随机探索。6.3 监控与迭代用数据驱动工具调整工具编排不是一次性的工作需要持续迭代。我现在的做法是每周看一次工具调用统计重点关注三个指标调用次数哪些工具高频哪些低频成功率哪些工具经常失败误调用率哪些工具经常被错误选择根据这些数据调整分层高频高成功率的留在核心层低频的移到备用层高误调用率的要么改描述要么直接删掉。这个习惯坚持了两个月我的工具集从 30 多个精简到了 12 个但实际工作效率反而提升了。因为每个留下的工具都是经过验证的模型的选择准确率也上去了。6.4 关于“给 AI 减负”的再思考回到标题那句话“给 AI 减负”是个伪命题。我现在更愿意把它叫做**“给 AI 精准供给”**。减负的思路是“少给点”但少给不一定对精准供给的思路是“给对的”在正确的时间给正确的工具。这两者的区别在于减负是被动的看到问题就删精准供给是主动的根据任务需求动态调整。前者容易矫枉过正把有用的工具也删了后者需要更多前期设计但长期来看更稳定。我现在的工具集不是最少的但每个工具都有明确的定位和使用场景。模型不需要在 30 个工具里大海捞针也不需要因为工具太少而无法完成任务。这种“刚刚好”的状态是我折腾了几个月才找到的平衡点。最后分享一个我最近在用的技巧给每个工具加一个“使用场景”标签比如#coding、#testing、#debugging。加载工具时按标签筛选比按文件名筛选更符合直觉。这个技巧是从一个做推荐系统的朋友那里学来的本质上是把工具当成“内容”来做召回和排序思路挺有意思的。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进