HISTOR is our public log of MCP tool definitions. It reads every remote server in the official MCP registry every 30 minutes to 6 hours and records each change to the tools it advertises. From 29 October to 1 November it quietly stopped reading 2,195 servers it had read without trouble on 28 October.
Nothing on our dashboards turned red. We found out on 1 November, by comparing our log with one another team had started two days earlier. This post covers what broke, why the numbers we watched didn't show it, and what anyone writing an MCP client can take from it.
Two ways to open an MCP connection
The MCP revision of 2026-07-28 changed how a session starts. Older revisions, up to 2025-11-25, open with an initialize request: the client and server agree on a protocol version, then the client asks for tools/list. The new revision drops the handshake. The client sends server/discover, and every request carries its protocol version and capabilities with it.
A client that speaks both has to work out which kind of server it is talking to. For Streamable HTTP, the specification's backward-compatibility rule says how: send the new-style request first. If the server refuses it with an HTTP 4xx whose body is not one of the new revision's own errors, it is an older server, so send initialize instead.
What we built, and what we got wrong
In late October HISTOR's reader learned the new revision and started trying it first. We made the fallback strict on purpose. The reader went back to initialize only when the refusal looked exactly like an old server rejecting an unknown method:
- HTTP 400, 404 or 405;
- a JSON-RPC error −32600, −32601 or −32602;
- the request id we had sent.
Anything else stopped the read, with a reason like "no downgrade". Being strict felt safe: a client that falls back too easily can be talked into an older protocol than both sides support.
For a crawler that only reads tool lists, that was the wrong trade. The strict rule went live around 29 October. Here is what real servers answered. These are the 2,195 servers that read fine on 28 October and not once from 30 October until the fix, by their last error:
| What the server answered | Servers |
|---|---|
| a legacy error, but not under our request id | 789 |
HTTP 401 or 403 to server/discover
|
746 |
| a discovery reply that didn't list the version we asked for | 304 |
| an error body that wasn't well-formed JSON-RPC | 120 |
| no protocol version both sides support | 37 |
| something else: 5xx, timeouts, 404, 402 | 199 |
Two servers show the pattern.
mcp.quotewise.io answered server/discover with HTTP 400 and this body:
{"jsonrpc": "2.0", "id": "server-error",
"error": {"code": -32600,
"message": "Bad Request: Unsupported protocol version: 2026-07-28. Supported versions: 2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25"}}
That is as clear a "I'm an older server" as one can write. It even lists the versions it does speak. Our reader refused it because the id was "server-error", not ours. That id isn't a quirk of one server. The official Python SDK, in its 1.x line, sends it for every error it raises before reading the request id: id="server-error", # We don't have a request ID for general errors. Most of the 789 servers in the first row answered with that id.
mcp.kaiku.tech answered server/discover with 401 and the message «A Bearer token is required for anything but the introduction (initialize, tools/list)». The server is deliberately open for exactly the two requests a crawler needs, and asks for a token for everything else, including a method it has never heard of. Most of the 746 in the second row look like this. Sent initialize, kaiku answers at once.
Why our numbers looked fine
We watched the share of reads that succeed. In the hour before the fix, HISTOR made 9,514 remote reads, and 9,400 of them succeeded: 98.8%.
That number was true and useless. A server that fails is retried after 6 hours, then 12, then 24; a 401 waits at least a day. So the 2,195 broken servers were almost never in the queue. The reader spent its hour on servers that worked, and the share of successful reads stayed high precisely because the failures had been pushed out of sight.
The number that did move was a different one: servers with at least one successful read that day. It fell from about 28,200 on 29 October to about 23,000 on 31 October. We had no alert on it.
How a second log caught it
On 30 October ScraperWatch started a public log of MCP tool changes. It reads a fixed random panel of 3,000 registry servers once a day and publishes daily diffs, field by field. Two independent logs of the same servers can check each other, so on 1 November we compared every one of their 98 tool-change records with HISTOR, path by path.
64 matched exactly. For 18 records on 15 servers, HISTOR had nothing to compare: it had failed where ScraperWatch had read the tools. ScraperWatch's reader only speaks the 2025-06-18 protocol and always sends initialize. Ours tried the new revision first and gave up. Those 15 failures led back to the reader, and a query over the whole log turned 15 servers into 2,195.
ScraperWatch later found a bug of its own in the same exercise: its robots.txt parser had let it read about 250 panel endpoints whose robots.txt disallows them, and it removed those reads. Its log for those two days now holds 81 tool-change records, 53 of them matching ours path by path. The figures above are for the log as we fetched it on 1 November; the handshake finding does not depend on the removed records.
A system can't easily notice what it fails to see. A second reader with different code, run by someone else, notices at once.
The fix
The reader now follows the specification's rule as written. It falls back to initialize, once, when the first new-style request is refused:
- Statuses: 400, 404, 405, and also 401 and 403;
- Error bodies: a legacy error code under any id, or a body that isn't JSON-RPC at all;
- Version lists: an error or discovery reply that lists only versions older than 2026-07-28;
-
Not a discovery reply: an answer to
server/discoverwithoutsupportedVersions; -
The first page of
tools/list: refused in the legacy way, before anything was read. One server advertised 2026-07-28 and then refused it.
These still stop the read: rate limits, timeouts, 5xx, redirects, the new revision's own errors, and ambiguous bodies.
The fix went live on 1 November at 08:44:57 UTC. In the first 90 seconds, 171 of 172 remote reads succeeded, and 145 of those used an older protocol. The 2,195 servers come back as their retry delays run out; we did not reset the schedule. By 04:21 UTC the next morning, 1,893 of the 2,193 still listed had been read again. Of the other 300, 161 now fail for reasons of their own: HTTP 400, 401 and 402, server errors, timeouts. 139 still stop in our reader with a protocol error, on answers the rule treats as ambiguous rather than as a legacy refusal; 73 of them send an error body that is JSON-RPC but malformed. Whether those deserve a fallback too is the next thing we look at.
How many servers speak the new protocol
As a by-product we now know. Of the 21,474 remote servers HISTOR read successfully between 25 October and 1 November, the latest successful read negotiated:
| Protocol version | Servers |
|---|---|
| 2026-07-28 | 4,097 |
| 2025-06-18 | 13,744 |
| 2025-03-26 | 2,853 |
| 2024-11-05 | 650 |
| 2025-11-25 | 126 |
Three months after the new revision, 19% of remote servers speak it. Four out of five still need initialize. One caveat: a server that speaks both and was last read before 29 October counts under its older version, which applies to 165 of them.
The data is open (CC BY 4.0): https://histor.modelmarket.dev/datasets/mcp-protocol-eras-2026-11-01/. It also has the list of the 2,195 servers and their last error.
If you write an MCP client
-
Treat 401 and 403 on
server/discoveras "older server". Many servers leaveinitializeandtools/listopen and lock everything else. -
Don't require your own id on a legacy error. The Python SDK's 1.x line answers with
"server-error". -
Read the version list in the error. If every version it names is older than 2026-07-28, send
initialize. - Count servers, not reads. With retry backoff, a falling count of servers read today shows trouble that the success rate per read hides.
What we changed, and what we haven't yet
The comparison with ScraperWatch now runs by itself after every weekly snapshot, in both directions, and is published at https://histor.modelmarket.dev/datasets/peers/scraperwatch/latest/. We have not yet added an alert on the daily count of servers read successfully; until we do, the weekly comparison is what would catch the next one of these.
Disclosure: we build HISTOR. AI assistance was used for the analysis, the code and the writing. The numbers come from HISTOR's production database, read-only, and from the published files linked above.



Top comments (0)