DEV Community

Ahab
Ahab

Posted on • Originally published at indieseek.co

Qwen Code 0.21.5 MCP Safe Replay Checklist

Qwen Code 0.21.5: stop duplicate MCP writes after disconnects

Quick answer

Qwen Code 0.21.5 changes what happens when an MCP connection disappears after a tool may already have completed its work. It now replays the current call only when the MCP server is trusted, the workspace is trusted, and the tool explicitly declares either idempotentHint: true or a consistent read-only annotation. Missing, false, or conflicting annotations stop automatic replay and return an unknown-outcome error instead.

That is the correct default for writes such as sending a message, creating a ticket, charging a card, publishing content, or appending a record. A dropped response does not prove the operation failed. Retrying blindly can turn one approved action into two committed side effects.

Upgrade to 0.21.5 or newer, but do not stop at the version check. Classify every MCP tool, verify the server and workspace trust boundary, force a post-commit disconnect in a disposable test server, and rehearse manual outcome reconciliation. The rollout passes only when unsafe tools execute once, safe tools replay only under every required gate, and a reconnected server cannot inherit stale annotations.

Who this is for

This guide is for Qwen Code users, MCP server authors, and teams that expose external systems to coding agents. It matters most when tools can create, update, delete, send, publish, bill, deploy, or otherwise change state.

The problem is different from forked-subagent prompt isolation: that boundary controls which instructions another agent can see. This one controls whether one approved MCP invocation can be executed again after its response is lost. Keep the broader MCP conformance checklist for protocol and transport coverage.

What changed in 0.21.5

The merged Qwen change describes the failure precisely: an MCP server can commit a side effect and then lose the response connection. Before the fix, the reconnect path could rediscover the server and send the same tools/call again even when annotations were missing or explicitly non-idempotent. The PR's independent harness reproduced a counter reaching two, and repeated disconnects could drive one user invocation to as many as four server executions.

Version 0.21.5 makes replay eligibility conservative and checks it twice: before reconnect and again after tool rediscovery.

Tool state Automatic replay after connection loss Why
Trusted server + trusted workspace + readOnlyHint: true Eligible The tool declares no environmental change
Trusted server + trusted workspace + idempotentHint: true Eligible Repeating identical arguments should add no effect
Missing annotations Blocked MCP defaults do not establish replay safety
idempotentHint: false Blocked A repeated write may duplicate the side effect
Conflicting read-only annotations Blocked The client cannot establish a consistent safe claim
Untrusted server or workspace Blocked Tool metadata is not a security claim from an untrusted source
Safe before reconnect, unsafe after rediscovery Blocked Replacement process metadata must be checked again

The MCP specification calls these fields hints, not guarantees. It says clients must not base security decisions on annotations from untrusted servers. It also defaults readOnlyHint and idempotentHint to false when absent. Qwen's two trust gates reduce accidental replay, but they do not prove that a trusted server described its tool truthfully.

A post-commit disconnect test

1. Freeze the client and inventory tools

Record the exact Qwen Code version, install channel, MCP server source, effective trust setting, workspace trust state, and the discovered tool annotations. Do not enable trust: true merely to recover transparent retries. Trust means Qwen may rely on server-supplied replay metadata.

Classify each tool as read-only, idempotent write, non-idempotent write, or unknown. Treat unknown as non-replayable.

2. Use two disposable canary tools

Build a local test MCP server with no production credentials:

  • increment_then_drop increments a durable counter, closes the first response connection, and has no safe annotation. It models a non-idempotent write.
  • set_status_then_drop writes a value under a stable idempotency key, closes the first response connection, and declares idempotentHint: true. Repeating it must leave one logical result.

Add a read-only lookup tool with readOnlyHint: true. Keep the counter, discovery count, and received arguments in a temporary test store. Do not use email, payment, deployment, or production APIs for this lab.

3. Run the unsafe matrix

For increment_then_drop, test missing annotations, idempotentHint: false, untrusted server, and untrusted workspace. Every case must produce exactly one server invocation, zero replay calls, and an unknown-outcome response that does not leak arguments or the upstream error.

Then start with an eligible tool, replace the server during reconnect, and remove the safe annotation or trust. Rediscovery may complete, but the replacement process must receive zero replay calls.

4. Run the safe controls

For set_status_then_drop, enable server trust, use a trusted workspace, and declare idempotentHint: true. One bounded replay may occur, but the durable store must still contain one logical status transition. Repeat with the read-only lookup tool and confirm replay does not mutate state.

Never label a counter increment, append, send, charge, or create-with-random-ID operation idempotent just to make the test green. Add a stable request key and server-side deduplication first.

5. Reconcile unknown outcomes before retrying

When Qwen says the result may be unknown:

  1. Freeze automatic and manual retries.
  2. Record the tool name, redacted argument hash, target, time window, and correlation or idempotency key.
  3. Query external truth through a read-only endpoint, audit log, or get operation.
  4. Mark the result committed, not_committed, or still_unknown.
  5. Retry only not_committed, with an explicit human decision and the same stable idempotency key. Escalate still_unknown instead of guessing.

This is the same evidence principle used in the Qwen Goal completion checklist: transport success is not business-state proof.

Eight rollout gates

Gate Required evidence
Version The launching process reports Qwen Code 0.21.5 or newer
Inventory Every exposed MCP tool has an owner and replay class
Trust Server and workspace trust are explicit and independently justified
Defaults Missing or conflicting annotations produce no replay
Unsafe canary Post-commit disconnect yields one invocation and unknown outcome
Safe control Read-only or truly idempotent tools replay without extra effect
Rediscovery Replacement tool metadata and trust are revalidated before replay
Reconciliation The team can prove committed/not-committed before manual retry

Use a compact evidence record for every canary:

qwen_version: 0.21.5
tool: increment_then_drop
server_trusted: true
workspace_trusted: true
annotations: missing
server_invocations: 1
replay_invocations: 0
external_state: committed_once
client_result: unknown
manual_retry: blocked
verdict: pass
Enter fullscreen mode Exit fullscreen mode

Common mistakes

Treating connection loss as failure. The response is unknown; the side effect may already be committed.

Trusting an annotation from an unreviewed server. MCP annotations are descriptive hints. Review the implementation and test the behavior before trusting the server.

Marking a write idempotent without a stable key. Identical arguments do not help if every call creates a new resource or appends another row.

Fixing the client but not the server. Client-side replay guards reduce duplicates. Durable idempotency keys, outcome lookup, and deduplication at the tool boundary remain the stronger guarantee.

FAQ

Does Qwen Code 0.21.5 guarantee exactly-once execution?

No. It prevents automatic replay when safety cannot be established. Exactly-once business behavior still requires a server-side idempotency design or a durable operation record.

Should all MCP writes set idempotentHint: true?

No. Set it only when repeated calls with identical arguments have no additional effect. Otherwise leave it false or absent and provide a read-only way to reconcile an unknown result.

Sources

Top comments (0)