Cheatsheet 3 — Tool Design & MCP
Domain 2 (18%).
Tool descriptions are the primary selection mechanism
The model chooses a tool from its description. Not from the name, not from the system prompt. Fix selection problems in the descriptions first.
A good description states:
| Element | Example |
|---|---|
| What it does | "Retrieve a single order by its exact order ID." |
| Input formats | "order_id must be the full ID including the ORD- prefix." |
| Example queries | "Use for: 'where is order ORD-10023841'." |
| Edge cases | "Returns an error if the order is older than 24 months (archived)." |
| Explicit boundaries | "Does not search by customer name — use get_customer first." |
🎯 The boundary clause is what prevents misrouting. Say what the tool is not for.
Selection failure → the fix
| Symptom | Fix |
|---|---|
Two tools with overlapping descriptions (analyze_content vs analyze_document) get confused |
Rewrite both descriptions with explicit boundaries; rename if the names collide semantically |
| One generic tool used for everything, poorly | Split it: analyze_document → extract_data_points / summarize_content / verify_claim_against_source |
| The right tool exists but is never chosen | Check the system prompt for keyword-sensitive instructions pulling the model elsewhere |
| Selection accuracy degraded after adding tools | Too many tools. 18 instead of 4–5 degrades selection. Consolidate or scope per agent. |
An MCP tool loses to a built-in (Grep) |
Enhance the MCP tool's description so its advantage is stated |
Rewrite vs split: rewrite when the tools are right but the boundaries are unclear. Split when one tool does several genuinely different jobs.
Scope the common case: if 85% of uses are one narrow job, provide a scoped tool for that job (verify_fact) rather than a general one everyone misuses. Replace an unvalidating fetch_url with a validating load_document.
Least privilege: each agent gets only the tools its role needs. This is both a safety property and a selection-accuracy property.
tool_choice
| Value | Behaviour |
|---|---|
auto |
Model decides whether to use a tool. Default. |
any |
Model must use some tool. |
{"type": "tool", "name": "..."} |
Model must use that tool. Use for forced structured output. |
MCP configuration
| File | Scope | Committed? |
|---|---|---|
.mcp.json (repo root) |
Project — the whole team | ✅ Yes |
~/.claude.json |
Just you | ❌ No |
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
},
"jira": {
"command": "npx",
"args": ["-y", "mcp-jira"],
"env": { "JIRA_API_TOKEN": "${JIRA_API_TOKEN}" }
}
}
}
${ENV_VAR}expansion is how you commit config without committing secrets.- Tools are discovered at connection time and are all simultaneously available thereafter.
- MCP resources — for exposing content catalogs, as opposed to callable actions.
- Prefer a community MCP server for a standard integration (Jira, GitHub, Slack). Build custom only for your own systems.
- 🚫 Deploying/hosting MCP servers is out of scope. Don't pick infrastructure answers.
Error handling
isError: true is the MCP flag that marks a tool result as a failure.
🎯🎯 An access failure is NOT a valid empty result
| Situation | Correct result |
|---|---|
| Query ran, genuinely nothing matched | [] with isError: false |
| Permission denied, connection failed, collection missing | isError: true with a category |
Returning [] for a permission failure makes the agent report "no data exists." Silent and confidently wrong.
The four-category taxonomy
| Category | Retryable? | Example |
|---|---|---|
| transient | ✅ Yes | Timeout, 503, rate-limited upstream |
| validation | ⚠️ After correcting input | Malformed order ID |
| business | ❌ No | Refund window expired, policy prohibits |
| permission | ❌ No | Not authorized for this collection |
{
"isError": true,
"errorCategory": "business",
"isRetryable": false,
"message": "Refund window expired: order shipped 2026-03-14 (139 days ago); policy allows 90 days.",
"alternatives": ["escalate_to_human for a policy exception"]
}
📎 retriable: false on business rules is the key one — retrying a policy violation just burns turns.
Propagation
- Recover locally in the subagent where you can.
- Propagate only what you couldn't resolve, with partial results attached.
- 🚫 Never silently suppress an error and report success.
- 🚫 Never terminate the whole workflow because one component failed.
- ✅ Complete what you can, report what you couldn't, annotate the gap in the output.
Built-in tools
| Tool | Use for |
|---|---|
| Grep | Searching file contents |
| Glob | Finding files by path/name pattern — **/*.test.tsx |
| Read | Reading a full file |
| Write | Writing a full file |
| Edit | Targeted change anchored on unique text |
| Bash | Commands |
- Grep = contents. Glob = paths. This distinction is worth one item on its own.
- Edit needs a unique anchor. When the target text appears more than once, use Read + Write instead.
- Incremental codebase understanding: Grep for entry points → Read to follow the imports. Not "read every file."
Quick reflexes
- Selection problem → descriptions first.
- Too many tools → fewer, scoped tools per agent.
- Empty result → is this "nothing found" or "couldn't look"?
- Business-rule failure →
isRetryable: false. - Standard third-party integration → community MCP server.
- Hosting/deploying an MCP server → out of scope, wrong answer.