一句话说清 Mods 是什么

Mod 是一种特殊的 Claude Code 插件:它不是从外部喂脚本进来,而是用 JavaScript / TypeScript 写的事件处理器,直接跑在 Claude Code 进程内部。

Claude Code 在关键节点——工具调用、提示提交、界面绘制——会主动调用这些处理器。处理器可以:

  1. 观察:记下发生了什么,放行
  2. 改写:在事件继续之前动点手脚
  3. 接管:自己处理掉,让默认行为不执行

官方文档里那句概括最准确:a mod 是 “a plugin that changes how Claude Code looks and behaves”——改的是长相和行为,而不只是”给模型多喂一份说明”。

本文信息以 Claude Code 官方文档(code.claude.com/docs/en/plugins/mods)为准,需要终端版 v2.1.287+,桌面 App 自 v2.1.286 起内置。


为什么 Mods 和以前的 hooks 不是一回事

Claude Code 早就有 hooks,但那是外部钩子:在 settings.json 里配一条 shell 命令、一个 HTTP 请求或一段 prompt,事件触发时进程外跑一下。

Mods 是进程内函数。差别的本质是一张能力表:

ModSettings HookSkillMCP Server
是什么插件里的 JS/TS 函数,进程内被调用生命周期事件上跑的脚本/HTTP/prompt一份 SKILL.md 指令外部进程提供工具
能改什么工具调用、prompt、命令、回合、界面绘制放行/拦截/记录事件、改参数模型知道什么、做什么模型有哪些工具
能画界面能不能不能不能
你写什么JavaScript / TypeScript脚本 + settings 条目Markdown任意语言的服务器

关键分水岭就在画界面这一行。以前所有扩展方式都只能在”文本与工具”层面做文章,Mods 是第一个能让插件往 Claude Code 的屏幕上画东西的机制。


Mods 能干的四件狠事

1. 画你能用的界面

在对话记录旁边开一个 pane,或在输入框上方挂一条 band,带标签页、按钮、输入框。官方示例 token-weather 在输入框上方画”上下文窗口天气预报”,每轮请求后告诉你上下文还剩多少。

2. 重绘 Claude Code 自己画的东西

工具调用那一行、转圈的 spinner、提问对话框——这些原本是 Claude Code 私有的 UI,mod 可以替换或改样式。示例代码里最小的 demo 就是给 spinner 加后缀:

// 计数由两个 hook 共享
let calls = 0

export function register(on) {
  // 每次 Claude 准备用工具时 +1
  on('tool.call', async ($, e, next) => {
    calls += 1
    $.ui.invalidate('ui.render')
    return next(e)
  })

  // 每次画 spinner 时,把计数塞进去
  on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
    return next({
      ...e,
      props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' },
    })
  })
}

跑起来后 spinner 会变成 Thinking · tool calls: 3…。整个 mod 就这一个文件。

3. 步入工具调用或请求

示例 blast-radius 的做法很典型:扣住 rm -rf、force push 这类危险 shell 命令,先展示”这条命令会改掉什么”,配确认/取消按钮,你点了才放行。更狠的玩法是把某次请求改道发给另一个模型,或者不执行工具直接替你回答。

4. 命令与跨 hook 共享状态

/command 可以直接执行你的函数,不经过 Claude 回合,Claude 忙着的时候也能跑。同一 mod 文件里的 hooks 共享变量——一个 hook 计数,另一个 hook 把计数画出来,这是普通 settings hook 做不到的(它们每次都是独立进程)。


一个 Mod 长什么样

目录结构小到可笑:

first-mod/
├── .claude-plugin/
│   └── plugin.json      # 清单:名字、版本、描述
└── hooks/
    ├── hooks.json       # 指向代码文件
    └── register.js      # 你的代码(hooks 模块)

register.js 导出一个 register(on) 函数,用 on(事件名, 处理器) 订阅事件。处理器里除了 next(e) 放行,其他所有越界操作——画界面、加命令、调模型、读文件、发网络请求——必须走 mods API。这条约束是刻意的:因为出口收敛到一个 API,Claude Code 才能在你安装前就用 claude plugin validate 列出这个 mod 会碰哪些事件、会请求哪些能力。

安装分发复用整套插件机制:/plugin install token-chart@your-org,或开发期 --plugin-dir 直接加载本地目录。Anthropic 在 claude-code-playground 仓库放了三个示例 mod(token-weather、blast-radius、replay-theater),每个都是完整插件。


最有说服力的证据:官方功能自己就是 Mod

Mods 不是官方开发、用户旁观的功能,而是官方先吃自己的狗粮。/plugin 的 Installed -> Built-in 里能看到:

内置 mod干什么
cc-plugin-diff接管 /diff,画出 diff 面板
cc-plugin-agents-md把 AGENTS.md 当项目指令加载
cc-plugin-telemetry上报分析数据,也给其他 mod 提供方法
cc-plugin-sec-default组织环境下防护用户自装 mod
cc-plugin-you-should-know后台旁观 agent,发现你可能错过的事就在输入框上方提示(默认关闭)

diff、agents-md、sec-default、telemetry 的源码已开源在 anthropics/claude-code 仓库的 mods/ 目录,每个都是带测试的完整插件。读这些源码是学习 mod 写法最快的路径——没有之一。

换句话说:Claude Code 的一部分产品功能,本身就是用它提供给用户的扩展机制写的。 架构上说到做到,这比任何宣传都硬。


安全边界:不沙箱,装之前先审

