At 14:02, Claude Code asked an MCP tool to update 240 customer records. The request had a valid approval and a stable operation ID.
The downstream service timed out, so the adapter put the write on a retry queue. A worker picked it up seventeen minutes later. The idempotency check passed because the operation ID was unchanged. The approval had expired nine minutes earlier.
The worker executed the write anyway.
14:02:11 request accepted
14:02:11 approval valid until 14:10:00
14:02:42 downstream timeout
14:02:43 retry queued
14:19:06 operation ID matched
14:19:07 write executed
Nothing was duplicated. The wrong thing still happened.
Idempotency protected the operation from repetition. It did not prove that the agent still had permission to act. Any MCP workflow that can wait in a queue needs both controls.
Operation identity and authority answer different questions
An idempotency key asks whether the system has already processed an intended effect. Authorization asks whether this principal may create that effect now, against this target, under the current policy.
Keep those records separate:
operation_identity:
operation_id: op-731
intended_effect_hash: sha256:8ce4...
duplicate_status: first_execution
authority:
principal: agent-12
tenant: tenant-blue
approval_id: approval-88
policy_version: policy-41
valid_until: 2026-09-08T14:10:00Z
execution_time: 2026-09-08T14:19:07Z
status: expired
The matching operation ID should stop a duplicate. It should never revive an expired approval.
This distinction matters after more than a timeout. A queue can outlive a role change, a tenant switch, an emergency stop, or a policy deployment. The payload may remain byte-for-byte identical while the right to execute it disappears.
The operation ledger for parallel Claude Code writes gives each intended effect a durable identity. That solves one failure mode. The authority check decides whether that known effect is still allowed.
Recheck authority where the effect occurs
Checking permission only when a request enters the queue is too early. Delay changes the facts.
The worker that commits the effect should check the current principal, tenant, target set, approval scope, expiry, policy version, and revocation state. A queue field such as authorized_at_enqueue: true belongs in the history. It is not permission to act later.
execute(queued_operation):
identity = operation_ledger.claim(queued_operation.operation_id)
authority = policy.evaluate(
principal = queued_operation.principal,
tenant = queued_operation.tenant,
effect_hash = queued_operation.effect_hash,
approval_id = queued_operation.approval_id,
checked_at = clock.now()
)
require identity.is_first_execution
require authority.is_current
require not authority.is_revoked
return commit(queued_operation)
The check must happen close to the commit. If a worker validates authority, waits another ten minutes for a lock, and then writes, it has rebuilt the same gap on a smaller scale. For sensitive effects, include the authority decision in the transaction or use a short execution lease that the commit path verifies atomically.
Do not let the model make this decision from a timestamp in its context. Claude Code can describe the intended action, but the control plane owns the clock, current policy, revocations, and final allow or deny result.
Bind approval to the exact effect
An approval such as customer update allowed leaves too much room for reinterpretation after a retry or deployment. Bind it to a canonical effect description.
approved_effect:
tool: customer_admin.bulk_update
tenant: tenant-blue
resource_set_hash: sha256:1fa2...
mutation_hash: sha256:7b31...
maximum_records: 240
policy_version: policy-41
valid_until: 2026-09-08T14:10:00Z
The worker reconstructs the effect and compares it with this record. A changed tenant, target set, mutation, record count, or policy sends the operation back for review.
A stable operation ID does not excuse a changed effect. If the payload changes, create a new effect hash and approval. Keep a link to the old operation so the reviewer can see the history without mistaking the new request for a retry.
This is also where tenant context can go wrong. An MCP session may retain an old tenant after the user switches accounts. The tenant must be bound to every tool call and receipt, not recovered from whichever session happens to process the queue later.
Make expiry a terminal outcome
A worker should not classify an expired approval as a transient error. The queue must not keep trying until somebody quietly extends the deadline.
Issue a receipt that closes the attempt:
execution_authority_receipt:
operation_id: op-731
effect_hash: sha256:8ce4...
principal: agent-12
tenant: tenant-blue
approval_id: approval-88
policy_version_checked: policy-41
checked_at: 2026-09-08T14:19:06Z
valid_until: 2026-09-08T14:10:00Z
outcome: blocked
reason: approval_expired
effect_committed: false
retry_allowed: false
reapproval_required: true
That receipt belongs beside the operation and effect evidence. Yesterday’s durability receipt proved whether a write committed and became visible. This receipt proves whether it was allowed at execution. A production review needs both when an agent can change external state.
Reapproval is not a timestamp extension. Show the reviewer the current targets, payload, policy, prior attempts, cost already incurred, and any partial effects. If the task still makes sense, issue fresh authority against the current effect.
Decide what happens to a partial batch
Expiry is simple when the write is atomic and has not started. Batches are less tidy.
Suppose the approval expires after record 173 of 240. The worker should stop before the next item and report the committed subset. It should not finish the remaining records because the batch began while authority was valid. It should not launch compensation unless that rollback was already approved and remains safe.
The task contract needs a boundary before queueing:
batch_authority:
check_before_each_chunk: true
chunk_size: 20
on_expiry: stop_and_report
automatic_compensation: false
receipt_required_per_chunk: true
For financial-services systems, this distinction is familiar. An instruction can keep its identity while the account, mandate, or approval behind it changes. Agent tooling needs the same discipline. Identity follows the instruction. Authority belongs to the moment of effect.
Test time and revocation, not only duplicates
A duplicate-suppression test will pass while this bug remains. Add fixtures that move the clock and policy state:
- approval expires while the write waits in the queue
- the principal loses its role after enqueue
- the tenant changes before execution
- a policy deployment narrows the allowed effect
- the target set grows beyond the approved limit
- a stale policy cache says allow after revocation
- a partial batch crosses the authority deadline
- reapproval tries to reuse a changed effect hash
Assert the external state as well as the receipt. The expected result for an expired approval is no new effect, no retry, and a clear request for fresh review.
Keep the operation ID stable across genuine retries. Recheck authority at the point of effect. If the approval has expired or the scope has changed, close the attempt instead of letting yesterday’s permission act today.
Claude Code: Building Production Agents That Actually Scale covers MCP boundaries, permissions, rollback, evals, cost limits, observability, and review packets for teams moving beyond demos.