Claude Code Mods 怎么用:安装前先审查,再写第一个 mod
Claude Code Mods 是插件里的 JS/TS 函数,2.1.287 起默认开启,能画窗格、改写工具调用,还能替你批准权限确认;装前先跑 validate、翻源码。
文章目录

Claude Code Mods 是 Anthropic 在 2026 年 10 月 1 日推出的功能:一段放在插件里的 JavaScript 或 TypeScript 代码,Claude Code 在工具调用、提交提示、绘制界面这些时刻调用它,它可以旁观、改写,或者干脆接管这件事。终端里的 Claude Code 从 2.1.287 起默认开启,不用改任何设置。想最快、最安全地用上第一个 mod,顺序是:确认版本 → 用 claude plugin validate 看它会调用什么 → 对启动程序、读写文件、联网的调用翻一遍源码 → 用 claude --plugin-dir 只在一次会话里试 → 满意了再从 marketplace 安装。
需要先记住的一条:mod 不在沙箱里,拥有和 Claude Code 一样的本机权限,还能在你看到确认框之前替你批准工具调用。用个人 Pro 或 Max 账号、机器上又没有组织下发的 managed settings 时,你在 settings.json 里写的 deny 规则也可能挡不住它,原因见下文的权限一节。
下面的命令输出来自 macOS 上的 Claude Code 2.1.288(Claude 桌面应用自带的那份 CLI,未登录账号),实际运行过的有:claude plugin validate、claude plugin test、claude -p "/tally",以及从本地 marketplace 安装、停用、卸载官方示例 mod。没有运行的部分:没有开交互会话,所以窗格、提示框上方的横条、spinner 后缀、/plugin 里的 mods active 行、热重载和 Claude 写 mod 时的批准提示都没有亲眼看到;桌面端 Code 标签页、VS Code 扩展、Windows、Team/Enterprise 账号下的内置防护和任何社区 mod 也都没测。这些部分按 Claude Code 官方中文文档《Mods 概览》描述,文中会标明。
Claude Code mod 是什么:跑在 Claude Code 进程里的插件函数
mod 是一种插件。普通插件可以带 skill、斜杠命令、子代理、MCP server;插件里只要有一个 hooks/hooks.json 用 modules 键指向一个 JS/TS 文件,这个插件就是 mod。Claude Code 会在自己的进程里调用这个文件注册的函数,所以 mod 不是"另一套插件系统",而是插件能装的又一种东西,安装、更新、停用都走原来的 /plugin。
文档里有两种"hook",容易混:
- settings hook:写在
settings.json里、在生命周期事件上运行的 shell 命令、HTTP 请求或提示,就是以前大家说的 Claude Code Hooks。它没有被废弃,照常工作。 - mod 里的 hook:mod 代码里用
on('tool.call', ...)这类写法注册的事件处理函数。
Anthropic 的发布公告列出的能力包括:在提示送到模型之前改写它;拦截、改写或重试工具调用;批准或拒绝权限请求;从工具输出里抹掉密钥;修改或替换界面元素;加按钮和输入框。
另外,这里的 mod 和 Minecraft 之类的游戏 mod 无关;claudemod.com 是另一个打包 hooks、命令和 CLAUDE.md 配置的网站,也不是这个功能。
需不需要 mod:先对照 settings hook、statusline、skill 和 MCP
settings hook、statusline、skill、MCP server 都是从 Claude Code 外部起作用:跑一个脚本,或者给 Claude 一段文字、一组工具。mod 跑在 Claude Code 内部,所以只有它能画可交互的界面、改 Claude Code 自己画的东西、在工具调用中途插手。反过来说,你的想法不涉及这三件事,多半不需要 mod。
| 你想做的事 | 用什么 | 说明 |
|---|---|---|
| 用现成脚本拦截、放行或记录某个事件 | settings hook | 能改工具调用的参数和结果,不能画界面 |
| 在底部显示一行状态信息 | statusline 脚本 | 一个读 stdin、写 stdout 的本地命令 |
| 总在聊天里粘贴同一段说明 | skill | 一个 SKILL.md 文件 |
| 让 Claude 连到外部系统 | MCP server | 任何语言写的外部进程 |
| 在对话旁开一个窗格,或在提示框上方放一条带按钮、输入框的横条(官方中文文档称“带状区域”) | mod | 只有终端和桌面端能看到 |
| 改 Claude Code 自己画的 spinner、工具调用行、提问对话框 | mod | 权限确认框除外,任何 mod 都改不了 |
加一个 /命令,立刻运行你的函数、不经过 Claude 回合,Claude 干活时也能用 | mod | 命令本身不经过模型 |
| 在工具调用中途插手:先暂停问你、不运行工具直接给结果、把某次请求改发给另一个模型 | mod | 同一文件里的 hook 可以共享变量 |
表格依据Claude Code《Mods 概览》里 mod、settings hook、skill 与 MCP 的对比。四者可以装在同一个插件里,并不互斥。settings hook、skill 和斜杠命令之间怎么选,见 Claude Code Hooks、Skills 与斜杠命令:区别、选型和最小配置;只想要底部一行信息,Claude Code Statusline 配置更直接。
你的环境能不能用 mod:版本 2.1.287 起,以及一条检查命令
终端用户需要 Claude Code 2.1.287 或更新版本,用 claude --version 查看;旧版本先更新 Claude Code。桌面应用自带一份 Claude Code,从 2.1.286 起支持 mod,在 Code 标签页的本地会话里输入 /status,看 Claude Code 那一行的版本号。早期体验时设过 CLAUDE_CODE_ENABLE_FUNCTION_HOOKS 的,可以删掉:2.1.287 起这个变量被忽略,设成 0 也关不掉 mod。
版本够了,再在一个不含 mod 的空目录里跑 claude plugin test。它不需要会话,也不需要登录。在 2.1.288 上的输出是:
claude plugin test: .../empty: no hooks module to load; there is no hooks/hooks.json naming one in "modules"按Claude Code 官方文档《排查 mod 问题》,消息里的关键词对应三种状态:
no hooks module to load:可以加载 mod,只是这个目录里没有可测的 mod。hooks modules are turned off here:有设置挡住了 mod,可能是你自己设了disableAllHooks,也可能是组织策略。hooks modules are turned off in this process:Anthropic 远程关闭了已安装的 mod,本机任何设置都打不开。
组织管理员还可以设 allowManagedModsOnly,只放行组织自己的 mod,这条命令查不出来,只会表现为你装的 mod 不加载。
还要看你在哪里用 Claude Code。mod 的 hook 在大多数地方都会运行,但画出来的界面只在终端和桌面端可见:
| 使用方式 | mod 的 hook 是否运行 | mod 画的界面能否看到 |
|---|---|---|
终端里的 claude,含编辑器内置终端和 JetBrains 插件 | 运行 | 能 |
| 桌面应用 Code 标签页(非 WSL 会话) | 运行 | 能,标为仅限终端的元素除外 |
| 桌面应用里的 WSL 会话 | 不运行,WSL 会话不支持插件 | 不能 |
| VS Code 扩展的聊天面板 | 运行 | 不能 |
claude -p 和 Agent SDK | 运行 | 不能 |
| 从 claude.ai 或手机 App 用 Remote Control | 在你本机的会话里运行 | 显示在本机那个终端里 |
| 云端会话 | 插件带到云端时运行 | 不能 |
这张表来自Claude Code《Mods 概览》里 mod 在哪里运行的说明,上面各行都没有逐一实测。只在 VS Code 聊天面板里用 Claude Code 的话,画窗格、画横条一类的 mod 对你没有意义,改写工具调用一类的仍然有效。
mod 能碰到什么:它可以替你批准 ask 确认,deny 规则未必挡得住
mod 加载后,按Claude Code《Mods 概览》里 mod 能接触到什么的说明,它能做到:
- 以你的身份读写你账号能碰到的任何文件、启动程序、发网络请求;
- 读环境变量和设置文件,包括放在里面的 API key;
- 看到你发的每一条提示和 Claude 的每一次工具调用;
- 改写提示和工具调用,像你亲手输入一样提交提示,给你的另一个会话发消息;
- 在你被询问之前批准一次工具调用;
- 用你的套餐或 API key 调用模型,花你的用量。
几条和权限设置相关的事实,决定了你在 settings.json 里配的规则还算不算数:
- ask 规则可以被绕过:带
tool.checkhook 的 mod 可以批准一个按ask规则本该弹确认的调用,也可以批准一个被非组织托管的PreToolUsesettings hook 拦下的调用。在自动模式下,mod 批准的调用不再经过分类器检查。 - deny 规则只在内置防护加载时才是最终的:内置防护 mod 叫
cc-plugin-sec-default,它加载时,你的 mod 不能批准被deny规则拒绝的调用。它只在两种情况下加载:机器上有组织托管的 managed settings,或者你用 Team、Enterprise 套餐登录。用 API key、Amazon Bedrock、Google Cloud Agent Platform、Microsoft Foundry 认证的,只有机器上有 managed settings 才会加载。按这两个条件推断,用个人 Pro 或 Max 账号登录、机器上又没有 managed settings 时,没有这道防护,mod 可以批准被deny的调用。 - 即使防护加载了,deny 也管不到 mod 自己的读写和进程:deny 规则约束的是 Claude 的工具调用。设了
Read(.env)拒绝,mod 仍然可以用$.fs.read读.env,或者启动一个去读它的程序。 - 沙箱不包住 mod:开了沙箱,被隔离的是 Claude 运行的 Bash 命令;mod 启动的进程在沙箱外。组织的网络策略约束
$.http.fetch,但约束不到 mod 用$.process.run启动的程序。 - 权限确认框本身改不了:mod 能改 Claude Code 大部分界面,唯独改不了权限确认框显示的内容。它能做的是在确认框出现之前就替你决定。

