内容来源:Muhammad Moeed。https://moeed.app/posts/claude-code-skills-vs-mcp-vs-subagents-vs-hooks/
原题:Claude Code: Skills vs MCP vs Subagents vs Hooks (2026)
原发布时间:2026-05-09
用通俗语言比较 Claude Code 的 Skills、MCP 服务器、子智能体与 Hooks,并通过决策树、代码和常见误区说明该在何时选择哪一种。

只要使用 Claude Code 超过一周,你很可能就会遇到这个问题:想让 Claude 重复完成某件事时,应该把它做成 Skill、斜杠命令、MCP 服务器、子智能体,还是 Hook?它们看起来似乎都能胜任,文档又把它们当成彼此独立的概念,而各自的边界确实并不清晰。
之所以令人困惑,是因为这四种机制都处在同一条光谱上。它们都是扩展 Claude Code 的不同方式,回答的却是不同问题。Skills 回答的是“Claude 应该知道什么”,MCP 回答“Claude 应该能做什么”,子智能体回答“工作应该在哪里完成”,Hooks 回答“什么事情必须发生”。
这篇文章就是我刚上手时希望看到的那种比较:用通俗语言给出定义,提供一棵容易记住的决策树,展示每种机制的真实案例,并总结我在客户代码库中反复见到的坑。
**太长不看:**Skills 是知识,MCP 服务器是行动能力,子智能体负责隔离,Hooks 提供确定性。真实工作流大多会同时使用其中两三种——它们生来就是为了组合,而不是互相竞争。
用一句话定义四种机制
在开始比较之前,先用一句真正贴合实际用法的话定义每种机制。
| 机制 | 一句话定义 | 存放位置 |
|---|---|---|
| Skill | 一份 Markdown 指令文件;当你的消息与其触发条件匹配时,Claude 会将它载入上下文 | .claude/skills/<name>/SKILL.md |
| MCP 服务器 | Claude 可以通过 JSON-RPC 将其作为工具调用的外部进程 | 独立的二进制程序或服务 |
| 子智能体 | 一个全新、相互隔离的 Claude 实例,Claude 可以把工作委派给它 | .claude/agents/<name>.md |
| Hook | Claude Code 运行框架在生命周期事件发生时执行的 Shell 命令 | settings.json |
如果你只打算记住本文的一件事,那就记住:Skills 告诉 Claude 如何做一件事,MCP 赋予 Claude 做这件事的能力,子智能体把工作移到别处完成,而 Hooks 则强制某件事发生,无论 Claude 自己作何决定。
心智模型:知识、行动、隔离与保证
从远处看,这四种机制似乎可以互相替代,因为它们都能让你“扩展 Claude Code”。但实际上不能互换,因为它们扩展的是不同层面。
Skills 扩展 Claude 的知识
Skill 是一段文字。当你的请求看起来与其相关时,Claude 就会阅读它。Skill 本身不会“运行”任何东西,它只是指令。模型读完这些指令后,会相应地改变自己的行为。
当“缺少什么”这个问题的答案是“模型不知道我希望以什么方式完成这件事”时,就该使用 Skill。例如提交消息格式、Lint 规则、PR 模板、日志行的内部风格。这些都不需要代码,只需要让一段说明在恰当时机稳定地进入模型上下文。Skill 正是为此而生。完整教程见 Claude Code Skills 指南;简而言之,只需准备一个包含 SKILL.md 的文件夹,再写一段明确提及触发词的描述,就完成了。
MCP 服务器扩展 Claude 的行动能力
MCP 服务器正好相反:其中没有操作指令,提供的纯粹是能力。它是一个持续运行的进程,对外暴露 query_database、send_slack_message、create_jira_ticket 等可供 Claude 调用的工具。
当“缺少什么”的答案是“Claude 需要实际接触一个目前无法访问的系统”时,就该使用 MCP 服务器。例如读取数据库、向 Slack 发消息、调用内部 API,或读取 Notion 页面。模型本来就知道如何调用工具,真正缺的是工具本身。MCP 服务器就是用来补上这项能力的。如果你还没有构建过 MCP 服务器,可以从构建你的第一个 MCP 服务器开始,也可通过最佳 MCP 服务器清单了解整个生态。
子智能体扩展 Claude 的上下文
子智能体是一个独立的 Claude 实例,在自己的全新上下文中运行。Claude 会把任务简报交给它,再收回一份结果摘要。子智能体可以拥有自己的工具、权限,甚至使用不同的模型。
子智能体之所以存在,是为了解决上下文污染。如果要求主 Claude 阅读一个包含 50 个文件的代码库,整个对话就会被嘈杂的工具输出淹没;等到你提出真正的问题时,模型已经因为这些噪声而变笨了。子智能体会在旁路上下文中完成这些工作——只有答案会返回,噪声则留在那里。当任务繁重或嘈杂,而你又希望主对话保持清爽时,就应该使用子智能体。
Hooks 扩展的是运行框架,而非模型
Hook 是 Claude Code 运行框架在生命周期事件发生时执行的 Shell 命令:工具运行前、工具运行后、提交提示词时,或 Claude 完成任务时。Hooks 不是可选建议,也不取决于模型如何理解;只要接入,它就会执行。
当答案是“无论 Claude 如何决定,这件事每次都必须发生”时,就该使用 Hooks。例如阻止写入 production.env、每次编辑后运行 Linter,或当 Claude 空闲时向 Slack 推送通知。凡是不能只靠“模型应该记得去做”来保证的事情,都适合交给 Hook。完整模式参见 Claude Code Hooks 指南。
一棵能记在脑中的决策树
准备扩展 Claude Code 时,按顺序问自己下面几个问题。第一个得到“是”的问题,就是答案。
无论 Claude 做什么,这件事是否都必须每次执行?
→ Hook
Claude 是否需要接触一个目前无法访问的系统?
→ MCP 服务器
任务是否庞大、嘈杂或风险太高,足以破坏主上下文?
→ 子智能体
Claude 是否需要知道如何完成一件目前表现不够稳定的事?
→ Skill
请留意这个顺序。Hooks 排在第一,因为它是唯一具有确定性的选项。MCP 排在第二,因为增加能力通常比增加指令更具杠杆效应。子智能体排在第三,用于解决上下文污染。Skills 最后考虑,因为它是最轻量的工具,而大多数情况下你根本不需要更重的方案。
横向比较:真正重要的差异
| Skill | MCP 服务器 | 子智能体 | Hook | |
|---|---|---|---|---|
| 增加了什么 | 知识 | 能力 | 隔离的上下文 | 确定性 |
| 是否运行 | 否,它只是指令 | 是,它是一个进程 | 是,它是一次模型调用 | 是,它是一条 Shell 命令 |
| 由谁触发 | Claude,在描述匹配时 | Claude,在选择该工具时 | Claude,在委派任务时 | 运行框架,在生命周期事件发生时 |
| Claude 能否跳过 | 能 | 能 | 能 | 不能 |
| 成本 | 极低——使用前约占 100 个 Token | 进程成本 + 工具 Token 成本 | 完整上下文 + 模型 Token | 脚本本身产生的成本 |
| 最适合 | 风格、约定、重复工作流 | 外部系统、真实 API | 大量读取、并行工作 | 防护、日志记录、CI 门禁 |
| 最不擅长 | 任何必须确定执行的事 | 任何不希望作为操作执行的事 | 快速查询 | 任何有状态或交互式的任务 |
理解这张表的一种实用方法是:每一行都指向同一个权衡。越靠近确定性与外部系统,机制就越可靠,但灵活性越低;越依赖模型决策,灵活性就越高,但越无法保证。
真实场景,真实选择
决策树是抽象版本。下面是一些具体情境,以及我实际会选择的方案。
“每次提交都应遵循 Conventional Commits 格式”
这应该使用 Skill。模型本来就会写提交消息,只需要得到稳定提醒,遵循你的内部风格即可。一份六行的 SKILL.md 就能做到。
---
name: conventional-commit
description: 只要用户要求提交、编写提交消息或创建 Git 提交,就使用此 Skill。消息应采用 Conventional Commits 格式。
---
主题行以以下类型之一开头:feat、fix、chore、docs、refactor、test、perf。
添加一个冒号,随后用简短的祈使句概括内容,不加句号,总长度保持在 70 个字符以内。
这里不需要 Hook,因为提交消息稍有偏差的代价很低,不值得为了它承受 Hook 中断工作流所带来的摩擦。
“智能体绝不能把密钥写进日志文件”
这应该使用 Hook,而不是 Skill。“绝不能”是关键词。Skill 表达的是“请不要这样做”,模型通常会遵守;只有 Hook 才能让这件事真正无法发生。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "scripts/check-no-secrets.sh"
}
]
}
]
}
}
每次调用 Write 前都会运行这个 Hook。只要文件路径或内容触发检查,Hook 就会以非零状态退出,并阻止写入。
“Claude 需要从我们的 Jira 中读取工单”
这应该使用 MCP 服务器。这里没有捷径——没有 MCP 服务器,Claude 就无法访问 Jira。如果根本没有调用 API 的传输通道,那么通过 Skill 告诉 Claude“使用 Atlassian API”毫无意义。
如果你的团队使用 Atlassian Rovo MCP,它应该已经接入。如果使用其他技术栈,这就是最容易交付的“第一个 MCP 服务器”项目:只读、数据结构可预测,而且立刻就能产生价值。
“审计包含 200 个文件的 Rails Monorepo,找出未使用的路由”
这应该使用 子智能体。如果在主对话中完成,你将看到 200 次文件读取、数百条 grep 结果,整个上下文在当天剩余时间里都会变得难以使用。子智能体可以在全新上下文中完成同样的工作,然后只返回“这里是 14 条未使用的路由”,主对话则保持干净。
---
name: route-auditor
description: 审计 Rails 应用中的未使用路由。读取 routes.rb、扫描控制器,并返回结果清单。
tools: ["Read", "Grep", "Glob"]
---
读取 config/routes.rb。针对每条路由,找出对应的 controller#action。
对每个 action 使用 grep 搜索引用。返回一份 action 清单:
这些 action 除自身所在的控制器外,没有在其他任何位置被引用。
“每次编辑后运行 Linter,并且只有通过检查才能提交”
这是两项工作,应该使用两种机制。“运行 Linter”这部分属于 Skill——Claude 本来就能做,你只是想让它保持一致。“只有通过检查才能提交”则属于 Hook——这是保证,而不是指导原则。两者可以组合使用。
如何组合使用
大多数生产环境会同时使用这四种机制中的三到四种。下面是一种我经常见到的模式。
某个团队配备了一名编程智能体。它使用一项 Skill 来定义团队的 PR 描述格式,通过 MCP 服务器从 Linear 获取工单上下文,把任何需要读取较多文件的任务交给子智能体,并用 Hook 强制智能体绝不修改测试文件——只能创建或读取。
四种机制承担了四项不同工作。试图用其他机制替代其中任何一种,要么会降低可靠性,要么会污染主上下文。
真正该问的问题通常不是“应该使用 Skill 还是 MCP 服务器”,而是“这项具体扩展需要成为知识、行动、隔离,还是保证?”回答这个问题后,该选择哪种机制也就不言自明了。
我反复见到的五个错误
审计过几十套 Claude Code 配置后,我发现下面五种错误会一再出现。
把能力塞进 Skill
如果你的 SKILL.md 写着“使用 curl 从我们的 API 获取数据”,那就是一个假扮成 MCP 服务器的 Skill。它有时能用,却会以不可预测的方式失败。请构建 MCP 服务器。
把保证塞进 Skill
把“绝不删除 data/ 中的文件”写进 Skill,只是一项愿望,而不是规则。Claude 大多数时候会遵守,却可能偏偏在你最需要它记住时忘掉。把规则移到 PreToolUse Hook 中,并添加匹配 data/ 的 matcher。
用子智能体完成小型查询
处理繁重任务时,子智能体带来的收益足以抵消成本。可如果只是快速查询“这个函数做什么”,启动全新上下文的成本反而高于直接在主上下文中完成。请把子智能体留给真正庞大的任务。
为 Skill 就能完成的事情构建 MCP 服务器
如果工作只是“用某种格式呈现输出”或“遵循某种提交约定”,你不需要服务器,只需要一段说明。MCP 服务器用于补充模型不具备的能力,而不是纠正模型已经具备、只是执行得不够稳定的行为。
把 CLAUDE.md 当作四种机制中任意一种的替代品
CLAUDE.md 用于始终开启的上下文,例如项目约定、技术栈,以及这个代码库中每次对话一开始都应知道的信息。它不适合存放工作流(“提交时执行 X”)、能力(“查询数据库”)或保证(“阻止编辑 production.yml”)。把这四类内容全塞进 CLAUDE.md,只会让主上下文臃肿,而且没有一项能得到可靠执行。
常见问题
Skill 和斜杠命令是一回事吗?
差不多。一份 Skill 文件既会提供一项可自动发现的 Skill,也会免费提供一条 /skill-name 斜杠命令。斜杠命令是手动入口,Skills 则负责自动触发。同一份文件,两种调用方式。
什么时候应该用 Hook,而不是 Skill? 当忘记执行的代价很高时。Skills 依赖模型主动选中,Hooks 则始终运行。如果“模型应该记得”不足以构成可靠保证,就使用 Hook。
MCP 服务器与 Skills 能否配合使用?
可以,而且这是最实用的模式之一。Skill 可以规定:“当用户要求 X 时,调用 lookup_customer 工具,然后按这种格式呈现结果。”Skill 是方法论,MCP 工具则负责行动。
子智能体能看到我的对话吗? 不能。这正是它存在的意义。它会从 Claude 那里收到任务简报,再返回报告。你希望它知道的任何事情,都必须写进简报,或放在它能够读取的文件中。
Hook 能否调用 Claude? Hook 本身只是一条 Shell 命令,因此从技术上说可以——你可以通过 Shell 调用 Anthropic API,甚至调用 Claude Code 自身。但在实践中,Hooks 应该简短、确定,而且不依赖模型。请用它们实现防护和日志记录,而不是发起新的模型调用。
密钥应该存在哪里——Skill、MCP、子智能体还是 Hook? 都不应该。密钥应放在环境变量中,或存入 MCP 服务器和 Hook 可以读取的密钥管理器。Skills 会提交到 Git,子智能体定义会被 Claude 读取,Hook 脚本则会被记录。请把这四者都视为不可信的密钥存放位置。
应该先学哪一种? Skills。它最轻量、编写速度最快,也最能帮助你理解 Claude 实际如何发现并采用指令。之后我通常建议按以下路径学习:Skill → Hook → MCP 服务器 → 子智能体。
简短的结语
之所以需要四种机制,是因为“扩展 AI 智能体”并非一个问题,而是四个:知识、行动、隔离与保证。每个问题都需要不同形态的解决方案,而进展顺利的项目,往往会尽早选对这种形态。
如果今天要在一个新代码库中配置 Claude Code,我建议采用与决策树相同的顺序。先用 CLAUDE.md 提供始终开启的上下文;为反复出现的约定添加一两项 Skills;为绝不能遗忘的规则添加一个 Hook;当 Claude 显然无法访问某个系统时,再添加一台 MCP 服务器;只有当繁重任务开始污染上下文时,才启用子智能体。
其中三项改动本周就能交付,第四项等真正需要时再做。这才是正确的节奏。
接下来去哪里
搭好这四种基础机制后,要满足严肃应用的需要,还可以再补上两个部分。
• Claude Code Ultraplan 将规划阶段迁移到云端;当任务规模大到难以在终端中舒适规划时,这一点就很重要。
• Claude Code Outcomes 增加了按评分量规进行的检查,让智能体的工作依据真实标准接受评判,而不是只看“我觉得没问题”。
• 如果还在挑选主要的 AI 编程工具,值得阅读 Claude Code 与 Cursor 对比。
• 当你已经确定 Skill 是适合当前工作的基础机制时,可以通过 Claude Code Skills 实践指南逐步学习如何编写 SKILL.md。
当前版本准确性说明(检查于 2026-07-21)
• 本文来自 Muhammad Moeed 的个人技术博客,并非 Anthropic 官方文章。
• “知识、行动、隔离、保证”这四个词构成的模型是一种实用的启发式框架,但各机制之间的边界没有原文描述得那么绝对。
• Skills 并不局限于被动指令。它们还可以捆绑脚本和参考资料、注入动态 Shell 输出、预先批准工具、限制工具、附加生命周期 Hooks,以及在分叉的子智能体上下文中运行。
• MCP 并非只有能力、没有指令。该协议会暴露工具、资源与提示词;工具描述和服务器提供的指令同样会影响模型如何使用这些能力。
• 子智能体的确会从独立上下文开始,而不是复制整段聊天。不过,自定义子智能体会载入项目上下文,可以预加载 Skills 与 MCP 服务器、使用持久记忆,也可以带着此前的对话记录恢复运行。
• Hooks 并不局限于 Shell 命令。目前的处理程序包括 command、HTTP、MCP tool、prompt 与 agent。Prompt 和 agent Hooks 会调用模型,也可能消耗 Token。
• 匹配的 Hook 会由运行框架分派,但“保证”和“真正无法发生”的说法过于绝对。Hooks 可能失败、超时、被禁用或配置错误,而且并非每种事件都支持阻断。对于某些强安全边界,权限拒绝规则与沙箱是更稳妥的选择。
• 原文的 PreToolUse 示例声称任意非零退出码都会阻止 Write。按照当前 Hook 语义,只有退出码 2 表示阻断错误;其他非零退出码属于非阻断错误。此外,防护脚本还必须解析并验证 JSON 格式的工具输入。
• 如果确实要求每次编辑后都运行 Linter,那么使用 PostToolUse Hook 更自然。当 Lint 只是由模型自行选择的工作流步骤,而非自动强制执行时,才适合使用 Skill。
• 密钥应避开 Skills 与智能体定义,但只使用环境变量并不能构成完整的密钥处理策略:还必须限制并脱敏子进程、Hooks、MCP 服务器、日志以及模型可见的工具结果。