Skip to content
aipatterns.dev

Code smellOther Smells

Thin Tool Wrapper

A tool for an agent or API consumer that passes an upstream operation through raw - one endpoint, one tool, provider-shaped payloads - so every real task becomes a chain of calls over low-signal data.

Also known as
Pass-Through Tool, Raw Endpoint Exposure

Signs

  • A tool's description says what it calls ("wraps the Users API") instead of when to use it - and consumers guess.
  • One task takes a chain of calls: get_user, then list_transactions, then list_notes, each returning a page the caller must join themselves.
  • Responses carry provider-shaped payloads - uuids, mime types, pagination cursors, twelve metadata columns - when the caller needs a name, a total, and a link.
  • Two or more tools overlap: either could answer the call, and the consumer must guess which is meant.
  • The tool count grows with the upstream API's surface - a new upstream endpoint produces a new tool, not a decision.

Why it happens

Wrapping each upstream endpoint one-to-one feels complete and is easy to review - the mapping is mechanical, so nobody has to think. The provider's schema is documented and at hand, while the consumer's actual tasks are nobody's assigned job. And every downstream consumer quietly adapts: they write the joins, filter the noise, and learn the unwritten rules, which hides the smell from the team that shipped it.

Example

Three thin tools over one CRM. Answering "who is this customer and what did they last buy?" costs three calls and returns fields nobody reads:

TypeScript
// Tools as endpoint mirrors - the consumer does the work.
const crmTools = {
  getUser: (userId: string) =>
    fetch(`${crm}/users/${userId}`).then((r) => r.json()), // 14 metadata columns
  listTransactions: (userId: string) =>
    fetch(`${crm}/users/${userId}/transactions`).then((r) => r.json()),
  listNotes: (userId: string) =>
    fetch(`${crm}/users/${userId}/notes`).then((r) => r.json()),
};

// Every caller chains the trio and joins by hand:
const user = await crmTools.getUser("u-9");
const txns = await crmTools.listTransactions("u-9");
const notes = await crmTools.listNotes("u-9");
const lastPurchase = txns.items[0]?.amount_cents; // buried in the raw row

The tools are correct and useless: each one is Middle Man with an HTTP client. The chain is Message Chains across a network.

Treatment

Consolidate the endpoints into task-shaped tools: one tool per task the consumer actually performs, returning only the fields that task reads, with a description that says when to use it and when not to. See Consolidate Tools for the step-by-step.

If the consumer is a human browsing a dashboard, the diagnosis changes: people can chain calls and skim columns, and the raw surface may be the product. The smell is specific to consumers that dispatch on descriptions and pay per token - agents and automated callers.

Payoff

  • Tasks stop being call chains: one call, one answer, fewer round trips and failures.
  • Responses shrink to what is read, which for machine consumers is a direct token saving on every call.
  • Descriptions that say when to use a tool let consumers pick right the first time; overlapping guesses disappear.
  • The upstream API can change behind the task-shaped tool without breaking every caller.

When to ignore

  • The consumer is a human with a screen and time - they can chain and skim; optimize their UX, not their call count.
  • The wrapper's job is fidelity: a debugging tool that mirrors the upstream API one-to-one is the product, not a smell.
  • There is exactly one task per endpoint and no chain forms - a thin wrapper serving one real task is just an API.

Agent checklist

  • Each tool's description states when to use it, not what it wraps.
  • No consumer task requires chaining two or more of these tools to get its answer.
  • Responses contain only fields at least one caller reads.
  • No two tools on the surface could both plausibly serve the same call.
  • Tool count is derived from the task list, not from the upstream API's endpoint list.

Treatments

See also

Original writing and examples. Topic coverage follows www.anthropic.com, which explains this topic with its own illustrations.