Skip to main content

Tool Surfaces Are an Interface Design Problem, Not a Wrapper Problem

Agent tool misuse usually starts at the interface: human APIs wrapped in JSON hide partial failure and lack terminal error semantics. Designing machine-facing surfaces fixes it.

Close-up of a hand holding an ATEM Micro Panel with a dark, blurred background.

What the corpus shows

Enterprise write-ups about agents in production converge on a narrow set of failures, and most of them are not model failures. They are interface failures. The same friction appears across otherwise unrelated deployments:

  • Tools return unstructured success. A response like {"status": "ok"} or a bare HTTP 200 with a human-readable body leaves the agent to infer whether the intended effect actually occurred.
  • Partial failure is hidden. A batch operation that processed some items and skipped others reports success because the transport succeeded.
  • One intent requires a multi-call ritual. Creating a resource means calling a create endpoint, polling a status endpoint, then calling a finalise endpoint. Each call is a chance to stop halfway, and the intermediate states are not modelled anywhere in the tool description.
  • Retries run against endpoints that never signal a terminal state. Incident reports describe agents looping on a call that keeps returning a transient-looking error, because nothing in the response distinguishes "try again" from "this will never work".

The pattern is consistent: the tool surface is being asked to carry semantics it was never designed to express. The agent is not misbehaving so much as completing the only interpretation the interface permits.

The wrapper fallacy

Wrapping an existing human API in a JSON schema does not produce a tool surface. It produces a remote procedure call with a thin description. The schema tells the model what fields to send; it says almost nothing about what the call means, when it is valid, or what a given response implies about the world.

Human APIs are designed for a reader who can compensate for missing information. That reader can infer intent from surrounding context, ask a colleague what a status code means, or notice that a result looks stale and investigate. None of those recovery paths exist for an agent operating inside a loop. When the interface is ambiguous, the agent's only available strategy is to guess, retry, or abandon, and all three are expensive in production.

The wrapper approach also imports assumptions that do not survive the translation. A human API may treat a 200 with an error body as a convention everyone on the team knows. It may treat an empty list as either "no results" or "the query was malformed", because a human would check. It may leave destructive operations non-idempotent because a human would not click the button twice. Each of these is a reasonable design decision for a human caller and a defect for a machine caller.

What agents need instead is a surface with explicit preconditions, explicit postconditions, and a terminal error taxonomy. A precondition states what must be true before the call is valid. A postcondition states what will be true after a successful call. A terminal error taxonomy states which failures are retryable, which are permanent, and which require a different call entirely.

White / light blue / purple / dark blue sail; without harness or cover bag; 18ft. 5 1/2in. leading edge long; aluminum and dacron; 1974. In

One tool call, one intent

A tool call should express a single intent. If accomplishing one goal requires a sequence of calls with no way to resume, the surface invites partial completion and ambiguous retries. Where a multi-step process is unavoidable, model the intermediate states explicitly and make each transition a first-class operation with its own preconditions, rather than hiding them behind a ritual the caller is expected to perform correctly.

Every response is one of three things

Every response must be classifiable as success, retryable failure, or terminal failure. There is no fourth state. A response that is ambiguous forces the caller to invent a policy, and invented policies are where retry storms come from. Concretely, this means a response envelope with a small, closed set of outcome kinds, and a rule that unknown outcome kinds are treated as terminal failures rather than retried.

A useful mechanism is a response envelope with an explicit outcome discriminant and a bounded retry hint:

{
  "outcome": "retryable_failure",
  "reason": "upstream_unavailable",
  "retry_after_ms": 2000,
  "observed_state": null
}

The important property is not the specific field names but that the caller can branch without interpretation. terminal_failure means stop and surface the error. retryable_failure means retry within the stated bound. success means the postcondition holds.

Destructive operations are idempotent by key

Destructive operations should be idempotent by key so retries are safe by construction. The caller supplies a stable idempotency key derived from the intent, and the surface returns the same result for a repeated key rather than performing the effect twice. This removes the need for the agent to reason about whether a previous attempt landed, which is exactly the reasoning it

A response should return the observed state alongside the claimed outcome so the caller can verify rather than trust. If the tool claims a resource was created, it should return the resource's current observable state. Verification then becomes a comparison the agent can perform, rather than a belief it has to hold. This is also what makes stale-data success detectable: the claimed outcome and the observed state disagree.

Glass bottles being manufactured on an automated production line in a factory.

Honest trade-offs

These rules are not free. Explicit outcome envelopes make responses larger, which consumes context and can push a tool description past the point where a model reliably attends to it. Idempotency keys require server-side state and a retention window, which is operational overhead for teams that previously had none. Splitting a ritual into explicit, resumable operations produces more tools, and more tools means more selection ambiguity for the caller. The design question is not whether to pay these costs but where the failure mode is worse without them. In most production systems, an ambiguous retry is more expensive than a larger response.

Evaluation implications

If tool surfaces are the problem, then tool-call correctness is the wrong thing to score on task completion alone. A run that completes a task by retrying through a terminal failure is not a correct run; it is a run that got lucky against a surface that failed to communicate. Scoring should separate two dimensions:

  • Intent-to-call mapping. Did the agent select the right tool with the right arguments for the stated intent, including the cases where the correct behaviour is to call nothing?
  • Failure-class handling. For each failure class the surface can emit, did the agent respond with the correct policy: retry within the bound, stop and surface, or switch to a different call?

This means regression suites need adversarial tool responses as first-class fixtures, not as occasional chaos experiments. The four that matter most are slow success, partial success, silent failure, and success with stale data. Each one probes a different assumption:

  • Slow success tests whether the agent's timeout policy distinguishes latency from failure.
  • Partial success tests whether it reads the observed state rather than the claimed outcome.
  • Silent failure tests whether it verifies postconditions at all.
  • Success with stale data tests whether it compares timestamps or versions instead of trusting the response.

A surface that cannot produce these responses in a test harness is a surface whose failure modes are unknown until they appear in production. Building the fixtures is part of building the interface.

Closing

Tool surfaces are an interface design problem. The wrapper approach treats the interface as a translation layer over an existing human API, and inherits every assumption that API made about a reader who can compensate. Designing for a machine caller means making preconditions, postconditions, and terminal error semantics explicit, and accepting the costs that come with it. The alternative is a system whose failures are indistinguishable from its successes until someone reads the logs.

ShareXLinkedInPermalink6 min read

Related reading

Questions this answers

What is a tool surface in an agent system?

A tool surface is the interface an agent calls to act on the world: the set of tools, their preconditions, their postconditions, and the semantics of every response they can return. It is distinct from the underlying API, which is usually designed for a human caller.

Why does wrapping a human API in JSON fail for agents?

A JSON schema describes what fields to send, not what the call means. Human APIs rely on a reader who can infer intent, ask a colleague, or notice something is off. Agents in a loop have none of those recovery paths, so ambiguity becomes retries, abandonment, or silent wrong behaviour.

What are the three response states every tool should return?

Success, retryable failure, and terminal failure. There should be no fourth state. Any unknown outcome should be treated as terminal rather than retried, so the caller can branch without interpretation.

How should destructive tool operations handle retries?

They should be idempotent by key. The caller supplies a stable key derived from the intent, and a repeated key returns the same result instead of performing the effect twice. This removes the need for the agent to reason about whether a previous attempt landed.

How does tool surface design change agent evaluation?

Tool-call correctness should be scored on intent-to-call mapping and on correct handling of each failure class, not on task completion alone. Regression suites should include adversarial responses such as slow success, partial success, silent failure, and success with stale data.