CLAUDE LABJP
CODE — Claude Code adds Trusted Devices, verifying a machine before remote admin sessions beginCODE — CPU use drops about 37% during streaming, keeping long always-on automation steadierCODE — Fullscreen mouse-click controls, voice dictation fixes, and better Linux voice detection landAUTH — Static API keys can now be replaced with short-lived, scoped WIF credentialsTEAM — You can tag Claude directly in Slack and delegate tasks while you focus elsewhereWORKFLOW — Dynamic workflows arrive in research preview, breaking complex work into steps on their ownCODE — Claude Code adds Trusted Devices, verifying a machine before remote admin sessions beginCODE — CPU use drops about 37% during streaming, keeping long always-on automation steadierCODE — Fullscreen mouse-click controls, voice dictation fixes, and better Linux voice detection landAUTH — Static API keys can now be replaced with short-lived, scoped WIF credentialsTEAM — You can tag Claude directly in Slack and delegate tasks while you focus elsewhereWORKFLOW — Dynamic workflows arrive in research preview, breaking complex work into steps on their own
Articles/API & SDK
API & SDK/2026-06-28Advanced

Did That Post Actually Go Through? Safely Retrying an Interrupted MCP Write Without Double-Executing

When an MCP write tool call is interrupted by a dropped connection, you can't tell whether the server ran it. Here's why naive retries cause double-execution, and a working wrapper that uses idempotency keys and a reconcile read to retry safely — with examples from an unattended pipeline.

Claude API91MCP37idempotency6automation78reliability11

Premium Article

One of my unattended publishing jobs once got its connection cut mid-request while posting to X. All I got back was a timeout error, with no way to know whether the post had landed. The log recorded a "failure" — yet a few minutes later the same post was sitting on the timeline. The server had succeeded; only the result never reached me.

If I had naively decided "it failed, so retry," two identical posts would have ended up side by side. As an indie developer automating announcements across several sites, that is an incident that has nothing to do with content quality. To a reader, posting the same thing twice just signals sloppy operations. If you are going to run write-type tool calls unattended, you have to design for this "did it go through?" state head-on.

"Failed" and "uncertain" are not the same thing

There are two kinds of errors. One is a clear rejection — the server says "I will not accept your request." The other is unknown — the connection dropped before any response came back. The first is safe to retry, because you know the server did nothing.

The troublesome one is the second. The request may have reached the server and been processed, or it may have been cut off before arrival. From your side, you cannot tell. This is the well-known problem in distributed systems: the sender can never be certain the receiver executed. Timeouts, connection resets, and mid-stream disconnects all belong in this "uncertain" bucket.

The June 27, 2026 update improved MCP resilience in Claude Code so that partial responses are preserved even when a stream is cut mid-flight. The receiving side is genuinely more robust now. Even so, the uncertainty that remains the moment a write tool call is interrupted — "did the server execute it?" — is not something a more resilient receiver alone can remove. That part lives in your application.

A common implementation trap is error classification. An HTTP 5xx can mean "the server failed to process" or "it processed but only the response was lost," so pushing it straight to failed is dangerous. I treat every ambiguous error as uncertain. Since uncertain operations are settled by reconciliation during recovery, over-classifying as uncertain never causes double-execution — whereas mislabeling a truly uncertain call as failed leads to it immediately. When in doubt, fall to the safe side: uncertain.

Why naive retries cause double-execution

When people write retry logic, they usually think in two states: success or failure, and retry on failure. That design is the breeding ground for double-execution.

If you collapse "uncertain" into "failed," you re-run even the cases that actually succeeded on the server. A post becomes a double post, a charge becomes a double charge, an email becomes a second copy. That is exactly the trap I fell into first: I wrapped my retry logic in a sloppy except Exception: and unconditionally resent inside it. It never reproduced in testing, and the first double post showed up on a night when the production connection got flaky.

The correct approach splits state into three: committed, failed, and uncertain. Only failed is safe to retry directly. For uncertain, you always insert one extra step — "check before you redo."

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A three-state ledger that treats a dropped connection as 'uncertain' rather than 'failed', and why two states break down
A Python wrapper that protects MCP tools without idempotency support, using a correlation token and a reconcile read
A table for deciding when retrying is safe and when it isn't, weighed by the cost of a duplicate vs the cost of a miss
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API & SDK2026-06-26
When the Same Model Name Starts Behaving Differently: A Startup Canary for Unattended Pipelines
An in-place Opus upgrade can change your output, and an unattended publishing pipeline will never notice. Here is a lightweight startup canary that fingerprints behavior, catches drift, and halts the batch — with measured cost and latency.
API & SDK2026-06-25
Reach a Remote MCP Server in a Single API Request: Implementing the Messages API MCP Connector
How to call a remote MCP server's tools using only the Messages API's mcp_servers and mcp_toolset—no local MCP client. Covers allowlist/denylist design, response handling, and the pitfalls to avoid before unattended production use.
API & SDK2026-06-16
Trusting Claude's Structured Output in Production — Validation Gates and Repair Loops
When Claude's structured output breaks 'occasionally' in production, combine tool-use enforcement, a schema validation gate, a single repair loop, and a graceful degradation fallback to eliminate broken JSON from your operations — with working TypeScript code.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →