§ 04 · The pattern
What a hook actually is.
Hooks in the Claude Agent SDK are callbacks that fire at fixed lifecycle points.
PreToolUse fires before a tool is invoked. PostToolUse fires after. They
execute in your code, not the model’s — deterministic, debuggable, testable.
A hook receives the same context the model used to decide, plus the tool’s input (for
PreToolUse) or its output (for PostToolUse). It returns a structured
response: continue, block, or mutate. For the refund case, thirteen lines are enough.
// PreToolUse hook — fires before every tool invocation
const verificationHook = async ({ toolName, toolInput, session }) => {
if (toolName !== 'process_refund') {
return { continue: true };
}
const verified = session.metadata.verifiedCustomers?.includes(
toolInput.customer_id
);
if (!verified) {
return {
continue: false,
reason: 'Identity not verified. Call get_customer first.',
};
}
return { continue: true };
};
agent.hooks.PreToolUse.push(verificationHook);
The hook runs every time the model decides to call process_refund, no exceptions.
If verification hasn’t happened, the tool call is blocked and a structured reason is returned
to the model. The model sees the block, adjusts, calls get_customer instead,
succeeds, and retries the refund. From outside the system the agent appears to always verify
first. From inside, the guarantee lives in a small, testable function.
Identify the decision point. Write the rule in code. Place the rule on the lifecycle event that
fires before the decision takes effect. Prompt engineering, few‑shot examples, and
reasoning scaffolds are all secondary reinforcement — they bias the model toward the right path
but do not enforce it.