第 36 章:开发完整插件

个人公众号

源码验证日期:2026-05-15,基于 commit 0d81bb6

前面几章,我们学了工具的输入验证、权限规则、MCP 服务器接入和多 Agent 协作。这些是”零件”。现在,是时候把这些零件组装成一个完整的产品了。

Claude Code 的插件系统是零件的容器。一个插件可以包含:

  • Skills(技能)——可被 AI 调用的命令
  • Agents(代理)——特定类型的工作单元
  • Hooks(钩子)——在特定事件发生时执行的脚本
  • MCP Servers——外部工具服务器
  • Commands(命令)——用户可用的斜杠命令

这一章,我们从零开始开发一个完整的插件,覆盖从目录结构到验证分发的全流程。


路线图

1
2
3
4
5
6
7
graph LR
CH35["第 35 章<br/>构建多 Agent 协作"] --> CH36["🔧 第 36 章<br/>开发完整插件"]
CH36 --> CH37["第 37 章<br/>编写测试"]

style CH36 fill:#4CAF50,color:#fff,stroke:#333
style CH35 fill:#e8f5e9,stroke:#333
style CH37 fill:#e1f5fe,stroke:#333

插件的结构全景

一个标准的插件目录看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
db-assistant/
├── .claude-plugin/
│ └── plugin.json # 插件 manifest(身份证)
├── skills/ # 技能定义
│ ├── query-database/
│ │ └── SKILL.md # 技能的 Markdown 文件
│ └── migrate-schema/
│ └── SKILL.md
├── agents/ # Agent 定义
│ └── db-inspector.md # Agent 的 Markdown 文件
├── commands/ # 斜杠命令
│ └── db-status.md # 命令的 Markdown 文件
└── hooks/
└── hooks.json # 钩子配置

注意几个关键点:

  • plugin.json 必须放在 .claude-plugin/ 目录下——这是插件系统查找 manifest 的标准位置
  • Skills 必须是 skills/<name>/SKILL.md 的格式——每个技能是一个目录,目录下有 SKILL.md 文件
  • Agents 和 Commands 是平铺的 .md 文件——不需要子目录

编写 plugin.json manifest

plugin.json 是插件的身份证:

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "db-assistant",
"version": "1.0.0",
"description": "Database query and management tools for Claude Code",
"author": {
"name": "Your Name",
"email": "[email protected]"
},
"skills": ["skills/query-database", "skills/migrate-schema"],
"agents": ["agents/db-inspector"],
"hooks": "hooks/hooks.json"
}

源码中 manifest 的 schema 定义在 src/utils/plugins/schemas.ts

1
2
3
4
5
6
7
8
9
10
11
12
// 文件:src/utils/plugins/schemas.ts
export const PluginManifestSchema = lazySchema(() =>
z.object({
...PluginManifestMetadataSchema().shape, // name, version, description, author
...PluginManifestHooksSchema().partial().shape, // hooks 路径
...PluginManifestCommandsSchema().partial().shape, // commands 路径
...PluginManifestAgentsSchema().partial().shape, // agents 路径
...PluginManifestSkillsSchema().partial().shape, // skills 路径
...PluginManifestMcpServerSchema().partial().shape, // MCP 服务器配置
...PluginManifestSettingsSchema().partial().shape, // 插件设置
}),
)

每个子 schema 都是 .partial() 的——意味着 manifest 中所有的组件都是可选的。一个插件可以只提供 skills,也可以同时提供 skills、agents、hooks 和 MCP 服务器。

name 字段的命名规则:推荐使用 kebab-case(小写字母、数字、连字符),比如 db-assistant


创建 Skill

Skill 是 AI 可以调用的能力,定义在 Markdown 文件中。每个 Skill 由 YAML frontmatter(元数据)和 Markdown 正文(提示词)组成。

创建 skills/query-database/SKILL.md

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
description: "Execute a SQL query against the configured database and return results"
allowed-tools:
- Bash
- Read
---

