How to Create a Claude Code Mod
From an empty folder to a mod anyone can install with one command. The example below is tested on Claude Code 2.1.296 with claude plugin validate and claude -p.
by ccmods.app editors · Updated
What is a mod made of?
A mod is a Claude Code plugin with a hooks module. The hooks module is a JavaScript or TypeScript file that exports a register function. Claude Code calls it when the mod loads, and you register the events you handle with on(event, hook).
Every hook receives $ (the mods API, such as $.ui and $.command), e (the event) and next (run the plugins beneath and Claude Code's own behavior). Returning without calling next answers the event yourself.
| You want to | Hook or call |
|---|---|
| Open a pane or a band above the prompt | ui.render (Pane / AbovePrompt), $.ui.open |
| Redraw Claude Code's own interface | ui.render (e.g. Spinner, tool rows) |
| Block, rewrite or watch a tool call | tool.call |
| Rewrite a prompt or the system prompt | prompt.submit, prompt.compose |
| Add an instant slash command | $.command.register + command.run |
| Status line entry or toast | $.ui.status, $.ui.toast |
| Call a model, read files, run processes | $.model, $.fs, $.process |
What is the fastest way to make one?
Ask Claude. Claude Code ships a plugin-authoring skill: describe the mod in a session, for example "show a toast when a test fails", and Claude writes it and offers to hot-reload it. See the official Create a mod page.
How do you write a Hello World mod by hand?
This mod adds a /hello command and counts tool calls in the status line. The folder:
hello-mod/
├── .claude-plugin/
│ ├── plugin.json
│ └── marketplace.json ← makes the repo installable
└── hooks/
├── hooks.json
└── register.js{
"name": "hello-mod",
"version": "0.1.0",
"description": "Counts tool calls and adds a /hello command",
"author": {
"name": "your-github-name"
}
}{ "modules": ["./register.js"] }// Shared by every hook in this file. Resets when the mod reloads.
let toolCalls = 0
export function register(on) {
// Add the /hello command when a session starts.
on('session.start', async ($, e, next) => {
await $.command.register({ name: 'hello', description: 'Say hello and show the tool call count' })
return next(e)
})
// Answer /hello right away, without a Claude turn.
on('command.run', { command: 'hello' }, async () => {
return { text: `Hello from hello-mod! Claude has made ${toolCalls} tool calls this session.` }
})
// Count each tool call and show the count in the status line.
on('tool.call', async ($, e, next) => {
toolCalls += 1
$.ui.status(`tool calls: ${toolCalls}`)
return next(e)
})
}The /hello hook returns { text } without calling next, so the command answers instantly with no Claude turn. The tool.call hook counts, then calls next(e) so the tool runs as usual.
How do you test a mod locally?
Validate it, then load it with --plugin-dir.
$ claude plugin validate ./hello-mod$ claude --plugin-dir ./hello-modType /hello. An interactive session watches the folder, so saving a file reloads the mod. A hook that throws is skipped rather than breaking the session; start claude --debug to see why. For automated tests, write *.test.ts files and run claude plugin test ./hello-mod, as described in Test a mod.
How do you publish a mod on GitHub?
Add a marketplace file beside plugin.json so the repository itself is installable:
{
"name": "hello-mod",
"description": "A hello world Claude Code mod",
"owner": {
"name": "your-github-name"
},
"plugins": [
{
"name": "hello-mod",
"source": "./"
}
]
}Push it as a public repository. Anyone can then install it with:
$ claude plugin install hello-mod --marketplace your-github-name/hello-modPre-release checklist
claude plugin validatepasses with no errors.- The plugin name does not start with
claude-,anthropic-orcc-plugin-. Those prefixes are reserved and fail validation. - The README shows the install command, what the mod accesses (files, network, model usage) and any API keys or external tools it needs.
- Guard hooks that refuse tool calls have a
.catchhandler; without one, the guarded action goes ahead when the hook fails. - The repository has a LICENSE file.
How do you get listed on ccmods.app?
We list mods that pass claude plugin validate and have a README, and search GitHub for new ones regularly. To be added sooner, open an issue with the repository link.
Go deeper with the Mods reference for every event, method and element, or read the source of the UI mods and tool mods listed here.