Writing ·
How to make AI agent tool calls idempotent
Make tool calls idempotent by separating intent, side effect, and acknowledgement — so a retry never double-charges, double-sends, or double-writes.
By Youssef Hemimy · agent reliability · idempotency · MCP
Make tool calls idempotent by separating intent, side effect, and acknowledgement. The agent can retry the intent freely; the server should commit the side effect exactly once, then return the same result for duplicate requests. This is the foundation of agent reliability — without it, every retry is a chance to double-charge, double-send, or double-write.
01
Agent intent
retried freely on timeout
02
Dedupe ledger
keyed, durable, write-once
invoice:cust_123:2026-06First time
side effect commits once
Duplicate
returns the stored result
What actually works
- Give every tool invocation a stable request key.
- Persist a dedupe record before or with the side effect.
- Return the same response body for repeats.
- Treat reads as replay-safe; make writes explicitly idempotent.
- Put the retry boundary outside the side effect, not in the prompt.
The pattern
Use a request envelope that carries identity with the intent:
{
"request_id": "toolcall_01HZX...",
"operation": "create_invoice",
"idempotency_key": "invoice:customer_123:2026-06",
"payload": { "customer_id": "customer_123", "amount_cents": 4999 }
}Store a durable ledger row keyed by idempotency_key before you emit the irreversible effect. If the tool is retried, return the original result instead of redoing the work.
Where agents usually fail
- They retry after a timeout and create a duplicate order, email, or ticket.
- They treat a tool exception as “nothing happened” even when the side effect already landed.
- They use one retry policy for all tools, including irreversible writes.
- They hide request identity in the prompt instead of the tool contract.
Production checklist
- Every write tool accepts a stable request id.
- The server can answer “already processed” with the original result.
- Timeouts are shorter than duplicate-detection retention.
- Retries are only automatic for transient failures.
- External side effects have a separate audit trail.
- Tool calls are traced so duplicates are visible in logs and spans.
FAQ
Where should the retry boundary live?
Outside the side effect, not inside the model prompt. The agent retries the intent; the tool server decides — via the dedupe ledger — whether to commit or replay. Putting retry logic in the prompt hides request identity from the layer that has to enforce uniqueness.
What makes a good idempotency key?
Something stable across retries and unique to the intended effect — e.g. invoice:customer_123:2026-06, or sha256(agent_id + step + payload). Avoid timestamps or random IDs regenerated on each attempt; those defeat deduplication.
Are reads idempotent by default?
Treat reads as replay-safe by default and make writes explicitly idempotent. The danger is irreversible writes — orders, emails, charges, tickets — retried after a timeout that actually succeeded.
Building something that has to hold up?
We do this work for teams — agent reliability hardening, custom MCP servers, and full-stack AI systems built to survive production.