Anthropic shipped a new extension point for Claude Code called mods, available from version 2.1.287 on. The name undersells what changed. Claude Code already had three ways to extend it (settings hooks, skills, MCP servers), and all three work the same way: they sit outside the agent loop and hand it something. A settings hook is a shell command the loop runs and waits on. A skill is a markdown file the model reads. An MCP server is a separate process that exposes tools over a protocol. None of them run inside Claude Code’s own process, and that boundary is the whole reason none of them can draw a pane, hold a tool call mid-flight, or rewrite what the spinner says while the model is still thinking.
A mod runs inside the process. It’s JavaScript or TypeScript that Claude Code imports and calls directly, as a function, when an event happens: a tool call, a submitted prompt, a frame of the interface being drawn. The function gets the event and three options: let it continue unchanged, change it, or answer it itself and skip the usual handling. That’s the entire model. Anthropic’s own documentation gives the smallest possible example, and nothing about the API is hidden behind it. This is close to the complete surface for a working mod:
let calls = 0
export function register(on) {
on('tool.call', async ($, e, next) => {
calls += 1
$.ui.invalidate('ui.render')
return next(e)
})
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
}
Two hooks, one shared variable. The first counts tool calls and asks the interface to redraw. The second intercepts Claude Code’s own spinner component and appends the count to whatever it was already going to say. Eleven lines turn “Thinking…” into “Thinking · tool calls: 3…”, and the mechanism generalizes to everything else a mod can do: a pane beside the transcript, a band above the prompt, a /command that runs without spending a model turn, a hook that holds a risky shell command and asks the user a question before it runs.
What the sandbox boundary actually buys you
The feature comparison is less interesting than the trust boundary underneath it, and Anthropic’s docs are unusually direct about where that boundary sits. A settings hook runs as a subprocess with whatever input/output contract you wrote. An MCP server runs as a separate process and can only act through the tools it declares. A mod runs as code loaded into Claude Code’s own process, with your permissions, and nothing isolates it from the rest of what Claude Code can do. The documentation lists this plainly: a mod can read any file your account can read, read environment variables and settings files (including an API key sitting in either), see every prompt and tool call in your session, rewrite a prompt before it’s sent, submit text as if you’d typed it, and approve a tool call before you’re ever asked.
That last one is the sharp edge. Claude Code’s permission system is supposed to be the backstop: a deny rule or an ask rule that makes a risky command stop and wait for a human. A mod that registers a tool.call hook sees that event before the permission check does anything the user would notice, and it can answer the event itself. The documentation’s own phrasing: such a mod “can approve one that an ask rule would prompt for, or that one of your own PreToolUse hooks blocked,” including, in some configurations, a call that a deny rule would otherwise refuse. If you turn on sandboxing, it isolates the Bash commands Claude runs, not the mod. A process a mod starts runs outside that sandbox entirely.
None of this is a defect report; it’s the cost the feature has to pay. A pane that charts your context window in real time, or a guard that catches rm -rf before it executes and shows you exactly what it would delete, has to sit close enough to the loop to see the event before anything happens and close enough to the interface to draw. Settings hooks and MCP servers are kept at arm’s length specifically so that a buggy or malicious one can only do what its narrow contract allows. A mod gives up that distance on purpose, in exchange for capabilities nothing at arm’s length can offer.
The part that keeps this honest
What keeps the whole thing from being a plain liability is that a mod can’t hide what it’s capable of. The documentation states the constraint directly: to do anything outside its own code (draw, call a model, read a file, start a process, make a network request) a hook has to call the mods API, and there’s no other way in. That means the full set of things a given mod might do is enumerable before you ever run it. claude plugin validate on a mod’s directory lists every event it handles and every API call it makes, without executing any of it. You can read that output and see, for instance, that a mod never calls the network-request method at all, or that it calls the tool-approval method and now you know to go look at exactly how.
That’s a meaningfully different trust model from “this plugin is probably fine,” and it’s also exactly what the earlier three mechanisms didn’t need, because their narrow contracts made the question moot. A skill can’t approve a tool call because skills aren’t code. A settings hook’s blast radius is whatever that one script does, in plain sight, in your settings file. Mods reopened the question only because they reopened the capability, and Anthropic’s answer to “how do I know what this does” is a static list of calls rather than a sandbox. Whether that’s enough is still a judgment the person installing the mod has to make. The tool just makes sure they have something to judge.
References
- Mods overview — Claude Code documentation, accessed October 2026.