You are a database query expert. When the user asks you to query data:

1. Analyze the user's request to determine what data they need
2. Construct a safe SQL query (only SELECT statements)
3. Use the Bash tool to execute: `psql $DATABASE_URL -c "<query>" -t -A`
4. Format the results in a readable table
5. If the query fails, explain the error and suggest a fix

Important rules:
- NEVER execute DROP, DELETE, or TRUNCATE statements
- Always use parameterized queries when possible
- Limit results to 100 rows unless the user specifies otherwise

frontmatter 中的字段:

  • description——告诉 AI 什么时候应该使用这个 skill
  • allowed-tools——这个 skill 可以使用哪些工具

Markdown 正文是给 AI 的系统提示词——当 skill 被调用时,这段文字会被注入到 AI 的上下文中。

Skill 的四大来源

来源加载方式典型场景
Bundled(内建)编译时打包进二进制核心命令如 /help/init
skills/ 目录扫描项目或全局配置的 skills/ 目录项目级自定义提示词
MCP(远程)通过 MCP 服务器动态注册第三方工具提供的命令
Plugin(插件)从插件的 skills/ 目录加载插件分发的可复用技能

加载顺序是 bundled > skills/ > MCP > plugin,同名 skill 后加载的不会覆盖先加载的。


创建 Agent

创建 agents/db-inspector.md

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---
description: "Inspect database schema, indexes, and performance"
tools:
- Bash
- Read
- Grep
max_turns: 15
---

You are a database inspector agent. Your job is to analyze database schema and performance.

Available commands:
- `psql $DATABASE_URL -c "\dt"` — list all tables
- `psql $DATABASE_URL -c "\d table_name"` — describe a table
- `psql $DATABASE_URL -c "\di"` — list all indexes

When asked to inspect a database:
1. Start by listing all tables
2. Describe the relevant tables
3. Check indexes on frequently queried columns
4. Report findings in a structured format

Keep your analysis under 500 words.

Agent 的 frontmatter 字段和上一章看到的 AgentDefinition 是对应的:toolsmax_turnsdescription


创建 Hook

Hooks 是在特定事件发生时自动执行的脚本。创建 hooks/hooks.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 validate_sql.py"
}
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 log_query.py"
}
]
}
]
}

这个 hooks 配置做了两件事:

  • PreToolUse——在每次调用 Bash 工具之前,验证命令安全性
  • PostToolUse——在每次调用 Bash 工具之后,记录操作日志

HookCommand 的四种类型

类型说明适用场景
command执行 Shell 命令运行脚本、调用 CLI 工具
prompt注入一段 AI 提示词修改 AI 行为,注入上下文
agent启动一个子 Agent需要独立工具集的复杂处理
http发送 HTTP 请求调用远程 API、通知外部系统

command 类型最常用。prompt 类型不执行代码,而是在 AI 上下文中注入额外指令。agent 类型最重,会启动一个完整的子 Agent。http 类型适合和外部系统集成。

inline 与 fork 执行模式

模式工作方式适用场景
inline在主进程中同步执行需要结果才能继续(如安全检查)
forkfork 子进程执行发后即忘的操作(如日志记录)

选择原则:需要结果 -> inline,只需执行 -> fork

HookEvent 完整列表

Claude Code 定义了 28 种 HookEvent,覆盖了会话、工具、权限、协作等各个阶段:

工具相关PreToolUsePostToolUsePostToolUseFailure

权限相关PermissionDeniedPermissionRequest

会话相关SessionStartSessionEndStopStopFailure

子 Agent 事件SubagentStartSubagentStop

压缩相关PreCompactPostCompact

协作相关TeammateIdleTaskCreatedTaskCompleted

交互相关ElicitationElicitationResultUserPromptSubmit

环境相关ConfigChangeWorktreeCreateWorktreeRemoveInstructionsLoadedCwdChangedFileChangedNotification

