Claude Code Mods: Extending the AI Coding Runtime with Functional Hooks
This article dissects Claude Code's Mods system — functional hooks that let developers intercept tool calls, prompts, and UI rendering — explaining the event chain, four built-in mods (diff, agents-md, sec-default, telemetry), security boundaries, and when to choose mods over Skills or MCP.
1. What Is a Mod? Placing It Back in the Plugin System
Mods are still organized and distributed as plugins. They have a standard .claude-plugin/plugin.json and declare hooks in hooks/hooks.json via a modules entry pointing to a code entry that exports register(on, options). The on function registers event handlers. A single plugin can contain mods, Skills, and other components simultaneously, so these concepts are not mutually exclusive.
To avoid confusion, choose by "what needs to change":
Team code-review process → Skill (provides instructions and reference material for the model)
Query tickets, databases, external systems → MCP (provides callable tools and data connections)
Run existing check scripts before/after tool calls → Settings hook (executes configured handlers on lifecycle events)
Show interactive panel beside chat, transform commands or event handling → Mod (runs functional hooks inside Claude Code)
Bundle and distribute these capabilities together → Plugin (packaging and distribution unit)
This table is a selection entry point; capabilities do overlap. Existing settings hooks can also block calls, change parameters, or supply context. The distinctive trait of mods is that internal events, shared state, and UI extensions can compose into a continuously running capability. Therefore, if the problem is merely repetitive code-review prompts, a Skill usually suffices. Only when the requirement lands on the execution path or workbench interaction does it justify the code and compatibility cost of a mod.
2. Core Mechanism: Events Pass Through an Interceptable Call Chain
A functional hook typically receives three parameters: $: mods API for reading files, manipulating UI, registering commands, etc. e: current event input, e.g., tool name and call arguments. next: passes the event to the next handler and obtains the result.
The key is next. return next(e) continues the original flow; passing a modified copy rewrites the input; not calling next and returning an allowed result handles the event at this layer, preventing downstream handlers from running. await next(e) allows post-processing of the return value. The event object e is deeply frozen and cannot be mutated directly.
Mods can forward events downstream or answer at the current layer and terminate the chain. For developers familiar with HTTP middleware, this structure is familiar, but the scope is broader: tool calls, user input, commands, model requests, and UI rendering each have corresponding events. Each event allows different return structures; you cannot arbitrarily transplant the refusal format of tool.call to other events.
A direct consequence: plugin load order affects final behavior. An earlier mod that rewrites input means later mods see the rewritten input; an earlier mod that answers directly may prevent downstream handlers from ever receiving the event. Placing a logging mod at the end of the chain does not guarantee it records every original operation.
3. Four Built-in Mods Demonstrating Four Different Transformations
The official repository currently lists four built-in mods: diff, agents-md, sec-default, and telemetry. These are source code built with Claude Code; the repository notes they are not listed in the marketplace — the client-bundled versions are used at runtime.
diff: Attaching Change Inspection to Ongoing Work
/diffshows uncommitted file changes and refreshes after Claude edits files or runs commands. In layouts that support docking, it can appear beside the conversation; UI and terminal width affect presentation. A notable detail is the file's "ask" button: selected file diffs can be attached to the next user input as context and are then released. This connects "seeing the change" with "asking about the change." It also supports different comparison baselines and viewing edits from an earlier turn. The insight here is interaction distance: users no longer need to copy diffs from another window and switch back to explain which segment they are questioning. Of course, showing a diff does not mean the change has passed tests or review.
agents-md: Integrating Project Instruction Files into Context Building
agents-mdbrings AGENTS.md into Claude Code's project instruction loading process. The current source provides four instructionFiles modes: only CLAUDE.md; fall back to AGENTS.md when no project-specific Claude instruction file exists; load both; and managed-only.
The default fallback is not as simple as "auto-add AGENTS.md wherever CLAUDE.md is missing." The source checks whether the project already has a Claude instruction file, including .claude/CLAUDE.md and CLAUDE.local.md. Choosing joint loading also requires handling duplicate imports and conflicting specification content. For teams using multiple coding agents, this provides an entry point for shared project conventions. But unifying the filename is only step one; each tool's load timing and instruction boundaries still need separate verification. In particular, managed-only must not be understood as an absolute guarantee that no project instructions appear: the mod's documentation retains the engine's restriction of attaching nested CLAUDE.md during Read operations.
sec-default: Holding the Line Between Org Configuration and Personal Plugins
When personal plugins can rewrite events, organizational rules may also enter rewritable paths. sec-default maintains existing boundaries so that protected organizational hooks, instructions, settings, and tool policies cannot be rewritten by personal plugins; it does not add new business policies itself.
The source uses operations such as skipping the personal plugin layer, rejecting specific sources, and allowing others. It sits at the outer layer on machines with managed settings or in Team/Enterprise org scenarios, and is further influenced by the managed prependPlugins configuration. This shows that permission governance has become part of extension design. However, this protection layer does not equate to an OS-level sandbox for mods; the distinction is explained later.
telemetry: Turning Internal Observability into a Composable Interface
telemetryadds $.telemetry via engine.create when needed, handling log, mark, and similar events so other built-in features can record usage. It also stores interface types in its own types/index.d.ts, giving implementers and callers a shared contract.
Two boundaries exist: the current implementation serves built-in plugins and rejects calls from installed plugins; when Claude Code's own analytics are disabled, these analytics data are not sent either. Therefore, it should not be presented as a general-purpose telemetry service that third-party authors can freely use.
Together, the four examples show mods already covering user interaction, context loading, organizational policy, and internal services. The assessment is that Anthropic is moving a portion of original product behavior into the same extension structure to make them easier to read, compose, and maintain; this does not prove the entire Claude Code kernel can be replaced by plugins.
4. A Small Example: Counting File Edit Requests Reaching the Mod
A restrained example demonstrates the code shape: count Edit and Write requests arriving at the mod, displayed via /edit-count. It does not modify call arguments or invoke file/network APIs.
Three files: .claude-plugin/plugin.json:
{
"name": "edit-counter",
"version": "0.1.0",
"description": "Count Edit and Write requests received by this mod"
} hooks/hooks.json:
{
"modules": ["./register.js"]
} hooks/register.js:
export function register(on) {
let edits = 0;
on('session.start', async ($, e, next) => {
await $.command.register({
name: 'edit-count',
description: '显示本次模块加载后的编辑请求数',
});
return next(e);
});
on('tool.call', { tool: ['Edit', 'Write'] }, async ($, e, next) => {
edits += 1;
return next(e);
});
on('command.run', { command: 'edit-count' }, async () => ({
text: '收到的编辑请求:' + edits,
}));
}This counts requests : subsequent rejections or failures may still be counted, while requests already intercepted by earlier mods never reach here. It also does not separate main session from sub-agents. Module reload resets the in-memory counter, so it cannot serve as a persistent audit log. Interface references: command registration, matchers, and call chain.
In a mods-enabled client, inspect structure and capability declarations first, then load the directory:
claude --version
claude plugin validate ./edit-counter
claude --plugin-dir ./edit-counterAfter entering an interactive session, use /plugin to confirm loading, then run /edit-count. When writing TypeScript, execute /plugin-types in that session to get local declarations; with test files, run claude plugin test ./edit-counter. validate shows and checks declared hooks and API calls but does not replace behavioral testing or manual review. This is a teaching example written against the current official interface. The author's environment runs v2.1.218, below the terminal threshold in current docs, so no on-machine run verification was claimed and the client was not auto-upgraded.
5. Extending the Runtime Also Expands the Trust Boundary
A common misconception when discussing mods: "It can only call host interfaces via $, so it should be quite safe." The source declarations state that hook modules lack the usual Node/DOM environment and external operations must go through host APIs, allowing Claude Code to identify which interfaces a module calls in advance. But controlled interface entry points and restricted operational permissions are two different things : recognizing $.fs.read does not mean it is only allowed to read the current project.
Claude's tool calls and mod-spawned processes pass through different controls; both may ultimately touch files or network, but the controls they traverse differ. The Bash sandbox in the diagram refers only to Bash commands run by Claude when sandboxing is enabled.
Official admin docs explicitly state: mods have no sandbox and can read/write files, spawn processes, and access the network with user permissions. sec-default defaults to guarding deny rules on Claude tool calls, but Read(.env) being prohibited does not mean a mod cannot use $.fs.read to read the same file . Mod-spawned processes are also not isolated by Claude's Bash sandbox; to restrict a mod's own API calls, you must prevent it from loading or use an org policy mod to manage the corresponding calls.
Thus, selecting a mod requires reviewing source code, provenance, and updates — not just what it displays in the UI. A panel that appears to only show token usage but also declares process spawning, environment variable reads, or network requests should have a functional justification matching those capabilities.
This gives claude plugin validate 's hooks and calls output practical value: it helps narrow the review scope. However, the interface list will not automatically judge whether outbound data is appropriate, file write paths are reasonable, or business rules are correct.
6. What to Anticipate: Workbenches Closer to the Task
From the disclosed mechanisms, the value of mods will likely appear first in three categories of needs. The following are capability-based application projections, not all yet official built-in features.
First: place the review object next to the operation. diff already demonstrates this path. Similarly, teams can put files pending review, test status, or relevant context in panels, letting users act on specific objects. The gain depends on whether it reduces searching and switching; more panels do not necessarily mean clearer work.
Second: make existing processes appear at the right moment. A Skill can tell Claude: "Check change scope before deploy." A mod can execute program logic when relevant events fire, pausing to ask if necessary. The cost is that the team must handle call-chain ordering, failures, timeouts, and different call entry points — not just write code that works in the ideal case.
Third: shape the work environment per project. Data analysis, infrastructure changes, and frontend development each benefit from different visible statuses and action entry points. Programmable UI lets these differences enter the workbench directly. But UI capabilities have client differences: current docs show terminal and Desktop can display mod UI, while VS Code chat panel and claude -p do not render these drawings. Team-facing mods need text or command fallbacks for those scenarios.
Also note a cost: mod logic need not call the model, but it can invoke $.model.complete for extra model usage or alter context sent to the model. How many operations it saves, how much latency and usage it adds, must be measured per implementation — cannot be deduced from "uses mods" alone.
7. The Starting Need Decides Whether It's Worth Maintaining
If trying it out, start with a sufficiently concrete problem: repeatedly switching windows to check diffs, wanting to show impact scope before an operation, or needing a unified team interaction entry point. Begin with a small mod that only observes events; it's usually easier to see what it receives, when it runs, and which clients it works on.
If an existing Skill, settings hook, or MCP fully solves the problem, continuing to use them is perfectly reasonable. Introducing a mod means long-term maintenance of code behavior, load order, data access, and client compatibility; these costs should be borne by clear work benefits.
For the author, the most noteworthy change mods bring is that AI coding tools now let developers write their own processes into the runtime. Teams can decide not only which specs Claude should read, but also what users see during operations, which events are handled programmatically, and which steps continue to the engine.
What's worth watching next is not just how many new panels appear, but whether teams can make these customizable behaviors explainable, auditable, and still reliable after updates.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ops Development & AI Practice
DevSecOps engineer sharing experiences and insights on AI, Web3, and Claude code development. Aims to help solve technical challenges, improve development efficiency, and grow through community interaction. Feel free to comment and discuss.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