这些细节在 Claude Code 官方文档《为您的组织管理 mods》里。换句话说,装一个会用 tool.check 的 mod,效果接近把一部分权限判断交给了它的作者;如果你本来就在考虑跳过权限确认,先看 --dangerously-skip-permissions 的禁用场景和更安全替代,两者都会让一部分工具调用不经你确认就执行。
装 mod 之前怎么审查:validate 看能力,源码看实际行为
审查分两步。第一步不运行代码:把插件文件拿到本地(比如 git clone),对插件目录运行:
claude plugin validate ./some-mod它做的静态分析和 Claude Code 加载 mod 时做的一样。输出里的 hooks: 行列出 mod 处理哪些事件(花括号里是过滤条件),calls: 行列出它调用了哪些 mods API。mod 想碰文件、进程、网络、模型,只能通过这套 API;Claude Code 读不懂的写法直接拒绝加载,所以 calls: 行是这段 mod 代码的完整能力清单。它不涵盖同一插件里的 settings hook 脚本和 MCP server,那些要另外看。命令还有 --json 和 --strict 两个参数。
calls: 里出现下面这些时要停下来看:
calls: 里出现 | 意味着 | 下一步 |
|---|---|---|
$.fs.read、$.fs.write | 读写你能碰到的任何文件 | 翻源码看读写哪些路径 |
$.process.run、$.process.spawn | 以你的身份启动程序 | 翻源码看启动的是什么程序、带什么参数 |
$.http.fetch | 发网络请求 | 翻源码看发到哪里、带上了什么 |
$.env.get、$.settings.read | 读环境变量和设置,里面可能有 API key | 先看 env reads: 行列出的变量名,涉及 key 再翻源码 |
$.env.set | 改环境变量,影响之后启动的命令和 MCP server | 看 env writes: 行,再翻源码 |
$.model.complete | 用你的套餐或 API key 调模型 | 看调用时机和发送的内容 |
$.prompt.submit | 提交提示,可以当成你说的话发出去 | 翻源码看提交什么 |
$.session.send | 给另一个会话或子代理里的 Claude 发消息 | 翻源码看发什么 |
$.mcp.call | 调用已连接 MCP server 的工具,受会话权限规则约束 | 看调用哪个工具 |
hooks: 行也有几项值得注意:tool.call 和 prompt.submit 表示它能看到并改写每一次工具调用和每一条提示;session.append 表示它能在对话记录保存前改写每一行;ui.render{component=AskUserQuestion} 表示它能重画 Claude 向你提问的对话框;tool.check 就是上一节说的,能在确认框出现前批准或拒绝调用。
对 Anthropic 的三个官方示例 mod 实际跑 validate(claude-code-playground 仓库的 mods 目录,2026 年 10 月 1 日的提交 569c5283),三个都通过,输出的关键行如下:
token-weather(在提示框上方显示上下文窗口的“天气预报”)
❯ ./token-weather.mjs hooks: session.start, turn.complete, ui.render{component=AbovePrompt}
❯ ./token-weather.mjs calls: $.session.usage (via takeReading), $.ui.invalidate (via takeReading), $.ui.resolve
blast-radius(拦住高风险 shell 命令,显示影响范围,给出继续/取消按钮)
❯ ./blast-radius.mjs hooks: tool.call{tool=Bash}, ui.render{component=Pane}, ui.render{component=AbovePrompt}
❯ ./blast-radius.mjs calls: $.clock.now, $.process.run, $.session.cwd, $.ui.close, $.ui.invalidate, $.ui.open, $.ui.resolve, $.ui.toast
replay-theater(加一个 /replay 命令,逐步回放上一回合的文件改动)
❯ ./replay-theater.mjs hooks: session.start, command.run{command=replay}, tool.call, turn.start, turn.complete, ui.render{component=AbovePrompt}, ui.render{component=Pane}, ui.close
❯ ./replay-theater.mjs calls: $.clock.sleep (via openReplay), $.command.register, $.fs.exists (via stepsFor), $.fs.read (via stepsFor), $.session.cwd (via stepsFor), $.ui.close (via replayView), $.ui.invalidate, $.ui.open (via openReplay), $.ui.resolve三者都没有调用 $.http.fetch、$.env.get 或 $.model.complete,也就是不联网、不读环境变量、不花模型用量。token-weather 只读会话用量、画界面,风险最低。replay-theater 会读文件,(via stepsFor) 告诉你读文件发生在哪个函数里,要确认读的是哪些文件,就去看 stepsFor。
第二步是翻源码,blast-radius 是个好例子。validate 只说它会"启动程序",没说启动什么。翻 hooks/blast-radius.mjs 才知道,它启动的是这几样:
// 节选自 hooks/blast-radius.mjs
$.process.run(["sleep", POLL_SECONDS], { timeoutMs: 5000 }) // 等你点继续或取消时的轮询
$.process.run(["bash", "-c", CD_SCRIPT, "blast-radius", dir], ...) // 解析 cd 的目标目录
$.process.run(["bash", "-c", RM_SCRIPT, "blast-radius", ...risk.targets], { cwd, timeoutMs: 15000 }) // 统计 rm 会删掉多少文件和字节源码注释解释了为什么用 sleep:一个 hook 自己只有 10 秒执行时间,待在 $ 调用里的时间不算。它会拦下的命令包括带 -r 或 -f 的 rm、git reset --hard、git push --force(以及 -f、--force-with-lease、+ref 写法)、alembic upgrade、rails db:migrate、prisma migrate、manage.py migrate。按源码,这些进程只是等待用的 sleep 和预演统计用的脚本;被拦下的命令要等你点继续才会照常执行。这层信息 validate 给不出来。
把两步合起来,装前检查清单是:
- 作者和 marketplace 是否可信,README 写没写测过的 Claude Code 版本。
claude plugin validate能否通过,hooks:里有没有tool.check、prompt.submit、session.append。calls:里有没有上表那些调用;有就翻源码,看路径、程序、地址和发送的内容。- 有
tool.check时,想清楚它会批准什么,以及你的deny规则在本机是否真的生效。 - 先用
--plugin-dir在一次会话里试,确认行为符合 README,再安装。

