给插件添加组件
插件能包含的各类组件及示例:Skill、命令、智能体、Hook、MCP 与 LSP 服务器、可执行文件、默认设置、主题与输出风格、channel、monitor,以及用户配置对话框、路径变量和依赖安装。
插件的组件决定它给 Claude Code 增加了什么。每种组件在插件根下有固定的位置,通常只需要把文件放对地方,Claude Code 在插件启用时自动发现它们。本页为每种组件给出:它的文件放在插件的哪里、一个能通过校验的示例、插件加载后用户看到什么,以及改变默认位置的清单键。你的插件需要哪些就加哪些,没有哪个是必需的。下面的版本要求以官方为准。
| 组件 | 位置 | 说明 |
|---|---|---|
| Manifest | .claude-plugin/plugin.json | 插件元数据和配置;Anthropic 目录要求提供 |
| Skill | skills/<name>/SKILL.md | Claude 在相关时加载、用户也可以当作 /<plugin>:<name> 命令运行的指令 |
| 命令 | commands/*.md | 平铺的 Markdown 文件,是 Skill 的较旧形式;新插件用 skills/ |
| 智能体 | agents/*.md | 子智能体定义,Claude 可以委派给它们 |
| Hook | hooks/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 服务器 |
| Monitor | monitors/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_toolhook 里指名该服务器时用同样的形式。 - 工具名:
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 没触发时怎么办。