跳转到主要内容

Claude Code Mods 怎么用:安装前先审查,再写第一个 mod

Claude Code Mods 是插件里的 JS/TS 函数,2.1.287 起默认开启,能画窗格、改写工具调用,还能替你批准权限确认;装前先跑 validate、翻源码。

LaoZhang AI Team发布于27 分钟阅读
文章目录
Claude Code Mods 装前先审查:mod 能改写工具调用、在确认框之前替你批准,而且不在沙箱里

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 里配的规则还算不算数:

  1. ask 规则可以被绕过:带 tool.check hook 的 mod 可以批准一个按 ask 规则本该弹确认的调用,也可以批准一个被非组织托管的 PreToolUse settings hook 拦下的调用。在自动模式下,mod 批准的调用不再经过分类器检查。
  2. 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 的调用。
  3. 即使防护加载了,deny 也管不到 mod 自己的读写和进程:deny 规则约束的是 Claude 的工具调用。设了 Read(.env) 拒绝,mod 仍然可以用 $.fs.read 读 .env,或者启动一个去读它的程序。
  4. 沙箱不包住 mod:开了沙箱,被隔离的是 Claude 运行的 Bash 命令;mod 启动的进程在沙箱外。组织的网络策略约束 $.http.fetch,但约束不到 mod 用 $.process.run 启动的程序。
  5. 权限确认框本身改不了:mod 能改 Claude Code 大部分界面,唯独改不了权限确认框显示的内容。它能做的是在确认框出现之前就替你决定。

装了 mod 之后各项权限设置的效果:ask 规则可被 mod 批准,deny 规则只在内置防护加载时才是最终的,deny 和沙箱都管不到 mod 自己的读写和进程,权限确认框内容改不了

这些细节在 Claude Code 官方文档《为您的组织管理 mods》里。换句话说,装一个会用 tool.check 的 mod,效果接近把一部分权限判断交给了它的作者;如果你本来就在考虑跳过权限确认,先看 --dangerously-skip-permissions 的禁用场景和更安全替代,两者都会让一部分工具调用不经你确认就执行。

装 mod 之前怎么审查:validate 看能力,源码看实际行为

审查分两步。第一步不运行代码:把插件文件拿到本地(比如 git clone),对插件目录运行:

bash
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 才知道,它启动的是这几样:

js
// 节选自 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 给不出来。

把两步合起来,装前检查清单是:

  1. 作者和 marketplace 是否可信,README 写没写测过的 Claude Code 版本。
  2. claude plugin validate 能否通过,hooks: 里有没有 tool.check、prompt.submit、session.append。
  3. calls: 里有没有上表那些调用;有就翻源码,看路径、程序、地址和发送的内容。
  4. 有 tool.check 时,想清楚它会批准什么,以及你的 deny 规则在本机是否真的生效。
  5. 先用 --plugin-dir 在一次会话里试,确认行为符合 README,再安装。

装 mod 前的四步:跑 claude plugin validate 看能力清单,翻源码看实际行为,用 --plugin-dir 试一次会话,满意后再从 marketplace 安装

先用 --plugin-dir 试一次会话,再从 marketplace 安装

只试一次、不安装,用 --plugin-dir 指向 mod 目录:

bash
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》整理,没有实际跑过。

  1. 在会话里直接描述,比如 make a mod that shows the current git branch above the prompt(用中文描述也可以)。Claude 会用内置的 plugin-authoring skill,你也可以先运行 /plugin-authoring 手动加载。
  2. 文件写在 ~/.claude/dev-mods/ 下以会话 ID 命名的目录里。~/.claude 是受保护路径,在 default 和 acceptEdits 权限模式下,每个文件都要你批准。
  3. 写第一个文件时,Claude Code 会问是否为本次会话开启热重载:选 Enable for this session,mod 在回合结束时加载,之后每次改动都会重载;选 Not now,文件留着,下次启动这个会话时再加载。
  4. 运行 /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.js

first-mod/.claude-plugin/plugin.json 是插件清单:

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:

json
{
  "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 自己的默认行为。

js
// 下面几个 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(官方教程原文):

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 failed

hooks.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 问题》,可以这样查:

  1. 先跑 claude plugin validate:事件名拼错、清单有问题、模块读不懂,不开会话就能查出来。
  2. 找那一行说明:Claude Code 跳过 mod 时会写一行带 mod 名字的说明。用 --plugin-dir 或开了热重载的会话,它以灰字出现在对话里;从 marketplace 安装的 mod,只写进调试日志,要用 claude --debug 启动才看得到;claude -p 写到 stderr。
  3. 看拒绝原因:以 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(重名插件先加载了)。
  4. 新目录里一个 mod 都不加载:还没接受这个目录的信任提示。先在该目录运行 claude,接受信任提示。
  5. 所有已安装插件都不加载:多半是用 --safe-mode 启动的。
  6. 组织账号下的防护消息:mods are limited to your organization's by policy (allowManagedModsOnly) 表示组织只放行自己的 mod;tried to lift a deny rule in your settings 表示你的 mod 想批准一个被 deny 拒绝的调用,调用仍被拒绝。
  7. 超出时间或大小上限:一个 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日。

  1. 1.Claude Code 官方中文文档《Mods 概览》code.claude.com/docs/zh-CN/plugins/mods/overview
  2. 2.Anthropic 的发布公告claude.com/blog/claude-code-mods
  3. 3.Claude Code 官方文档《排查 mod 问题》code.claude.com/docs/zh-CN/plugins/mods/troubleshoot
  4. 4.Claude Code 官方文档《为您的组织管理 mods》code.claude.com/docs/zh-CN/plugins/mods/admin
  5. 5.claude-code-playground 仓库的 mods 目录github.com/anthropics/claude-code-playground/tree/main/claude-code/mods
  6. 6.Claude Code 插件安装文档code.claude.com/docs/zh-CN/plugins/install
  7. 7.Claude Code 仓库的 mods 目录github.com/anthropics/claude-code/tree/main/mods
  8. 8.Claude Code 官方文档《创建一个 mod》code.claude.com/docs/zh-CN/plugins/mods/create
  9. 9.Claude Code 官方文档《mod 参考》的 Limits 一节code.claude.com/docs/zh-CN/plugins/mods/reference
  10. 10.awesome-claude-code-modsgithub.com/karanb192/awesome-claude-code-mods
更多 Claude Code
Claude Code 泄露事件与公开模型、账号合同现实并列展示
Claude Code

Claude Code 源码泄露:封号风险、当前模型和帐号逻辑说明(2026)

Claude Code 在 2026 年 3 月确实发生过源码暴露。Anthropic 对 Axios 表示,这是一场发布打包错误,不是外部入侵,而且没有暴露客户数据或凭证。真正更难的部分,是把这件事和真实封号边界、当前模型合同,以及 Claude/Console 账号逻辑拆开。

15 分钟
Claude Code 的 API 密钥、网关和云平台配置示意
Claude Code

Claude Code API 配置:先选路由,再设置 Key、模型和网关

已有 Anthropic API Key,就设置 ANTHROPIC_API_KEY 并在交互模式批准使用;已有企业网关,就按其认证方式配置地址和凭据。settings 文件可能覆盖终端变量,模型与 VS Code、Desktop 入口还需分别设置。

20 分钟