Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

扩展系统

Claude Code 的扩展能力由六个不同概念组成:Command、Skill、Tool、Plugin、MCP 和 Hook。它们可以被同一个 Plugin 打包,也可能在一次任务中互相调用,但职责并不重叠。

最容易记的边界是:Command 是用户入口,Skill 是可加载的方法说明,Tool 是模型动作协议,Plugin 是分发容器,MCP 是外部能力协议,Hook 是生命周期拦截器。

flowchart TD
    P["Plugin 分发容器"] --> C["Commands"]
    P --> S["Skills"]
    P --> A["Agents"]
    P --> H["Hooks"]
    P --> M["MCP Servers"]
    P --> L["LSP / 设置 / 输出样式"]

    U["用户输入 /name"] --> C
    C -->|Prompt 型| Q["编译为消息并查询模型"]
    C -->|本地型| X["直接改变 CLI / UI 状态"]
    S --> ST["Skill 工具或 /skill 调用"]
    ST -->|inline| Q
    ST -->|fork| F["独立 Agent 上下文"]
    M --> T["动态 Tools / Prompts / Skills / Resources"]
    T --> Q
    H --> E["会话、Prompt、Tool、Agent、压缩等事件"]
    E --> Q

六个概念的职责边界

模块谁触发核心产物是否直接成为 API tool典型用途
Command用户输入 slash command,或 CLI 自身本地操作结果、UI,或一组要交给模型的消息配置、登录、会话管理、把预制 Prompt 送入对话
Skill用户通过 /skill,或模型通过 Skill 工具一段带元数据、资源和约束的任务方法Skill 本身不直接成为独立 tool;统一经 Skill 工具调用复用领域知识和多步工作流
Tool模型生成 tool_use一个有名称、描述和输入 schema 的动作协议文件、Shell、网页、Agent、MCP 等可执行动作
Plugin用户/组织安装并启用一组 Commands、Skills、Agents、Hooks、MCP、LSP、设置等组件Plugin 本身不是 tool分发、版本、依赖和策略管理
MCP连接外部进程或服务动态 Tools、Prompts/Skills、Resources其 tools 会动态成为 API tools接入外部系统和认证数据
Hook生命周期事件自动触发允许/拒绝、改写输入、附加上下文、停止、重试或通知安全策略、自动化、审计与上下文注入

这套分层避免了一个常见架构错误:把“扩展包”“模型能力”“用户命令”“生命周期回调”都叫作插件,然后让权限和执行顺序变得不可解释。

Command:CLI 的入口层

Command 首先是 Claude Code 的控制平面。它有三种形态:

  • 本地 Command:直接完成无模型参与的本地操作;
  • 本地 UI Command:打开交互界面并回传结果;
  • Prompt Command:把预制内容、参数和附件编译成消息,再进入查询循环。

因此 /config/mcp 一类控制命令与 Skill 看起来都使用 slash 入口,但架构语义不同。模型只能通过 Skill 调用允许模型调用的 Prompt 型能力,不能猜测并执行任意内置 Command。

Command 还有独立的可用性边界:认证提供方、交互/非交互模式、功能开关、远程模式和是否对用户隐藏。它们决定“用户能否看到和运行这个入口”,不等同工具执行权限。

Skill:声明式能力包

Skill 本质上是带 frontmatter 的 Markdown 能力包。正文描述如何完成任务,frontmatter 描述何时可见、谁能调用、用什么模型、允许哪些工具、是否 Fork、绑定哪些 Hook,以及适用哪些路径。

Claude Code 不在启动时把所有 Skill 正文塞进 Prompt。发现阶段只把名称、描述和适用条件等轻量元数据暴露给模型;只有被调用或被 Agent 预加载时,正文才进入 Agent 上下文。这样 Skill 数量不会直接变成常驻 Prompt 成本。

stateDiagram-v2
    [*] --> 发现来源
    发现来源 --> 元数据索引: 读取名称、描述、when_to_use、paths
    元数据索引 --> 相关提醒: 自动匹配任务或路径
    元数据索引 --> Skill调用: 用户或模型显式选择
    Skill调用 --> Inline: 默认
    Skill调用 --> Fork: context=fork
    Inline --> 当前对话后续回合
    Fork --> 独立上下文与预算
    Fork --> 当前对话后续回合: 返回摘要结果
    元数据索引 --> Agent预加载: Agent frontmatter 声明 skills
    Agent预加载 --> Agent初始上下文

Skill 从哪里被发现

当前源码把多个来源汇入统一 Command/Skill 索引:

  • 组织托管的 Skills;
  • 用户级 Skills;
  • 项目及从工作目录向上的 .claude/skills
  • --add-dir 指定目录;
  • 兼容旧版 .claude/commands
  • Bundled Skills;
  • 已启用 Plugin 提供的 Skills;
  • 条件启用的 MCP Skills;
  • 会话中因访问嵌套目录而动态发现的 Skills。

