Short answer: send one POST to the server URL with the headers the current MCP spec requires and a tools/list body. A JSON or SSE response with a tool list means the server is up and speaks the 2026-07-28 protocol. A 400 whose body is one of the spec's modern JSON-RPC errors means it is up and your request needs fixing; a 401 means it is up and wants a login; a 400 without such an error, a 404 or a 405 points to an older server that still expects the initialize handshake, so fall back to that. As of 16 October 2026, ToolVet runs these handshakes on a schedule against 28,233 listed remote MCP servers with uptime data; 23,462 of them (83.1%) have a 30-day uptime of 99.9% or more, and 4,771 (16.9%) are below 95%.
Some background: a remote MCP server is an HTTP endpoint, typically https://example.com/mcp, that an agent talks to with JSON-RPC over the Streamable HTTP transport. When a client refuses to connect, the error it shows is often not the real one: a client that follows the spec's fallback tries the 2026-07-28 request, then initialize, then the old GET, and the error it shows may belong to the last attempt. The fix is to make the requests yourself, one generation at a time, and read the raw status codes. ToolVet does this on a schedule for every listed remote MCP server, and the free MCP scanner does it once for any URL you paste, then checks the tool definitions for prompt injection.
Two generations of the same transport
The 2026-07-28 revision changed Streamable HTTP in ways that matter for a connection test:
- The
initialize/notifications/initializedhandshake is gone. Every request carries its protocol version and client capabilities inparams._meta. - Protocol-level sessions and the
Mcp-Session-Idheader are gone. - The HTTP GET stream is gone; a server may answer GET with
405. - Every POST must carry
MCP-Protocol-VersionandMcp-Methodheaders, plusMcp-Nameontools/call,resources/readandprompts/get. - A new
server/discoverrequest lets a client ask for supported versions and capabilities up front.
Protocol versions 2025-03-26 through 2025-11-25 used the same endpoint but with the handshake, optional sessions and the GET stream. The older HTTP+SSE transport from 2024-11-05, with its separate SSE and POST endpoints, is deprecated and still deployed. A test has to cover all three.
Step 1: the modern request
curl -s -D - https://example.com/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-H 'mcp-protocol-version: 2026-07-28' \
-H 'mcp-method: tools/list' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientInfo":{"name":"curl","version":"1.0"},
"io.modelcontextprotocol/clientCapabilities":{}}}}'
The Accept header must list both content types, because the transport spec lets the server answer a request either with one JSON object (Content-Type: application/json) or with an SSE stream (text/event-stream) that ends in the JSON-RPC response. Both are a pass. The -D - prints the response headers, which you need for the next step.
Step 2: read the status code
| Response | What it means | Server up? |
|---|---|---|
200, JSON or SSE with result.tools |
Current protocol, working | Yes |
400, body is a JSON-RPC error with code -32022 |
Up, but it doesn't support 2026-07-28; the error lists the versions it does | Yes |
400, body is a JSON-RPC error with code -32020 |
Up; your headers don't match the body (missing Mcp-Method, wrong version header) |
Yes |
400, body empty or not one of the modern error codes (-32020, -32021, -32022) |
Probably an older server; go to step 3 | Unknown yet |
401 |
Up; it wants OAuth or an API key | Yes |
402 |
Up; it wants payment, for example over x402 | Yes |
403 |
Up; a firewall, proxy or auth gate refused you, or the server rejected an Origin header you sent |
Yes |
404, body is a JSON-RPC error with code -32601 |
Up; a current server that doesn't implement the method you asked for | Yes |
404 or 405, no JSON-RPC error |
Wrong path, or an HTTP+SSE server; go to step 4 | Unknown yet |
Connection refused, timeout, TLS error, 5xx |
Down, or unreachable from where you are | No |
The spec is explicit about the 400 case: a client "SHOULD inspect the response body before falling back", because modern servers also use 400 for an unsupported version (-32022), a missing client capability (-32021) and header validation (-32020). A -32022 body is the server telling you which versions to retry with. Only a body that is empty or carries none of these errors justifies the fallback.
One code trips people up. A 401 is not a failure: the server is up and wants credentials, which a test from a terminal doesn't have; ToolVet counts a login or payment request as answering for the same reason. (403 is similar when it comes from the server itself: the spec requires a 403 for an invalid Origin header, to block DNS rebinding.)
Step 3: the handshake fallback
Older servers need initialize first. Send it with the latest version of that generation, and watch the response headers for Mcp-Session-Id:
curl -s -D - https://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":"curl","version":"1.0"}}}'
A 200 with result.protocolVersion and result.serverInfo means the server is up and speaks this generation. If it also sent an Mcp-Session-Id header, every later request must repeat that header, and the 2025-11-25 transport rules say a server that requires sessions should answer 400 when it is missing and 404 once the server has ended the session; on a 404 the client must start over with a new initialize. So a 404 that appears after a working initialize is a session problem, not a dead server.
The client then confirms with a notifications/initialized POST, which the server answers with 202 Accepted and no body, and lists the tools, with the negotiated version in the MCP-Protocol-Version header and the session id if it got one (drop that line if initialize returned no Mcp-Session-Id):
curl -s https://example.com/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-H 'mcp-protocol-version: 2025-11-25' \
-H 'mcp-session-id: <id from the initialize response>' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
Step 4: the HTTP+SSE fallback
If POST gave you a 400, 404 or 405 without one of the modern JSON-RPC errors, the spec's fallback is a GET: curl -N -H 'accept: text/event-stream' https://example.com/mcp. A server on the 2024-11-05 transport opens a stream and sends an endpoint event with the URL to POST to. A stream with no endpoint event is a 2025-era standalone stream: stop it and go back to step 3. A 405 means a Streamable HTTP server of any revision that offers no GET stream, so your earlier POST failed for another reason: recheck the path and the body. HTML means the URL is a web page, not an MCP endpoint.
Things that break a working server
- Redirects. A server that redirects
/mcpto/mcp/, orhttptohttps, works in a browser and can fail in a client whose HTTP library turns the redirected POST into a GET, which RFC 9110 allows for301,302and303. ToolVet follows up to two redirects, only on the same host, and never from HTTPS to HTTP; a server that needs more than that counts as failed. - Proxies buffering SSE. The spec tells servers to send
X-Accel-Buffering: noon SSE responses so reverse proxies such as nginx pass events through immediately. Without it, a streamedtools/listcan sit in a buffer until the client gives up.
How ToolVet decides a server is up
Each listed remote MCP server gets a scheduled check that runs the handshake (initialize, then tools/list), falling back to the 2026-07-28 request form when a server rejects the handshake as legacy, and records the exposed tools, with the user agent ToolVetProbe/1.0 (+https://toolvet.app/methodology). A server that asks for a login or a payment counts as reachable. Two failed checks in a row open an outage; the next passing check closes it. Verified, featured and actively used listings are checked every 30 minutes, the rest every few hours, so outage timing is only as precise as the check interval. The methodology page has the details, including what happens when many listings fail in the same round.
What that produces, as of 16 October 2026, the first day of ToolVet's published daily history:
| 30-day uptime | Remote MCP servers | Share |
|---|---|---|
| 99.9% or more | 23,462 | 83.1% |
| 95% to under 99.9% | 0 | 0% |
| Under 95% | 4,771 | 16.9% |
Uptime is the share of passed checks over 30 days, so a listing with few checks moves in large steps, and the empty middle band probably reflects how young many listings' check histories are. Every listing has a status page at /status/<listing> with an Atom feed, and GET /api/v1/watch?ids= returns outages for up to 25 listings in one free call, so an agent can skip a server that is down before it tries to connect.
Frequently asked questions
Is a 401 from a remote MCP server an error?
No. It means the server answered and wants credentials, for example OAuth under the MCP authorization spec. The server is up; your client needs to complete the login flow. ToolVet counts a login or payment request as a passing check for the same reason.
How do I find out which MCP protocol version a remote server supports?
Send a 2026-07-28 request. A current server answers it, or, if it doesn't support that version, answers 400 with a JSON-RPC error of code -32022 that lists the versions it supports; the server/discover method, required of 2026-07-28 servers, returns the same list on request. An older server answers 400, 404 or 405 without a recognised modern error body: send initialize and read protocolVersion from its result.
Why does my MCP client get a 404 after connecting?
Under protocol versions 2025-03-26 to 2025-11-25 a server could assign a session in the Mcp-Session-Id header and answer 404 once that session ended; the client must then start over with a new initialize. The 2026-07-28 revision removed sessions, so if you see this you are talking to an older server, and reconnecting is the correct fix.
Does an HTTP 200 on GET mean a remote MCP server is up?
No. The current spec removed the GET stream, so a healthy 2026-07-28 server may answer GET with 405, and a 200 with HTML means you hit a web page. What proves a server is up is a POST that returns a JSON-RPC result, or a 401 or 402 asking for credentials or payment.
Can I monitor a remote MCP server without running checks myself?
If it is listed on ToolVet, its status page at /status/<listing> shows every check and has an Atom feed for outages and recoveries; if it isn't, submit it for a free check.
How we check and score explains the rest.