这些事件在 src/schemas/hooks.ts 中通过 HookEvent 类型定义。绝大多数插件只需要 PreToolUsePostToolUse


定义 MCP 服务器

插件还可以自带 MCP 服务器。在 plugin.json 中添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "db-assistant",
"version": "1.0.0",
"mcpServers": {
"db-query": {
"command": "node",
"args": ["./mcp-servers/db-query-server.js"],
"env": {
"DATABASE_URL": "${DATABASE_URL}"
}
}
}
}

插件 MCP 服务器的名字会被加上 plugin: 前缀。比如上面的 db-query 会变成 plugin:db-assistant:db-query

插件 MCP 服务器还有去重机制——如果插件提供的服务器和用户手动配置的服务器实际上是同一个(相同的命令或 URL),插件版本会被抑制。手动配置总是优先于插件配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 文件:src/services/mcp/config.ts
export function dedupPluginMcpServers(pluginServers, manualServers) {
const manualSigs = new Map()
for (const [name, config] of Object.entries(manualServers)) {
const sig = getMcpServerSignature(config)
if (sig) manualSigs.set(sig, name)
}

const servers = {}
for (const [name, config] of Object.entries(pluginServers)) {
const sig = getMcpServerSignature(config)
if (manualSigs.has(sig)) continue // 手动配置优先
servers[name] = config
}
return { servers }
}

插件的生命周期

一个插件从创建到运行,经历以下阶段:

加载(Loading)loadAllPluginsCacheOnly() 读取所有启用插件的 manifest,验证结构,收集组件路径:

1
2
3
4
5
6
7
8
9
// 文件:src/utils/plugins/pluginLoader.ts
export const loadAllPluginsCacheOnly = memoize(async (): Promise<PluginLoadResult> => {
const result: PluginLoadResult = { enabled: [], disabled: [], errors: [] }
// 加载 marketplace 插件
// 加载内建插件
// 加载 session 插件
// 验证每个插件的 manifest
return result
})

注册(Registration)。加载后,每个组件被注册到对应的系统:Skills 注册到命令系统,Agents 注册到 AgentTool 的可用列表,Hooks 注册到 hook 执行器,MCP 服务器注册到连接管理器。

运行(Runtime)。注册完成后,插件在后台工作。AI 可以调用它的 skills,系统会执行它的 hooks,MCP 服务器提供额外的工具。

热重载(Hot Reload)。当远程策略设置变更时,插件系统会检测到变化并重新加载:

1
2
3
4
5
6
7
8
9
10
11
12
// 文件:src/utils/plugins/loadPluginHooks.ts
export function setupPluginHookHotReload(): void {
settingsChangeDetector.subscribe(source => {
if (source === 'policySettings') {
const newSnapshot = getPluginAffectingSettingsSnapshot()
if (newSnapshot === lastPluginSettingsSnapshot) return
clearPluginCache(...)
clearPluginHookCache()
void loadPluginHooks()
}
})
}

验证插件

Claude Code 提供了内置的验证工具:

1
claude plugin validate .

验证器会检查:

  1. JSON 语法——plugin.json 是否是合法的 JSON
  2. Schema 合规——是否符合 PluginManifestSchema 的要求
  3. 路径安全——组件路径是否包含 ..(路径穿越攻击)
  4. 命名规范——name 是否是 kebab-case
  5. 组件完整性——引用的 skill/agent/command 文件是否存在
  6. hooks 格式——hooks.json 是否符合 PluginHooksSchema

验证逻辑在 src/utils/plugins/validatePlugin.ts 中。通过验证的插件应该零错误。


打包一个完整示例

让我们把所有组件打包成最终的插件。回顾一下完整的目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
db-assistant/
├── .claude-plugin/
│ └── plugin.json
├── skills/
│ ├── query-database/
│ │ └── SKILL.md
│ └── migrate-schema/
│ └── SKILL.md
├── agents/
│ └── db-inspector.md
├── commands/
│ └── db-status.md
├── hooks/
│ └── hooks.json
└── mcp-servers/
└── db-query-server.js

