Domain 2 — Tool Design & MCP Integration
CCA Foundations course · Page 3 of 8 · ← Back to all courses · Weight: 18%. Mark complete at the bottom to advance.
2.1 — Tool Descriptions as the Routing Mechanism · Core
Descriptions are the primary signal the model uses to pick a tool — two minimal, overlapping descriptions (e.g. get_customer = "Retrieves customer information" vs lookup_order = "Retrieves order details") get confused on ambiguous input like "check my order #12345."
- Include input format with examples and explicit boundaries ("do NOT use for…").
- Add ordering hints when tools are related (e.g. "run
extract_metadatabefore this tool") — without them, the model has no way to know two tools are meant to be sequenced rather than chosen between. - Grep = search file contents; Glob = match file paths. Conflating them is one of the most common misrouting patterns the exam tests, precisely because both sound like "find X."
- Fix misrouting by rewriting descriptions first — the lowest-effort change that addresses the root cause. Few-shot examples add token overhead without fixing the ambiguity; a keyword router over-engineers it; consolidating tools is more work than a first step warrants.
- Rename + re-describe to remove functional overlap (a vague
analyze_content→ a web-specificextract_web_results), or split a generic tool into purpose-specific tools with defined I/O contracts (analyze_document→extract_data_points+summarize_content+verify_claim_against_source). Splitting is worth the extra tool count when the generic tool's single description can't honestly describe three unrelated jobs at once. - Check the system prompt too — keyword-sensitive wording elsewhere in the prompt can create unintended tool associations that override even well-written descriptions.
⚠ Often-missed — Why Rewriting Descriptions Beats the Alternatives · Gap
When two minimal tools get confused, the exam offers several plausible-looking fixes — but only one is the first step. Few-shot examples add token overhead on every call without touching the root ambiguity. A keyword router in front of the model over-engineers a problem a better description solves for free. Consolidating the tools into one is more invasive than the situation warrants. Rewriting the descriptions — input formats, examples, edge cases, explicit "do NOT use for X" boundaries — is the lowest-effort change that fixes the actual cause.
2.2 — Structured Error Responses · Core
Return structured metadata so the coordinator can recover intelligently — a generic "Operation failed" hides the context needed to decide anything:
isError,errorCategory(transient = retry; validation = fix input, no retry; business = policy violation, explain to user; permission = not authorised, escalate),isRetryable,attemptedQuery,partialResults, and — for business-rule failures — acustomerMessagethe agent can relay directly.isRetryableis guidance, not a directive. The tool only signals a retry is possible; the agent loop owns the retry budget — max attempt count, backoff between attempts, escalation once the budget is exhausted. Marking a permanent failureisRetryable: truecauses infinite retry loops. A retry-after hint on the error, when present, should drive the backoff delay; when absent, the loop falls back to exponential backoff on its own.- Distinguish access failures from valid empty results. A timeout (needs a retry decision) is not the same as a successful query that legitimately found nothing (
isError: false, results: []) — conflating the two hides information the agent needs, and can make a broken connection look, from the agent's perspective, exactly like a search that simply had no matches. - Use a
verdictfield, not a boolean, for verification tools. A fact-checker needs three distinct outcomes:"supported"(sources confirm it),"contradicted"(sources refute it),"not_found"(simply absent from any source) —"not_found"and"contradicted"demand different downstream handling (report "unverifiable" vs. flag as actively wrong), and a boolean collapses that distinction into an ambiguous singlefalse. - Subagents recover locally from transient errors first, and only propagate what they can't resolve, along with what was attempted and any partial results.
- A business-rule error still needs a technical
errorCategoryand a plain-languagecustomerMessage— the agent uses the category to decide what to do next (don't retry, don't escalate as a bug), and relays the message so the customer isn't shown an internal string like"REFUND_LIMIT_EXCEEDED". Neither field alone is enough: the category without the message leaves the agent unable to explain anything; the message without the category leaves it unable to decide what to do next.
2.3 — Distribute Tools & Configure tool_choice · Core
- 4–5 tools per agent is optimal; ~18 tools degrades selection reliability and causes agents to misuse tools outside their specialisation (e.g. a synthesis agent attempting web searches).
- Give each agent a scoped role with limited cross-role tools for high-frequency needs — e.g. a narrow
verify_facttool for the synthesis agent's simple 85%-case lookups, while complex verifications still route through the coordinator to the web-search agent. Over-provisioning with the full toolset violates least privilege. - Replace generic tools with constrained alternatives — swap a free-form
fetch_urlfor aload_documentthat validates document URLs at the API level. tool_choicevalues:{"type": "auto"}(model decides, default),{"type": "any"}(must call some tool — guarantees structured output, no plain text),{"type": "tool", "name": "..."}(must call this specific tool — good for forcing a required first step),{"type": "none"}(no tool calls).- Force a specific tool only for the turn that needs it (e.g. extract metadata before enrichment), then switch back to
"any"or"auto"for subsequent steps — don't re-apply forced selection on every turn. - Principle of least privilege drives distribution decisions. When a synthesis agent burns 2–3 extra round-trips on simple fact-checks, the fix is a scoped
verify_facttool for that 85% case, not handing it the entire web-research toolset "to be safe" — over-provisioning re-creates the exact selection-reliability problem tool scoping exists to prevent.
2.4 — MCP Configuration · Core
.mcp.json(project root, committed) → team / version-controlled tooling, discovered by everyone who clones the repo.~/.claude.json→ personal / experimental servers, not shared via version control. Both are discovered at connection time and available simultaneously.- Environment-variable expansion for credentials (e.g.
${GITHUB_TOKEN}) — never hardcode a token in.mcp.json; git history is permanent. - MCP resources vs tools: resources expose readable content catalogs (issue summaries, doc hierarchies, database schemas) at connection time, giving visibility without exploratory tool calls; tools perform actions. Confusing the two means an agent burns several exploratory
list_issues/get_schema-style tool calls just to learn what's available — content a well-designed resource would have handed it up front, for free, at connection time. - Write detailed MCP tool descriptions — a thin description makes Claude fall back to a built-in (e.g.
Grep) instead of a more capable MCP tool. The fix when this happens is always to enrich the MCP tool's description — never to remove or disable the built-in, which would just eliminate a useful fallback for cases the MCP server doesn't cover. - Prefer an existing community MCP server for standard integrations (GitHub, Jira); reserve custom servers for team-specific, proprietary workflows.
- Scope is the question, not capability. A
/reviewcommand every developer should get on clone goes in project-scoped.claude/commands/; a personal, still-experimental MCP integration goes in user-scoped~/.claude.json— "shared via version control" vs. "just me" is the entire decision, independent of how polished either one is.
2.5 — Built-in Tools: Read, Write, Edit, Bash, Grep, Glob · Core
- Grep — search file contents for a pattern (function names, error strings, imports).
- Glob — find files by path/name pattern (e.g.
**/*.test.tsx), regardless of location. - Read — load a full file's contents. Write — create or fully replace a file. Edit — targeted change via a unique text anchor; the most efficient option when it applies.
- Edit → Read+Write fallback: when the anchor text isn't unique, Edit can't safely locate the spot — fall back to Read the full file, then Write it back with the change, rather than a blind global replace that would touch every occurrence.
- Build understanding incrementally — Grep first to find entry points, Read only what's relevant to follow imports and trace flows. Don't load every file up front.
- Tracing a function across wrapper modules — identify all exported/re-exported names first, then Grep for each alias; a single-name search misses callers using a wrapper name.
- Bash is the fallback — reach for it when no other built-in fits (running a script, a package-manager command), not as a default first move ahead of the more targeted tools above.
⚠ Often-missed — Tool Distribution / Separation of Concerns · Gap
If an agent misuses a tool, the fix is to remove the tool and create a dedicated subagent for that job. NEVER patch it with instructions, few-shot examples, or tool_choice — the model will keep reaching for a tool that's simply outside its role.
⚠ Often-missed — Edit Anchor Mismatches (Whitespace) · Gap
An Edit that reports "not found" immediately after a successful Read almost always means a whitespace mismatch — spaces vs tabs, or LF vs CRLF line endings. Copy old_string verbatim from the Read output rather than retyping it.
⚠ Often-missed — Tool Batching to Reduce Round Trips · Gap
When several tools are needed together, prompt Claude to emit multiple tool_use blocks in one turn. Don't build a composite "do-everything" tool — that just trades one round-trip problem for a brittle, hard-to-route mega-tool that's harder for the model to select correctly and harder for you to give clean, structured errors from.
⚠ Often-missed — .mcp.json Credentials Are Never Hardcoded · Gap
A hardcoded token in .mcp.json — even one later removed — stays recoverable in git history forever. The only correct pattern is environment-variable expansion (${GITHUB_TOKEN}) with the actual secret injected at runtime from the environment, never committed in any form.
Exam reflexes for Domain 2
- "Tool misrouting" → fix tool descriptions first (not few-shot, not
tool_choice, not a keyword router). - "Agent misusing a tool" → remove it + dedicated subagent, never patch with instructions.
- "18+ tools per agent" → split into scoped agents (4–5 each).
- "Timeout returns empty success" → silent suppression anti-pattern → structured error with
isError. - "Boolean verification result loses nuance" →
verdict: supported / contradicted / not_found. - "
isRetryable: truecausing infinite loop" → the agent loop owns the retry budget, not the tool. - "Multiple tools, different formats" →
PostToolUsehook to normalise (Domain 1). - "Team-shared MCP server" →
.mcp.json· "personal/experimental" →~/.claude.json. - "Edit fails right after a successful Read" → whitespace/line-ending mismatch — copy
old_stringverbatim. - "Find all callers of X" → Grep; "find all
*.test.tsx" → Glob. - "Agent making exploratory calls just to see what's available" → expose an MCP resource (content catalog), not more tools.
- "Synthesis agent doing 2–3 extra fact-check round-trips" → scoped
verify_facttool for the common case, not the full toolset. - "MCP tool ignored in favor of a built-in" → enrich the MCP description; don't remove the built-in fallback.
Test yourself on this domain. Take the Domain 2 practice quiz — 60 questions, instant scoring, an explanation for every answer.