Tool design is API design under uncertainty

Agentic AI Growing Planted Aug 2026 · Tended Aug 2026

A tool schema is an API. Everything we know about API design — clear contracts, good errors, least surprise — still applies. What changes is the caller: it's probabilistic. A tool is the contract between a deterministic system and a non-deterministic agent, and that one shift rewrites the priorities. Ambiguity that a human developer would resolve by reading your source becomes, for an agent, a wrong call at runtime, sampled thousands of times a day.

Start with documentation, because the economics inverted. Human developers skim docs once and internalize them; an LLM re-reads your tool descriptions on every single call — they're injected into the context, at 100–300 tokens per tool, every time. The description isn't supplementary material; it's the only documentation the caller ever sees, and it's load-bearing. Anthropic's "intern test" is the right bar: could a competent newcomer, given only the description, use the tool correctly? The highest-leverage sentence in most descriptions is the one teams never write — the when-NOT-to-use clause that draws the selection boundary against a neighboring tool.

Ambiguity compiles to wrong calls

With a probabilistic caller, every underspecified corner of the schema becomes a failure distribution. The taxonomy is well documented: parameter key errors (hallucinated argument names), value errors (out-of-range, invalid), type mismatches. The mitigations are classic API discipline turned up a notch. Name parameters unambiguously — user_id, not user, because "user" invites a name, an email, or an object. Use enumerated values instead of free strings wherever the domain is closed. Apply poka-yoke thinking: design arguments so the wrong call is hard to express. Default fail-closed. Then validate before execution and enforce the schema strictly — treat the model's output as untrusted input, the same way you'd treat any external caller's.

Errors deserve the same inversion. A human reads a stack trace and reasons about your internals; an agent reads your error string and decides its next action from it alone. So the error message must teach recovery: what was wrong, what a valid call looks like, whether retrying can help. The anti-patterns are all failures of that duty — silent failures that return success-shaped nothing, opaque codes like ERR_4012, raw stack-trace dumps. What works is an error taxonomy the agent can branch on: validation, auth, not-found, transient, system. "Transient" tells the agent to retry; "validation" tells it to fix the call; "auth" tells it to stop and escalate. That's control flow you're shipping inside a string.

Least surprise, finally, becomes a budget problem. Tool selection accuracy degrades measurably as the catalog grows — there's a soft ceiling somewhere under twenty functions before confusion sets in — so fewer, broader tools beat many fine-grained ones, and consolidation beats 1-to-1 mapping of your API endpoints. (Beware the opposite ditch: the god-object tool that does everything through a mode parameter.) At real scale, retrieval takes over — deferred loading and server-side tool search can serve catalogs of up to 10,000 tools while cutting context by 85%. Context economy is the unifying constraint that classic API design never had: every tool you expose taxes every decision the agent makes, whether or not that tool is used. This is harness engineering, not prompt polish.

The honest caveat: strict schema enforcement has genuinely absorbed part of this problem. With strict mode, structurally invalid calls essentially disappear, and some poka-yoke effort is now redundant. But strict mode guarantees shape, not sense — the model can still pick the wrong tool, or pass a perfectly valid, perfectly wrong value. The syntax is solved; the semantics are yours. Design for the caller you actually have: brilliant, literal, and guessing.