Writing Effective Tools for AI Agents
Core mental model: "Tools are a new kind of software which reflects a contract between deterministic systems and non-deterministic agents." Traditional APIs optimize for predictability (same input → same output). Agent tools must optimize for ergonomics — an agent might use a tool differently every time, or skip it and rely on its own knowledge. Design for how the agent actually thinks, not for API tidiness.
When to use this
- Standing up a new MCP server / function-call toolset.
- An agent calls the wrong tool, chains too many calls, or ignores a tool it should use.
- Tool output floods the context with raw data (UUIDs, full dumps, logs).
- You're wrapping an existing REST API 1:1 and the agent struggles.
- You want to iterate on tools with data, not vibes.
1. Context is a scarce resource
Agents read token-by-token with a limited window — unlike software that cheaply iterates memory. Push filtering, search, and pagination into the tool, don't make the agent process raw data.
- Prefer
search_contactsoverlist_contacts. - Prefer
search_logs(returns relevant lines + surrounding context) overread_logs.
2. Choose the right tools — consolidate, don't proliferate
"More tools don't always lead to better outcomes." Don't just wrap every endpoint. Build a few thoughtful tools targeting high-impact workflows that mirror how a human subdivides the task.
| Instead of many low-level tools | Build one workflow tool |
|---|---|
list_users, list_events, create_event |
schedule_event (finds availability and schedules) |
read_logs |
search_logs (relevant lines with context) |
get_customer_by_id, list_transactions, list_notes |
get_customer_context (compiles recent info) |
Start with a handful matched to your eval tasks, then scale.
3. Namespace your tools
Help the agent pick the right one:
- Prefix:
asana_search,jira_search - Resource-based:
asana_projects_search,asana_users_search
Prefix vs. suffix has non-trivial, model-dependent effects — test your naming against your evals. Good namespacing also reduces how many tools load into context.
4. Return meaningful context (signal over flexibility)
Return high-signal, contextually relevant fields. Avoid low-level identifiers the agent can't reason about.
- Use
name,image_url,file_type— notuuid,256px_image_url,mime_type. - Agents handle natural-language names far better than cryptic UUIDs. Resolve arbitrary IDs to meaningful language (or 0-indexed IDs) to sharply improve retrieval precision.
Response-format enum (let the agent control verbosity)
enum ResponseFormat { DETAILED = "detailed", CONCISE = "concise" }
Example: a Slack thread tool returns 206 tokens detailed (thread_ts, channel_id, user_id …) vs 72 tokens concise (thread content only) — roughly ⅓ the tokens. Also test the structure (XML / JSON / Markdown) against your evals — it measurably affects performance; there's no universal winner.
5. Optimize token efficiency
- Sensible defaults for pagination, range selection, filtering, truncation.
- Claude Code caps tool responses at 25,000 tokens by default — a useful reference ceiling.
- Steer via truncation messages: don't just cut off. Append guidance like "Consider using filters or pagination for more targeted results" to push the agent toward efficient strategies.
6. Actionable error messages
Errors are a steering surface. Replace opaque codes/tracebacks with specific next steps.
- ❌
404 Not Found - ✅
No user found with name 'jane'. Try searching by email (e.g., jane@company.com) or use list_users filter_by_department='engineering'.
7. Prompt-engineer tool descriptions
Descriptions collectively steer tool-calling — write them like onboarding a new teammate; make implicit context explicit: query formats, niche terminology, resource relationships, unambiguous inputs/outputs.
- Name params clearly:
user_id, notuser. - Even small refinements yield dramatic improvements — Claude Sonnet 3.5 hit SWE-bench SOTA after precise description tweaks; Anthropic's web-search tool stopped uselessly appending "2025" to queries after a description fix.
8. Evaluation-driven development (the loop that ties it together)
Prototype: stand up quick tools → give Claude the library/API docs (look for llms.txt on docs sites) → wrap in a local MCP server → claude mcp add <name> <command> [args...] → test locally, gather feedback.
Generate eval tasks: dozens of prompt/response pairs on realistic data, complex enough to need many tool calls. Strong vs weak:
- ✅ "Schedule a meeting with Jane next week to discuss the Acme Corp project; attach the planning notes; reserve a conference room."
- ❌ "Schedule a meeting with jane@acme.corp next week."
- ✅ "Customer ID 9182 was charged three times for one purchase — find all log entries and determine if other customers were affected."
Verifiers: pair each task with a verifiable outcome (exact-match → Claude-as-judge). Don't over-strict on formatting/punctuation; optionally note expected tool calls but don't overspecify — multiple valid paths exist.
Run programmatically: simple agentic while-loop with direct API calls:
for each task:
while not finished:
response = claude_api_call(task, tools)
if response.calls_tool:
result = execute_tool(); feed_result_back()
Have the agent output reasoning before the tool call (or enable interleaved thinking) so you can see why it called or skipped a tool.
Collect metrics beyond accuracy: per-tool runtime, total runtime, tool-call count, token consumption, tool errors. Redundant calls → consolidation opportunity; frequent invalid-parameter errors → clearer descriptions needed.
Analyze strategically: read raw transcripts, not just the agent's stated feedback — "what agents omit can be more important than what they include." Watch where they get stumped; agents don't always know the correct answer.
9. Collaborate with agents to improve tools
Concatenate eval transcripts and paste into Claude Code — agents are strong at analyzing transcripts and refactoring multiple tools at once while keeping them consistent. Use a held-out test set (separate from the eval set you iterate on) to avoid overfitting; this reliably extracts gains beyond hand-written or first-pass Claude-generated tools.
Best-practices checklist
- Selection: few thoughtful workflow tools > many overlapping endpoint wrappers.
- Namespacing: consistent prefix/resource scheme, tested against evals.
- Content: natural-language names, high-signal fields, no raw UUIDs.
- Format: concise/detailed enum; test XML/JSON/Markdown.
- Token efficiency: pagination + filtering + truncation with steering messages.
- Errors: specific, actionable guidance.
- Descriptions: explicit context, unambiguous param names.
- Evals: measure, iterate, use held-out sets.
Distilled from Anthropic Engineering, "Writing tools for agents": https://www.anthropic.com/engineering/writing-tools-for-agents