P3.7 · D2 + D3 · Process38% of CCA-F24 min build

Agentic Tool Design.

A meta-skill scenario about designing tool registries. The optimum is 4-5 tools per agent (past 5, routing accuracy drops 8% per extra tool); each tool follows the Anthropic 4-line description pattern (what / when / edge cases / ordering); risky calls go through a PreToolUse hook; results pass through a PostToolUse hook for normalization and side-effect guards; errors are labeled with one of four structured buckets (Transient · Permission · Data · Business) so retry logic can branch correctly. The most-tested distractor: a 15-tool agent that 'just needs a smarter model'. No, it needs a smaller registry.

Mental modelArchitecture chooses the flow. Deterministic controls enforce policy. Structured state preserves truth.
Loop mascot illustrating Agentic Tool Design.
Share
01 · System & parts

What this system is, and its 5 parts

Think of this as designing the toolbox before the agent picks it up. What tools to give, how to describe them, what guard-rails to put around them. The trap is to give the agent fifteen tools and hope; the discipline is to give it four or five really well-described tools, write each description in the same four-line pattern (what / when / edge cases / ordering), wrap risky calls in PreToolUse hooks, normalize the results in PostToolUse hooks, and label every error with one of four buckets so retry logic can reason about it. The whole point is that tool design IS agent design. Get the toolbox right and the rest of the agent works.

5Components
D2Primary domain
8Build steps
9Decision traps
8Concept links
Stack · Claude SDK · MCP server (optional) · structured loggingNeeds · tool_use · hooks · error handling
Agentic Tool Design component architecture.
5 components. Each owns one concept.
01

Tool Registry (4-5 Tools)

the optimum, not the maximum

The agent's toolbox. Empirically, 4-5 tools is the routing-accuracy sweet spot; past that, accuracy drops ~8% per added tool because descriptions overlap and the model alternates. If the use case needs more tools, split into specialist sub-agents. Each with its own 4-5-tool registry. And route between them with a triage classifier.

ConfigurationCap: 4-5 tools. Beyond: split agents. Don't cram a customer-support tool, a refund tool, a sentiment tool, and 11 admin tools into one agent. That's three agents pretending to be one.
Concept: tool-calling
02

4-Line Description Pattern

what · when · edge cases · ordering

The canonical tool description shape. Line 1: what the tool does. Line 2: when to call it. Line 3: edge cases (returns, failure modes). Line 4: ordering (which tools must come before / after). This pattern makes routing structural. The model reads the pattern in every description and learns the shape. Vague one-liners produce ~12% wrong-tool selection; the 4-line pattern drops it below 3%.

Configurationdescription: "Look up a customer by customer_id and confirm they are active.\nUse this BEFORE any other tool that mentions the customer.\nEdge cases: returns 'not_found' if customer_id is missing.\nAlways run before lookup_order or process_refund."
Concept: tool-calling
03

PreToolUse Hook (Policy Gate)

deterministic, before exec

Sits between the model's tool_use request and the actual tool execution. Reads tool_input (e.g., refund_amount), compares to policy (amount <= cap), exits 0 (allow) or 2 (deny with stderr message). Deny routes the model back with the policy reason, and the agent re-plans. The single-most-effective lever for converting probabilistic prompt-only policies into 100%-deterministic gates.

Configurationmatcher: "process_refund". Hook reads stdin JSON: {tool_name, tool_input}. Exits 0 to allow, exits 2 with stderr to deny. SDK forwards stderr back to the model as a tool_result with is_error=true.
Concept: hooks
04

PostToolUse Hook (Normalization + Audit)

after exec, before next turn

Fires AFTER the tool runs but BEFORE the result is fed to the model. Normalizes raw outputs (timestamps to ISO-8601, status codes to enum names, field renames), captures side-effect signals, and writes the canonical audit log entry. Without it, the model sees inconsistent output shapes across calls; with it, every call has a predictable contract and an audit trail.

Configurationmatcher: '*'. Hook reads stdin: {tool_name, tool_input, tool_result}. Transforms tool_result into normalized shape. Writes audit row to durable log. Always exits 0 (does not deny. That's PreToolUse's job).
Concept: hooks
05

4-Bucket Structured Error Contract

Transient · Permission · Data · Business

Every tool that can fail emits an error in one of four explicit buckets, not a free-form string. The harness reads is_error: true + error.bucket, then routes: Transient → retry; Permission → escalate (don't retry, won't fix itself); Data → surface to user; Business → block + log + escalate. Without this contract, the agent retries permission errors forever and surfaces transient ones as catastrophes.

