Curriculum › Agentic Architecture & Orchestration · 27% of the exam

Agent SDK hooks for interception and normalization

What you’ll be able to do

  • Implement a PostToolUse hook that normalizes heterogeneous data formats from different MCP tools before the agent processes them
  • Implement a tool-call interception hook that blocks a policy-violating action and redirects it to an alternative workflow
  • Choose hooks over prompt instructions when a business rule requires guaranteed compliance

What you need to know

Hooks sit between the model and its tools

In the Claude Agent SDK, hooks are application code that runs at defined points in the agent's tool use. Two patterns matter here, and they sit on opposite sides of a tool call:

  • Result interception (PostToolUse) — runs after a tool returns and before the model processes the result. It can transform that result.
  • Call interception — runs when the model has requested a tool call and before it executes. It can inspect the arguments and allow, block, or redirect the call.

What makes both valuable is that they're deterministic. A hook runs every time, on every matching call, and does exactly what its code says. A prompt instruction is something the model weighs while reasoning; it's followed most of the time, which makes compliance probabilistic.

PostToolUse: normalize before the model reasons

Agents that pull from several MCP tools get data in whatever format each backend chose. One returns "created": 1729526400 (Unix seconds), another "created_at": "2024-10-21T16:00:00Z" (ISO 8601), a third reports order state as "status": 3 while a fourth says "status": "SHIPPED". Left as-is, the model has to interpret each format every time — and occasionally it will misread a Unix timestamp, compare dates across formats incorrectly, or guess what status 3 means.

A PostToolUse hook removes that burden. It intercepts each result and converts it to one canonical shape before the model ever sees it.

illustrative PostToolUse normalization — registration syntax varies by SDK language
STATUS_CODES = {1: "pending", 2: "paid", 3: "shipped", 4: "delivered"}

def post_tool_use(tool_name, result): for key in (“created”, “created_at”, “updated”, “shipped_on”): if key in result: result[key] = to_iso8601(result[key]) # Unix or ISO in, ISO out if isinstance(result.get(“status”), int): result[“status”] = STATUS_CODES[result[“status”]] # numeric code -> label return result # what the model receives

The model now reasons over consistent data from every tool. Normalization has to happen here — at the result boundary — rather than at the end of the run, because by then the model has already made decisions based on the raw formats.

Call interception: block, then redirect

The second pattern enforces compliance rules on outgoing calls. Suppose policy says refunds above $500 require a human. A call-interception hook on process_refund checks the amount before the call executes:

illustrative call-interception hook
REFUND_LIMIT = 500

def before_tool_call(tool_name, args): if tool_name == “process_refund” and args[“amount”] > REFUND_LIMIT: return block( reason=f”Refunds over ${REFUND_LIMIT} require human approval.”, redirect=“escalate_to_human” # tell the agent what to do instead ) return allow()

Two parts matter. Block: the refund doesn't execute, regardless of how persuasive the conversation was. Redirect: the agent is told why and pointed at the alternative workflow — here, escalation with a structured handoff (lesson 1.4) — so the customer's case moves forward rather than stalling on a bare refusal.

Placement matters too. An alert raised after the refund has been processed tells you the policy was broken; it doesn't stop the money going out. Enforcement has to happen before execution.

Choosing hooks over prompts

Use the same test as for prerequisite gates. If a rule is a preference, a prompt instruction is fine. If the business requires guaranteed compliance — refund ceilings, mandatory approvals, data that must always be in a specific format before decisions are made — put it in a hook. Keep the prompt instruction too, so the agent usually plans correctly and the hook rarely has to fire; but the hook is the guarantee.

Key concept

Hooks give deterministic guarantees; prompts give probabilistic compliance. Use PostToolUse to normalize heterogeneous tool results before the model processes them, and call interception to block policy-violating actions and redirect them to an alternative workflow.

When a scenario has the agent misreading data from mixed sources, think PostToolUse normalization. When it has a hard business limit being exceeded "occasionally," think call interception with a redirect.

Practice scenario

ScenarioA support agent uses an orders MCP tool that returns Unix timestamps and numeric status codes, and a shipping MCP tool that returns ISO 8601 dates and text statuses. It sometimes tells customers an order is outside the 30-day return window when it isn't. The system prompt also says refunds over $500 need a manager, yet two $650 refunds went through last month.
Work it through, then open this

Two hooks. For the date errors, add a PostToolUse hook that converts every timestamp to ISO 8601 and every status code to a text label before results reach the model, so return-window comparisons are made on consistent data. For the refunds, add a call-interception hook on process_refund that blocks amounts over $500 and redirects the agent to escalate_to_human. The prompt instruction gives probabilistic compliance; the business rule requires a guarantee.

Build exercise — Normalize results and gate a refund

Intermediate · 30 min

What you’ll learn

  • Writing a PostToolUse hook that canonicalizes mixed formats
  • Writing a call-interception hook that blocks and redirects
  • Verifying that hooks fire regardless of model behavior
  1. Collect sample outputs from two or more tools that represent the same concepts differently (timestamps, status codes) and define one canonical format for each.

    • Why: You can’t normalize until you’ve decided what “normal” is.
    • You should see: A short mapping table, such as Unix seconds and ISO strings → ISO 8601, numeric codes → named statuses.
  2. Implement a PostToolUse hook that applies the mapping to every tool result, and log the before/after for a few calls.

    • Why: The model should only ever see the canonical shape.
    • You should see: Every result the model receives using ISO 8601 and named statuses, whichever tool produced it.
  3. Implement a call-interception hook on process_refund that blocks amounts over a threshold and redirects to escalate_to_human. Then try to talk the agent into a large refund.

    • Why: A hook’s guarantee shouldn’t depend on how the conversation goes.
    • You should see: The refund blocked every time, and the agent escalating with a handoff instead of stopping at a refusal.

Exam traps

Enforcing a hard business limit only through system-prompt instructions

Prompt compliance is probabilistic. A limit that must never be exceeded needs a call-interception hook.

Asking the model to interpret mixed timestamp and status-code formats instead of normalizing them in a hook

Every interpretation is a chance to misread. A PostToolUse hook gives the model one consistent format.

Normalizing tool data after the agent has already reasoned over it

Normalization belongs at the result boundary, before the model processes the result. Later is too late.

Blocking a violating call without redirecting it to an alternative workflow such as human escalation

A bare block leaves the case stuck. Tell the agent why and route it to the approved path.

Using an after-the-fact alert where the rule required preventing the action

An alert after execution reports the violation; only interception before execution prevents it.

Sources

Quick check

Reviewers find that about a third of research reports have thinly supported sections, even though the synthesis subagent itself writes "limited evidence found" in those sections. The coordinator sends the first synthesis output straight to the report subagent. What is the best improvement?