curl -s https://www.pathwren.workers.dev/mcp-transport.json   # this page, as JSON

No key, no account, no handshake — every page here has a JSON twin one hop away. Machine doors: 6 keyless GET tools · documents.json · changes · llms.txt · openapi.json · agent card · mcp · a2a

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-stream200 text/event-streamA real stream: valid, empty, closed immediately, with the one call that needs nothing from you written into its comment lines. 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       # 200, and the stream says what to POST

What a stream-open GET returns, and why that changed

A GET that ranks text/event-stream at or above everything else is asking to open a standalone server-to-client SSE stream. Until 2026-09-03 we answered 405 Method Not Allowed. We now answer 200 with a real, empty, immediately-closed stream. Both are correct, and the change is worth writing down because the page previously argued the opposite case in full.

The specification allows either, and says so in one sentence.
“The server MUST either return Content-Type: text/event-stream in response to this HTTP GET, or else return HTTP 405 Method Not Allowed.” (2025-06-18.) The 405 clause this page used to quote — “HTTP GET or DELETE to the MCP endpoint: respond with 405 Method Not Allowed” — is addressed to “a server that supports ONLY this revision”, and this host also speaks 2025-06-18 and 2025-03-26, where the stream exists. Both answers conform; the choice was ours.
What decided it was what the 405 was the first thing a client ever saw.
In the 24 hours to 2026-09-03T00:18Z these endpoints answered 16 stream-open GETs with 405 across 5 client keys — every one of them arriving on the registry's own channel path, /c/mcp-registry-official/…. Three were that client's opening request to the endpoint (node, 08:52:59 on /mcp/robots, 09:04:41 on /mcp/doctor and /mcp/triage) and none of those three ever came back. A refusal read in context is an answer; a refusal read as the first row of a scan is a record that this endpoint refused.
The old third argument was right, and is why the stream closes at once.
These servers are stateless: every JSON-RPC reply is returned inside the POST that asked for it, so a stream held open would carry nothing for as long as the client held it. So we do not hold it open. The stream sets a 24-hour retry: — no client reconnect-loops on it — writes the one call that needs nothing from the caller into SSE comment lines, which every parser ignores and every log keeps, and closes. No client waits for a message that is never coming.

What this page said before was that “every client that has met this 405 here went on to finish its session over POST anyway.” That was true of the log it was written from and stopped being true: in the window above, undici recovered every time and node did not once. The revert condition is written into the code beside the change — if notifications/initialized per initialize does not improve and GET rows start looping, the 405 document goes back, and it is kept whole in the source for exactly that.

What the stream says

Comments, and nothing else — a client SDK sees a valid empty stream and no JSON-RPC frame it never asked for, while anything that reads bytes gets the complete first call:

curl -s https://www.pathwren.workers.dev/mcp/doctor -H 'accept: text/event-stream'

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 stream above: HEAD with accept: text/event-stream returns that 200 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 six servers: ai-crawler-index, crawler-log-triage, agent-discovery-doctor, robots-policy-lint, crawler-ip-verifier, mcp-endpoint-lint. Specification: modelcontextprotocol.io.

This page as markdown: /mcp-transport.md — the same text, no markup to strip, no JavaScript, no key, CC0. Every page here has one: add .md to any address (also .mdx, <page>.html.md, <page>.html.mdx), or send Accept: text/markdown to this one. All of them in a single index: /sitemap.md.