What a mod is: event handlers inside a plugin
Per Anthropic’s docs, a mod is a plugin that changes how Claude Code looks and behaves, made of JavaScript or TypeScript event handlers that Claude Code calls when Claude is about to use a tool, you submit a prompt, or part of the interface is drawn. The docs call these handlers hooks; the older kind configured in a settings file is now a “settings hook”.
A hook runs before Claude Code acts, so it can observe the event and let it continue, rewrite it first, or answer it so the usual behaviour doesn’t run (refusing a command, say). Anything beyond its own code — drawing, commands, model calls, files, processes, network — goes through the mods API, the $ in the code, which is why Claude Code can list what a mod does before you install it. Mods on the same event run in load order, so mods from different authors stack.
Some built-in features are already mods: /diff is drawn by cc-plugin-diff, which you can disable in /plugin (the older built-in /diff takes over) or replace with your own. Anthropic plans to move more built-in features to mods.
What mods can do that hooks, skills and MCP can’t
Settings hooks, skills, status lines and MCP servers work from outside Claude Code. A mod runs inside, so it can:
- Draw an interface: a pane beside the transcript or a band above the prompt, with tabs, buttons and text fields;
- Redraw Claude Code’s own interface: a tool call’s row, the spinner, the dialog Claude asks questions in;
- Step into a tool call or request: hold it while asking you something, answer without running the tool, or send one request to a different model;
- Run code on a command: a /command that runs your function at once, with no Claude turn, even while Claude is working;
- Share data between hooks: one hook records, another shows.
The launch post adds rewriting a prompt before it reaches the model, retrying a tool call, approving or denying a permission request, and redacting secrets from tool output before Claude reads it. The docs’ comparison:
| Mod | Settings hook | Skill | MCP server | |
|---|---|---|---|---|
| What it is | Functions in a plugin that Claude Code calls in its own process | A shell command, HTTP request or prompt run on a lifecycle event | A SKILL.md file of instructions Claude reads | An external process or service that gives Claude tools |
| What it can change | Tool calls, prompts, commands, turns, and what the interface draws | Whether a tool call or prompt goes ahead, a tool call’s arguments and result, context added for Claude | What Claude knows and does | Which tools Claude has |
| Draws in the interface | Yes | No | No | No |
| What you write | JavaScript or TypeScript | A script and a settings.json entry | Markdown | A server in any language |
| Pick it when | You want a pane, a band above the prompt, a custom command, or to rewrite an event | You want to block, allow or log an event with a script you already have | You keep pasting the same instructions into chat | Claude needs to reach an external system |
A plugin can hold all four, so a mod can ship next to a skill and an MCP server. Anthropic’s advice: if a settings hook, skill or MCP server already does the job, compare them before you write a mod.
Before you start: the version check
- Terminal: Claude Code v2.1.287 or later; check with
claude --versionand update if needed. - Desktop app: its bundled Claude Code supports mods from v2.1.286; enter
/statusin a local session in the Code tab and read the Claude Code row. - On by default. If you set
CLAUDE_CODE_ENABLE_FUNCTION_HOOKSduring early access, remove it: v2.1.287 and later ignore it, so a 0 no longer keeps mods off. - What’s loaded:
/pluginshows a dim line such as1 mod active · first-modunder its tabs.
Try a sample mod first
Anthropic’s anthropics/claude-code-playground repository keeps sample mods in claude-code/mods. Each is a complete plugin with a README on how it was built, shared as-is without support:
token-weather: draws a forecast of your context window above the promptblast-radius: holds a risky shell command (rm -rf, force push), shows what it would change, with buttons to proceed or cancelreplay-theater: adds a /replay command that steps through the file edits Claude made in the last turn
Load one for a single session with --plugin-dir, or add the clone’s claude-code/mods folder as a marketplace to keep it (it stops loading if you move or delete the clone):
# Try one for a single session
git clone https://github.com/anthropics/claude-code-playground
claude --plugin-dir ./claude-code-playground/claude-code/mods/token-weather
# Keep it: in a Claude Code session, add the folder as a marketplace and install
/plugin marketplace add ./claude-code-playground/claude-code/mods
/plugin install token-weather@claude-code-playground-modsThe easy way: ask Claude to write it
Describe the mod in an interactive session and Claude writes it, guided by the built-in plugin-authoring skill (where mods go, which events and methods your version has, how they load); you can also run /plugin-authoring yourself.
- Describe it, for example make a mod that shows the current git branch above the prompt. Claude writes it under
~/.claude/dev-mods/plus the session ID; as~/.claudeis a protected path, the default and acceptEdits modes ask before each file. - Choose hot reloading on the first save: Enable for this session loads the mods when the turn ends and reloads them after each change; Not now waits until the session next starts.
- Check and iterate:
/plugin→ Installed lists the mod; ask Claude for changes and they reload when the turn ends.
Such a mod loads only in its own session and is cleaned up after cleanupPeriodDays; copy it to, say, ~/mods/git-branch and load it with claude --plugin-dir ~/mods/git-branch to keep it. It won’t load in claude -p or dontAsk mode, in untrusted workspaces, under --safe-mode, --bare or disableAllHooks, or where managed settings block it. Writing it uses your plan like any other session.
Write your first mod yourself: three files
Anthropic’s tutorial mod, first-mod, counts Claude’s tool calls, shows the count beside the spinner and adds /tally. No Node.js, bundler or build step: Claude Code loads .js and .ts files directly. The files are .claude-plugin/plugin.json, hooks/hooks.json, hooks/register.js:
first-mod/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.js1. Create the folders, then 2. save the manifest as first-mod/.claude-plugin/plugin.json. Mods need no special fields, but names that look like Anthropic’s own, such as ones starting with claude-, fail validation:
mkdir -p first-mod/.claude-plugin first-mod/hooks{
"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" }
}3. Point Claude Code at your code: the modules key in first-mod/hooks/hooks.json lists one path, relative to that file. Having it is what makes the plugin a mod.
{
"description": "The first-mod hooks module",
"modules": ["./register.js"]
}4. Write first-mod/hooks/register.js. Claude Code calls your exported register with on; each call to on registers a hook for one event:
// The count, shared by the hooks below
let calls = 0
// Claude Code calls this once when the mod loads
export function register(on) {
// Runs when the session starts: add the /tally command (observe)
on('session.start', async ($, e, next) => {
await $.command.register({
name: 'tally',
description: 'Show how many tool calls Claude has made',
})
return next(e)
})
// Runs each time Claude is about to use a tool (observe)
on('tool.call', async ($, e, next) => {
calls += 1
$.ui.invalidate('ui.render')
return next(e)
})
// Runs only for /tally and returns its own text (answer)
on('command.run', { command: 'tally' }, async () => {
return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
})
// Keeps Claude Code's spinner, with the count added (rewrite)
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
}Each hook receives $ (the mods API), e (the event as plain data) and next (passes the event on and returns the result). session.start and tool.call observe; command.run answers without calling next, limited to /tally by its matcher { command: 'tally' }; ui.render rewrites the spinner’s suffix.
5. Load it and try it:
# Load first-mod for this session only
claude --plugin-dir ./first-mod
# Check the command without an interactive session
claude -p "/tally" --plugin-dir ./first-mod
# first-mod: Claude has made 0 tool calls since this mod loadedAsk for something that takes a few tool calls (list the files here and read the README): the spinner reads Thinking · tool calls: 2…, and /tally then prints first-mod: Claude has made 2 tool calls since this mod loaded with your own count. No /tally in the command list means the module didn’t load.
6. Edit it live: change ' · tool calls: ' to ' · tools used: ' and save. first-mod reloads and the next spinner shows the new text; each reload runs register again, so the count resets.
Prefer TypeScript? Name the file register.ts and set modules to ["./register.ts"]. When loading from --plugin-dir, Claude Code writes .d.ts declarations for your exact version into .claude-plugin/types/, so your editor can autocomplete and type-check hooks. When a docs page and those files disagree, trust the files.
Install, validate, test, turn off, share
Install with /plugin install <plugin>@<marketplace> in a session or claude plugin install <plugin>@<marketplace> in your shell (then run /reload-plugins in any open session).
Validate with claude plugin validate: Claude Code’s load-time checks and static analysis, without running your code. hooks: lists the events (filters in braces), calls: every mods API method called; env reads: / env writes: appear when a module touches environment variables:
claude plugin validate ./first-mod
❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
❯ ./register.js calls: $.command.register, $.ui.invalidate
✔ Validation passedA missing event usually means a typo ("tool.calls" is not an event). Write API calls in full, like $.store.get('notes'), not through an alias or a destructured $, and event names as string literals.
Test with claude plugin test, which fires events at your hooks with no session, sign-in or network.
Turn off one mod in /plugin → Installed; every installed mod for one session with --safe-mode (your other customizations go too); or every installed mod everywhere with "disableAllHooks": true in ~/.claude/settings.json (settings hooks and a custom status line stop too). Built-in mods such as /diff are disabled separately in /plugin.
Share it as a folder or .zip, through your team’s own marketplace, organization-wide via managed settings, or publicly via a public marketplace or Anthropic’s directory. Develop against the folder with --plugin-dir: installed plugins are cached by version, so edits reach them only after a version bump and reinstall.
Security: what a mod can reach
Anthropic is blunt: mods have the same access to your machine as Claude Code itself, are not sandboxed, and should come only from sources you trust. Once loaded, a mod can:
- Act as you: read and write your files, start programs, make network requests;
- Read your secrets: environment variables and settings files, API keys included;
- See your session: every prompt you send and every tool call Claude makes;
- Change your session: rewrite prompts and tool calls, submit a prompt as if you typed it, message your other sessions;
- Act without asking: approve a tool call before you are asked;
- Spend your usage: call a model on your plan or API key.
Sandboxing doesn’t help here: it isolates the Bash commands Claude runs, not processes a mod starts. A mod can approve a call that an ask rule would prompt for, or that your own PreToolUse hook blocked; only the permission prompt itself can’t be restyled.
- Install only from authors and marketplaces you trust.
- Clone first and run
claude plugin validate ./some-mod: the events and calls it lists should match what the mod claims to do. - Mind your keys: mods can read environment variables, and per Anthropic’s help center, with
ANTHROPIC_API_KEYset Claude Code bills that key at API rates instead of your subscription. - If something behaves oddly, start with
--safe-modeto rule your mods out.
Where mods run
Hooks run in every session that loads the plugin; only the terminal and the Desktop app show what a mod draws:
| Where you run Claude Code | Hooks run | What the mod draws appears |
|---|---|---|
claude in a terminal, including editor terminals and the JetBrains plugin | Yes | Yes |
| The Code tab of the Desktop app, except in a WSL session | Yes | Yes, except elements marked terminal-only |
| A WSL session in the Desktop app | No: plugins aren’t available in WSL sessions | No |
| The VS Code extension’s chat panel | Yes | No |
claude -p and the Agent SDK | Yes | No |
| Remote Control from claude.ai or the mobile app | Yes, in the session on your machine | In the terminal on your machine |
| A cloud session | Yes, for a plugin that reaches the cloud session | No |
A drawing mod can detect where it runs and fall back to text, so guard and logging logic still works in VS Code and in CI runs of claude -p.
Teams and enterprises
- Plugin controls apply: admins allow or block marketplaces — Team and Enterprise owners in the admin console, admins on Claude API and third-party API plans through managed settings.
- sec-default loads first on Team and Enterprise plans and on machines with managed settings, stopping user-installed mods from risky moves such as overriding permission deny rules; admins who load their own mods first should keep it in the list.
allowManagedModsOnlyblocks users’ own mods, while their plugins’ skills, commands, agents and MCP servers still load.- Team ideas from Anthropic: a CI/CD status pane, a confirmation step before any command touches production config, and an audit mod that loads first and records every call other mods make.
Which plan you need, and who should buy what
Mods are free — the docs only set a version requirement — but they run inside Claude Code, which Anthropic’s pricing page lists for Pro and Max, not Free. Claude Code shares one usage pool with the Claude apps, and asking Claude to write a mod, or running a mod that calls a model, draws on it.
- Claude Pro ($20 a month at Anthropic): on your own account ¥249 ≈ 37.11 USDT · ready-made ¥329 ≈ 49.04 USDT
- Claude Max 5x ($100 a month at Anthropic): on your own account ¥999 ≈ 148.89 USDT · ready-made out of stock
- Claude Max 20x ($200 a month at Anthropic): on your own account ¥1,799 ≈ 268.11 USDT · ready-made out of stock
- Trying mods, light Claude Code use: Claude Pro is enough.
- Daily agent work, often at Pro’s limit: Anthropic’s help center points to Max 5x ($100 a month, about 5x Pro’s usage per session).
- Still at the limit on Max 5x: Max 20x ($200 a month, about 20x Pro). Max sessions reset every five hours, with a weekly limit on top.
- Company teams that need central control of marketplaces and mods: Anthropic’s Team or Enterprise plans.
Top-up or ready-made. Card declined, or Claude not sold in your country? Pay us in USDT (TRC20 or BEP20). A top-up goes on your own account: paste your claude.ai cookie export (Cookie-Editor JSON) on the order page; it’s checked before you pay, usually lands in 5–30 minutes, and the credential is cleared after delivery. Max top-ups need an account that signs in normally and isn’t banned; a Free account never paid by bank card can’t take Max, and brand-new accounts wait 3–15 days for Max 20x. Otherwise buy ready-made: one account per order, for one user, and per the product description you can bind your own email. After paying, verify a delivery email (the link comes from Cloudflare); ready-made Max is delivered by hand (the order page shows 30 minutes to about 4 hours), and for ready-made Pro you submit its Organization ID on the order page. Then sign in to Claude Code with that account. More in ready-made Claude accounts and paying with USDT. Ready-made Max 5x / Max 20x is sold out for now, so for Max 5x / Max 20x use the top-up on your own account (Max 5x / Max 20x) under the eligibility rules above.
Renewals and refunds. Orders run 30 days, with reminders 7, 3 and 1 day before expiry. Renew a ready-made account by messaging Telegram @ccnb1937 with your order ID, at the same-tier top-up price; renew a top-up with a new order. If Anthropic’s regional policy makes the service unavailable, the unused days are refunded in USDT; bans for breaking Anthropic’s usage policy are not refunded; refunds are processed within 1 business day. Contact the bottom-right live chat promptly if anything goes wrong, or email support@claudemax.shop.
ClaudeMax is independent — not Anthropic, and not an authorized reseller — and has served more than 20,000 users since November 2024 (our own figure). This guide follows Anthropic’s docs as checked on 2026-10-07; for mod events and APIs, the latest official docs win.