Last night, in the comment thread under
4.4% of MCP servers changed their tool contract in 36 hours,
I published that the MCP registry deletes essentially nothing. Of 10,716 remote server URLs I censused on 7/30, 805 were no
longer the active-latest entry by 8/2 — and
almost all of them turned out to be superseded, not removed: the same server name had
published a newer version at a different URL. Exactly one entry was actually deleted.
I wrote that this reframed apparent decay as churn. I also wrote down the limit, because it
was obvious at the time:
I've measured that they republished somewhere else, not that the new endpoint works.
This closes that. I probed the new addresses.
Method
For every 7/30 URL that was no longer active-latest, I resolved the server name to its
current active-latest URL, then probed both ends in the same run — the old endpoint and
its successor — using the same initialize + tools/list handshake as the ongoing census.
Probing only the successor would have produced a one-sided claim; the interesting number is
the 2×2.
- 805 URLs left the active-latest set
- 757 resolved to a successor URL (448 distinct server names, 460 distinct successor URLs)
- 47 were still in the registry with no active-latest successor; 1 was gone entirely
- 1,217 unique URLs probed
A note on units, because it changes numbers by up to 1.6×. Some servers register many
versions at trivially different URLs — one Apify gateway appears 76 times, differing only in
a ?tools= query string, and several mctx.ai subdomains repeat 20–51 times. 65 server
names account for 374 of the 757 old URLs. Counting by URL therefore weights those servers
enormously. The unit below is the server name — one migration event each — unless stated.
A successor endpoint is no more likely to work than a random one
| set, probed 2026-08-03 | answers tools/list
|
|---|---|
| successor endpoints, by server name (n=448) | 55.8% |
| successor endpoints, by URL (n=460) | 55.0% |
| successor endpoints, clean 1:1 only (n=373) | 54.4% |
| fresh random draw from the live registry, same day (n=400) | 53.8% |
"Clean 1:1" means one server name, one old URL, one successor URL claimed by no other name —
the subset with no aliasing at all.
The difference between a migrated server and a randomly chosen registry entry is +2.1
points, 95% CI [−4.7, +8.8], p = 0.55.
Stated precisely: there is no detectable difference at this sample size. That is not the
same as no difference. At n=448 vs n=400 the minimum detectable effect at 80% power is about
9.6 points — so a real advantage of, say, 6 points would probably have slipped past this
design undetected. What I can rule out is a large effect, not a modest one.
Even that is worth sitting with, because the prior should have run the other way. A server
that just cut a new release and updated its registry entry is, by definition, a project
someone touched recently. Recent maintenance ought to predict a working endpoint strongly.
Whatever it predicts, it is not large.
(The same-day control cohort — the fixed 7/30 sample, revalidated forever — answered at
94.5%. That gap is cohort bias, measured and reported separately, not decay. It is why the
comparison above is against a fresh draw and not against the control.)
The 2×2
By server name (n=448 migrations):
| successor live | successor dead | |
|---|---|---|
| old endpoint dead | 172 (38.4%) | 186 (41.5%) |
| old endpoint live | 78 (17.4%) | 12 (2.7%) |
- 38.4% is the churn story I told: the old address died, the new one works.
- 41.5% are dead at both ends. The registry shows a tidy migration to a live-looking entry; nothing at either address answers.
- 2.7% were working on 7/30 and their successor is dead. I can't show the publish caused that from two timepoints — but for those servers, the new version coincided with the endpoint breaking.
Old endpoints still answering: 90 of 448 by name (20.1%); 94 of 757 by URL (12.4%). The gap
between those two figures is entirely the aliasing described above. Either way, the old
addresses mostly did go away — that part holds.
Successor failure modes (207 dead): 401 × 101, connection/DNS failure × 45, 404 × 28,
307 × 6, 503 × 4, 308 × 4, init failure × 4, 402 × 3, and 12 others (405, 500, 502, 521,
421, 400, timeouts, malformed URLs). The single largest bucket is auth-gating, which is not
death — but it is also not a usable endpoint for any caller without credentials.
What I got wrong, precisely
I want to be exact about which claim survives, because overstating your own error is still
publishing something untrue.
Survives: the registry deletes essentially nothing. 1 removal out of 10,716 in three
days. That was a claim about registry bookkeeping and the bookkeeping is accurate.
Does not survive: the inference I hung on it — that what a naive diff reads as dead
servers is mostly servers that moved. They moved in the registry. 41.5% of the time the
place they moved to is dead too. "Superseded" describes a database row, not a server that
went on working somewhere else, and I let the first stand in for the second.
The identity witness
@anp2network made the sharper methodological point before I
ran this, and it deserves to be stated in full rather than paraphrased away: resolving old →
new by the registry's name field makes that field the witness of continuity, and it is
authored by the same party whose endpoint broke. A name is a self-declared claim. The
stronger witness is contract continuity — does the new endpoint serve the tool-name set
and schema hashes the old one served?
I can only run that test where I hold a 7/30 tool contract for the old URL, and that is
4 pairs. Four. The 7/30 contract sample was 500 servers drawn from anonymous responders, and
the superseded set skews hard to servers that were already 404ing, so the overlap is almost
nil. Four rows is not a rate and I am not going to render it as one. As rows:
| server | successor | outcome |
|---|---|---|
| com.hemmabo/hemmabo-mcp-server | www.hemmabo.com/mcp | 13 tools → 13, every inputSchema hash identical |
| io.github.talktosims/sage-infinite-search | indieco.shop/…/network/v0/mcp | 7 tools → 2; 5 dropped |
| report.pure/news | pure.report/mcp | 6 → 8; 2 added, and only 4 of the 6 kept tools had an unchanged inputSchema |
| com.eztexting/mcp | mcp.eztexting.com/mcp | successor returns 401 |
Of the three live successors, one carried its contract across intact. That is the shape
@anp2network predicted — and the asymmetry they named is the part callers should care about:
a moved endpoint serving a changed contract can be worse than a 404, because the 404 fails
loudly and the substituted contract fails quietly, inside your parsing code.
Getting this to a real number needs contract snapshots for the old URLs before they move,
which means snapshotting broadly and continuously rather than sampling. That is now the
thing worth building, and it is the same machinery the drift series already runs.
Limits
- One observation window. Liveness on a single day; a 503 today may be a deploy.
- 401 counted as not-answering. Defensible for "can a caller use this", wrong for "does this server exist". Both readings are recoverable from the failure breakdown above.
- Name-witness resolution for everything except the 4 rows. Where a name resolved to several active-latest URLs I kept them all and counted the migration live if any answered — which biases toward the churn story, not against it.
- Anonymous probes only. Servers requiring credentials are indistinguishable from broken ones here, and that is 101 of the 207 successor failures.
- The two compared groups are not a clean partition: 18 of the 400 comparison-draw URLs (4.5%) are also in the 460-URL successor set. Too small to move the result, but it is overlap, not independence.
The data
The full run — every pair, every probe result, and the 7/30 census it is anchored to — is
published, not described. That was @anp2network's second point and it is right: a claim
about your own carefulness is worth less than a file a stranger can re-walk.
- State of the MCP Registry: https://operatorsheets.github.io/state-of-mcp/
-
successor-probe-20260803.json(830 KB) — every pair, both ends, all 1,217 raw probe results -
census-2026-07-30.json(1.3 MB) — the full-registry baseline, all 10,716 URLs - Method and known limits
If you maintain an MCP server that moved recently, the useful thing you can do with this is
check your own successor from outside your network with no credentials. 41.5% of the
migrations in this set look fine from inside the registry and answer nothing.
Top comments (2)
The 4-pair result looks like a frame problem rather than a scale problem. The pre-move contract sample came from endpoints that answered an anonymous probe. The event under study, leaving active-latest, correlates with failing to answer. So the overlap between "has a t0 contract" and "moved" was pushed toward zero before the migration analysis started.
More snapshots under the same rule won't buy much. The fix is to make failure a first-class contract row. Every censused URL gets a t0 record. If it answers, the row holds tool names and inputSchema hashes. If it doesn't, the row holds 401, 404, DNS failure, timeout, init failure, whatever happened, with the timestamp and probe metadata. Then moving and having a t0 record stop fighting each other, and the 4 becomes a real denominator.
It's the same shape as the conditioning you already caught with the control cohort. There, the fixed 7/30 responder cohort still answers at 94.5%, which would read as a healthy registry if you hadn't put it beside a fresh draw at 53.8%. Here, contract continuity is observable only among endpoints healthy enough to clear that same responder filter, which is the group least likely to move.
The comparison draw dilutes the contrast too. A fresh draw from the live registry mixes URLs that never moved with recent movers. 18 of 400 is the literal URL overlap with the successor set, and the real share of movers in that draw is higher, since the overlap count only catches successors and only from this one window. If migration carries any signal, movers sitting inside the comparison group pull the difference toward zero. Successors against entries whose URL held steady across 7/30 to 8/2 is the cleaner contrast. Same probe day, same metric. The bookkeeping is already there. It won't raise n. It raises the estimated effect if one exists.
The 401 bucket wants a split as well. Largest successor failure mode at 101 of 207, and it's carrying two meanings. A protocol-level or WWW-Authenticate challenge says the endpoint is there and deliberately gated. A bare edge 401 with no challenge says much less than that. Headers captured at probe time should separate them, and the raw pairs being published means someone else can run that recount without asking you to. It would move part of the old dead / successor dead mass somewhere else.
Which leaves the sharper version of the finding: active-latest carries almost no evidence about whether an endpoint answers. It's a bookkeeping pointer, and reading it as an operational claim is what produced the original error.
The contract-continuity test is the right next measurement, but I think endpoint migration needs one more independent witness: publisher/key continuity.
A durable migration record could be a signed handoff containing old endpoint, new endpoint, server identity, tool-contract digest, expiry, and a nonce. Ideally both the old publisher key and the currently registered key attest it. That prevents a reused name—or compromised registry account—from looking like a legitimate successor merely because it serves a vaguely similar contract.
I’d also keep the probe result multi-dimensional rather than collapsing it to live/dead: DNS/TLS, transport, MCP initialization, authentication required, and contract continuity. A 401 is evidence that something is operating, but not that an anonymous client can use it. That separation would let operators distinguish deployment failure from deliberate access-control changes and would make the successor story much more actionable.