Configurationtool_result on failure: { is_error: true, content: { bucket: "Transient"|"Permission"|"Data"|"Business", code, detail, retryable: bool } }. Agent reads bucket and retryable; never branches on detail text.
Concept: structured-outputs
02 · Problem framing

The problem

What the user needs
  1. A tool registry the agent routes accurately. The right tool fires on the first try ≥ 95% of the time.
  2. Risky operations gated structurally. Refund cap, destructive Bash, write access policed by hooks not prompts.
  3. Errors that the agent can reason about. A permission-denied looks different from a transient timeout, and the agent retries accordingly.
Why naive approaches fail
  1. 15-tool registry → routing accuracy drops 8% per tool past 5; the agent alternates and misses obvious matches.
  2. Vague one-line tool descriptions → agent picks the wrong tool ~12% of the time.
  3. Prompt-only policy enforcement ('never refund > $500') → leaks 3-5% in production despite emphatic phrasing.
Definition of done
  • Tool count per agent ≤ 5; rare tools moved to specialist sub-agents
  • Every tool description follows the 4-line pattern (what / when / edge cases / ordering)
  • PreToolUse hook gates every policy-bearing tool; exit 2 on violation
  • PostToolUse hook normalizes outputs and logs every call to the audit trail
  • Tool errors emit one of the 4 structured buckets (Transient · Permission · Data · Business)
  • MCP servers used for cross-agent tool sharing. No inline duplication
03 · Data flow

One run, traced end to end

Agentic Tool Design sequence diagram.
Agentic Tool Design end-to-end flow.
04 · Build

8 steps to production

01

Cap the registry at 4-5 tools (split otherwise)

Audit your current tool list. Past 5, you're guaranteed losing routing accuracy. The fix is structural: identify the use cases that actually share state vs those that don't, then split into specialist agents with their own 4-5-tool registries. Use a triage classifier (or a top-level coordinator agent) to route the user request to the right specialist.

Concept: tool-calling
Python
# AUDIT: count + classify tools
SUPPORT_TOOLS = ["verify_customer", "lookup_order", "process_refund",
                 "escalate_to_human", "audit_log"]  # 5. At the optimum
ADMIN_TOOLS = ["create_user", "delete_user", "reset_password",
               "lock_account", "unlock_account", "audit_admin"]  # 6. Split

# WRONG: cram all 11 into one agent
# tools = SUPPORT_TOOLS + ADMIN_TOOLS  # 11. Routing accuracy drops ~32%

# RIGHT: two specialist agents, triage routes between them
def triage(user_request: str) -> str:
    """Tiny classifier. Pick the specialist agent."""
    if any(w in user_request.lower() for w in ["refund", "order", "ticket"]):
        return "support"
    if any(w in user_request.lower() for w in ["password", "account", "user"]):
        return "admin"
    return "support"  # default

def route(user_request: str) -> dict:
    specialist = triage(user_request)
    tools = SUPPORT_TOOLS if specialist == "support" else ADMIN_TOOLS
    return run_agent(tools=tools, message=user_request)
02

Write every tool description in the 4-line pattern

Line 1: what (one sentence). Line 2: when (which user intent triggers this). Line 3: edge cases (what happens on failure, missing args). Line 4: ordering (which tools must come before / after). This pattern is the model's structural cue. It reads the pattern across all 5 tool descriptions and routes accordingly. Vague one-liners produce ~12% wrong-tool selection; this pattern drops it below 3%.

Concept: tool-calling
03

Wire the PreToolUse hook on policy-bearing tools

For every tool that touches money, identity, or destructive state, the PreToolUse hook is the architectural gate. It reads tool_input from stdin JSON, applies the policy check in code (not in a prompt), and exits 0 or 2. Exit 2's stderr message is fed back to the model as a tool_result with is_error: true. The model re-plans with the policy in view.

Concept: hooks
04

Wire the PostToolUse hook for normalization + audit

PostToolUse fires AFTER the tool runs, BEFORE the model sees the result. Two jobs: normalize the output shape (timestamps to ISO-8601, status codes to enum names, ms to seconds, etc.) so the model sees a consistent contract across calls; and write a canonical audit row capturing tool_name, tool_input, normalized_output, latency, and stop_reason context. The audit log is the replay tool when production breaks at turn 18.

Concept: hooks
05

Emit errors in 4 structured buckets

Every tool that can fail returns an error tagged with one of four buckets: Transient (network blip, retry), Permission (403/401, escalate. Don't retry, won't fix itself), Data (input malformed, surface to user), Business (policy violation, log + escalate). The agent reads bucket and retryable, and routes accordingly. Without this contract, the agent retries permission errors forever and surfaces transient blips as catastrophes.