官方警告写得非常直白,mod 装上后可以:

  • 以你的权限读写任意文件、起进程、发网络请求
  • 读你的环境变量和 settings(包括里面的 API Key)
  • 看你每一个 prompt 和每一次工具调用
  • 改写 prompt / 工具调用,甚至替你批准本该弹窗询问的操作
  • 花你的模型额度

而且 mod 不在沙箱里——即便你开了 sandboxing,mod 自己起的进程也在沙箱外。唯一能改的界面是权限弹窗本身(改不了它的内容,但 mod 能提前替你点”允许”)。

防线有三道:

  1. 安装前 claude plugin validate ./some-mod,读 hooks: 和 calls: 输出
  2. 只装信任的作者与 marketplace(官方 / 社区 / 第三方分层)
  3. 组织侧用 managed settings 管控;个人侧 --safe-mode 或 disableAllHooks 一键全停

怎么上手

  1. 确认版本:claude --version,需要 v2.1.287+(桌面 App v2.1.286+)
  2. 先玩内置的:会话里 /plugin -> Installed -> Built-in,看看 cc-plugin-diff 这些长什么样
  3. 装个示例:克隆 anthropics/claude-code-playground,--plugin-dir 单会话加载 claude-code/mods/token-weather
  4. 让 Claude 替你写:官方内置了 plugin-authoring skill——在会话里直接描述你想要的功能,Claude 把 mod 写出来
  5. 自己读源码:github.com/anthropics/claude-code/tree/main/mods

第 4 步是这个功能名字的真正含义:一句话魔改自己。你不需要先学会 mods API,先让 Claude 写一个,再对着生成的代码学——这大概是最符合 Agent 时代的扩展方式了。


总结

  1. Mod = 进程内的 JS/TS 事件处理器,是 Claude Code 插件体系里最深的一档,能观察/改写/接管工具调用与 UI
  2. 画界面是分水岭:所有旧扩展方式都做不到,这是第一个能改 Claude Code 长相的机制
  3. 官方自己在用:/diff、AGENTS.md 加载都是内置 mod,源码开源,架构说到做到
  4. 不沙箱,全权限:claude plugin validate 先审再装,组织用 managed settings 兜底

最后:和 DeepSeek Harness 的「万物可插件」对比

Claude Code 不是第一个把「扩展」做成核心卖点的工具。我在《万物可插件:读懂 DeepSeek Harness 的插件化架构》里拆过 dsh:基于 Cordis 运行时,模型适配、工具注册表、会话日志、甚至 Agent 循环本身都是可替换插件,没有必须改源码才能扩展的特权内核——你看到的「产品」只是 Profile + Bundle + Patch 组合出来的默认结果。

一边是 dsh 的「万物皆插件」,一边是 Claude Code 的 Mods,两条路线摆在一起:

DeepSeek HarnessClaude Code Mods
一句话主张Everything is a plugin,核心只是组合器进程内开放扩展面,连官方功能都用它写
插件替换的对象运行时的骨架:Agent 循环、模型适配、会话存储运行时的行为与表皮:事件、工具调用、UI 绘制
扩展点Cordis 服务 + 类型化事件,apply(ctx) 挂载mods API 的 on(事件, 处理器),观察/改写/接管
代码形态TS 插件模块,inject 声明依赖,可回滚副作用JS/TS hooks 模块,随插件分发,状态跨 hook 共享
能否画界面无标准化 UI 扩展点,Web/CLI/SDK 是不同入口能:pane、band、重绘 spinner,官方第一个做到的
分发形态Profile / Bundle / Patch 配置组合marketplace + /plugin 安装,user/project/local 三作用域
谁在用给做 Agent 基础设施的开发者官方内置功能(/diff、AGENTS.md)就是 mod
成熟度developer preview,破坏性变更频繁终端版 v2.1.287+ 已默认开启

两者的差异可以压缩成一个方向感:

  • dsh 是往下挖——把 Agent 的地基做成可换的。你嫌默认循环不合适?换掉。想接别的模型后端?在适配缝上注册。改的是「Agent 是怎么跑的」。
  • Mods 是往外开——在一个已经定型的成品工具上,把行为与表皮开出安全的接口。循环不让你碰,但每次工具调用、每帧界面绘制都是你的钩子。改的是「Claude Code 用起来是什么感觉」。

两条路并不互斥,各自也有明显的空缺:

  1. dsh 缺「画界面」:插件能换循环,但没有一个标准化的方式往 Web UI 上挂自定义面板——扩展点是事件与服务,不是像素
  2. Claude Code 缺「换骨架」:Mods 玩得再花,Agent 循环、模型调度层都是闭源核心,你永远只能在它给的事件缝里做事
  3. 分发哲学不同:dsh 的 Patch 层是给「组合产品」的开发者用的,粒度在配置行;Claude Code 的 marketplace 是给「装功能」的终端用户用的,粒度在插件包

值得留意的收敛信号:两边都走向了「官方能力 = 用户扩展机制的实例」——dsh 的产品本体是默认插件组合,Claude Code 的 /diff 是内置 mod。当一家开始用自己的扩展机制写核心功能,另一家也被迫这么做时,「运行时愿意打开多少」就成了 AI 编程工具真正的护城河。

Mods 出现意味着这件事又进了一步:竞争正在从「模型多强」转向「谁的运行时愿意真正打开」——而 dsh 证明了打开到骨架是什么样,Mods 证明了打开到皮肤是什么样。剩下的问题是:谁会第一个把骨架和皮肤一起打开?