A tool call can be perfectly valid structured data and still describe the wrong operation. A date can have the right format but the wrong timezone. A customer name can match several people. An empty search result can mean no matching records, or it can mean that the search failed.
When I design an agent-facing tool, I treat those distinctions as part of its contract. The model needs to know what the inputs mean, what evidence the result provides and which uncertainty remains after the call.
Use identifiers with an explicit meaning
Suppose an assistant receives “check the order for Alex.” A tool accepting only a free-text customer field encourages it to push ambiguity into the backend. A better interaction first resolves candidate accounts, then passes a stable identifier to the order lookup.
If several candidates remain, the result should expose that ambiguity without silently selecting the first one. A tool description can explain the intended sequence, but the backend should still reject an identifier outside the authenticated account’s scope.
That last check is necessary even when the model followed the documented sequence. Instructions explain correct use; execution rules determine what the application actually permits.
Document missing, empty and unknown values
An omitted filter, an empty filter and a filter set to “unknown” can have different business meanings. For example, omitting a date range might use a default window, while an empty range might be invalid. Leaving the distinction implicit forces the model to guess.
I prefer contracts that state defaults and limits explicitly. A search response should report whether it is complete, whether more pages exist and whether any requested source could not be queried. “No results” should be reserved for a search that actually completed within its stated scope.
The customer-support context of Conviro, the platform I build, makes this especially concrete. An assistant must be able to distinguish unavailable information from evidence that an event never happened. Those are different answers to a customer.
Return errors that support the next decision
A useful error distinguishes an invalid request from a temporary service problem, an access denial and an ambiguous result. Each should lead to a different action. Retrying cannot fix a permission failure, and asking the customer to rephrase cannot repair an unavailable service.
For a lookup, I would define outcomes such as found, not_found, ambiguous and unavailable, alongside a result version. The names are illustrative. The important point is that the caller can make the next decision without inferring success from prose.
Detailed diagnostics can remain available to an operator without exposing credentials, internal queries or private records to the model. An actionable error does not need to include every implementation detail.
Describe effects separately from proposals
A tool named “update order” hides more uncertainty than “propose delivery-address change.” If a tool changes state, its contract should state the target, preconditions and result that confirms acceptance. A generated preview should never look like proof that the update already happened.
I discuss that execution boundary further in separating an agent’s recommendation from its side effects. Precise naming is useful, but the distinction must survive the entire request and response.
Version the contract as the system grows
Changing a default or the meaning of a result field can break an agent even when its input still validates. I would retain representative calls and expected interpretations, including ambiguous identities and incomplete searches, as contract examples.
Tool descriptions, schemas and runtime validation should describe the same operation. When a tool supports a new kind of work, revisit the workflow that decides when it is called. Good contracts reduce guesswork on both sides of the model boundary and make incorrect calls easier to identify.
Updated 30 September 2026.