1. 从一次 tool_use 报错说起:Skills、MCP、Rules 到底谁在调用谁
先还原一个真实场景。你给项目配了.claude/rules/*.md,又装了两个 MCP Server,还从社区抄了一个SKILL.md放进.claude/skills/。跑起来之后,模型该遵守的规范没遵守,该调的工具没调,日志里只有一行tool_use的 JSON。你开始怀疑:是不是 Rules 没生效?是不是 MCP 没连上?是不是 Skill 描述写得太短?
我试过把这三样东西拆开单独测,最后发现一个反直觉的结论:Skills、MCP、Rules 在 Claude Code 里根本不是三个平行的功能模块,它们最终都收敛到同一套 tool_use 协议和 messages 注入机制上。你看到的"区别",大部分是信息被塞进了 API 请求的不同位置,而不是底层能力有什么本质不同。
这篇文章面向正在给多个 AI 工具配接入方式、被多套 Key 和 Base URL 搞烦的开发者。我会从调用链角度拆开三者,给出可复制的settings.json与 Base URL 配置片段,把 endpoint 统一改到 TaoToken,并附一次 tool_use 触发验证动作,让你亲眼看到 Skills、MCP、Rules 在统一通道下的实际行为差异。核心检索词先摆出来:Claude Code 的 Skills 是可复用提示词、MCP 是标准化工具协议、Rules 是项目级行为规范,三者都通过 tool_use 或 messages 注入参与推理。
先建立一个最小认知模型。每次 Claude Code 调用模型,本质是一个 HTTP 请求,请求体三个核心字段:system(你是谁、你该怎么做)、tools(你能做什么)、messages(对话发生了什么)。Rules 走的是messages最前面的<system-reminder>注入;MCP 同时占tools[]和system动态区两个位置;Skills 则是先注册一个叫Skill的工具,模型触发后把 Markdown 文本作为isMeta的 user 消息塞回messages。三者最终都变成模型上下文里的一段文本或一个工具定义,模型本身不"执行"任何东西,它只输出结构化 JSON,真正干活的是 Claude Code 客户端。
理解这一点,后面所有"区别"都能自己推出来。下面按"原问题 → 前置 → 配置 → 验证 → 排障 → 收口"的顺序展开,每一步都给可复制的片段。
2. 前置认知:tool_use 是三者共同的底层协议
在动手改配置之前,必须先把 tool_use 的多轮协议讲清楚,否则你会在排障时把"模型没触发"和"客户端没路由"混为一谈。
Claude 的工具调用是一个结构化的多轮对话。用户发消息后,模型推理并输出一个tool_use块,形如{"type":"tool_use","id":"toolu_xxx","name":"工具名","input":{...}}。注意,模型到这里就停了,它没有执行任何操作。调用方(也就是 Claude Code 客户端)拿到这个块,去执行对应工具,然后把结果作为tool_result追加回对话:{"type":"tool_result","tool_use_id":"toolu_xxx","content":"执行结果"}。下一轮模型读到结果,继续推理。这个循环就是 Agent 的全部秘密。
Rules 的特殊之处在于它不走 tool_use。它是被动注入:每次 API 调用前,Claude Code 把 CLAUDE.md 和.claude/rules/*.md的内容格式化后,通过prependUserContext()塞到messages最前面,用<system-reminder>包裹,role是user,带isMeta: true。isMeta只是客户端 UI 标记,消息仍完整发给 API,只是终端不展示。注入时还会带一句强制指令头,大意是"以下指令覆盖默认行为,必须严格遵守"。所以 Rules 不需要模型主动调用,模型在推理时自然"看到"。
MCP 是 tool_use 最直接的应用。每个 MCP 工具通过toolToAPISchema()转成tools[]格式,命名是mcp__<serverName>__<toolName>。模型输出tool_use后,Claude Code 识别mcp__前缀,路由到对应 MCP Client,发 JSON-RPC 到 Server 进程,拿到真实结果回填tool_result。这是名副其实的远程过程调用。
Skills 最容易被误解。它注册了一个名为Skill的工具,模型触发后,Claude Code 读取本地SKILL.md文本,包装成isMeta: true的 user 消息注入对话历史,而tool_result只返回一句"Launching skill: commit"。真正的"能力"来自那段被注入的 Markdown 指令,模型读到后按步骤调用 Read、Edit、Bash 等已有工具完成任务。所以 Skills 是提示词注入机制,tool_use 只是触发器。
三者对比可以这样记:Rules 是每次自动注入的短文本;MCP 是注册进 tools 并真实执行的外部函数;Skills 是注册进 tools 但只注入文本的伪工具。它们的共同点是——最终都影响模型下一轮推理时能看到什么。
这里有个关键推论:既然 Skills 注入的就是一段 Markdown,那你手动@commit-rules.md引用一个同等内容的 Rules 文件,模型看到的也是一段自然语言指令,效果几乎一样。Skills 多绕的tool_use → 读文件 → 注入几步,价值在于工程便利(可发现、可分发、可 Fork 隔离),而不是能力本身。这个结论后面验证环节会用日志坐实。
3. 可复制配置:把 Base URL 统一到 TaoToken
多工具接入最烦的就是 Key 和 Base URL 满天飞。Claude Code、Cline、Codex 各一套,改一个忘一个。我的做法是全部收敛到 TaoToken 一个 endpoint,Key 也只维护一份。下面给可直接复制的片段。
先拿 Key。打开https://taotoken.net/api-keys(deep link 带归因:?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite),创建一个 Key,复制备用。注意 API 根地址是https://taotoken.net/api,不要加 UTM。
Claude Code 的配置走~/.claude/settings.json,把模型请求指向统一通道。可复制片段如下:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "sk-你的TaoTokenKey", "ANTHROPIC_MODEL": "claude-sonnet-4-5-20250929", "ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-4-5-20251001" }, "permissions": { "allow": ["Bash(gh:*)", "Read", "Edit"], "deny": [] } }如果你用 Cline 或 Roo Code 这类 VS Code 插件,配置在插件的 settings 里,三件套必须写全:Base URL 填https://taotoken.net/api,API Key 填上面那个,Model ID 填claude-sonnet-4-5-20250929。少任何一个都会在请求时炸。
Codex 用户走~/.codex/auth.json,同样三件套:
{ "OPENAI_BASE_URL": "https://taotoken.net/api", "OPENAI_API_KEY": "sk-你的TaoTokenKey", "model": "gpt-5-codex" }注意 Codex 的字段名和 Claude Code 不同,别把ANTHROPIC_前缀抄过来。踩过的坑就是字段名混用,报错却是 401,看起来像 Key 错,其实是变量名没被识别。
MCP Server 的配置在~/.claude.json(user scope)或项目根.mcp.json(project scope)。一个最小 stdio 传输示例:
{ "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/project"] } } }Rules 不需要额外配置,放在项目根CLAUDE.md或.claude/rules/*.md即可。条件规则用 frontmatter 的paths字段限定生效范围:
--- paths: - "src/components/**/*.tsx" - "src/hooks/**/*.ts" --- 在 React 组件中始终使用函数式组件和 hooks,禁止 class 组件。Skill 放在.claude/skills/<name>/SKILL.md,描述要精准,因为 Skill 列表有严格 token 预算——只占上下文窗口的 1%(默认 8000 字符),每个描述最多 250 字符。描述模糊的 Skill,模型大概率不会自动触发。
配置改完,先别急着跑复杂任务。下一步用一次最小 tool_use 验证通道是否真的通了。
4. 验证请求:一次 tool_use 触发看穿三者行为差异
验证的目标不是"能不能聊天",而是"tool_use 有没有真的走通、三者行为差异能不能在日志里看到"。我建议分三步。
第一步,验证 Base URL 和 Key 通不通。在项目目录下启动 Claude Code,输入一句会强制触发工具的话,比如"列出当前目录下所有 .md 文件并读取第一个"。如果通道正常,你会看到模型输出一个tool_use,name是Bash或Read,然后客户端执行并回填tool_result。这一步能过,说明ANTHROPIC_BASE_URL和 Key 都对了。
第二步,验证 Rules 注入。在CLAUDE.md里写一条极显眼的规则,比如"所有回复开头必须加[RULES-OK]"。重启会话后随便问一句,如果回复带了这个前缀,说明 Rules 通过prependUserContext()注入成功。注意 Rules 是每次调用自动注入,不需要模型主动调用,所以它生效与否和 tool_use 无关。
第三步,验证 Skills 和 MCP 的差异。给一个 Skill 写SKILL.md,描述里明确触发条件;同时配一个 MCP Server。然后输入一个同时可能命中两者的任务。观察日志:MCP 工具触发后,tool_result.content里是外部 Server 的真实输出(比如文件列表、API 返回);Skill 触发后,tool_result只有一句"Launching skill: xxx",真正的指令文本以isMetauser 消息出现在下一轮messages里。这个差异是三者中最本质的——MCP 回传真实数据,Skill 回传的是"接下来该怎么做"的文本。
如果你想更直观,可以在 TaoToken 的模型对话页(https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite)手动构造一次带 tools 的请求,观察返回的tool_use块结构。这能帮你确认模型侧输出的 JSON 长什么样,和客户端日志对照。
验证通过后,你会得到一个清晰结论:Rules 影响的是"模型看到什么规范",MCP 影响的是"模型能调什么真实函数",Skills 影响的是"模型被喂了什么流程文本"。三者都在同一套 tool_use 协议和 messages 注入机制下工作,区别只是信息位置。
5. 常见报错排查:401、local proxy failed、reading choices、OAuth
配置统一通道后,最容易撞的几类报错,我按真实日志对照给你排。
401 Unauthorized。最常见,但原因不止一种。先确认 Key 有没有复制全(有时尾部空格);再确认 Base URL 是不是写成了https://taotoken.net/api/带尾斜杠,某些客户端会拼出双斜杠导致鉴权失败;最后确认字段名——Claude Code 用ANTHROPIC_AUTH_TOKEN,Codex 用OPENAI_API_KEY,混用会 401。三件套(Base URL + Key + Model ID)缺任何一个都可能报 401,别只盯着 Key。
local proxy failed。这个报错通常出现在客户端尝试走本地代理但代理没起来,或者环境变量里残留了HTTP_PROXY/HTTPS_PROXY指向一个不存在的端口。检查 shell 配置里有没有遗留的代理变量,清掉后重启终端。注意,这里说的是清理本地无效代理配置,不是让你去配什么网络工具。
reading choices 相关报错。多出现在 OpenAI 兼容格式的响应解析上,典型是cannot read property 'choices' of undefined。原因通常是 Base URL 指向了一个返回非标准 JSON 的 endpoint,或者 Model ID 写错导致服务端返回错误结构。确认 Base URL 是https://taotoken.net/api,Model ID 用文档里列出的有效值。
OAuth 相关报错。Claude Code 某些版本会尝试 OAuth 流程,如果你已经用 Key 鉴权,需要在 settings 里确保没有残留的 OAuth token 字段,否则会优先走 OAuth 然后失败。清掉~/.claude/下的凭据缓存再重启。
MCP 连不上。先看~/.claude.json里mcpServers的command路径对不对,npx能不能在非交互环境跑。stdio 传输的 Server 如果启动就退出,Claude Code 会标记为 disconnected,此时它的工具不会出现在tools[]里,模型自然调不到。用npx -y @modelcontextprotocol/server-filesystem /tmp手动跑一次,看有没有报错。
Skill 不自动触发。这是最高频的"假故障"。源码里 Skill 列表 token 预算只有上下文 1%,每个描述最多 250 字符,模型判断是否触发依赖这点信息和whenToUse字段。描述写得模糊,模型就不会自动调。解决办法是把触发条件写具体,或者干脆让团队成员手动/skill-name调用——手动触发和自动触发最终注入的文本是一样的。
排障时记住一个原则:先确认通道(Base URL + Key + Model),再确认工具注册(tools 里有没有),最后确认触发(模型有没有输出 tool_use)。三层分开查,比一股脑改配置高效得多。
6. 收口:把三者当同一套协议的不同入口
走到这里,你应该能自己回答开头那三个问题了。Rules 和 Skills 的区别没有想象中大,因为 Skills 执行后注入的就是一段 Markdown,和你手动@一个 Rules 文件,模型看到的都是messages里的一段 user 文本。真正的工程差异只有两点:触发方式(Rules 自动注入,Skills 需模型判断后调 tool_use)和执行隔离(Skills 可配context: 'fork'在独立上下文跑,Rules 没有这层隔离)。
MCP 和内置 Tools 对模型来说也没区别,tools[]里格式一样,区别纯粹在客户端执行路由。MCP 的价值不在"能调外部系统"(Bash 也能),而在持久化连接、复杂操作原子封装、权限隔离这三个点。简单 CLI 操作直接让模型用 Bash,别折腾 MCP。
Skills 的"标准化流程"不是代码层面的流程化,源码里没有任何 if-else 控制执行步骤,所谓流程就是一段结构化的 Markdown,靠模型的指令遵循能力跑。Skill 的质量等于提示词的质量,换个弱模型流程可能就乱。
实际落地建议:项目级短规范用 Rules;长指令、有明确触发时机、需要隔离的用 Skills;需要持久连接或权限约束的用 MCP。别迷信 Skills 自动触发,把核心 Skill 的快捷命令告诉团队,比指望模型识别靠谱。
最后给一个实用技巧:把 Base URL 和 Key 统一到 TaoToken 后,你可以在一个地方管理所有工具的接入,改一次全生效。长期跑编码和 Agent 任务的话,Coding Plan(https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite)比按量更省心;接入文档在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite,遇到字段名不确定时直接查,比猜快。