Claude Code asks an MCP service to run a hosted regression suite. The service accepts the request, starts a job, and begins charging the account.

Sixty seconds later, the client gives up waiting. Claude Code sees a timeout and retries with a new request ID.

The first job is still running. The retry starts a second one. Both complete at $17.84, but the review packet contains only the result from the second request.

The tests passed. The accounting did not.

A timeout tells you that the client stopped waiting. It does not tell you whether the remote service accepted the work, changed state, or incurred a charge. Treating that timeout as a clean failure turns an ordinary retry into duplicate external work.

Paid MCP retry receipt for Claude Code

Keep the uncertain state visible

Do not collapse a transport timeout into tool_failed. Record what the client knows and what it does not:

attempt:
  request_id: req-701
  client_result: timed_out
  remote_accepted: unknown
  remote_effect: unknown
  charge_status: unknown

Those unknowns matter. If the tool can send email, start a cloud build, run a browser grid, purchase data, or change an account, the missing response says nothing about the external effect.

This is the same boundary I use for a timed-out MCP write. Paid tools add another question: even if the result is harmless, did the retry buy the work twice?

Give the task one operation ID

Each network attempt needs its own request ID. The logical operation needs one stable ID across all attempts.

operation_id: settlement-regression-842
attempts:
  - request_id: req-701
    client_result: timed_out
  - request_id: req-702
    client_result: completed

The operation ID should describe the work the user approved, not the connection used to request it. An MCP server, proxy, or reconciliation worker can then recognise req-702 as another attempt to obtain the result of settlement-regression-842.

This only works when the operation ID reaches the service that creates the effect. Storing it in Claude Code’s transcript is not enough. Pass it as an idempotency key, provider metadata field, or durable proxy record. Bind any returned provider job ID to it.

Look up before you retry

For a read from a local cache, another attempt may be cheap and harmless. For paid or state-changing work, the retry rule should be lookup_then_attach.

LOOKUP settlement-regression-842
STATUS running
JOB hosted-suite-9912
COST accruing
ACTION attach_to_existing_job

The lookup should distinguish not_found, accepted, running, completed, failed, and indeterminate. If the first job is running, attach to it. If it completed, fetch that result. If the service returns indeterminate, stop for review.

Not found also needs care. A delayed index or broken lookup can report nothing while the job exists elsewhere. The provider should document when a missing operation is safe to retry. Without that contract, Claude Code is guessing with somebody else’s account.

A service that cannot query operations and does not honour idempotency is a poor fit for automatic retries. Put the stop rule in the tool policy rather than hoping the model notices the risk in time.

Put external spend inside the permission boundary

Token budgets do not cover the full cost of an agent run. A short prompt can trigger an expensive build matrix, cloud scan, hosted evaluation, or paid API call.

Carry a spend ceiling and remote job limit with the approval:

approval:
  operation_id: settlement-regression-842
  tool: hosted_test.run_suite
  max_total_cost_usd: 20.00
  max_remote_jobs: 1
  retry_policy: lookup_then_attach
  expires_at: 2026-08-26T11:30:00Z

The enforcement point should count the operation’s total external cost, not the price of each request in isolation. Two requests that each cost less than $20 still violate a $20 operation ceiling when both create work.

Estimate the route before execution when the tool fans out. A single call may launch several runners or invoke another paid service. I covered that control in pricing the tool path before Claude Code starts.

Reconcile the effect and the bill

A successful second response does not settle the first attempt. Before handoff, query every known request ID and provider job ID. Record all jobs, results, cancellations, and charges under the operation.

paid_tool_retry_receipt:
  operation_id: settlement-regression-842
  intended_remote_jobs: 1
  attempts: 2
  observed_remote_jobs: 1
  attached_attempts: 1
  duplicate_jobs: 0
  estimated_cost_usd: 18.00
  final_cost_usd: 17.84
  unpriced_attempts: 0
  result_used: hosted-suite-9912
  reconciliation_status: complete
  review_status: ready

This receipt is the production artifact. It proves that two client attempts produced one remote job and one charge. If observed_remote_jobs is greater than one, unpriced_attempts is nonzero, or a charge is still pending, the run remains open.

Name the next action when reconciliation fails. That may be cancelling a duplicate, requesting a refund, preserving evidence for an incident, or assigning an owner to check a late invoice. A vague cost warning leaves the reviewer to rediscover the whole event.

Connect the receipt to the broader MCP effect receipt. The effect receipt proves what changed. The retry receipt proves how many attempts and paid jobs caused it.

Test the cases that billing exposes

A happy-path test will miss the failure because the second request returns a valid answer. Add fixtures that inspect provider jobs and charges:

fixtures:
  - name: response_lost_after_remote_accept
    expected: attach_to_existing_job
  - name: retry_uses_new_operation_id
    expected: deny_retry
  - name: provider_lookup_is_indeterminate
    expected: require_review
  - name: duplicate_job_started
    expected: cancel_duplicate_and_block_handoff
  - name: final_charge_exceeds_approval
    expected: flag_cost_boundary_breach
  - name: completed_job_has_no_invoice_record
    expected: reconciliation_incomplete

The evaluator should count external jobs and charges. Do not pass the run merely because Claude Code returned one test report. That checks the answer while ignoring what the answer cost to obtain.

Put the receipt in the review packet

The reviewer needs the stable operation ID, attempt lineage, provider job IDs, result chosen, final charge, and unresolved cost. That is much more useful than retried once after timeout.

Add the receipt to the same review packet that carries the patch, test evidence, permissions, and rollback note. The handoff can then answer two separate questions: did the code work, and what did the run cause outside the repository?

My rule is simple. Claude Code may retry a consequential MCP call only when it can identify the original operation, look up its state, and prove that the retry will not duplicate work. Afterward, it must reconcile every remote job and charge.

Claude Code: Building Production Agents That Actually Scale covers MCP boundaries, cost loops, permissions, evals, observability, rollback, and review packets for production coding agents.