MCP transport: the GET and HEAD leg

Every request shape our three MCP endpoints can be sent, what it returns, and why. Applies identically to /mcp, /mcp/triage, /mcp/doctor and to their /c/<channel>/ copies. Measured live, not asserted.

RequestStatusBody
POST JSON-RPC200The JSON-RPC result. This is the transport; everything else on this page is discovery.
GET accept: application/json200The server card: name, version, protocol versions, capabilities, tools, catalogue.
GET accept: application/json, text/event-stream;q=0.9200The same card. JSON is ranked above the stream, so JSON is what you get — RFC 9110 §12.5.1.
GET no accept header200The same card. No Accept means anything is acceptable.
HEAD (any of the above)mirrors the GETNothing. Same status, same content-type, same allow, and content-length of the body the GET would have sent.
GET or HEAD accept: text/event-stream405A JSON-RPC error explaining that this revision has no GET stream, and naming POST as the thing to do instead. See below.
OPTIONS204CORS preflight; every origin is allowed.
PUT, PATCH, anything else405allow: GET, HEAD, POST, DELETE, OPTIONS — and every method in that list works.
# check any row of that table yourself
curl -si https://www.pathwren.workers.dev/mcp/doctor -H 'accept: application/json' | head -1        # 200
curl -sI https://www.pathwren.workers.dev/mcp/doctor                                                # 200, no body
curl -si https://www.pathwren.workers.dev/mcp/doctor -H 'accept: text/event-stream' | head -1       # 405, and it says why

Why a stream-open GET is 405, and stays 405

This is the one refusal on the page, so it is the one that gets the argument in full.

A GET that ranks text/event-stream at or above everything else is asking to open a standalone server-to-client SSE stream. We answer 405 Method Not Allowed. Three reasons, in the order they decide it:

The protocol revision we implement deleted that leg.
The 2026-07-28 Streamable HTTP specification opens by listing what changed: “Removal of the GET stream endpoint. Removal of protocol-level sessions.” There is no GET stream in this version of the transport to offer. Building one would be implementing a mechanism the specification removed.
The same revision names this exact status.
“A server that supports only this revision and receives such traffic from an older client SHOULD respond as follows: HTTP GET or DELETE to the MCP endpoint: respond with 405 Method Not Allowed.” The 405 is the specified answer, not a gap.
Answering 200 would be worse for the client than refusing.
These servers are stateless: every JSON-RPC reply is returned inside the POST that asked for it, so a stream we opened would carry nothing for as long as the client held it. Returning 200 text/event-stream would trade a refusal a client can act on in one round trip for a stream it waits on for messages that are never coming. It would make our numbers look better and our clients wait longer, which is the wrong way round.

That last point is not a guess. Every client that has met this 405 here went on to finish its session over POST anyway, seconds later, on the same endpoint — the SDKs treat “no stream” as the ordinary answer it is. The refusal costs nothing, so we kept it and made it legible instead of removing it.

What the refusal says

It is a document, not a bare status. application/json, a real JSON-RPC error (so a client following the backward-compatibility algorithm does not mistake us for a pre-2025 server and retry against the deprecated HTTP+SSE transport), the reason, the clause, and the request that works:

curl -s https://www.pathwren.workers.dev/mcp/doctor -H 'accept: text/event-stream' | jq '.error.data | {how_to_call, reason, allow}'

HEAD is GET without the body

Not “HEAD returns 200” — mirrors. The endpoint builds the real GET response and removes the body, so HEAD is still correct after whatever GET does next, including the 405 above: HEAD with accept: text/event-stream returns 405 with no body. RFC 9110 §9.3.2.

Until 2026-09-01 HEAD fell through to the unsupported-method branch and returned 405 on every one of these endpoints, which cost at least one real client three failed probes before anybody owned the row.

The Allow header never contradicts the response

A 405 carrying allow: GET, … on a refused GET tells a reader that the header and the behaviour disagree, and it is the first thing an automated grader checks. So:

Protocol versions, said plainly

These servers accept 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05 for version negotiation on POST, and POST answers all of them. The mechanisms the older revisions added around the POST — the standalone GET stream, Mcp-Session-Id sessions, Last-Event-ID resumption — are not implemented, and per 2026-07-28 a session id is ignored rather than minted or echoed. If your client needs the deprecated 2024-11-05 HTTP+SSE transport specifically, it will not work here, and the 405 says so rather than letting you discover it after a handshake.

The three servers: ai-crawler-index, crawler-log-triage, agent-discovery-doctor. Specification: modelcontextprotocol.io.