Concept: structured-outputs
06

Use tool_choice 'auto' for specialists; 'forced' only for mandatory extraction

tool_choice: 'auto' is the right default. The model decides whether to call any tool, and which one, based on the request. tool_choice: 'any' forces the model to call SOME tool (rarely useful). tool_choice: { type: 'tool', name: ... } forces a specific tool. Only correct for extraction pipelines where the tool is mandatory. Forced tool_choice on a conversational specialist agent removes the agent's reasoning capacity.

Concept: tool-choice
07

Share tools across agents via MCP servers

When two agents both need lookup_order or verify_customer, don't duplicate the tool inline. Expose it through an MCP server. Each agent connects to the MCP server, advertises it as a tool, and gets a single source of truth. Updating the tool's behavior is a single deploy; the agents pick it up automatically. MCP also abstracts auth, observability, and rate-limiting away from each agent.

Concept: mcp
08

Test routing accuracy with a 50-intent eval set

Tool design is empirical. Build a 50-intent eval set where each intent has a known correct tool. Run the agent over it, count first-call accuracy. Below 95% routing accuracy means the descriptions need work; below 90% likely means too many tools. Re-run the eval after every tool addition or description tweak.

Concept: evaluation
05 · Right call, wrong call

9 decisions the exam turns into distractors

01 · Tool count for a specialist agent
Looks right

15 tools 'because the model is smart enough'

Actually right

4-5 tools (the optimum); split into specialist sub-agents past 5

DEC-01
02 · Tool description format
Looks right

One vague sentence ('verifies a customer')

Actually right

4-line pattern: what / when / edge cases / ordering

DEC-02
03 · Refund-cap policy enforcement
Looks right

System prompt: 'never refund more than $500'

Actually right

PreToolUse hook reads tool_input.amount, exits 2 on violation

DEC-03
04 · Tool error contract
Looks right

Free-form error messages parsed by the agent

Actually right

4 buckets (Transient · Permission · Data · Business) + retryable boolean

DEC-04
05 · Tool count > 5
Looks right

15-tool agent with overlapping descriptions. Routing accuracy at first-call drops to ~65%; the agent alternates between similar tools, sometimes calls 3 tools before settling on the right one. Latency up; cost up; quality down.

Actually right

Cap at 4-5 tools per agent. Move rare tools (used <10% of conversations) to specialist sub-agents. Use a triage classifier to route requests to the right sub-agent. Each sub-agent stays at 4-5 tools.

AP-ATD-01
06 · Vague tool descriptions
Looks right

Tool descriptions are one-liners ('verifies the customer', 'looks up an order'). Agent misroutes ~12% of calls because it can't tell when each tool applies.

Actually right

Anthropic 4-line pattern: what / when / edge cases / ordering. Each line targets a specific routing decision the model has to make. Wrong-tool rate drops below 3%.

AP-ATD-02
07 · No PreToolUse hook on risky operations
Looks right

Refund cap enforced via system-prompt language. Production logs show 3-5% of refunds violate the cap. Audit fails; finance rolls back; trust in the agent drops.

Actually right

PreToolUse hook reads tool_input.amount, compares to policy, exits 2 with stderr message on violation. Deterministic, not probabilistic. Policy violations drop to 0.

AP-ATD-03
08 · Unhandled tool errors
Looks right

Tool returns 403; agent retries indefinitely. Tool returns 500; agent crashes. Tool returns 400 with malformed input; agent gives up without surfacing the input issue to the user.

Actually right

4-bucket structured error contract (Transient · Permission · Data · Business) with retryable boolean. Agent reads bucket, branches: Transient → retry with backoff; Permission → escalate; Data → surface to user; Business → block + log + escalate.

AP-ATD-04
09 · No tool ordering guidance
Looks right

Agent calls lookup_order before verify_customer. Wrong record returned 12% of the time because the customer_id wasn't validated first. Bad data pollutes downstream decisions.

Actually right

Tool descriptions explicitly state ordering ('Always run BEFORE process_refund'; 'Use ONLY after verify_customer has confirmed the customer is active'). The 4-line pattern's last line is for ordering precisely because ordering is so often the routing failure.

AP-ATD-05
Agentic Tool Design failure map.
06 · Budget

Cost & latency

~5-15ms latency; ~0% token costPer-tool-call overhead (Pre + Post hooks)

Hooks run as subprocesses reading stdin JSON. No LLM call. Pure local Python/TS. The latency is below the noise floor of a typical tool API call.

