Designing Good Tools: Narrow, Described, Typed, Few
Why tool descriptions are prompt engineering, how typed parameters cut error rates, and why a small tool surface beats a comprehensive one.
Tool selection is a language problem, not a routing problem. The model has no access to your source code, your database schema or your team's conventions — only the name, description and inputSchema you supplied. Almost every "the model called the wrong tool" bug is a tool-definition bug.
One tool, one job
A tool should do a single, nameable thing with a predictable shape. get_order_status is a good tool. manage_order with an action parameter accepting cancel, refund, reship and lookup is four tools wearing a trenchcoat: the description cannot honestly describe when to use it, the schema cannot express which fields matter for which action, and you lose the ability to permission or gate the destructive branches separately.
Write the description for the model
Say what the tool does, when to reach for it, and — crucially — when not to. State what it returns and what it does not cover. "Gets weather" tells the model nothing about whether to use it for a forecast or a historical average. Two or three plain sentences beat a one-line stub every time. Do the same for each parameter: a description on every property is the cheapest accuracy improvement available.
Type everything you can
Use enum for closed sets, so the model picks "emea" rather than inventing "Europe". Use format: "date" and say the expected pattern in the description. Mark only genuinely mandatory fields in required, and give the model a way to express "I don't know" rather than forcing it to fabricate a value. Remember the schema is guidance to the model, not a contract Bedrock enforces — see the lesson on validation.
Keep the surface small
Every tool definition is prompt text sent on every request, and every additional tool is another chance to choose wrongly. Accuracy degrades as the catalogue grows, well before you hit any service quota. If an agent needs dozens of capabilities, group them behind a small number of coarse tools, split them across specialised agents, or retrieve a relevant subset of definitions per request rather than shipping the whole catalogue every time.
Tool or knowledge base?
The distinction the exam likes: a knowledge base answers "what do our documents say" through retrieval and is the right home for policy text, manuals and FAQs. A tool performs an action or fetches live, per-entity state. In Amazon Bedrock Agents these are separate attachments — a knowledge base for grounding, action groups for doing. Reaching for an action group to serve static documentation, or a knowledge base to place an order, is the classic wrong answer.
Design for what comes back
The tool result re-enters the context window, so return the fields the model needs and nothing else. Dumping a 200-field API response wastes tokens and buries the signal. Finally, treat irreversible actions as a separate design category: a tool that sends money or emails customers deserves a confirmation step, an idempotency key and an audit record, none of which the model can be trusted to supply on its own.
Example
// WEAK - vague name, no guidance, untyped, does four things
{
"toolSpec": {
"name": "manage_order",
"description": "Manages orders.",
"inputSchema": {"json": {
"type": "object",
"properties": {
"action": {"type": "string"},
"id": {"type": "string"},
"data": {"type": "object"}
},
"required": ["action", "id"]
}}
}
}
// STRONG - one job, told when to use it, typed and bounded
{
"toolSpec": {
"name": "cancel_order",
"description": "Cancel a single order that has not yet shipped. Use only after the customer has explicitly confirmed the cancellation and you have called get_order_status to verify the order is still in the 'pending' or 'processing' state. Does not issue refunds - use refund_order for that. Returns the new order state.",
"inputSchema": {"json": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"pattern": "^ORD-[0-9]{5}$",
"description": "Order ID in the form ORD-12345."
},
"reason_code": {
"type": "string",
"enum": ["customer_request", "duplicate", "payment_failed", "other"],
"description": "Why the order is being cancelled."
},
"reason_note": {
"type": "string",
"maxLength": 280,
"description": "Free-text detail. Required when reason_code is 'other'."
}
},
"required": ["order_id", "reason_code"],
"additionalProperties": false
}}
}
}When to use it
- A team halves mis-routed calls in a support agent by rewriting three overlapping search tools into one with an explicit enum for the corpus to search.
- An engineering assistant exposes read-only diagnostic tools to every user but keeps restart_service as a separate tool behind a confirmation step and a tighter IAM role.
- A retail agent attaches product manuals as a Bedrock knowledge base and keeps only stock lookup and order placement as action groups, rather than building a document-search action.
Discussion