Overview
The model reads each tool’s name, description, and input schema before deciding whether and how to call it, so tool design mistakes show up as wrong arguments, skipped fields, and the wrong tool picked. Write each description as the primary prompt, shape tools around workflows, and return recoverable failures as tool results. The general rules for any provider (clear jobs, typed arguments, validation) are in tool-use-and-function-calling.
Name tools within what the spec and clients accept
The spec says tool names should be 1 to 128 characters, case-sensitive, unique within a server, and limited to letters, digits, _, -, and .. The Claude API accepts only ^[a-zA-Z0-9_-]{1,128}$, so skip dots. Use verb_object in snake_case (search_issues), not noun-first (issues_search) or vague verbs. Prefix names with the service (github_list_prs) when one tool library spans several services; a client that aggregates servers should also prefix to avoid collisions, and Claude Code already exposes tools as mcp__<server>__<tool>.
Write descriptions as the primary prompt
Anthropic calls the description the most important factor in tool performance. State what the tool does, when to use it and when not to, what each parameter means, what it returns, and what it does not cover; aim for at least three or four sentences. MCP tools have no input_examples field, so put one example call in the description.
{
"name": "search_issues",
"description": "Search GitHub issues by query string. Returns up to 30 issues sorted by relevance with id, title, state, and labels. Does not return comments; use get_issue for those. Example: search_issues({ \"q\": \"is:open label:bug repo:org/repo\" }).",
"inputSchema": {
"type": "object",
"required": ["q"],
"properties": {
"q": { "type": "string" },
"state": { "type": "string", "enum": ["open", "closed", "all"] },
"limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 30 }
}
}
}Use enums and numeric bounds so the model picks from valid values. Mark required only what the tool cannot default. inputSchema defaults to JSON Schema 2020-12; a tool with no parameters uses { "type": "object", "additionalProperties": false }.
Shape tools around workflows, and keep writes separate from reads
Anthropic recommends fewer, more capable tools over one per API endpoint: a schedule_event that finds availability and books beats list_users, list_events, and create_event. Merge operations that are always called in sequence. Do not merge reads and destructive writes behind an action enum, because permission rules and client confirmation work per tool: Claude Code can allow mcp__github__list_issues while denying mcp__github__delete_*, but it cannot split one manage_issues tool. Declare annotations such as readOnlyHint and destructiveHint, and remember clients must treat them as untrusted unless the server is trusted.
Return recoverable failures as tool results
Use a JSON-RPC protocol error for unknown tools and malformed requests. Report API failures, validation failures, and business-rule violations in the result with isError: true and text the model can act on, such as “Invalid departure date: must be in the future. Current date is 2026-10-01.” Clients should pass these to the model so it can retry with corrected arguments.
Return lean, high-signal results
- Return natural-language or semantic identifiers (slugs, names) with the opaque ID only when a follow-up call needs it. A
response_formatparameter withconciseanddetailedvalues lets the model control verbosity. - Paginate, filter, and truncate with sensible defaults, and have a truncated result tell the model how to narrow the query.
- When output has a shape, declare
outputSchema, returnstructuredContentthat conforms, and also return the serialized JSON as a text block for older clients. - Claude Code warns when a result exceeds 10,000 tokens and caps it at 25,000 by default; see claude-code-mcp.
Prune and test the tool set
Every tool adds tokens to the model’s choice. Remove tools the logs show are never called and merge tools always called together; mcp-logging gives the usage data. Test with realistic tasks that need several tool calls, read the transcripts for confusion, and iterate on names and descriptions. Return state-bearing results as explicit handles; see mcp-servers.