同一个实体可能经符号链接或多条目录路径被发现,加载器按真实路径去重。裸模式会跳过大部分自动目录遍历,只保留显式来源与单独注册的 Bundled 能力;组织策略还可以把 Skills 限制为仅 Plugin 来源。

路径条件与嵌套发现

Skill 可以声明 paths。这类 Skill 启动时只进入候选集合;当 ReadEditWrite 触及匹配文件后才激活。

文件操作还会从目标文件目录向当前工作目录回溯,发现更深层的 .claude/skills。越靠近目标文件的 Skill 具有更具体的作用域。新发现结果通过尾部提醒进入后续回合,而不是回写旧 Prompt。

用户可调用与模型可调用是两条开关

user-invocable 决定用户能否用 slash 入口看到并调用;disable-model-invocation 决定模型能否通过 Skill 工具选择它。

因此可以存在:只给用户的 Skill、只给模型的 Skill、两者都可调用的 Skill。隐藏 UI 不等于禁止模型调用,反之亦然。

Skill 的三种进入 Agent 的方式

方式上下文归属正文如何进入allowed-toolsSkill frontmatter Hooks
Inline 调用当前 Agent作为元用户消息加入当前对话追加为当前调用后的权限允许规则在当前会话作用域注册
Fork 调用新的子 Agent作为 Fork 的起始任务追加到 Fork 的权限上下文当前快照的 Fork 分支不经过 Inline 的 Skill Hook 注册路径
Agent 预加载新 Agent 的初始上下文Agent 定义的 skills 列表并行加载正文不自动应用不自动注册

这个差异非常关键。**预加载 Skill 等于给 Agent 教材,不等于执行一次 Skill。**它不会自动获得 Skill 声明的权限,也不会因为正文被放入初始上下文就安装 Skill Hook。

Fork Skill 也不是“大号 Inline”:它选择一个 Agent 定义,在独立上下文和 Token 预算中运行,只把最终结果带回调用方。它适合输出噪音大、步骤多、需要隔离上下文的流程。

allowed-tools 是权限增量,不是能力安装

Skill 的 allowed-tools 会把声明的规则加入该次 Inline 或 Fork 的允许上下文。它解决的是“这个已知工作流被允许使用哪些现有工具”,而不是:

  • 向工具池安装一个不存在的工具;
  • 让被 Agent 白名单移除的工具重新可见;
  • 覆盖更高优先级的 deny;
  • 绕过 Sandbox、路径边界或外部服务授权。

这让 Skill 可以携带完成工作所需的最小权限提示,同时仍服从系统的硬边界。

Skill Hooks 是有生命周期的临时扩展

Inline Skill 被实际调用时,它的 Hooks 注册到当前会话作用域,并与 Skill 根目录绑定。Hook 策略可以限制非受信来源注册 Hook;压缩时,已调用 Skill 的必要内容会按 Agent 作用域恢复,防止不同 Agent 的 Skill 状态互相泄漏。

Hooks 不属于 Skill 正文。正文影响模型决策,Hook 在确定事件点影响运行时决策;两者必须分别审计。

Plugin:组件的分发与治理容器

Plugin 的职责不是“运行”,而是把多个组件作为一个可安装、可启用、可升级和可治理的单元交付。

一个 Plugin 可以声明:

  • Commands 与 Skills;
  • Agent 定义;
  • Hooks;
  • MCP Servers 与 channel;
  • LSP Servers;
  • 输出样式;
  • 默认设置和用户可配置项。

加载过程先解析 manifest 与约定目录,再合并 marketplace 元数据、启用状态和组织策略。缺少依赖的 Plugin 会从 enabled 集合降级;组织可以强制禁用 Plugin,跨 marketplace 自动依赖还有单独信任边界。

Plugin 组件会带来源命名空间,MCP Server 也会增加 Plugin scope,避免两个 Plugin 的同名 Skill、Command 或 Server 静默覆盖。热重载通过缓存失效重新装配组件,而不是让旧 Hook、旧 MCP 和新命令各自漂移。

MCP:独立于 Plugin 的运行时协议

Plugin 可以携带 MCP 配置,但 MCP 不依赖 Plugin 才能存在。用户、项目、SDK、Plugin 或动态 Agent 都可以提供 MCP Server 配置。

连接成功后,一个 Server 可以贡献:

  • 动态工具;
  • Prompt Commands;
  • 条件启用的 MCP Skills;
  • Resources;
  • Server instructions 与通知。

需要认证时,真实工具尚不可见,运行时可先暴露该 Server 的认证工具。工具列表变化后会刷新工具池,并通过 ToolSearch 增量提醒模型。

所以 Plugin 解决“如何分发一组能力”,MCP 解决“如何在运行时与外部能力服务协商”。

Hook:不经过模型选择的拦截平面

Hook 在事件发生时自动运行,来源可以是设置、Plugin、Skill、Agent frontmatter 或 SDK 回调。它不是 Tool,模型不能通过编造一个 Hook 名称来调用它。

