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, 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.