Skip to content
FunCoding

Search

Search docs, agents, posts, Skills and MCP servers

给插件添加组件

插件能包含的各类组件及示例:Skill、命令、智能体、Hook、MCP 与 LSP 服务器、可执行文件、默认设置、主题与输出风格、channel、monitor,以及用户配置对话框、路径变量和依赖安装。

插件的组件决定它给 Claude Code 增加了什么。每种组件在插件根下有固定的位置,通常只需要把文件放对地方,Claude Code 在插件启用时自动发现它们。本页为每种组件给出:它的文件放在插件的哪里、一个能通过校验的示例、插件加载后用户看到什么,以及改变默认位置的清单键。你的插件需要哪些就加哪些,没有哪个是必需的。下面的版本要求以官方为准。

组件位置说明
Manifest.claude-plugin/plugin.json插件元数据和配置;Anthropic 目录要求提供
Skillskills/<name>/SKILL.mdClaude 在相关时加载、用户也可以当作 /<plugin>:<name> 命令运行的指令
命令commands/*.md平铺的 Markdown 文件,是 Skill 的较旧形式;新插件用 skills/
智能体agents/*.md子智能体定义,Claude 可以委派给它们
Hookhooks/hooks.json在 Claude Code 生命周期节点运行的命令,格式与设置里的 hooks 相同
MCP 服务器.mcp.json插件启用期间 Claude Code 连接的工具服务器
LSP 服务器.lsp.json告诉 Claude Code 用哪个命令启动语言服务器、处理哪些扩展名
可执行文件bin/目录被加进 Bash 工具 shell 的 PATH
默认设置settings.json插件启用时生效的默认设置
主题与输出风格themes/、output-styles/向 /theme 和 /output-style 贡献选项
Channel清单里声明把外部事件推进会话的 MCP 服务器
Monitormonitors/monitors.json在后台运行并把输出行反馈给 Claude 的监视器

逐类添加组件

Skills

Skill 是一个 SKILL.md 文件,Claude 可以在它的描述与任务匹配时加载它;用户也可以把它当作命令运行。把每个 skill 保存在 skills/ 下它自己的目录里:

my-plugin/
├── .claude-plugin/
│   └── plugin.json
└── skills/
    └── review/
        └── SKILL.md

给 SKILL.md 一个 description,让 Claude 知道什么时候用它:

---
description: Reviews a pull request for style and test coverage. Use when asked to review code.
---

Review the changed files. Report style problems first, then missing tests.

加载插件之后,/my-plugin:review 运行该 skill。命令名以及谁能调用它遵循这些规则:

  • 命令名:/<plugin>:<directory>,所以 my-plugin 里的 skills/review/SKILL.md 是 /my-plugin:review。如果你在 frontmatter 里设置 name,它替换最后一段,插件前缀保留。
  • 谁来调用:Claude、用户或两者,由 frontmatter 控制(见 Skills 页的「控制谁能调用 skill」)。

你也可以把 skills 放在默认的 skills/ 目录之外:

  • 额外目录:把它们列在 skills 清单键里。它们加到默认的 skills/ 扫描之上而不是替换它(与 commands 和 agents 不同)。
  • 插件根目录的单个 skill:没有 skills/ 目录也没有 skills 清单键时,插件根目录的 SKILL.md 作为一个 skill 加载。要在它的 frontmatter 里设置 name,否则从市场安装时会以它的缓存目录而不是你的插件来命名这个 skill。

要在插件里包含指令,把它们写成 skill。Claude Code 不加载插件根目录的 CLAUDE.md,claude plugin validate 会警告 CLAUDE.md at the plugin root is not loaded as project context。如果一条规则必须每次都成立(如阻止编辑受保护文件),把它作为 hook 而不是 skill 加进插件;两者怎么选,见功能概览「比较相似功能」里的 Hook vs Skill 标签页。frontmatter 字段和辅助文件见 Skills 页。

命令

命令是用户按名称运行的单个 Markdown 文件,如 /my-plugin:about。注意:命令是较旧的格式,skills 在新工作里取代了它:skill 以同样的方式按名称运行,还可以在自己的目录里带辅助文件。把 commands/ 留给你从 .claude/commands/ 迁移过来的文件。

把命令保存在 commands/<file>.md,它成为 /<plugin>:<file>;子目录增加一段,所以 commands/db/migrate.md 是 /my-plugin:db:migrate。命令文件使用与 skills 相同的 frontmatter。

在清单里定义命令

只有当你想把命令文件放在 commands/ 之外,或想在 plugin.json 里定义一个无需单独 Markdown 文件的短命令时才需要这个。设置 commands 清单键,Claude Code 就读取它而不是扫描 commands/。该键接受路径、路径数组,或把每个命令名映射到 source 文件或内联 content 的对象。这个清单内联定义 /my-plugin:about,没有 Markdown 文件:

{
  "name": "my-plugin",
  "commands": {
    "about": {
      "content": "Summarize what this repository does in three sentences.",
      "description": "Summarize the repository"
    }
  }
}

加载插件并在会话里运行 /my-plugin:about 确认它已加载;完整的键语法见插件参考。

智能体

子智能体是一个独立的助手,有自己的指令和上下文窗口,Claude 可以把任务委派给它。agents/ 下的每个 Markdown 文件定义一个:

---
name: security-reviewer
description: Reviews code changes for security issues. Use after edits to authentication or input handling.
model: sonnet
---

You are a security reviewer. Read the changed files and report injection, authentication, and secrets-handling risks.

这个智能体名为 my-plugin:security-reviewer,用户可以用 @agent-my-plugin:security-reviewer 显式调用它。名称形式是 <plugin>:<name>,其中 <name> 来自 frontmatter,没有时取文件名。agents 清单键替换 agents/ 的扫描。

在子文件夹里组织智能体

你可以把插件的智能体文件放在 agents/ 的子文件夹里。Claude Code 递归加载它们,并用冒号连接插件名、每个子文件夹名和文件名,形成智能体的限定名。例如,名为 my-plugin 的插件里 agents/review/security.md 加载为 my-plugin:review:security。两个设置会改变这个名称:frontmatter 的 name 只替换文件名,所以 agents/review/security.md 里的 name: audit 加载为 my-plugin:review:audit;清单的 agents 字段里列出的文件不带子文件夹名加载,所以 "agents": "./custom/review/security.md" 加载为 my-plugin:security。

插件智能体里的 frontmatter 字段

插件智能体的 frontmatter 遵循这些规则:

  • 支持的字段:name、description、model、effort、maxTurns、tools、disallowedTools、skills、memory、background、omitClaudeMd、isolation、color,以及 experimental 的 cacheTtl 键;isolation 唯一有效的值是 "worktree"。
  • 被忽略的字段:permissionMode、hooks、mcpServers 和 initialPrompt。智能体文件自己不能添加 hooks 或 MCP 服务器,所以把它们作为插件的 hooks 和 MCP 服务器添加。
  • 无法解析的 frontmatter:智能体仍会加载,但所有字段都被忽略;它以文件名命名,描述是 Agent from my-plugin plugin。在 shell 里运行 claude plugin validate 找到这些文件。

每个字段做什么以及优先级规则,见子智能体页。

Hooks

Hook 在 Claude Code 生命周期的某一点自动运行某些东西,例如每次文件编辑之后:一个 shell 命令、一个 HTTP 请求、一次 MCP 工具调用、给模型的提示或一个子智能体。把插件的 hooks 保存在插件根目录的 hooks/hooks.json 里,放在顶层 "hooks" 键下,形状与 settings.json 里的 hooks 对象相同,所以你可以原样复制现有的设置 hook。这个 hook 在每次 Write 或 Edit 之后运行一个捆绑的脚本:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/format.sh\""
          }
        ]
      }
    ]
  }
}

把脚本保存为 scripts/format.sh 并使它可执行。加载插件并让 Claude 编辑一个文件。退出码为 0 的 PostToolUse hook 在转录里什么都不显示,所以通过调试日志,或通过脚本自己改变的内容来确认它运行了。hooks/hooks.json 里的 hooks 和 hooks 清单键里的 hooks 都会加载;每个事件及其负载见 hooks 参考。

要把 hooks 写成在 Claude Code 内部运行、能绘制其界面的 JavaScript 函数,在同一个 hooks/hooks.json 里的 modules 键下列出模块文件;带有它的插件就是 mod(见「创建 mod」)。

插件 hooks 何时触发

插件的 hooks 不会等待插件的某个 skill 或命令被使用:Claude Code 在会话加载插件时注册它们,此后它们在各自的事件上触发。要限制 hook 何时运行,收窄它的 matcher。如果 hook 从不触发,见插件排障页的「hooks that don't fire」。

环境、引号和匹配 MCP 工具

hook 的环境、${CLAUDE_PLUGIN_ROOT} 的引号,以及对插件自己 MCP 工具的 matcher,规则如下:

  • 环境:每个 hook 进程的环境里都收到 CLAUDE_PLUGIN_ROOT 和 CLAUDE_PLUGIN_DATA,以及每个用户配置值对应的 CLAUDE_PLUGIN_OPTION_<KEY>,所以你的脚本可以从那里读取它们。
  • 引号:command 没有 args 时它通过 shell 运行,所以像上面 hooks/hooks.json 例子那样把 ${CLAUDE_PLUGIN_ROOT} 路径包在双引号里,让展开的路径保持为一个 shell 词;改传 args 时,每个元素作为一个参数传递,不经过 shell,无需加引号(见 hooks 页的 exec 形式与 shell 形式)。
  • 匹配插件自己的 MCP 工具:本插件声明的 MCP 服务器里的工具名为 mcp__plugin_<plugin>_<server>__<tool>,所以在 matcher 里写出这个完整名称;只写服务器名的 matcher 永远不会触发。

MCP 服务器

MCP 服务器给 Claude 来自外部系统的工具。在插件根目录的 .mcp.json 里声明它,形状与项目的 .mcp.json 相同。这个 .mcp.json 声明一个名为 db 的服务器:

{
  "mcpServers": {
    "db": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"]
    }
  }
}

你也可以省略 mcpServers 包装,把 db 放在文件的顶层。加载插件并运行 /mcp,确认服务器显示为 plugin:my-plugin:db。claude plugin validate 检查 .mcp.json,并把 Claude Code 加载时会丢弃的服务器条目报告为错误(需要 Claude Code v2.1.281 或更高)。加载时坏条目在哪里出现,见插件排障页的「MCP servers that don't start」。mcpServers 清单键接受内联服务器映射、JSON 文件路径,或这些的数组;清单里的服务器与 .mcp.json 里的同名时,清单服务器替换它。

触达 claude.ai 和 Cowork 上的用户

本地 stdio 服务器(如上面的 db 服务器)在 Claude Code 里以及在 Claude Desktop 应用里运行在你机器上的 Cowork 会话里运行,但不在 claude.ai 上运行。要也触达那里的用户,用它的 https:// URL 引用远程服务器,claude.ai 和 Cowork 会把它作为连接器提供给用户(见「把 MCP 连接器与它的 skill 捆绑」)。

服务器名、工具名和重新加载

服务器的名称、变量替换和重新加载行为遵循这些规则:

  • 服务器名:plugin:<plugin>:<server>,所以 my-plugin 里的 db 服务器在 /mcp 里是 plugin:my-plugin:db;在 mcp_tool hook 里指名该服务器时用同样的形式。
  • 工具名:mcp__plugin_<plugin>_<server>__<tool>,所以那个 db 服务器上的 query 工具是 mcp__plugin_my-plugin_db__query;在权限规则和 hook matcher 里用这个名称。
  • 替换:${CLAUDE_PLUGIN_ROOT} 和其他路径变量在 command、args 和 env 里被替换;args 里不需要引号,因为每个元素作为一个参数传递。
  • 重新加载:当用户运行 /reload-plugins 且重新加载适用时,配置未变的服务器保持它的连接;配置改变的服务器重新连接,被你移除的服务器断开。

包含打包的 MCPB 服务器

mcpServers 键也接受作为 MCPB 文件打包的服务器,扩展名是 .mcpb 或较旧的 .dxt。把该键指向文件,可以是插件内的路径或 https:// URL:

{
  "name": "my-plugin",
  "mcpServers": "./servers/db.mcpb"
}

服务器的名称取自包的清单里的 name。包自己的清单可以在 user_config 块里声明服务器需要用户提供的设置。带有必需设置而没有已保存值的捆绑服务器不会启动,/plugin 的 Errors 标签页显示 Bundled MCP server "<name>" was not started: it needs configuration。用户用两种方式之一提供值:在 /plugin 里,在 Installed 标签页选择该插件并选 Configure;或在安装时从 shell,向 claude plugin install 传 --config <server>.<key>=<value>(需要 Claude Code v2.1.285 或更高,且只适用于打包在插件内的包)。传输方式和认证见 MCP 页。

LSP 服务器

LSP 服务器给 Claude 一种语言的诊断和代码导航。如果官方的代码智能插件已经覆盖你的语言,安装那个而不是自己写;否则在插件根目录的 .lsp.json 里声明服务器:

{
  "gopls": {
    "command": "gopls",
    "args": ["serve"],
    "extensionToLanguage": {
      ".go": "go"
    }
  }
}

该文件把每个服务器名直接映射到它的配置,映射外面没有包装对象。command 是二进制文件的名称,参数在 args 里;extensionToLanguage 至少需要一个扩展名,每个以 . 开头。claude plugin validate 不读取这个文件:任何条目无效时,整个文件在加载时被跳过,/plugin 的 Errors 标签页出现 Invalid LSP server config for ".lsp.json"。

你的插件配置连接,但不安装服务器二进制文件,并且每个文件扩展名只有一个服务器:

  • 缺失二进制:Claude Code 按名称从用户的 PATH 启动 command。二进制不在那里时,服务器启动失败,claude --debug 记录 LSP server <name> failed to start。
  • 扩展名冲突:两个已启用的服务器声明同一个扩展名时,先注册的处理那些文件,另一个不用于它们,不论两个服务器来自一个插件还是两个;/plugin 的 Errors 标签页显示警告 LSP server "<name>" is not used for <ext> files。

lspServers 清单键接受内联的同样映射、JSON 文件路径或这些的数组,它的服务器加到 .lsp.json 里的服务器之上;清单服务器与 .lsp.json 里的同名时,清单服务器替换它。transport、超时、重启和其他字段见插件参考的 lspServers。把日志输出发到 stderr 而不是 stdout:Claude Code 把服务器的 stdout 只当作协议消息读取,接受最大 64 KiB 的消息头和最大 32 MiB 的消息体。Claude Code 会断开超出任一限制或向 stdout 写入非协议输出的服务器,并把该断开算作 restartOnCrash 和 maxRestarts 的一次崩溃;用 --debug 运行时,Claude Code 会向调试日志写入点名原因的错误。

可执行文件

插件启用期间,插件根目录 bin/ 里的文件在 Bash 工具 shell 的 PATH 上,所以 Claude 可以把它们作为裸命令运行。添加一个可执行脚本:

#!/bin/bash
echo "hello from my-plugin"

用 chmod +x bin/hello-plugin 使它可执行并加载插件;当你让 Claude 运行 hello-plugin 时,Bash 工具的结果显示该脚本的输出。插件的 bin/ 目录排在用户自己的 PATH 条目之后,所以插件不能遮蔽 git、ls 或其他系统命令。claude.ai 和 Cowork 不安装带有顶层 bin/ 目录的插件,包括你通过 claude.ai 组织设置分发的。

默认设置

要设置插件启用期间适用的默认值,在插件根目录添加 settings.json,或把同样的对象内联放在 settings 清单键里。有两个键生效,agent 和 subagentStatusLine,其他每个键都被丢弃。设置 agent 以把插件自己的某个智能体作为主线程运行:

{
  "agent": "security-reviewer"
}

加载插件并开始会话;Claude 随后在主对话里以 security-reviewer 智能体的系统提示和模型作答。该键控制的一切见设置参考里的 agent 设置。同一个键在多个地方设置时,这些规则决定哪个值适用:

  • 文件优先于清单:两者都存在且 settings.json 设置了至少一个受支持的键时,settings.json 适用,清单的 settings 被忽略。
  • 用户设置优先于插件默认值:跨设置来源,插件默认值是最低的一层,所以用户自己在 ~/.claude/settings.json 里的 agent 会覆盖你的。
  • 两个插件设置同一个键:后加载的插件的值适用,claude --debug 记录 overrides setting。

subagentStatusLine 的形状见状态行页的子智能体状态行。

主题与输出样式

插件可以包含颜色主题和输出样式,二者出现在与用户自己的相同的选择器里。对任一个,设置清单键都会替换文件夹扫描。

组件保存为格式出现在清单键
主题themes/<slug>.json用户在 ~/.claude/themes/ 里写的自定义主题文件格式/theme,在文件的 name 下experimental.themes
输出样式output-styles/<name>.md自定义输出样式格式,带 name 和 description frontmatter/output-style,显示为 <plugin>:<name>outputStyles

插件主题是只读的,所以用户在 /theme 里编辑它时,编辑会作为副本保存在他们自己的主题目录里。这个主题在暗色预设上重新给提示强调色和错误文字上色:

{
  "name": "Dracula",
  "base": "dark",
  "overrides": {
    "claude": "#bd93f9",
    "error": "#ff5555"
  }
}

Channels

Channel 让聊天应用这样的外部系统向会话发送消息。在插件里,channel 是 MCP 服务器之一,加上一个绑定到它、并能提示填写自己配置的 channels 条目。这个清单把 channel 绑定到 telegram 服务器,并要求一个 bot 令牌:

{
  "name": "my-plugin",
  "mcpServers": {
    "telegram": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"],
      "env": { "BOT_TOKEN": "${user_config.bot_token}" }
    }
  },
  "channels": [
    {
      "server": "telegram",
      "userConfig": {
        "bot_token": {
          "type": "string",
          "title": "Bot token",
          "description": "Telegram bot token",
          "sensitive": true
        }
      }
    }
  ]
}

server 必须匹配 mcpServers 里的某个键;每个 channel 的 userConfig 采用与顶层 userConfig 键相同的形状。服务器必须实现什么以及用户如何启用 channel 插件,见 Channels 参考的「Package as a plugin」;字段表见插件参考的 channels。

Monitors

Monitor 是在整个会话期间在后台运行的 shell 命令;它打印的内容以通知的形式到达 Claude,所以 Claude 可以对日志或状态变化作出反应而无需被要求去盯着。把条目保存在 monitors/monitors.json 里:

[
  {
    "name": "error-log",
    "command": "tail -F ./logs/error.log",
    "description": "Application error log"
  }
]

命令在 shell 里、在会话启动所在的工作目录中运行。monitor 的命令在从哪里启动、能引用什么方面有限制:

  • 仅限交互会话:插件 monitors 在交互会话里启动,在带 -p 标志的非交互模式里从不启动;它们也只在 Monitor 工具可用的地方启动。
  • 没有用户配置:command 能得到路径变量和环境里的 ${ENV_VAR},但永远没有 ${user_config.*}。引用它的 monitor 不会启动,monitor 进程也不接收 CLAUDE_PLUGIN_OPTION_<KEY>。
  • 会话中途禁用:如果你在会话中途禁用插件,Claude Code 不会停止已经在运行的 monitor,它们在会话结束时停止。

experimental.monitors 清单键接受内联的同一数组或 JSON 文件的路径,并替代 monitors/monitors.json 被读取。when 触发器和其他字段见插件参考的 monitors。

向用户请求配置值

在 userConfig 清单键里声明你的插件需要用户提供的值,使用户不必自己编辑 settings.json。每个选项出现在一个对话框里,以它的 title 为标签,description 在其下。对令牌或密码设置 "sensitive": true:对话框随后遮蔽输入,值存储在安全存储里而不是 settings.json。这个清单要求一个端点和一个令牌:

{
  "name": "my-plugin",
  "userConfig": {
    "api_url": {
      "type": "string",
      "title": "API URL",
      "description": "Base URL of your team's API"
    },
    "api_token": {
      "type": "string",
      "title": "API token",
      "description": "Token for your team's API",
      "sensitive": true
    }
  }
}

配置对话框何时出现

该对话框是交互式 /plugin 界面的一部分。用户做以下任一操作时,它会为尚未设置的任何选项打开:在 /plugin 里安装插件;在会话内运行 /plugin install <plugin>@<marketplace>;在 /plugin 的 Installed 标签页里启用插件。要随时打开同一个对话框,用户运行 /plugin configure <plugin>@<marketplace>。VS Code 扩展的管理插件对话框在安装之后以表单形式询问未设置的选项,插件那一行上的齿轮图标会再次打开带所有选项的表单。

claude plugin install 这个 shell 命令从不提示 userConfig 值。要从 shell 设置值,安装时把每个值作为 --config KEY=VALUE 传入,或之后把 JSON 对象通过管道传给 claude plugin configure --values-stdin。有选项仍未设置时,claude plugin install 打印一行 userConfig options not yet set;该行的确切文字见插件排障页的「The userConfig dialog never appears」。选项字段、每个值存储在哪里、组件如何引用已保存的值,以及哪些字段拒绝 ${user_config.*},见插件参考的「用户配置」。

引用插件路径并存储数据

你不知道插件会安装在哪里,所以通过这些变量而不是固定路径引用它的文件和数据。它们在 skill、命令和智能体内容里、在 hook 和 monitor 命令里,以及在 MCP 和 LSP 服务器配置里被替换,也被导出给 hook、MCP 和 LSP 进程:

  • ${CLAUDE_PLUGIN_ROOT}:插件的安装目录。每个版本有自己的缓存目录,所以插件更新时路径会变,不要在那里写状态。
  • ${CLAUDE_PLUGIN_DATA}:跨更新保留的目录,用于 node_modules、虚拟环境和缓存。它解析为 ~/.claude/plugins/data/<id>/,在第一次被引用时创建。
  • ${CLAUDE_PROJECT_DIR}:项目根目录,与 hooks 收到的值相同。

在数据目录路径里,<id> 是把插件标识符里字母、数字、_ 和 - 之外的每个字符替换为 - 的结果,所以 my-plugin@my-marketplace 变成 my-plugin-my-marketplace。在 Windows 上,被替换的路径使用正斜杠,这样 shell 不会把反斜杠读作转义。

把依赖安装到数据目录

对从市场安装的插件,Claude Code 在缓存插件时会自动安装符合条件的 Node.js 包依赖,所以你可能不需要自己安装。需要时,这个 SessionStart hook 在首次运行时,以及在更新改变了 package.json 之后再次,把 node_modules 安装进 ${CLAUDE_PLUGIN_DATA}:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "diff -q \"${CLAUDE_PLUGIN_ROOT}/package.json\" \"${CLAUDE_PLUGIN_DATA}/package.json\" >/dev/null 2>&1 || (cd \"${CLAUDE_PLUGIN_DATA}\" && cp \"${CLAUDE_PLUGIN_ROOT}/package.json\" . && npm install) || rm -f \"${CLAUDE_PLUGIN_DATA}/package.json\""
          }
        ]
      }
    ]
  }
}

第一个会话之后,~/.claude/plugins/data/<id>/node_modules 就存在了;MCP 服务器随后可以在它的 env 里把 NODE_PATH 设为 ${CLAUDE_PLUGIN_DATA}/node_modules。哪些字段替换哪个变量,见插件参考的环境变量。

下一步

  • 插件清单参考:plugin.json 的字段、路径规则和标准布局。
  • 用评测测试插件:检查你添加的组件是否按你的意图改变 Claude 的行为。
  • 发布与分发插件:给插件定版本并放进市场。
  • 排查插件:组件没加载或 hook 没触发时怎么办。