先用 --plugin-dir 试一次会话,再从 marketplace 安装
只试一次、不安装,用 --plugin-dir 指向 mod 目录:
git clone --depth 1 https://github.com/anthropics/claude-code-playground
claude --plugin-dir ./claude-code-playground/claude-code/mods/token-weather这个参数只对本次会话生效,还会在你保存文件时热重载 mod 的代码;要同时加载几个,就重复写几次。没法传命令行参数的应用,可以用环境变量 CLAUDE_CODE_PLUGIN_DIRS 达到同样效果。加载后在会话里运行 /plugin,按文档,标签页下方会有一行灰字,比如 1 mod active · first-mod,列出本次会话加载的 mod(不含内置 mod)。
想长期用,就把它装成插件。mod 都从 marketplace 安装:会话里用 /plugin install 插件名@市场名,shell 里用 claude plugin install。marketplace 的来源可以是 GitHub 的 owner/repo、任意 git 地址、以 ./ 或 ../ 开头的本地路径,或一个托管的 marketplace.json 地址。下面是把示例仓库的 mods 目录加成本地 marketplace、安装 token-weather、再停用和卸载的完整过程,在 2.1.288 上的真实输出(配置目录做了隔离,没有动真实的 ~/.claude):
$ cd claude-code-playground/claude-code/mods
$ claude plugin marketplace add ./
✔ Successfully added marketplace: claude-code-playground-mods (declared in user settings)
$ claude plugin install token-weather@claude-code-playground-mods --scope user
✔ Successfully installed plugin: token-weather@claude-code-playground-mods (scope: user)
$ claude plugin list
Installed plugins:
❯ token-weather@claude-code-playground-mods
Version: 0.1.0
Scope: user
Status: ✔ enabled
$ claude plugin disable token-weather@claude-code-playground-mods
✔ Successfully disabled plugin: token-weather (scope: user)
$ claude plugin uninstall token-weather@claude-code-playground-mods
✔ Successfully uninstalled plugin: token-weather (scope: user)几点补充:
- 安装范围默认是 user,也可以用
--scope project或--scope local,详见 Claude Code 插件安装文档。 - 会话开着时在 shell 里装或更新了 mod,要在会话里跑
/reload-plugins才会加载,否则下次启动才生效。 - 从本地克隆添加的 marketplace 指向那个目录,克隆被移动或删除后,mod 就不再加载。
- 在会话里也可以运行
/plugin,按 Tab 切到 Installed 标签页,停用、更新或卸载。
关掉 mod:单个停用、--safe-mode 和 disableAllHooks
按想停多少、停多久,有三种做法:
- 只停一个:在
/plugin的 Installed 标签页停用或卸载它所在的插件,或在 shell 里用claude plugin disable/claude plugin uninstall。 - 本次会话停掉所有已安装的 mod:用
claude --safe-mode启动,它同时会关掉你的其他自定义项。怀疑某个 mod 惹了麻烦时,先用这个排除。 - 所有会话都停掉你装的 mod:在
~/.claude/settings.json里设"disableAllHooks": true。代价是你的 settings hook 和自定义 statusline 也一起停;组织托管的部分照常运行。
disableAllHooks 和组织的 allowManagedModsOnly 只停 mod 代码,插件里的 skill、命令、子代理和 MCP server 仍会加载。
Claude Code 自己的一部分功能就是内置 mod,在 /plugin 的 Installed 标签页 Built-in 下能看到。它们不能更新或卸载,disableAllHooks、--safe-mode 和 --bare 也停不掉,要关只能在 /plugin 里逐个停用(cc-plugin-sec-default 除外):
/plugin 里的名字 | 作用 |
|---|---|
cc-plugin-agents-md | 把 AGENTS.md 当作项目说明加载 |
cc-plugin-diff | 接管 /diff 并画出它的窗格 |
cc-plugin-plugin-authoring | 给 Claude 提供写 mod 用的 plugin-authoring skill,本身不含 mod 代码 |
cc-plugin-sec-default | 上一节说的内置防护,用户关不掉 |
cc-plugin-telemetry | 发送 Claude Code 及内置 mod 的分析记录 |
cc-plugin-you-should-know | 一个旁观代理,长任务中发现你可能漏看的事就在提示框上方提醒;默认关闭 |
最后一个要手动开启:/plugin enable cc-plugin-you-should-know@builtin。其中 diff、agents-md、sec-default、telemetry 的源码公开在 Claude Code 仓库的 mods 目录,想看一个完整 mod 怎么写,可以从这里读起。
让 Claude 写一个 mod:文件在 ~/.claude/dev-mods,记得拷出来
这是最省事的做法,需要一个已登录的交互会话,下面的流程按 Claude Code 官方文档《创建一个 mod》整理,没有实际跑过。
- 在会话里直接描述,比如
make a mod that shows the current git branch above the prompt(用中文描述也可以)。Claude 会用内置的plugin-authoringskill,你也可以先运行/plugin-authoring手动加载。 - 文件写在
~/.claude/dev-mods/下以会话 ID 命名的目录里。~/.claude是受保护路径,在default和acceptEdits权限模式下,每个文件都要你批准。 - 写第一个文件时,Claude Code 会问是否为本次会话开启热重载:选 Enable for this session,mod 在回合结束时加载,之后每次改动都会重载;选 Not now,文件留着,下次启动这个会话时再加载。
- 运行
/plugin到 Installed 标签页,确认 mod 已列出。效果不对就告诉 Claude 改哪里。
要注意的是,Claude 写的 mod 只在写它的那个会话里加载,而且这个会话的 mods 目录超过 cleanupPeriodDays 设置的天数就会被删掉。想留下来,把它的目录拷到自己的位置(比如 ~/mods/git-branch),以后用 claude --plugin-dir ~/mods/git-branch 加载,或者放进一个 marketplace。
在 claude -p、dontAsk 模式、尚未信任的目录,或者 mod 被关掉的会话里,Claude 写的 mod 不会加载,因为没人能批准它,或者环境不允许。
自己写第一个 mod:三个文件、validate、test 和 claude -p
自己写一遍,最能看懂 mod 的结构。不需要 Node.js、打包工具或构建步骤,Claude Code 直接加载 .js 和 .ts。下面是官方教程里的 first-mod:统计 Claude 的工具调用次数,显示在 spinner 后面,并加一个 /tally 命令打印次数。
first-mod/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.jsfirst-mod/.claude-plugin/plugin.json 是插件清单:
{
"name": "first-mod",
"version": "0.1.0",
"description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",
"author": { "name": "Your Name" }
}first-mod/hooks/hooks.json 里的 modules 键指向代码文件,有了它,这个插件才是 mod:
{
"description": "The first-mod hooks module",
"modules": ["./register.js"]
}first-mod/hooks/register.js 导出 register(on),每调用一次 on 就注册一个 hook。每个 hook 都收到三个参数:$ 是 mods API,e 是事件数据,next 把事件交给后面的 mod 和 Claude Code 自己的默认行为。
// 下面几个 hook 共享的计数
let calls = 0
export function register(on) {
// 会话开始时注册 /tally 命令,然后照常继续
on('session.start', async ($, e, next) => {
await $.command.register({
name: 'tally',
description: 'Show how many tool calls Claude has made',
})
return next(e)
})
// Claude 每次要用工具时计数,并请求重绘界面
on('tool.call', async ($, e, next) => {
calls += 1
$.ui.invalidate('ui.render')
return next(e)
})
// 只在输入 /tally 时运行,直接返回结果,不调用 next
on('command.run', { command: 'tally' }, async () => {
return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
})
// 每次绘制 spinner 时,在原来的文字后面加上计数
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
}四个 hook 正好展示了三种处理方式:session.start 和 tool.call 做完自己的事后返回 next(e),属于旁观;command.run 自己给出结果、从不调用 next,属于接管;ui.render 把改过的事件传给 next,属于改写。
用 validate、test 和 claude -p 检查 first-mod
写完先跑 validate。2.1.288 上的输出:
$ claude plugin validate ./first-mod
Validating plugin manifest: .../first-mod/.claude-plugin/plugin.json
Validating hooks: .../first-mod/hooks/hooks.json
❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
❯ ./register.js calls: $.command.register, $.ui.invalidate
✔ Validation passed再加一个测试文件 first-mod/tests/first-mod.test.ts(官方教程原文):
import { expect, test } from 'claude-code/testing'
test('/tally reports the tool calls the mod has seen', async ($, on) => {
// 代替 Claude Code 回答每个工具调用,不真的运行工具
on('tool.call', () => ({ result: 'ok' }))
await $.tool.call({ tool: 'Bash', command: 'ls' })
await $.tool.call({ tool: 'Read', file_path: 'README.md' })
const answer = await $.command.run({ command: 'tally', args: '' })
expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})在 first-mod 目录里运行 claude plugin test,不需要会话、登录和网络:
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [64.48ms]
1 pass
0 fail
Ran 1 test across 1 file. [0.34s]耗时每次都会不同。最后用非交互模式跑一次命令:
$ claude -p "/tally" --plugin-dir ./first-mod
first-mod: Claude has made 0 tool calls since this mod loaded这条在未登录的 CLI 上也能返回结果,因为 command.run 直接作答,没有经过模型。Claude Code 会在命令输出前加上插件名。交互会话里 spinner 后面出现的 Thinking · tool calls: 2… 和保存后热重载的效果,在官方教程的录屏里能看到,本机没有在交互会话里验证。
静态分析的写法规则和两条真实报错
validate 读不懂的写法,Claude Code 会拒绝加载,所以写 mod 有几条硬规则:
- 每个 API 调用写全:
$、命名空间、方法,比如$.store.get('notes')。不要把$或它的命名空间赋给变量、解构或用计算出来的名字取下标,const ui = $.ui会报$.ui is used as a value。可以把$传给同一文件顶层声明的函数,calls:行会显示成(via 函数名)。 on的事件名必须是字符串字面量,不能用变量或循环。- 只能用顶部的
import声明导入插件目录内的文件,唯一允许的裸导入是claude-code;不能用动态import(),也不能用require。 - 插件名看起来像 Anthropic 官方的(比如以
claude-开头),validate 会失败。
从 --plugin-dir 加载时,Claude Code 会把当前版本的类型声明写进 mod 目录的 .claude-plugin/types/。事件和方法会随版本变化,和文档对不上时以这些类型文件为准。
故意写错两处,2.1.288 给出的报错是这样的。事件名拼成 'tool.calls':
✘ Found 1 error:
❯ modules../register.js: bad-mod: .../hooks/register.js:7: "tool.calls" is not an event; $ is always spelled $.noun.event(...) at the call site, on is always on("<event>", hook), and next.to always next.to(e, "<tier>")
✘ Validation failedhooks.json 里把 modules 写成 module,又没有 hooks 键:
✘ Found 1 error:
❯ root: hooks.json must have `hooks` (the hook matchers) or `modules` (hooks modules), or both
✘ Validation failed官方排错文档还提到另一种情况:modules 键缺失或拼错时,validate 会通过但不输出 hooks: 行。推测那是 hooks.json 里还有 hooks 键的情形。总之,validate 通过却没有 hooks: 行,同样说明 modules 没写对。
写好的 mod 想给别人用:发目录或 .zip;放进自己的 marketplace(比如一个私有仓库);组织管理员通过 managed settings 统一安装;或者公开仓库、提交到 Anthropic 的插件目录。README 里写清你测试过的 Claude Code 版本。继续开发时用 --plugin-dir 指向源目录,因为已安装的副本按版本号缓存,不升版本重装,改动到不了已安装的那份。
mod 装了没反应:按这个顺序排查
mod 的模块或某个 hook 出错时,Claude Code 会跳过它、让会话继续,所以坏掉的 mod 看起来就像什么都没做。按Claude Code 官方文档《排查 mod 问题》,可以这样查:
- 先跑
claude plugin validate:事件名拼错、清单有问题、模块读不懂,不开会话就能查出来。 - 找那一行说明:Claude Code 跳过 mod 时会写一行带 mod 名字的说明。用
--plugin-dir或开了热重载的会话,它以灰字出现在对话里;从 marketplace 安装的 mod,只写进调试日志,要用claude --debug启动才看得到;claude -p写到 stderr。 - 看拒绝原因:以
hooks module 插件名 not loaded:开头,冒号后是原因,比如disableAllHooks in managed settings(组织关了插件 hook)、only managed plugins and built-in plugins run(设了allowManagedHooksOnly,或在非 managed 设置里设了disableAllHooks)、带(--bare)的(用--bare启动)、another plugin of that name loads first(重名插件先加载了)。 - 新目录里一个 mod 都不加载:还没接受这个目录的信任提示。先在该目录运行
claude,接受信任提示。 - 所有已安装插件都不加载:多半是用
--safe-mode启动的。 - 组织账号下的防护消息:
mods are limited to your organization's by policy (allowManagedModsOnly)表示组织只放行自己的 mod;tried to lift a deny rule in your settings表示你的 mod 想批准一个被deny拒绝的调用,调用仍被拒绝。 - 超出时间或大小上限:一个 hook 每次事件自己的执行时间上限是 10 秒(
prompt.edit是 50 毫秒),$.process.run默认 30 秒、最长 10 分钟,$.fs读写单个文件上限 4 MiB,claude plugin test里单个测试默认 5 秒。hook 超出自己的时间上限会被跳过。这些上限出自 Claude Code 官方文档《mod 参考》的 Limits 一节,可能随版本调整。
Claude Code Mods 的收费、去哪找和分享
Claude Code Mods 要额外付费吗?
公告和文档里没有为 mod 单独收费的说法,它随 Claude Code 版本一起提供。要留意的是用量:调用了 $.model.complete 的 mod 会用你的套餐或 API key 调模型,validate 的 calls: 行能看出来。只响应 /命令、不调用 $.model.complete 的 mod(像上面的 /tally)直接作答,不经过模型。
去哪找别人写好的 Claude Code mod?
起点是 Anthropic 的三个示例(token-weather、blast-radius、replay-theater),仓库说明它们按原样分享、不提供支持。作者也可以把 mod 提交到 Anthropic 的插件目录。第三方的 awesome-claude-code-mods 汇总了从 GitHub 扫描到的公开 mod,并附上每个 mod 的 validate 结果,截至 2026 年 10 月 6 日列出 2,685 个;它自己说明是独立扫描、不是官方目录,验证通过也不代表行为安全。不管从哪里找到,都按上面的审查清单过一遍。
写好的 Claude Code mod 怎么分享给同事?
最简单是把目录或 .zip 发过去,对方用 claude --plugin-dir 加载;团队长期用,就建一个私有仓库当 marketplace,大家 claude plugin marketplace add 后安装;整个组织统一下发,由管理员通过 managed settings 安装。和 skill、MCP 一起打包也可以,Claude Code 最值得先用的 Skills和最值得先加的 MCP可以帮你挑同一插件里的其他组件。
参考来源10
本文引用的外部页面,按正文出现顺序排列。最后更新于 2026年10月6日。
参考来源10
本文引用的外部页面,按正文出现顺序排列。最后更新于 2026年10月6日。
- 1.Claude Code 官方中文文档《Mods 概览》code.claude.com/docs/zh-CN/plugins/mods/overview
- 2.Anthropic 的发布公告claude.com/blog/claude-code-mods
- 3.Claude Code 官方文档《排查 mod 问题》code.claude.com/docs/zh-CN/plugins/mods/troubleshoot
- 4.Claude Code 官方文档《为您的组织管理 mods》code.claude.com/docs/zh-CN/plugins/mods/admin
- 5.claude-code-playground 仓库的 mods 目录github.com/anthropics/claude-code-playground/tree/main/claude-code/mods
- 6.Claude Code 插件安装文档code.claude.com/docs/zh-CN/plugins/install
- 7.Claude Code 仓库的 mods 目录github.com/anthropics/claude-code/tree/main/mods
- 8.Claude Code 官方文档《创建一个 mod》code.claude.com/docs/zh-CN/plugins/mods/create
- 9.Claude Code 官方文档《mod 参考》的 Limits 一节code.claude.com/docs/zh-CN/plugins/mods/reference
- 10.awesome-claude-code-modsgithub.com/karanb192/awesome-claude-code-mods





