MCP Server Connection Failed? 53 Endpoints Tested [2026]
Most MCP server connection failures are not failures. What 53 public endpoints returned, and the retry rule most clients skip. Check your own endpoint.
MCP Hunter team 12 min read
Most of them are not failures. We connected to 53 public MCP endpoints between 20 July and 5 August 2026, and 44 of them were behaving correctly: 30 were guarding themselves behind authorization, 14 answered in full. Four were genuinely broken, three were the wrong URL, and two domains had stopped existing.
The interesting part is not that table. It is the three status codes that stay ambiguous after you read them, where the specification tells you to send a second request before deciding anything. Almost nobody does, and until 8 August 2026 our own connection gate did not either. This post is the taxonomy plus the bug we found in ourselves while writing it.
TL;DR:
- A 400, 404 or 405 is not a verdict. The transport spec says to retry as a GET before concluding, because a server on the deprecated 2024-11-05 transport answers exactly this way [2].
- We got this wrong in production. Our gate read a bare 405 as "your URL is a web page" and a 404 as "your server is broken". Both are false for a legacy server. Fixed 8 August 2026.
- An SSE stream is not proof you found one.
router.mcp.so/sseanswers GET with a livetext/event-streamand is not an MCP server. Theendpointevent is the signal, not the content type. - 57% of the endpoints refused an anonymous caller. That is 30 of 53, and it is the most common outcome by a wide margin.
What does "connection failed" actually mean here?
Six distinct outcomes, and only four of the 53 endpoints pointed at a broken server. We POSTed a JSON-RPC initialize request to each address, then called tools/list, and recorded what came back. Counting each endpoint once by its most recent check, the distribution is heavily weighted toward a single result.
| Outcome | Endpoints | Share | Is the server broken? |
|---|---|---|---|
| Authorization required (401 or 403) | 30 | 57% | No |
| Connected and listed tools | 14 | 26% | No |
| Answered, but not with valid MCP | 3 | 6% | Yes |
| Served a web page, not an endpoint | 3 | 6% | No, the URL is wrong |
| Hostname did not resolve | 2 | 4% | The domain is gone |
| No usable response | 1 | 2% | Yes |
One note on the denominator, because it is easy to compare two of our pages and think they disagree. This table counts endpoints, 53 of them. Our connection test glossary entry counts checks, 82 of them, because several addresses were tested more than once. Both are the same corpus read two ways.
The biggest row is the one people misread. A 401 or 403 means the server completed a TCP connection, negotiated TLS, parsed your request, and made a deliberate decision about it. The spec's own error table assigns 401 to "Authorization required or token invalid" and 403 to "Invalid scopes or insufficient permissions" [1]. Neither describes a broken endpoint, and auth_required covers why that is the expected shape for an internet-facing server rather than an edge case.
What do 400, 404 and 405 actually tell you?
Less than they appear to, and this is the one place where the obvious reading is wrong often enough to matter. All three are listed in the transport specification as statuses that should trigger a second request, not a conclusion. The exact instruction to clients is [2]:
If it fails with the following HTTP status codes "400 Bad Request", "404 Not Found" or "405 Method Not Allowed": Issue a GET request to the server URL, expecting that this will open an SSE stream and return an
endpointevent as the first event.
The reason is backwards compatibility. Before Streamable HTTP, MCP used an HTTP+SSE transport in which the URL you were given did not accept a POST at all. You opened a stream with GET, and the server told you where to post. A server still running that transport answers your initialize POST with exactly the codes above, and a client that stops there reports a working server as a broken one.
There is a second reading for 405 specifically, and it is the common one: page routes in most web frameworks accept GET and refuse POST, so a documentation page answers 405 to a JSON-RPC request. The tell is the Content-Type. A page answers text/html; an MCP endpoint never does.
So the honest procedure is: if the body is HTML, you have a web page and you are done. If it is not, send the GET before you decide.
The bug this found in our own gate
We were not doing it. Until 8 August 2026 our connection tester treated a bare 405 as conclusive proof of a web page, and let a 404 fall through to a generic protocol error. Six endpoints in our own corpus hit those statuses, so for months the gate could have been telling makers one of two untrue things: that their URL was wrong, or that their server was broken.
The fix runs the GET probe on all three statuses before deciding, and reports a legacy server as its own outcome rather than as either falsehood. Two details cost us a revision each and are worth stealing if you implement this yourself:
- Give the probe its own time budget. We funded it from whatever the failed handshake had left, and a slow 404 routinely spends most of a 20 second budget. The probe then returns "no evidence" for timing reasons, which looks exactly like a real negative. That is the worst kind of bug: it produces a confident, wrong, quiet answer.
- Check the event, not the content type. More on that below, because it is the part that surprised us.
Does an SSE stream prove you found a legacy server?
No, and this is the trap on the other side of the fix. Our first implementation concluded "legacy transport" whenever the GET came back as text/event-stream. That is wrong in both directions, and one live endpoint proves it.
router.mcp.so/sse answers a GET with HTTP 200 and Content-Type: text/event-stream. A stream genuinely opens. What arrives is this:
event: stream
data: started
event: connection
data: ready
No endpoint event, ever. It is an SSE service that is not an MCP server, and a content-type check would have relabelled it as one. The specification is specific for a reason: the endpoint event is the identifying signal, because a current Streamable HTTP server may also answer GET with a stream. It just never sends that event on it.
The same section makes the opposite point about 405, which is worth holding in your head at the same time. A healthy modern server "MUST either return Content-Type: text/event-stream in response to this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating that the server does not offer an SSE stream at this endpoint" [2]. The same status code means opposite things depending on which verb you sent.
Is the deprecated transport still out there?
In our corpus, no. We re-ran all six affected endpoints against the fixed code on 8 August 2026 and not one of them changed classification. The three 405s were still web pages, and the three 404s were still 404s. Raw GET requests confirmed it independently of our own code: every one answered with HTML, a redirect to a marketing or docs page, or a plain 404.
That is a negative result and it is worth reporting as one. We found a spec-compliance bug in our own gate, fixed it, and measured zero servers affected in a 53 endpoint sample. Nothing we have published needs revising as a result.
The reason appears to be that the well-run legacy endpoints are not being left running. Two of the best known answer HTTP 410 Gone rather than lingering: mcp.deepwiki.com/sse returns a JSON body reading "The SSE transport has been deprecated in favor of Streamable HTTP. Please use the /mcp endpoint instead", and mcp.sentry.dev/sse also returns 410. Both checked 8 August 2026. That is the good outcome for everyone, and it is also why the retry rule still matters: 410 is not in the spec's list, so a client that only implements the three listed statuses will read those as failures too.
Why does one client connect when another fails?
Usually a version mismatch or a missing header, not a server problem. Across the 14 endpoints that connected for us, three different protocol revisions were negotiated: nine servers on 2025-11-25, three on 2025-03-26, and two on 2025-06-18. A client pinned to one revision meets servers speaking the other two every day.
Two header requirements cause most of the rest, and both are MUST-level in the transport spec [2]:
- The
Acceptheader must list both types. A client "MUST include anAcceptheader, listing bothapplication/jsonandtext/event-streamas supported content types." Send onlyapplication/jsonand a server that wants to stream has nowhere to go. - The protocol version rides on every later request. Clients "MUST include the
MCP-Protocol-Version: <protocol-version>HTTP header on all subsequent requests." If it is missing and the server cannot infer the version, the spec tells it to assume2025-03-26, which may not be what you negotiated.
There is also a session trap. If the server returned an MCP-Session-Id during initialization, you must echo it on every later request, and a server that requires one "SHOULD respond to requests without an MCP-Session-Id header (other than initialization) with HTTP 400 Bad Request" [2]. A 400 halfway through a working session usually means a dropped session header rather than a malformed request. Note that this is a third meaning for 400, alongside the malformed-authorization case [1] and the legacy-transport retry trigger [2].
How do you tell a dead endpoint from a guarded one?
Send one request and read three things: the status code, the Content-Type, and the WWW-Authenticate header. That is enough to place any endpoint in the table above without guessing.
curl -i -X POST https://mcp.example.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
"protocolVersion":"2025-11-25","capabilities":{},
"clientInfo":{"name":"probe","version":"1"}}}'
Then read the response against this:
- 401 or 403 with a
WWW-Authenticateheader: working server, you need credentials. Follow theresource_metadataURL it gives you. A 401 with no such header is a real implementation bug worth reporting, because clients "MUST be able to parseWWW-Authenticateheaders" and this one has left them nothing to parse [1]. - Any status with
text/html: you have a web page. Find the URL your client is meant to POST to, which is usually not the documentation page describing it. - 400, 404 or 405 without HTML: not a verdict yet. Retry as a GET with
Accept: text/event-streamand look for anendpointevent before concluding. - Connection refused, TLS error, or DNS failure: now you have an actual outage, and it is worth checking whether the domain still exists. Two of our 53 endpoints had simply stopped resolving.
You can run that whole exchange without the curl by pasting a URL into our connection tester, which returns the outcome, the negotiated revision, the tool list, and one round trip in milliseconds. If you want to know what each field in the response means first, the tools/list field reference has the full shape.
Worth being explicit about what none of this tells you. It says an address answered on a date with a specific list of tools. It says nothing about whether that server is safe, well maintained, or whether its tools do what their descriptions claim. We have not audited anyone's code or data handling, and we do not publish a rating that implies we have.
What 53 endpoints say about the ecosystem
The tool surface is small and lopsided. Across the 14 servers that connected, tool counts ran from 2 to 213 with 288 tools in total, and a median of 4.5. One outlier carried 213 of them, so the mean is meaningless and the median is the honest number: the typical public MCP server exposes four or five tools.
Two other findings from the same run:
- Every connected server carried tool descriptions, 14 of 14. Quality varied, but the field was never empty, which suggests the ecosystem has settled on documenting tools by default.
- None of them paginated. Not one returned a nextCursor, which is exactly why clients ship without cursor handling and break on the first server that does.
One number we are deliberately not publishing is latency. That run recorded a 2.7 to 6.4 second band, against 1.2 to 1.7 seconds for two of the same servers eleven days earlier, from the same code with the same definition of a single round trip. Forty-eight requests fired back to back from one consumer connection measure our afternoon at least as much as anyone's server. That band is not evidence of anything and we are not going to dress it up as a benchmark.
Start with the assumption that it works
The next time a client reports that an MCP server failed to connect, check the status code before you check the server. In our sample 44 of 53 endpoints were behaving correctly, and the client was either unauthorized or pointed at the wrong URL. Connection failure is the rare case, not the common one.
If you maintain a server, two cheap things remove an entire category of confusion: return a WWW-Authenticate header on your 401, and answer application/json from the exact path you document. If you are about to ship one, the pre-flight checks in how to launch an MCP server cover what to read in your own response before launch day.
And if you have already shipped, put it in a week. The listing carries exactly what your server returned on the date we asked it, including the 401 case, and when we cannot connect it carries no such claim at all.