~$0.05/runRouting accuracy eval (50 intents, weekly)

50 messages × ~500 tokens input + ~50 tokens output at Sonnet 4.5 prices. Cheap insurance against routing regressions; run on every tool registry change and weekly in CI.

~600-1000 tokens per call (cached after first)Tool description token cost (5 tools × 4 lines)

Tools array is stable; mark with cache_control: ephemeral. Schema-cache hit rate ≥ 70% drops effective per-call cost ~90% on the tools array.

~+50-100ms per tool call (network)MCP server overhead

MCP runs as a separate process or service; network round-trip adds latency. Worth the cost when 2+ agents share the tool. Single source of truth beats inline duplication.

~5ms; ~1KB per rowAudit log write

Append-only JSONL write in the PostToolUse hook. At 1000 calls/day, 1MB/day, 30MB/month. Negligible storage. Indispensable for production debugging.

07 · Ship checklist

Check every gate before release

0/11 checked
  • tool-calling
  • tool-calling
  • hooks
  • hooks
  • structured-outputs
  • tool-choice
  • mcp
  • evaluation
08 · Practice

5 exam-pattern questions

Work through one question at a time, check the architecture, then move through the set.

Question 1 of 5 · D2Choose the best answer

Your agent has 6 tools. Routing accuracy drops from 95 percent (with 5 tools) to 87 percent (with 6). What is the cause and the architectural fix?

09 · FAQ

Frequently asked

Why does the 4-line description pattern matter so much?

It gives the model structural cues across the registry. Each tool has the same 4 lines in the same order. After reading 5 such descriptions, the model has a stable mental model: 'when I want to know WHAT, I read line 1; WHEN, line 2; ORDERING, line 4'. Vague one-liners force the model to infer shape every time. The pattern cuts wrong-tool selection from ~12% to <3%.

Can I use the 4-line pattern in MCP tool descriptions?

Yes. Same pattern, same effect. MCP tools surface to the agent through the same tools[] array as inline tools; the description format is identical. If you ship an MCP server, write the 4-line pattern into the server's tool definitions. Downstream agents inherit the routing accuracy without doing anything.

What if the policy is too complex for a hook?

Then the hook calls a policy service. PreToolUse hooks are subprocesses, not pure functions. They can hit a Convex action, a feature flag service, a rules engine. The point is the gate is OUTSIDE the prompt: deterministic code makes the deny decision, not the model. Complex policies live in the service the hook calls; simple bounds live in the hook itself.

How do I add a new tool to the registry without breaking routing?

Three steps: (1) add the tool with a full 4-line description; (2) re-run the 50-intent routing-accuracy eval. If accuracy drops below 95%, the new tool's description overlaps with an existing one, fix the descriptions; (3) gate the deploy on the eval threshold. Adding tools blindly is the #1 way registries degrade.

Should every tool emit the 4-bucket error contract?

Every tool that can fail. Read-only lookups can mostly emit Transient or Data; write tools add Business; auth-protected tools add Permission. The contract is uniform: { is_error: true, error: { bucket, code, detail, retryable } }. The agent's retry logic relies on it; without uniformity, you'd write per-tool retry code and miss bugs.

When do I split into specialist sub-agents vs add tools to the existing one?

At 5 tools is the rule of thumb. Tools that share state (e.g. verify_customer + lookup_order + process_refund) can stay in one agent. Tools that don't share state (admin functions vs support functions vs analytics) belong in different agents. Split, route with a triage classifier, each agent stays at 4-5 tools.

Is the 4-bucket model Anthropic's or community-derived?

Community-derived but architecturally consistent with Anthropic's tool-use guidance. The buckets formalize the patterns Anthropic's docs hint at (transient retry; permission escalate; etc.). The scenario catalog marks this scenario as 🟡 OP-claimed (Reddit thread 1s34iyl) but architecturally well-grounded. Drilling it benefits real exam prep.

How does this scenario compose with MCP server security?

Tightly. When tools are exposed via MCP rather than inline, the 4-line description pattern, the PreToolUse policy gate, and the 4-bucket structured-error contract all live on the MCP server side. The MCP-security checklist (secrets via ${ENV_VAR}, every parameter untrusted, binary allowlist, HTTPS-only transport, audit-log every tool_use) is the operational hardening; the patterns on this page are the design contract. Cross-link: P3.4 (developer-productivity-agent) FAQ covers the MCP-security checklist in depth. Pair both pages when designing a new MCP server. Tagged related: mcp-security cluster.

Help someone build it

Share this scenario.

One share is one less team repeating the same architecture mistake.