Why MCP retries can duplicate real-world effects
MCP standardizes how clients discover and invoke tools. It does not make every tool invocation naturally safe to repeat. A tool may charge a card, issue a refund, create an order, send a message, reserve inventory, provision infrastructure or mutate production state.
The difficult failure mode is an ambiguous outcome: the tool or downstream provider performs the side effect, but the caller loses the acknowledgement. From the caller's point of view the invocation failed. From the external system's point of view it may already be complete.
The dangerous retry sequence
- An agent calls a state-changing MCP tool.
- The tool reaches an external provider.
- The provider commits the side effect.
- The response is lost, delayed or interrupted.
- The client observes a timeout or recoverable failure.
- A blind retry risks creating the same external effect again.
That is why a timeout should not automatically be mapped to "nothing happened." The safe state may be UNKNOWN.
Retry policy and idempotency are related, but different
Backoff, retry budgets and recoverable-error signals answer whether another attempt may be useful. Idempotency and execution safety answer a different question: whether another attempt can repeat the real-world effect.
GitHub's MCP Scripts retry policy makes this distinction explicit: because tool invocations may be non-idempotent, callers should apply idempotency safeguards before retrying state-changing tools and should perform explicit side-effect checks after ambiguous failures.
MCP annotations are risk hints, not an execution guarantee
MCP tool annotations include signals such as readOnlyHint, destructiveHint and idempotentHint. They are valuable for communicating expected behavior, but the MCP maintainers describe annotations as hints rather than trusted enforcement.
An idempotentHint: true annotation therefore does not create idempotency by itself. The implementation and downstream system still need the semantics that make repeated logical operations safe.
The Once decision model
Once treats retries as repeated attempts at one logical real-world operation. The operation keeps a stable identity while execution truth is tracked durably.
CONFIRMED → replay or suppress
ABSENT → execute
UNKNOWN → block or reconcile
CONFIRMED means the supported execution path shows the logical operation already succeeded, so another external execution is unnecessary. ABSENT means the protected operation is not known to have happened and may proceed. UNKNOWN means the system cannot safely establish execution truth yet, so it should reconcile or block rather than guess.
A practical safe-retry pattern for MCP tools
- Create stable operation identity. Retries of the same business intent reuse the same identity instead of generating a new one per transport attempt.
- Persist operation state. Execution state must survive process exits, transport failures and agent retries.
- Use provider-native idempotency when available. Pass a stable provider key through supported integrations instead of inventing a fresh key on each attempt.
- Reconcile ambiguous outcomes. Query sufficiently authoritative provider truth or another supported side-effect check before deciding that re-execution is safe.
- Fail closed when truth remains unknown. Uncertainty should not silently become permission to repeat a consequential write.
Example: refund tool times out after the provider commits
Agent → refund_order(order_4821)
Tool → provider refund request
Provider → refund succeeds
Network → acknowledgement lost
Agent → sees timeout
Agent → retries refund_order(order_4821)
Without stable logical-operation identity, the second call can look like a new request. With execution safety, the retry is tied to the original refund intent. If the first refund is confirmed, the prior result can be replayed or the duplicate execution suppressed. If the provider truth cannot yet be established, the state remains UNKNOWN rather than being treated as a clean failure.
When an MCP tool should evaluate Once
Evaluate Once when all four conditions are true:
- The tool can change external state.
- The same logical operation may be retried.
- The first attempt can become ambiguous after a timeout, lost response, crash, queue redelivery or similar failure.
- Blind duplicate execution would be undesirable or costly.
Typical examples include payments, refunds, payouts, orders, bookings, reservations, provisioning, production writes, side-effecting webhooks and consequential messages.
When not to add an execution-safety layer
Pure reads, searches, retrieval and generation-only operations usually do not need this pattern. The goal is not to wrap every MCP call. The goal is to protect the relatively small set of tool boundaries where retrying can duplicate an external effect.
MCP client responsibility versus tool implementation
A client can choose whether to retry. A tool or runtime can implement stable identity, durable state and provider reconciliation. The safest design treats those responsibilities as complementary: clients should not blindly retry consequential writes, while the execution layer should not assume the client will always behave perfectly.
How MCP retry safety relates to MCP idempotency
MCP idempotency is a core mechanism for making repeated logical operations safe. MCP retry safety is the broader execution problem: deciding whether to retry, determining whether the original side effect happened, replaying a confirmed result and preserving UNKNOWN when the truth cannot safely be established.
For the broader agent pattern, see AI agent retry safety. For the most dangerous failure state, see ambiguous timeout handling.
Primary references
The MCP maintainers describe tool annotations such as idempotentHint as hints rather than guaranteed behavior: Tool Annotations as Risk Vocabulary.
GitHub's MCP Scripts specification says callers should apply idempotency safeguards before retrying state-changing tools and perform side-effect checks after failures that may already have produced external effects: MCP Scripts Specification — Retry Policy.
Protect consequential MCP writes
Use Once when a state-changing tool can be retried after an outcome becomes ambiguous.
npx -y @once-agent/mcp
Official MCP Registry identity: io.github.stringsofthemind-oss/once
Try the Once tester · MCP idempotency guide · AI agent retry safety · Claude Code · Cursor · MCP package · Source