A tool is an interface between something that reasons probabilistically and something that has consequences. That asymmetry should drive every design decision, and mostly it does not - tools get generated from existing API surfaces and inherit shapes that assume a careful, deterministic caller.
Narrow tools beat generic tools
Given a generic create_calendar_event, the agent has to know opening hours, service durations, buffer times, and which resource handles which service. That knowledge ends up in the prompt, where it cannot be tested, cannot be versioned meaningfully, and degrades as the prompt grows.
Given find_slots(service, earliest, latest), the same knowledge lives in code. The prompt only has to be good at conversation.
| Generic | Narrow | |
|---|---|---|
| Business rules live in | The prompt | The tool |
| Testable | Not really | Ordinary unit tests |
| Wrong-answer mode | Plausible but invalid event | Empty result |
| Prompt size | Grows with every rule | Stable |
| New rule costs | Prompt edit + regression risk | Code change + test |
If a rule can be violated by a sentence in a prompt, it is not a rule. It is a suggestion with good intentions.
Idempotency is not optional
Agents retry. They retry because a call timed out, because the user asked "did that go through?", because a loop re-entered a step. Any tool with a side effect needs an idempotency key that is derived from the *intent*, not generated per attempt.
// Wrong: a fresh key per attempt means a retry creates a second booking.
const key = crypto.randomUUID()
// Right: same hold + same customer = same booking, however many times
// the agent (or the caller) asks for it.
const key = hash(`${holdId}:${customer.phone}:${slot.startsAt}`)Distinguish "failed" from "the answer is no"
The most common tool-layer bug we see: a lookup with no results reported as an error. The agent then does the reasonable thing with an error - it retries. The retry also returns no results. The agent is now in a loop that cannot terminate, and the caller is listening to it.
- Error - the tool could not answer: network failure, auth expired, upstream 500. Retrying may help.
- Empty result - the tool answered, and the answer is nothing. Retrying will never help.
- Refusal - the tool declined: out of scope, not permitted for this tenant. Retrying is a permission violation, not a retry.
Normalise these at the boundary, not in each tool. Wrapping an upstream API is where you decide which of its status codes mean which of the three - and it is the only place that decision can be made consistently.
Descriptions are prompts
Tool descriptions are read by the model at selection time and are, functionally, part of the prompt. We have watched two servers exposing the same capability produce noticeably different agent behaviour purely because of how their descriptions were worded.
- Say what the tool does and when not to use it. Negative guidance disambiguates more than positive guidance.
- Name the units and the timezone.
durationis ambiguous;durationMinutesis not. - Describe the empty case explicitly, so an empty result is expected rather than surprising.
- Keep names distinct across the catalogue. Two tools called
searchfrom different servers is a selection error waiting to happen.
Gate by consequence, not by tool
The useful axis for permissions is not which tool is being called but what it does to the world. Reads run freely; writes need a scope; irreversible actions need a human. Classifying by consequence means a new tool inherits the right treatment without anyone remembering to configure it.
- 01Proposed action
- 02Classify consequence
- 03Scope check
- 04Human approval if irreversible
- 05Execute
- 06Verify effect
The verify step is the one most often skipped. Confirming that a call returned 200 is not confirming that the intended thing happened - and an agent that assumes otherwise reports success it has not earned.
What we are still unsure about
How far the narrow-tool principle scales. Every business rule pushed into a tool is a rule the agent cannot adapt around, and there is presumably a point where a catalogue of very narrow tools is more brittle than a general one plus good instructions. We have not found that point, but we do not believe it is absent.