Claude Code mods let developers reshape the coding agent itself. A mod can watch or change a prompt, intercept a tool call, add a command, or draw a small interface in Claude Code. The feature is powerful because it runs inside the agent’s workflow; that same access makes trust and review essential.
Anthropic introduced Claude Code mods on 1 October 2026. They are JavaScript or TypeScript event handlers packaged as plugins, available in the Claude Code command-line interface and the Code tab of the desktop app. This guide explains the event chain, shows how mods differ from other extensions, and walks through a complete sample that blocks a basic Git force-push command.
What a Claude Code mod is
A mod is a plugin with a hooks module. Claude Code calls its registered function when a named event occurs, such as a prompt being submitted, a tool being called, a turn finishing or interface content being drawn. A handler can observe the event and pass it onward, rewrite what later handlers see, or answer the event itself. An answer can replace the normal action; for example, a handler can deny a tool call before Bash runs.
Think of the handler as middleware around a moment in Claude Code. The handler receives the event data, the $ API for approved interactions with the host, and a next function. Calling next(e) forwards the event through the rest of the chain and eventually to Claude Code’s normal behavior. Calling next with edited data changes what follows. Returning a result without calling next takes over that event. The exact event names and types are versioned, so use the declarations generated by the Claude Code version you run.
How the event chain works
- The plugin loads. Its manifest identifies the plugin. The plugin’s
hooks/hooks.jsonpoints to a JavaScript or TypeScript module. - The module registers handlers. Claude Code calls the module’s exported
register(on)function. The module callson(event, matcher, handler)for each event it wants to handle. - An event happens. When Claude Code is about to call a tool, for example, it sends the event through matching handlers in load order.
- Each handler chooses what happens next. It can inspect the event and call
next(e), alter the event before callingnext, or return an answer such as a denial. - Claude Code completes the event. Later handlers and the host see the result. A handler can also react after the downstream action by awaiting
next(e)and inspecting its result.
When multiple mods register for the same event, load order matters. The first-loaded mod receives the event first and sees the downstream result last. That can make mod composition useful, but it also means an early handler can affect what later handlers receive. Teams should make load order part of their review.
What mods can change
- Prompts and context: add or revise material before it reaches the model, subject to the organization’s managed controls.
- Tool calls: inspect a request, rewrite it, let it continue, or refuse it before the tool executes.
- Permissions: answer some permission events. Managed settings and the built-in security mod can constrain user-installed mods.
- Tool output: transform returned content before it becomes part of the model’s conversation, such as removing a secret.
- Interface: add a pane or a band above the prompt, or change selected interface elements. Mods can target the terminal, Claude Desktop’s Code tab and the VS Code extension; drawing support depends on the surface.
- Commands and tools: register a slash command or a tool implementation that runs through the mod API.
Some first-party functionality now ships as mods. Anthropic’s example is /diff, which can be disabled or replaced from the plugin controls. Mods extend the exposed Claude Code event system; they do not edit Claude Code’s source code.
Mods compared with hooks, skills and MCP
Claude Code already has several extension points, and a mod is not always the right one. A settings hook runs a configured shell command, HTTP request or prompt at a lifecycle event. A skill supplies reusable instructions or files for Claude to follow. An MCP server supplies tools or data through the Model Context Protocol. A mod runs as JavaScript or TypeScript inside Claude Code and can hold session state, participate directly in event handling and draw UI.
For a simple deterministic script that runs before or after a tool, a settings hook may be easier to maintain. Use a skill when the main need is reusable guidance. Use MCP when you need to connect a system or data source as tools. A mod makes sense when the workflow needs to intercept or replace an event, maintain live state, present controls, or add a command in the Claude Code interface.
A complete sample: block a basic force push
This teaching plugin refuses direct Bash commands that contain a recognizable git push --force or git push -f form. It lets other Bash commands and all other tools continue as usual. It is deliberately small enough to inspect. It is not a security boundary: shell quoting, aliases, scripts and wrappers can evade a simple text check. Use Git hosting branch protections and managed controls for real enforcement.
1. Create the plugin files
block-force-push/
├── .claude-plugin/
│ └── plugin.json
├── hooks/
│ ├── hooks.json
│ ├── register.ts
│ └── is-force-push.ts
└── tests/
├── register.test.ts
└── is-force-push.test.ts
2. Add the manifest
// .claude-plugin/plugin.json
{
"name": "block-force-push",
"version": "0.1.0",
"description": "Blocks basic direct Git force-push commands in Claude Code",
"author": { "name": "Your Name" }
}
The manifest gives the plugin its name and metadata. It does not contain the handler code.
3. Point the plugin at its module
// hooks/hooks.json
{
"description": "Refuses basic Git force-push Bash calls",
"modules": ["./register.ts"]
}
4. Register the tool-call handler
// hooks/register.ts
import type { On } from 'claude-code'
import { isBasicForcePush } from './is-force-push'
export function register(on: On) {
on('tool.call', ($, event, next) => {
if (event.tool === 'Bash' && isBasicForcePush(event.command)) {
return {
deny: 'This mod blocks a basic Git force-push command. Review and run it manually if appropriate.',
}
}
return next(event)
})
}
The matcher makes this handler relevant to tool-call events. The event.tool check narrows its work to Bash. When the helper recognizes a risky command, the handler returns a denial without calling next, so the shell command is not passed down the chain. In every other case, next(event) preserves the ordinary behavior.
5. Add the command checker
// hooks/is-force-push.ts
export function isBasicForcePush(command: string): boolean {
// This example recognizes simple command segments. It does not parse shell syntax.
return command
.split(/[;&|\n]+/)
.some((segment) => {
const words = segment.trim().split(/\s+/)
if (words[0] !== 'git') return false
const subcommand = words.indexOf('push', 1)
if (subcommand === -1) return false
const args = words.slice(subcommand + 1)
return args.some((arg) => arg === '--force' || arg === '-f' || /^-[a-z]*f[a-z]*$/.test(arg))
})
}This helper handles plain direct forms such as git push --force origin main, git push -f origin main, and the simple chained form echo ready; git push -f. It does not parse quoted shell strings, skip Git global options, understand aliases, inspect scripts, or detect other force forms such as a leading +main refspec or --force-with-lease. It may also match text that is not a command in more complex shell input. Treat the tests as examples of its intended scope, not proof of comprehensive protection.
6. Test the helper and handler
// tests/is-force-push.test.ts
import { describe, expect, test, tier } from 'claude-code/testing'
import { isBasicForcePush } from '../hooks/is-force-push'
tier('user')
describe('isBasicForcePush', () => {
test('recognizes the long force flag', () => {
expect(isBasicForcePush('git push --force origin main')).toBe(true)
})
test('recognizes the short force flag', () => {
expect(isBasicForcePush('git push -f origin main')).toBe(true)
})
test('recognizes a simple chained command', () => {
expect(isBasicForcePush('echo ready; git push -f')).toBe(true)
})
test('allows an ordinary push', () => {
expect(isBasicForcePush('git push origin main')).toBe(false)
})
test('does not mistake another Git command for a push', () => {
expect(isBasicForcePush('git branch -f main')).toBe(false)
})
})
// tests/register.test.ts
import { describe, expect, test, tier } from 'claude-code/testing'
tier('user')
describe('register', () => {
test('denies a basic force push', async ($, on) => {
on('tool.call', () => ({ result: 'passed through' }))
const result = await $.tool.call({
tool: 'Bash',
command: 'git push --force origin main',
})
expect(result).toEqual({
deny: 'This mod blocks a basic Git force-push command. Review and run it manually if appropriate.',
})
})
test('passes an ordinary push to the next handler', async ($, on) => {
on('tool.call', () => ({ result: 'passed through' }))
const result = await $.tool.call({ tool: 'Bash', command: 'git push origin main' })
expect(result).toEqual({ result: 'passed through' })
})
test('passes other tools through', async ($, on) => {
on('tool.call', () => ({ result: 'passed through' }))
const result = await $.tool.call({ tool: 'Read', file_path: '/tmp/notes.txt' })
expect(result).toEqual({ result: 'passed through' })
})
})Claude Code’s plugin test runner executes these tests against its mod runtime. The test registers a downstream handler with on, which stands in for what comes after this mod. That lets the test check both paths: a denial short-circuits the chain, while an ordinary action reaches the next handler.
7. Inspect, validate and run it
# Check your installed version; function hooks are enabled by default in supported versions.
claude --version
# Read every file before running it. This reports the hooks and requested host calls.
claude plugin validate ./block-force-push
# Run the included tests without installing it in your normal workflow.
claude plugin test ./block-force-push
# Load the local plugin for a test session.
claude --plugin-dir ./block-force-push
In the interactive session, inspect /plugin to confirm the mod loaded. Try a harmless ordinary push string by asking Claude to reason about it, and confirm a direct force-push attempt is refused. Do not test by actually pushing to a repository. The exact setup and command options can change; consult Anthropic’s current docs for the Claude Code version installed.
How to install and disable a community mod
As checked on 31 October 2026, Anthropic’s docs list Claude Code 2.1.287 or later for the terminal and 2.1.286 for the Desktop app Code tab. The app bundles its own version, so check with claude --version in the terminal or /status in the Code tab before troubleshooting.
Mods are installed through Claude Code’s plugin system. For a marketplace plugin, add the marketplace and then install the plugin using the names shown by that marketplace:
claude plugin marketplace add <owner>/<repository>
claude plugin install <plugin-name>@<marketplace-name>
From an active Claude Code session, the plugin interface can also install it. If an already-open session does not pick up a shell install, run /reload-plugins. Use claude plugin validate ./plugin-folder after downloading the source to list the hooks and host API calls requested before execution. Validation helps you review the code’s declared behavior; it does not prove that the code is safe.
Disable one plugin from the Installed tab in /plugin. For a temporary session that disables installed mods and other customizations, use --safe-mode. A managed configuration can also disable user-installed hooks; team and enterprise administrators can restrict approved marketplaces and centrally managed mods. Check current version-specific behavior before relying on a switch.
The security model: a mod has your access
Anthropic says mods are not sandboxed. Their code runs with the same access as Claude Code. Depending on the functions it calls, a mod can read and write files, start processes, make network requests, see prompts and tool calls, change requests, and use model calls that consume your plan or API usage. It can act without a separate approval prompt in some configurations. If Claude Code’s Bash sandbox is enabled, that does not automatically put a process started by the mod inside the same sandbox.
Before enabling one, inspect its complete source and marketplace, run the validator, and look at the listed events and requested host calls. Be especially careful with file reads, environment or settings access, network calls, process execution, permission decisions and model calls. For work repositories, test in a disposable checkout and avoid secrets. A plugin listing or a passing test is not a security audit.
Anthropic documents a built-in sec-default mod for managed settings and Team or Enterprise contexts. It protects some organization-managed rules and denies user-installed mods the ability to loosen certain controls. The exact behavior depends on how Claude Code is authenticated and managed; do not assume it neutralizes every untrusted mod. Teams should use administrator controls, pinned plugin sources and their own review process.
Should you use Claude Code mods?
Mods make sense when you need Claude Code to behave differently at a specific event or when a visible control would help you supervise a workflow. They can turn an idea such as “show the active branch,” “redact a known secret pattern,” or “hold a risky command for review” into a plugin that stays present in the coding session.
Start with the smallest possible handler, write tests for both the action it changes and the normal pass-through path, validate the plugin, then try it in a low-risk session. For enforcement that protects a repository or production system, put the real control in Git hosting, CI, operating-system permissions or centrally managed policy. A mod is a customizable layer in the developer’s toolchain, and its code deserves the same scrutiny as any program running with your account.
Editorial disclosure: This article was researched and drafted with AI assistance, then revised and checked against the linked sources. The cover is an AI-generated conceptual illustration, not a Claude Code screenshot. This article has no sponsorship, affiliate links or paid product placement.
Sources and further reading
Sources
- Customize Claude Code with mods — Anthropic
- Mods overview — Anthropic Claude Code documentation
- Mods — Anthropic Claude Code documentation
- Getting started with Claude Code mods — Anthropic
- Built-in Claude Code mods source and testing — Anthropic on GitHub
- Customize Claude Code with plugins — Anthropic



