Designing an API for a consumer that cannot read the docs
A tool set is an API whose only user is a model with no memory of your codebase and no way to ask. That constraint changes granularity, naming and error design in specific ways, and most tool sets are built as though a person were reading them.
TL;DR: Design for a consumer that reads only names, descriptions and schemas, has no persistence between calls, cannot ask a clarifying question, and will confidently guess rather than stop. That means fewer and coarser tools than an internal API would have, verbs that map to intentions rather than to endpoints, and every ambiguity resolved in the definition because there is no second chance to resolve it.
Where you are. You know a tool call is a request your code honours, and that names, descriptions and schemas are all the model sees. The design discipline that follows fills this module, starting with granularity, the decision made worst and earliest.
Four constraints that are not like a human consumer
Write them down, because each one implies a design rule.
No documentation beyond the definition. A person reads the guide, greps the source, asks a colleague. The model has the schema and nothing else. Every fact needed to use a tool correctly must be in its definition.
No memory between calls. It does not learn from the last call that status must be lowercase. It will make the same mistake in the same run, and again tomorrow.
No clarifying question. Faced with ambiguity, a person asks. The model guesses, and guesses plausibly, so the mistake looks like an answer.
Selection degrades with catalogue size. A person handles a hundred endpoints via search. Accuracy falls as your tool count grows, especially once tools overlap.
Granularity: fewer, coarser tools
The instinct from API design is to expose primitives and let the consumer compose them. That is right for a human developer and wrong here.
Consider three tools: get_customer, get_orders_for_customer, get_order_details. Perfectly factored. A model answering "why was this customer charged twice" has to compose three calls in the right order, and each hop is a chance to fail.
Now consider one tool: get_customer_billing_history(customer_id) returning the customer, their recent orders and the charges. One call, no composition, no ordering error.
The rule: a tool should do a whole unit of work that a user would recognise as one thing. Not the smallest reusable operation.
There is a real counter-pressure, and it bounds this. Coarse tools return more data, which is context, which is cost, and that is the next lesson's subject. So coarseness is bounded by what the result costs you, not by API aesthetics.
Practically: start coarse, at the level of an intention rather than an operation, and split only when a result is too large or a single tool has become several unrelated behaviours behind a parameter.
Naming for the choice being made
Names are read as a menu of intentions, so name them that way. search_knowledge_base beats query_kb_v2. issue_refund beats post_transaction.
Two rules that prevent most wrong-tool selection:
Make the distinguishing word first. search_tickets and search_docs differ at the end, and the model is choosing between two things that begin identically. ticket_search and documentation_search differ where attention lands.
Never expose two tools whose descriptions could both plausibly answer the same question. If they could, either merge them or make each description explicitly exclude the other's territory. This is the single highest-return audit you can run on an existing tool set.
The ambiguity budget
Every ambiguity in a definition will be resolved by guessing, so resolve them in advance.
The ones that recur, worth checking every tool for:
- Units and formats. Is
amountin cents or currency units? Isdatea date or a timestamp? Say so. - Defaults for optional parameters. State what happens when omitted, or the model supplies a value to be safe.
- Whether a search is exact or fuzzy. It changes how the model constructs the query.
- What an empty result means. Not found, or no permission, or the query was malformed? Three different next actions.
The interface you are really designing
Worth ending on, because it reframes the module: you are not exposing your system to a model. You are designing the vocabulary in which the agent is able to think about the problem.
A tool set of five well-named, intention-shaped tools defines a space the agent can reason in clearly. Twenty overlapping primitives define a space where it will mostly be wrong, whatever model you use. That is a design responsibility, and it is yours rather than the model's.
Do this before moving on
Take your tool set and do the overlap audit: for each pair, ask whether both descriptions could plausibly answer the same user question. Then for each tool, ask whether it does a whole unit of work someone would recognise, or a fragment requiring composition.
Most sets have at least one overlapping pair and at least two tools that should be one. Fixing those two things usually improves selection accuracy more than any prompt change.
Go deeper
- Function calling and tools is the protocol underneath, if you want the mechanics restated.
- Model context protocol is what changes when your tools are consumed by systems you did not write, where the definition really is all anyone gets.
- Practice question: Your agent picks the wrong tool or passes wrong parameters is this lesson as an interview question.
- Practice question: How would you design tools for MCP? is the granularity discussion in the context where it matters most.
- Practice question: How do you design an agent with many tools? is what to do once the catalogue is genuinely too large to send.
Key takeaways
- The consumer has no documentation, no memory between calls, no way to ask, and degrades as the catalogue grows.
- Prefer fewer, coarser tools shaped like intentions rather than the smallest reusable operations, bounded by what results cost in context.
- Put the distinguishing word first in a name, and never ship two tools whose descriptions could both answer the same question.
- Every unresolved ambiguity is resolved by a plausible guess. Units, defaults, match semantics and the meaning of empty all belong in the definition.
Check yourself before an interviewer does. Answer from memory first.
Why does exposing well-factored primitives, good practice in an internal API, work badly for an agent?