这个插件提供了:

  • 两个 skill:query-database(查询数据库)和 migrate-schema(执行迁移)
  • 一个 agent:db-inspector(检查数据库结构)
  • 一个 command:/db-status(快速查看数据库状态)
  • 一组 hooks:PreToolUse 验证 SQL 安全性,PostToolUse 记录查询日志
  • 一个 MCP 服务器:db-query(提供程序化的查询工具)

插件设计四原则

原则说明实践方式
单一职责每个插件只做一件事DB 插件专注 DB 操作,不要混入 Git 功能
幂等性多次安装结果一致settings.json 记录状态,不依赖文件系统快照
错误隔离插件崩溃不影响主程序所有 hook 执行用 try/catch 包装
版本兼容考虑主程序版本升级plugin.json 中指定兼容版本范围

错误隔离的具体实现:源码中插件 hook 的执行都经过 try/catch 包装。如果 hook 脚本抛出异常,系统捕获错误、记录日志,然后继续执行下一个 hook。


分发插件——Marketplace

插件通过 marketplace 分发。一个 marketplace 是一个 Git 仓库,包含一个 marketplace.json 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"metadata": {
"name": "my-marketplace",
"description": "My collection of Claude Code plugins"
},
"plugins": [
{
"name": "db-assistant",
"source": "./plugins/db-assistant",
"version": "1.0.0",
"description": "Database query and management tools"
}
]
}

用户通过以下命令安装:

1
claude plugin add db-assistant@my-marketplace

插件系统会克隆或拉取 marketplace 仓库,找到插件的源代码目录,验证 manifest,安装到本地缓存,注册到用户的 enabledPlugins 设置中。


常见错误

常见错误检查方法
skills 不显示skills 必须是 skills/<name>/SKILL.md 格式,子目录下必须有 SKILL.md
hooks.json 解析失败导致整个插件无法加载运行 claude plugin validate . 检查格式
插件 MCP 服务器和手动配置冲突手动配置优先。用 /mcp 查看哪个版本生效了
企业环境下插件被阻止联系管理员,把你的 marketplace 添加到允许列表
组件路径报错不要用 ..,所有路径相对于插件根目录

试试看

  1. 创建一个最小插件。只包含一个 skill 的插件——一个”代码格式化”skill。先运行 claude plugin validate .,确认通过。然后安装并让 AI 使用它。
  2. 给 db-assistant 插件添加一个 PostToolUse hook。当 AI 执行完 Bash 命令后,用 http 类型发送一条通知到 webhook。
  3. 创建一个包含自定义 Agent 的插件。Agent 只能使用 Read 和 Grep 工具,负责”阅读理解”——给定一个文件路径,Agent 阅读文件并生成摘要。

检查点

  • 目录结构——.claude-plugin/plugin.json 是 manifest,skills/agents/commands/hooks/ 是组件
  • plugin.json——插件的身份证,声明包含哪些组件,所有字段都是可选的
  • Skills——Markdown 文件,frontmatter 是元数据,正文是 AI 提示词,必须放在 skills/<name>/SKILL.md
  • Agents——独立的对话实体,有工具集和轮数限制
  • Hooks——事件驱动的自动化脚本,支持四种类型和 28 种事件
  • MCP Servers——插件自带的外部工具服务器,自动去重,手动配置优先
  • 生命周期——加载 -> 注册 -> 运行 -> 热重载
  • 验证——claude plugin validate 检查完整性和安全性
  • 分发——通过 marketplace 仓库分发插件

插件系统的设计哲学是组合优于继承。每个插件是一个独立的模块,可以只包含一个 skill,也可以同时包含 skills、agents、hooks 和 MCP 服务器。从小处着手,逐步扩展。


上一章:构建多 Agent 协作 | 下一章:编写测试