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:

ModSettings hookSkillMCP server
What it isFunctions in a plugin that Claude Code calls in its own processA shell command, HTTP request or prompt run on a lifecycle eventA SKILL.md file of instructions Claude readsAn external process or service that gives Claude tools
What it can changeTool calls, prompts, commands, turns, and what the interface drawsWhether a tool call or prompt goes ahead, a tool call’s arguments and result, context added for ClaudeWhat Claude knows and doesWhich tools Claude has
Draws in the interfaceYesNoNoNo
What you writeJavaScript or TypeScriptA script and a settings.json entryMarkdownA server in any language
Pick it whenYou want a pane, a band above the prompt, a custom command, or to rewrite an eventYou want to block, allow or log an event with a script you already haveYou keep pasting the same instructions into chatClaude 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 --version and update if needed.
  • Desktop app: its bundled Claude Code supports mods from v2.1.286; enter /status in a local session in the Code tab and read the Claude Code row.
  • On by default. If you set CLAUDE_CODE_ENABLE_FUNCTION_HOOKS during early access, remove it: v2.1.287 and later ignore it, so a 0 no longer keeps mods off.
  • What’s loaded: /plugin shows a dim line such as 1 mod active · first-mod under 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 prompt
  • blast-radius: holds a risky shell command (rm -rf, force push), shows what it would change, with buttons to proceed or cancel
  • replay-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-mods

The 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.

  1. 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 ~/.claude is a protected path, the default and acceptEdits modes ask before each file.
  2. 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.
  3. 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.js

1. 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 loaded

Ask 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 passed

A 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.

  1. Install only from authors and marketplaces you trust.
  2. Clone first and run claude plugin validate ./some-mod: the events and calls it lists should match what the mod claims to do.
  3. Mind your keys: mods can read environment variables, and per Anthropic’s help center, with ANTHROPIC_API_KEY set Claude Code bills that key at API rates instead of your subscription.
  4. If something behaves oddly, start with --safe-mode to 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 CodeHooks runWhat the mod draws appears
claude in a terminal, including editor terminals and the JetBrains pluginYesYes
The Code tab of the Desktop app, except in a WSL sessionYesYes, except elements marked terminal-only
A WSL session in the Desktop appNo: plugins aren’t available in WSL sessionsNo
The VS Code extension’s chat panelYesNo
claude -p and the Agent SDKYesNo
Remote Control from claude.ai or the mobile appYes, in the session on your machineIn the terminal on your machine
A cloud sessionYes, for a plugin that reaches the cloud sessionNo

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.
  • allowManagedModsOnly blocks 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.

Our Claude prices today (live from the order system)
  • 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.