1# Chapter 1: plugin, what Claude Code loads 2 3Claude Code does a lot on its own: it reads files, edits them, runs shell 4commands, and decides when it is done. At each of those moments it raises 5an **event** (`PreToolUse` just before a tool runs, `Stop` just before a 6turn ends, thirty-three kinds in all) and lets plugins have a say. A plugin 7is a folder Claude Code is pointed at. This is that folder: it is the whole 8of jevhooks as far as Claude Code can tell. 9 10It brings two things. 11 12- **A mod** (`hooks/`, chapter 3): TypeScript that Claude Code runs inside 13 its own process, with a function for each event it cares about. It is 14 where a command is held, judged, and let go or put to you. 15- **An MCP server** (`.mcp.json`): a program Claude Code starts once per 16 session. MCP, the Model Context Protocol, is how a program offers tools 17 to the assistant. This one offers two read-only ones (`status` and 18 `recent`), but its real job is to make sure the daemon is running before 19 the first command needs it (chapter 11). 20 21```mermaid 22flowchart LR 23 CC["Claude Code session"] 24 subgraph plugin["plugin/"] 25 manifest[".claude-plugin/plugin.json<br/>the name"] 26 hooks["hooks/register.tsx<br/>the mod"] 27 mcp[".mcp.json<br/>starts bin/jevhooks mcp"] 28 end 29 daemon["jevhooks serve<br/>one per machine"] 30 CC -- "reads" --> manifest 31 CC -- "runs in-process" --> hooks 32 CC -- "starts per session" --> mcp 33 mcp -- "makes sure it runs" --> daemon 34 hooks -- "HTTP on a Unix socket" --> daemon 35``` 36 37Notice that the mod does not go through the MCP server. It talks to the 38daemon directly, over a Unix socket, in a few milliseconds. The MCP server 39is the doorman, not the hallway. 40 41> **Aside: why a mod and not a settings hook?** Claude Code can also run a 42> shell command for each event, configured in your settings. That starts a 43> new process every time, and every Bash command would pay for one. A mod 44> is already loaded: a plainly read-only command is waved through in well 45> under a millisecond, without leaving Claude Code at all. And a mod can 46> draw: the band above the prompt is one. 47 48> **Try it.** `claude plugin validate plugin` reads this folder the way 49> Claude Code does and tells you what it found: the hooks the mod 50> registers (`session.start`, `command.run{command=jev}`, 51> `classic.PreToolUse`, `classic.Stop`, `classic.*`, 52> `ui.render{component=AbovePrompt}`), every host call it makes, and the 53> one piece of state it keeps. It needs no Rust and no daemon. 54 55## For the people who maintain it 56 57To run a session with it: `claude --plugin-dir ./plugin` from the 58repository root. The MCP server is `bin/jevhooks`, which is not committed: 59`just daemon` builds it and copies it here (chapter 11). Without it the mod 60still loads, finds no daemon, and passes on everything. 61 62`tsconfig.json` extends `.claude-plugin/types/tsconfig.json`, which Claude 63Code lays down itself (it is gitignored, so it appears only once the engine 64has loaded the plugin), and adds `allowImportingTsExtensions`, because a 65mod's imports name the `.ts` file. 66 67### In this folder 68 69| Path | What | 70| --- | --- | 71| [.claude-plugin/](.claude-plugin/) | Chapter 2: the manifest. | 72| [hooks/](hooks/) | Chapter 3: the mod, its bridge to the Rust, and the generated JavaScript. | 73| [types/](types/) | Chapter 4: the plugin's state, typed. | 74| [tests/](tests/) | Chapter 5: the plugin's tests, against a stand-in daemon. | 75| [.mcp.json](.mcp.json) | Starts `${CLAUDE_PLUGIN_ROOT}/bin/jevhooks mcp` once per session. | 76| [tsconfig.json](tsconfig.json) | For `tsc -p plugin`: the engine's config plus `.ts` imports. | 77| `bin/jevhooks` | Built by `just daemon`; not committed. | 78 79← Previous: [Prologue](../) · Up: [jevhooks](../) · Next: [Chapter 2, .claude-plugin/](.claude-plugin/) →