Skip to content
Mastering Claude

Home / Extending: skills, MCP, subagents, hooks, plugins

Extending: skills, MCP, subagents, hooks, plugins7 minApplication

Hooks: automating the lifecycle

A hook is an action the harness runs itself when a precise lifecycle event occurs. An instruction written in memory remains a preference the model may forget; a hook declared once runs on every occurrence, independently of what the model recalls at that moment.

A hook is an action the harness executes itself when a precise lifecycle event occurs, never a request the model interprets at the moment of acting. Writing to a memory file every time you commit, running the tests, remains a preference: the model may forget it after a long conversation or a compaction. A hook declared on the corresponding event runs on every occurrence, independently of what the model recalls at that moment.

The mechanism: an event, a command, a decision

A hook is declared in settings.json, or in a plugin's hooks.json file, tied to a lifecycle event and an action type: a shell command, an HTTP call, an MCP tool, a prompt, or an agent. When the event occurs, the harness runs that action and waits for a decision in return, before the question reaches the model.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "./verifier-avant-commande.sh" }
        ]
      }
    ]
  }
}

This declaration ties the PreToolUse event, restricted to calls of the Bash tool, to a script that runs before every proposed command. Each hook type carries its own default timeout, different depending on the event that triggers it: the next lesson details these timeouts event by event, along with the precise JSON fields a hook returns to block, allow, or enrich what the model sees.

Hook versus memory rule: what each actually guarantees

A memory rule depends on a model that rereads it and decides to apply it, within a context window that grows over the course of a session. A hook depends on the harness, which checks the event independently of what the conversation holds at that moment. The difference shows up most on a long session: an instruction repeated twenty times in a memory file can dilute within a loaded context, whereas a hook declared once continues to run identically on the thousandth call as on the first.

A hook that refuses must say what to do

A rule that has cost you once is mechanized as a PreToolUse hook. The refusal arrives before the effect, and its message names the alternative: with exit code 2, what the script writes to standard error is the text Claude receives, and it corrects its call instead of going in circles.

#!/bin/sh
# refuses and says what to do instead
echo "Refused: do not delete, move the file to archives/." >&2
exit 2

By default, a hook that fails lets the action through: a wrong path or a crashing script, and everything passes. For a security hook, declare "onFailure": "block" on a command or HTTP hook, which also blocks the action when the hook cannot start, times out or exits with an unexpected code. To see which hooks exist, type /hooks: a single list, grouped by event. To know whether one ran, start claude --debug, which logs the command, plugin, outcome and duration of each command hook on tool calls, prompts, SessionStart and Stop when it finishes. A <system-reminder> tag written in a hook's output is neutralized before it reaches Claude: a hook's output cannot pass itself off as a system instruction.

Figure 1

From trigger to decision

01
Lifecycle event
A command about to run, a session starting, a file changing: the harness detects the event, not the model.
02
Hook consulted
settings.json or a plugin's hooks.json states which action to launch for this precise event.
03
Action executed
A shell command, an HTTP call, an MCP tool, a prompt, or an agent runs, depending on the declared type.
04
Decision returned
The hook returns its response to the harness, which applies it before the conversation resumes its course.
The sequence shows where the harness steps in and where the model no longer has a say, between the event and the decision, no step relies on what the model recalls.
Figure 2

Memory rule versus declared hook

Making a repeated automatism reliableWhat triggers the actionBehaviour on a long sessionWhere the automatism is declared
Rule written in memoryA reading by the model, which must remember it at the right momentCan dilute after a compaction or a loaded contextA CLAUDE.md file, interpreted by the model
Declared hookA lifecycle event, observed by the harness itselfRuns identically on every occurrence of the eventsettings.json or a plugin's hooks.json, executed by the harness
The table isolates three criteria to distinguish a rule written in memory, which depends on a reading by the model, from a hook, which depends on an event observed by the harness.
Calibrate it yourself

A developer declares a PreToolUse hook in settings.json that blocks any call to the Bash tool containing the pattern rm followed by -rf. He then asks Claude Code to run a command containing this pattern, and sees the hook trigger.

Write in one sentence what this situation establishes, and in one sentence what it does not establish.

What to remember
  • A hook fires because a lifecycle event occurs, never because the model decides to apply it at the moment of acting.
  • A rule repeated in a memory file can dilute within a loaded context, a hook declared once runs identically on every occurrence of the event.
  • A hook is declared in settings.json or in a plugin's hooks.json file, tied to an action type among shell command, HTTP call, MCP tool, prompt, or agent.
  • A hook that refuses names the alternative on its error output with exit code 2, and Claude corrects its call instead of retrying identically.
  • A failing hook lets the action through by default: onFailure: "block" suits security hooks, /hooks lists them and --debug shows whether they ran.
Do this now

Create a minimal .claude/settings.json file in a test repository, declare a PreToolUse hook there that prints a message before every Bash command so you can watch the mechanism trigger, then remove that hook once the demonstration is done.

Check the source

Every datable claim in this lesson links here to the public text behind it. A source that does not open proves nothing.