flowchart LR
    I["生命周期事件"] --> M["按事件与 matcher 选 Hook"]
    M --> H["命令 / Prompt / Agent / HTTP / 回调 Hook"]
    H --> O{"结果"}
    O --> A["附加上下文或系统提示"]
    O --> P["允许、询问、拒绝或改写输入"]
    O --> S["停止、重试、异步唤醒"]
    O --> R["替换 MCP 输出"]

Hook 覆盖的事件包括会话开始与结束、用户 Prompt 提交、工具调用前后与失败、权限请求和拒绝、Subagent 开始与结束、压缩前后、通知、MCP elicitation、任务与队友状态、工作目录和文件变化等。

Hook 能做什么

  • PreToolUse 可以建议允许、询问或拒绝,附加上下文,也可改写工具输入;
  • PermissionRequest 可以对真正的权限询问给出结构化决定;
  • PostToolUse 可以附加结果上下文,对 MCP 结果还可以给出替换值;
  • 失败、停止、压缩、Subagent 等事件可以阻止继续或补充后续消息;
  • 异步 Hook 可以在后台完成并选择是否重新唤醒会话。

Hook 的“允许”不是超级权限。工具仍要经过设置中的 deny/ask 规则;Hook 也不能绕过 Sandbox。这个优先级防止低信任扩展通过一个 PreToolUse 回调推翻管理员策略。

Hook 的作用域与信任

设置级 Hook、Plugin Hook、Skill Hook 和 Agent Hook 会在统一事件总线上合并,但保留来源根目录和会话/Agent 作用域。相同命令只在相同来源上下文内去重,避免两个 Plugin 使用相同模板时互相吞掉。

Plugin-only 或 managed-only 策略会在注册点阻止不受信的 Skill/Agent Hooks,而不是事后笼统禁掉所有运行时 Hook。这样管理员提供的 Plugin Hook 与 SDK 内部 Hook仍能正常工作。

一次 Plugin Skill 调用穿过哪些边界

sequenceDiagram
    participant PL as Plugin加载器
    participant IDX as Command/Skill索引
    participant MD as 模型
    participant ST as Skill工具
    participant HK as Hook总线
    participant Q as 查询循环

    PL->>IDX: 注册带命名空间的Skill元数据
    IDX-->>MD: 只暴露相关Skill名称与说明
    MD->>ST: 选择Skill与参数
    ST->>ST: 校验来源、可调用性与执行上下文
    alt Inline
        ST->>HK: 注册该Skill的会话级Hooks
        ST->>Q: 追加正文、附件与权限增量
    else Fork
        ST->>Q: 创建独立Agent上下文与权限增量
        Q-->>ST: 返回Fork最终结果
    end
    Q-->>MD: 继续正常工具回合

这条链路说明,Skill 不是直接执行 Markdown。它先经过来源发现和调用资格,再选择上下文形态,最后才影响查询循环;Plugin、Hook 和权限各有自己的边界。

设计结论

Claude Code 扩展架构最值得借鉴的不是“支持很多插件”,而是把扩展拆成六个互相正交的维度:

  • 入口与 UI 属于 Command;
  • 方法与上下文属于 Skill;
  • 动作协议属于 Tool;
  • 分发与治理属于 Plugin;
  • 外部能力协商属于 MCP;
  • 生命周期控制属于 Hook。

当这些维度分开后,系统才能分别回答:谁安装、谁看见、谁触发、在哪个上下文运行、获得什么权限、何时被拦截。

源码定位

  • Command 类型、可用性与统一装配:src/types/command.tssrc/commands.ts
  • slash command 到消息:src/utils/processUserInput/processSlashCommand.tsx
  • Skill 来源、frontmatter、路径条件与动态发现:src/skills/loadSkillsDir.ts
  • Skill 调用、Inline 与 Fork:src/tools/SkillTool/SkillTool.tssrc/utils/forkedAgent.ts
  • Agent Skill 预加载:src/tools/AgentTool/loadAgentsDir.tssrc/tools/AgentTool/runAgent.ts
  • Plugin manifest 与组件模型:src/types/plugin.tssrc/utils/plugins/schemas.ts
  • Plugin 加载、策略与依赖:src/utils/plugins/pluginLoader.tssrc/utils/plugins/pluginPolicy.tssrc/utils/plugins/dependencyResolver.ts
  • Plugin Commands/Skills/Hooks/MCP:src/utils/plugins/loadPluginCommands.tssrc/utils/plugins/loadPluginHooks.tssrc/utils/plugins/mcpPluginIntegration.ts
  • MCP 连接与能力适配:src/services/mcp/client.tssrc/services/mcp/config.ts
  • Hook 协议与事件编排:src/schemas/hooks.tssrc/types/hooks.tssrc/utils/hooks.tssrc/services/tools/toolHooks.ts