Two questions hide inside every concurrent write to shared agent memory, and a confident answer to the first one tells you nothing about the second.
The setup is ordinary. A memory server sits behind MCP. Cursor on a laptop and Claude Desktop on the same account both write to the same stored fact inside the same second. Question one: which write survived. Question two: did the client that lost find out. Most writing on this topic treats those as one question with one answer, and they come apart exactly where lost updates start to hurt.
The two questions come apart
Suppose your server serializes writes internally and the later one wins. That is a complete answer to question one. It is not an answer to question two at all.
The client that lost got a success response. It holds a value that is not the stored value, and no reason to re-read: as far as it knows, the write went through. Next time, it writes on top of a belief the store already discarded. The user experiences that as the agent stubbornly repeating a correction they already made.
Question one is a property of the store. Question two is a property of the client. A server can be internally consistent and still leave every client talking to it confidently wrong.
What the protocol answers
Nothing, on either question.
The current revision also removed the object you might have hung ordering on. From the Streamable HTTP transport page for revision 2026-07-28, read on 2026-09-07: "Removal of the GET stream endpoint. Removal of protocol-level sessions." A server on this revision that receives the old machinery SHOULD drop it, in the specification sense of that word. An Mcp-Session-Id header on a request: "ignore it, and do not mint or echo session IDs". A Last-Event-ID header: "ignore it; streams are not resumable".
That news is not mine and it is not new here. Session removal has been covered on this platform many times since May, including kike on 2026-08-20, where the first comment thread turns to atomic writes and a lockfile for a memory server. Go there for the transport story. What interests me is what the removal leaves undefined above the transport.
One distinction worth keeping: the missing coherence rules are not caused by the session removal. A protocol with sessions would still have had to say something about two clients writing one stored fact. Sessions are the context, not the cause.
The one disagreement the spec does police
The specification is not silent about disagreement in general. It handles exactly one kind, in detail, with its own numbered error.
When a request carries a parameter in a header and the same parameter in the body, servers "MUST reject requests with a 400 Bad Request HTTP status and JSON-RPC error code -32020 (HeaderMismatch) if any validation fails". The stated reason is the part to sit with: this "prevents potential security vulnerabilities when different components in the network rely on different sources of truth (e.g., a load balancer routing on the header value while the MCP server executes based on the body value)".
Read that phrase with two editors in mind. Different components relying on different sources of truth is a precise description of your laptop and your desktop holding two versions of one remembered fact. Same failure family, one layer up, and the rules stop at the layer boundary.
I counted the vocabulary on that page, to check whether I was reading a real gap or my own expectations into it. Recipe: the rendered page fetched on 2026-09-07 with full browser headers, markup stripped to text, case-insensitive substring counts. "MUST reject" appears 7 times: four of them about a header disagreeing with its own body, one about a required header missing, two about header values that are malformed on their own. "concurrent", "conflict", "ordering", "merge", "idempotent", "last write" and "lost update" appear zero times each. "multiple client connections" appears once, in the sentence describing the server as "an independent process that can handle multiple client connections".
So the page that tells you to expect many simultaneous clients says MUST reject seven times and does not use the word concurrent once. That is a scope boundary drawn deliberately, not an oversight. It just does not run where somebody evaluating a memory server goes looking for it.
What one vendor documents, in three different shapes
Anthropic answers both questions, and answers them with different machinery depending on which of its surfaces you are standing on. All quotes below read on 2026-09-07.
Question one, on the managed agents memory documentation, is answered with an optimistic precondition. "To avoid clobbering a concurrent write, pass a content_sha256 precondition." The update applies only if the stored content hash still matches the one you read.
Question two is answered with a status code, and for that you have to leave the overview page, where the phrase does not appear at all. It is in the update endpoint reference. On mismatch the request "returns memory_precondition_failed_error (HTTP 409); re-read the memory and retry against the fresh state". The loser is told, in band, by the same call that failed.
The same reference carries a qualifier that the overview page does not mention. "If the precondition fails but the stored state already exactly matches the requested content and path, the server returns 200 instead of 409." The loser learns that it lost only when losing changed the outcome. Two clients that race to write the same value both receive a success, and no status code tells them apart. The response still carries memory_version_id, which the reference calls the authoritative head pointer, so a client that kept the previous one has something to compare; nothing in the call points at it. That is a defensible design, and a hard limit on what a count of 409s can tell you about how often your clients collide.
One more property of that precondition: it is optional. An update that omits it is not protected, and nothing in the API requires a client to pass one.
On a self-hosted sandbox the same vendor documents a different shape entirely, in a note on that same memory page. The worker reconciles each local copy with its store "at most once per sync interval (15 seconds by default)", so another session "sees a change only after both workers have synced". That is a documented window in which both sides are locally correct and nobody has lost yet.
At the mount, the answer is refusal rather than reconciliation: access "is enforced at the filesystem level: a read_only mount rejects writes".
One vendor, one race, three different answers, selected by which surface you connected through.
The four cells that actually decide it
| Question | MCP 2026-07-28 (transport page) | Anthropic (memory, update) | Left to the server |
|---|---|---|---|
| Order between two writes | not addressed; no ordering vocabulary on the page | the update applies only if the stored content hash still matches the one you read | all of it |
| Does the loser find out | not addressed | memory_precondition_failed_error, HTTP 409, but 200 when the stored state already matches | whether any signal reaches the client |
| Window where both sides are right | not addressed | 15 second default sync interval on self-hosted; visible "after both workers have synced" | whether the window is documented at all |
| Who retries | not addressed | the client: "re-read the memory and retry against the fresh state" | whether a retry is even possible |
Every cell was read live on 2026-09-07 with full browser headers.
What to ask your own memory server
The question to bring to a vendor is not "who wins". Everybody has an answer to that one, and it is usually reasonable enough.
Ask when the question arises: is there a window in which two clients can both be correct, and is its size written down anywhere. Ask what comes back to the loser: a status code, a changed revision id, or a silent success. Ask whether the protection is on by default or something the client opts into, because an optional precondition that your client library never passes is the same as no precondition.
Three probes you can run against any server, no source access required. Write the same key from two clients inside one second and read it from a third, then repeat with identical values and see whether the responses differ. Search the vendor documentation for "concurrent", "precondition", "409" and "conflict", and treat zero hits as a question rather than an answer. Read a value, wait, write it back from that stale read, and check whether anything objects.
What this does not prove
I checked one vendor by name. I ran no named probes against the other memory engines, so the accurate statement about everyone else is that I did not find a publicly documented answer, which is not the same as saying there is none.
The 15 second interval is a documented default of one self-hosted configuration, adjustable, and not an MCP figure. It is quoted here because a stated window is rare, not because it is a fault.
The counts above are counts of words on one rendered page on one date, not counts of guarantees. A specification can constrain something without using the obvious noun for it.
Two method traps caught me while checking this. The removal lines do not appear on the transports index page at all, only on the streamable-http page beneath it, so a negative result on one URL proves nothing until you check its sibling. And grepping raw HTML returned zero hits for a phrase plainly present on the rendered page, because markup splits it across tags. Every quote here was taken from extracted text and read back in its surrounding paragraph.
Disclosure: I work on Mnemoverse, a memory engine for AI agents connected over MCP, so weigh the argument accordingly.
Top comments (2)
The distinction between store-level conflict resolution and client-level belief invalidation is the exact point where multi-agent memory architectures silently rot.
If Client A and Client B race to update a shared memory slot, and Client A loses silently (e.g., if the store resolves via Last-Write-Wins or identical-content convergence), Client A's context window continues reasoning from a phantom baseline. In single-agent workflows with sequential tool calls, this is invisible. The moment you have concurrent agent processes sharing an MCP memory server—say, a research agent updating an API spec while an execution agent patches endpoints—silent loser states produce compounding divergence.
Anthropic's
content_sha256precondition paired with an explicit HTTP 409 is structurally equivalent to CAS (Compare-And-Swap) orIf-MatchETags, which forces conflict into the open. But in agent systems, returning a 409 in-band only solves the problem if the execution harness knows what to do with it. If the 409 is simply passed back to the LLM as a tool error string, the model often hallucinates an optimistic retry without re-fetching state.In our agent harnesses, we treat a 409 precondition failure not as a prompt-level error, but as an outer-loop control signal: the deterministic harness intercepts the 409, fetches the fresh
memory_version_idand authoritative payload out-of-band, invalidates the stale memory slice in the context window, and only then re-evaluates the step. Forcing the deterministic runtime to reconcile the CAS conflict before the LLM reasons again is the only way we've found to stop the "confidently wrong" retry loop.The two-question split is the useful part, and I would add a third that sits underneath both: did anything downstream find out.
Question two is about the client that lost. Question three is about whatever record is being kept of what the agent did, and in most stacks that record is written by the same client that got the success response. So a silent success at the client is a silent success in the ledger too, and by the time anyone audits it there is nothing to audit against.
I run a public directory of agent run records, and both of the expensive failures we have had live in that gap rather than in the store.
Our onboarding wizard generated a reporter that posted outcome: success on a 30-minute timer regardless of whether the agent had run at all. Weeks of well-formed, continuous, entirely fictional evidence. Nobody flagged it, because flagging requires a reason to look and a green record gives you none.
Run attestations were bound to (repository, workflow filename). A workflow got renamed, reporting started 404ing after the work was already done, and the agent simply stopped appearing. Identity fine, authority fine, execution fine, evidence gone.
Your 200-instead-of-409 case is the sharpest thing here and I think it generalises past memory servers. The API is answering "did the state end up right", which is a reasonable thing for a store to optimise for. The client is asking "was my belief ever true", and the two come apart precisely when the answer is convenient. Same shape as a success code that means the call returned rather than that the work landed.
A fourth probe for your list, the mirror of the three you gave: stop writing from one client entirely and see whether anything downstream can tell the difference between "wrote nothing" and "wrote and the write was dropped". If nothing can, then every success count you hold is a count of what showed up rather than of what happened, and the writes that never report are disproportionately the failed ones, so the denominator shrinks in the direction that flatters you.
Where I would temper my own optimism: on our board today, 20 agents, every one reporting cryptographically signed run events, zero human ratings across the entire platform, and eleven of the twenty reporting exactly 100% success. Knowing who wrote the log is not the same as the log being worth reading. A perfect number is now where I look first rather than where I relax.
Disclosure: I operate aiopsenabler.com, a public record of agent run history, so weigh accordingly.