cd /news/artificial-intelligence/designing-tools-llms-can-actually-us… Β· home β€Ί topics β€Ί artificial-intelligence β€Ί article
[ARTICLE Β· art-68140] src=tengli.dev β†— pub= topic=artificial-intelligence verified=true sentiment=Β· neutral

Designing tools LLMs can actually use: five failure modes I keep seeing

A production AI agent integrator identifies five failure modes that cause large language models to misuse tools, including undocumented parameters, competing tool names, prose constraints instead of schema constraints, catalog bloat, and unhelpful error messages. The author argues that tool reliability depends more on design for model comprehension than on engineering quality, and recommends fixes such as adding parameter descriptions with examples, using enums over prose constraints, and writing error messages as prompts.

read4 min views11 publishedJul 12, 2026
Designing tools LLMs can actually use: five failure modes I keep seeing
Image: Tengli (auto-discovered)

I integrate tools into a production AI agent for a living β€” first-party ones, third-party ones, HTTP APIs wrapped as tools, MCP servers. After enough integrations, you notice something uncomfortable: whether a model uses a tool correctly has surprisingly little to do with how well the tool is engineered.

A tool can have clean code, solid auth, perfect uptime β€” and the model still picks the wrong one, invents arguments, or ignores it entirely. The failures cluster into a handful of patterns. Here are the five I keep seeing.

1. The parameter with no description #

The single most common failure, by a wide margin.

"url": { "type": "string" }

Your type system knows this is a string. The model needs to know which URL β€” the full page URL? just the domain? with or without protocol? URL-encoded? A model facing an undocumented parameter doesn't stop and ask. It guesses. Sometimes it guesses right. In production, "sometimes" is a bug report.

The root cause is almost always schema generation: zod, Pydantic, or an OpenAPI converter produces structurally perfect schemas with zero semantics, and nobody goes back to add .describe()

. The type checker is satisfied. The model is starving.

The fix costs an hour: every parameter gets a description with format and one example value. It is the highest-leverage hour you can spend on agent reliability, and nobody spends it.

2. Tools that compete for the same intent #

get_user

and get_users

. search_docs

and query_docs

. send_message

and post_message

.

To you, the difference is obvious β€” you wrote them. To a model doing tool selection over a catalog it has never seen before, two near-identical names with near-identical descriptions is a coin flip. And a coin flip at step 2 of a 6-step task is a 50% failure rate you'll never reproduce locally.

Two rules of thumb: if two tools' descriptions could be swapped without anyone noticing, the model can't tell them apart either. And every description should answer when to use this instead of the neighbors β€” the best catalogs cross-reference ("for bulk queries, use X instead").

3. Prose constraints instead of schema constraints #

"status": {
  "type": "string",
  "description": "Must be one of: active, inactive, banned"
}

That constraint lives in prose, which means the model can violate it. Move it into "enum": ["active", "inactive", "banned"]

and it can't β€” most inference stacks enforce enums at decoding time.

The general principle: anything the schema can express, the schema should express. Descriptions are for semantics; constraints belong in the type system. Every constraint you leave in prose is a runtime error you've chosen to discover later.

4. The catalog tax #

Every tool you expose gets serialized into every single request. A catalog with 25 tools and generous schemas can cost several thousand tokens before the user has typed a word β€” you pay it on every turn, forever.

Worse than the money: selection accuracy degrades as catalogs grow. The model attends over everything; ten sharply-differentiated tools reliably beat thirty overlapping ones on both cost and correctness.

If your catalog is large, split it by domain, or gate rarely-used tools behind a mode. Your token bill and your error rate will both thank you.

5. Errors that strand the model #

When a model calls your tool wrong, your error message is the only feedback loop it has. Compare:

Internal Server Error

Missing required parameter "query". Expected a free-text search string, e.g. "login bug".

The first strands the model β€” it will retry the same call, or give up, or hallucinate a workaround. The second lets it self-correct in one turn. Error messages are prompts. Write them like prompts.

There's a nastier variant: the tool that silently accepts an invalid call β€” declared required

fields that aren't actually enforced, so the call "succeeds" with undefined

in the payload. A model can recover from a good error. It cannot recover from a lie.

The common thread #

None of these are engineering problems in the traditional sense. The code is fine. They're writing problems β€” descriptions, names, error messages β€” the parts that don't show up in code review because they don't break tests.

The mental model that helps: your tool definition is not an API contract, it's a prompt. It gets read by a language model under time pressure with no documentation open in another tab. Write it the way you'd write for a sharp intern on their first day: what this does, when to use it, what comes back, and what to do when it fails.

The teams that internalize this ship tools that agents use correctly on the first try. The teams that don't file bugs against the model.

Next up: how widespread are these failure modes across the MCP ecosystem, actually? I've been collecting data. It is not flattering. Stay tuned.

── more in #artificial-intelligence 4 stories Β· sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/designing-tools-llms…] indexed:0 read:4min 2026-07-12 Β· β€”