Tool Use & MCP Reference
A working reference for giving agents hands: the JSON tool-schema template, naming conventions, error-handling patterns, and when to use the Model Context Protocol (MCP) versus wiring tools by hand. Examples use Anthropic's Claude tool use API; the patterns carry to any function-calling model. Concepts: Tool Use — Giving Agents Hands.
Tool Definition: Anatomy
Each client tool is a JSON object with three required fields.
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | Must match ^[a-zA-Z0-9_-]{1,64}$. The identifier the model emits. |
description |
string | yes | Plaintext: what it does, when to use it, when NOT to. The biggest lever on quality. |
input_schema |
object | yes | A JSON Schema object; top level must be "type": "object". |
input_examples |
array | no | Schema-valid example inputs; helps with nested/format-sensitive params. |
Top-level input_schema does not support oneOf / allOf / anyOf — keep the root an object.
Clean Schema Template
Per-parameter description strings are read by the model — write them.
{
"name": "search_orders",
"description": "Search a customer's orders by status and date. Use when the user asks about past or pending orders. Returns up to 50 orders with id, status, and total. Does NOT return line items or issue refunds.",
"input_schema": {
"type": "object",
"properties": {
"customer_id": {"type": "string", "description": "Customer UUID, e.g. 'cus_8a31'."},
"status": {"type": "string",
"enum": ["pending", "shipped", "delivered", "cancelled"],
"description": "Filter to one status. Omit for all."},
"since": {"type": "string", "format": "date",
"description": "ISO-8601 date (YYYY-MM-DD); orders on or after this date."}
},
"required": ["customer_id"]
}
}
Naming & Description Conventions
| Rule | Bad | Good |
|---|---|---|
| Namespace by service | send |
chat_send_message |
| Consolidate related actions | create_pr, merge_pr |
github_pr with an action enum |
| Verb-first and concrete | data |
query_database |
| Describe behaviour, not just name | "Gets the price." | "Returns the latest USD trade price for a US-listed ticker." |
Vendor guidance: 3-4 sentences per description; fewer high-capability tools over many narrow ones; namespace across services; return only high-signal fields and stable IDs.
The Agentic Loop
Each turn: call messages.create(..., tools=TOOLS); if stop_reason == "tool_use", run each tool_use block via dispatch_tool (below), append the assistant turn plus a user message of tool_result blocks, and re-call. Loop until end_turn.
Each tool_result.tool_use_id MUST echo the id of the tool_use it answers; that is how the model correlates call and response. Set is_error: true on failure (see below).
Error Handling: Return, Don't Raise
The key discipline: when a tool fails, catch the exception and return it as a tool_result with is_error: true rather than letting it kill the loop. A clear message lets the model understand the failure and retry.
| Failure | Anti-pattern | Pattern |
|---|---|---|
| Tool raises | Exception kills the loop | Catch; return is_error: true with the message |
| Bad input | Silent wrong answer | Validate; return which field was invalid |
| Upstream 500 | Retry forever | Return the error; let the model decide |
| Empty result | Return null |
Return text ("No orders matched.") for signal |
def dispatch_tool(block):
try:
if block.name != "search_orders":
return _error(block.id, f"Unknown tool: {block.name}")
content = json.dumps(search_orders(**block.input))
return {"type": "tool_result", "tool_use_id": block.id, "content": content}
except Exception as exc: # never let this escape the loop
return _error(block.id, f"{type(exc).__name__}: {exc}")
def _error(tid, msg):
return {"type": "tool_result", "tool_use_id": tid, "content": msg, "is_error": True}
Error messages are read by the model: state what failed and what a valid call looks like ("since must be YYYY-MM-DD; got '03/2026'").
Controlling When Tools Fire
The tool_choice parameter has four modes:
{"type": "auto"}— model decides per turn (default whentoolspresent){"type": "any"}— must call some tool, model picks which{"type": "tool", "name": "..."}— forces a specific tool{"type": "none"}— no tools (default when notools)
any and tool prefill the turn, so no preamble precedes the call.
Model Context Protocol (MCP)
MCP is an open standard for connecting AI applications to external systems — its maintainers call it "a USB-C port for AI applications." Build a server once and any MCP-capable client (Claude, ChatGPT, VS Code, Cursor) can use it.
Architecture & Primitives
A host (the AI app) spins up one client per server, each a dedicated connection. The data layer is JSON-RPC 2.0; transport is pluggable — stdio (local process, one client, no network overhead) or Streamable HTTP (remote, HTTP POST plus optional Server-Sent Events, OAuth/bearer auth). A server exposes three primitives, each discovered via a */list method.
| Primitive | What it is | Example |
|---|---|---|
| Tools | Functions invoked via tools/call |
Run a query, send a message |
| Resources | Read-only context (resources/read) |
A DB schema, a file's contents |
| Prompts | Reusable interaction templates | A few-shot workflow template |
An MCP tool mirrors a Claude tool (name, description, inputSchema). Discovery is dynamic: servers emit notifications/tools/list_changed to make clients re-fetch.
MCP vs Ad-Hoc Function Calling
| Ad-hoc function calling when... | MCP when... |
|---|---|
| Tools private to one app | Tools reused across clients/hosts |
| Few tools, coupled to your code | A growing, shareable surface |
| Lowest latency, no extra process | You want standard discovery/lifecycle |
| Prototyping a single agent | Distributing for others to install |
Rule of thumb: start with direct function calling in one agent; adopt MCP when a capability must be shared or distributed. The two coexist in the same loop.
Quick Checklist
- [ ] 3-4 sentence description per tool, covering when NOT to use it.
- [ ] Each parameter has a
description; enums constrain choices; root schema isobject. - [ ] Names namespaced; related actions consolidated under an
actionenum. - [ ] Execution catches exceptions, returns
is_error: true— the loop never crashes. - [ ] Responses return only high-signal fields and stable IDs.
- [ ] Right model: direct calls for one app, MCP for shared/distributed surfaces.
Related: Building Your First Agent and Agent Safety & Guardrails.