Build your first mod
The fastest way is to ask Claude in plain language. Claude Code includes a plugin-authoring skill for building mods.
Ask in plain language
Section titled “Ask in plain language”Make a pane next to the conversation that counts this session's tool callsWhen Claude writes the first file in the mod folder, it asks “Enable hot reloading for this session?”. Choose Enable for this session and your changes reload right after each turn ends.
Write it yourself
Section titled “Write it yourself”-
Create the manifest.
.claude-plugin/plugin.json { "name": "first-mod", "version": "0.1.0", "description": "Counts tool calls" } -
List the hook module location. The path is relative to this file.
hooks/hooks.json { "modules": ["./register.ts"] } -
Register the hooks. Open the pane with
$.ui.open, and theui.renderhook draws its contents.hooks/register.tsx import type { Register } from 'claude-code'let calls = 0export const register: Register = on => {on('session.start', async ($, e, next) => {await $.command.register({ name: 'calls', description: 'Open the tool calls pane' })return next(e)})on('command.run', { command: 'calls' }, async $ => {await $.ui.open({ id: 'calls', title: 'Tool calls' })return { text: 'Opened the pane.' }})on('tool.call', async ($, e, next) => {calls += 1$.ui.invalidate('ui.render')return next(e)})on('ui.render', { component: 'Pane', requestId: 'calls' }, async ($, e) => {const { Text } = $.ui.resolve(e)return <Text>Tool calls: {calls}</Text>})} -
Load it in one session and try it.
Terminal window claude --plugin-dir ./first-mod
Validate and test
Section titled “Validate and test”claude plugin validate ./first-mod # list of hooks and calls, plus anything the engine will rejectclaude plugin test ./first-mod # run *.test.ts(x)Tests use test, expect, and mock from claude-code/testing. In a test, on lets you lay down fake responses in place of the engine, and $.ui.mount draws the pane identically for the terminal and desktop screens so you can press buttons and check the result. The engine installs the types in the mod folder’s .claude-plugin/types/, so once the mod has loaded, you can type-check with tsc -p <folder>.
Share it
Section titled “Share it”Put one marketplace file at the repository root and you’re done.
{ "name": "first-mod", "owner": { "name": "you" }, "plugins": [{ "name": "first-mod", "source": "./" }]}Others install it like this.
/plugin install first-mod --marketplace you/first-modAdd the claude-code-mod topic to your GitHub repository, and the community catalog picks it up within a few hours.
See also
Section titled “See also”- API cheatsheet: events and
$calls - Troubleshooting: pitfalls people actually hit, and how to fix them
- Official docs
Unofficial community guide. Not affiliated with or endorsed by Anthropic. Claude and Claude Code are trademarks of Anthropic.