---
title: "MCP transport: the GET and HEAD leg"
description: "Every request shape the pathwren MCP endpoints accept and what each returns: GET and HEAD return the server card, HEAD mirrors GET, and a stream-open GET gets a real, empty, immediately-closed SSE stream carrying the one call that needs nothing from the caller."
canonical: "https://www.pathwren.workers.dev/mcp-transport.html"
url: "https://www.pathwren.workers.dev/mcp-transport.md"
format: "markdown"
source: "the bytes of /mcp-transport.html, in the build that wrote the page"
generator: "surfaces/ai-crawler-index/build.py"
generated: "2026-09-14T09:49:20+00:00"
license: "CC0-1.0"
---

# MCP transport: the GET and HEAD leg

> Every request shape the pathwren MCP endpoints accept and what each returns: GET and HEAD return the server card, HEAD mirrors GET, and a stream-open GET gets a real, empty, immediately-closed SSE stream carrying the one call that needs nothing from the caller.

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.

| Request | Status | Body |
| --- | --- | --- |
| `POST` JSON-RPC | `200` | The JSON-RPC result. This is the transport; everything else on this page is discovery. |
| `GET` accept: application/json | `200` | The server card: name, version, protocol versions, capabilities, tools, catalogue. |
| `GET` accept: application/json, text/event-stream;q=0.9 | `200` | The same card. JSON is ranked above the stream, so JSON is what you get — RFC 9110 §12.5.1. |
| `GET` no accept header | `200` | The same card. No Accept means anything is acceptable. |
| `HEAD` (any of the above) | mirrors the GET | Nothing. Same status, same `content-type`, same `allow`, and `content-length` of the body the GET would have sent. |
| `GET` or `HEAD` accept: text/event-stream | `200` `text/event-stream` | A real stream: valid, empty, closed immediately, with the one call that needs nothing from you written into its comment lines. See below. |
| `OPTIONS` | `204` | CORS preflight; every origin is allowed. |
| `PUT`, `PATCH`, anything else | `405` | `allow: GET, HEAD, POST, DELETE, OPTIONS` — and every method in that list works. |

```text
# 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:

```bash
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:

- On a refused method — `PUT` and friends — `allow` is the full set
`GET, HEAD, POST, DELETE, OPTIONS`, and every method in it works.
- On the stream refusal, which is a refusal of a *representation* rather than of a
method, `allow` is `POST, OPTIONS`: what works *for a client sending that Accept header*. The complete method set is still in the body under
`allow_any_accept`, and `vary: accept` marks the response as negotiated,
so nothing is hidden by the narrower header.

## 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](https://www.pathwren.workers.dev/mcp.html),
[crawler-log-triage](https://www.pathwren.workers.dev/mcp-triage.html),
[agent-discovery-doctor](https://www.pathwren.workers.dev/mcp-doctor.html),
[robots-policy-lint](https://www.pathwren.workers.dev/mcp-robots.html),
[crawler-ip-verifier](https://www.pathwren.workers.dev/mcp-netcheck.html),
[mcp-endpoint-lint](https://www.pathwren.workers.dev/mcp-lint.html). Specification:
[modelcontextprotocol.io](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http).

## Sitemap

- [Full sitemap (XML)](https://www.pathwren.workers.dev/sitemap.xml) — every page, with dates
- [Full sitemap (markdown)](https://www.pathwren.workers.dev/sitemap.md) — the same map, readable
- [llms.txt](https://www.pathwren.workers.dev/llms.txt) — the whole host in one text file
- [documents.json](https://www.pathwren.workers.dev/documents.json) — every document, with its ETag
- [A2A agents](https://www.pathwren.workers.dev/a2a.html)
- [About and method](https://www.pathwren.workers.dev/about.html)
- [API](https://www.pathwren.workers.dev/api.html)
- [/c/<channel>/](https://www.pathwren.workers.dev/c)
- [Changelog](https://www.pathwren.workers.dev/changelog.html)
- [Compliance](https://www.pathwren.workers.dev/compliance)
- [Contact](https://www.pathwren.workers.dev/contact)
- [Impressum · Anbieterkennzeichnung](https://www.pathwren.workers.dev/impressum)
- [AI Crawler Index](https://www.pathwren.workers.dev/index.html)
- [No model runs here](https://www.pathwren.workers.dev/inference.html)
- [Legal](https://www.pathwren.workers.dev/legal)
- [MCP server](https://www.pathwren.workers.dev/mcp-doctor.html)
- [MCP server](https://www.pathwren.workers.dev/mcp-lint.html)
- [MCP server](https://www.pathwren.workers.dev/mcp-markdown.html)
- [MCP server](https://www.pathwren.workers.dev/mcp-netcheck.html)
- [MCP server](https://www.pathwren.workers.dev/mcp-robots.html)
- [MCP transport: the GET and HEAD leg](https://www.pathwren.workers.dev/mcp-transport.html)
- [MCP server](https://www.pathwren.workers.dev/mcp-triage.html)
- [MCP server](https://www.pathwren.workers.dev/mcp.html)
- [Packages](https://www.pathwren.workers.dev/packages.html)
- [Pricing](https://www.pathwren.workers.dev/pricing)
- [Privacy](https://www.pathwren.workers.dev/privacy.html)
- [API reference](https://www.pathwren.workers.dev/reference)
- [Access, keys and sign-up](https://www.pathwren.workers.dev/register)
- [Security posture](https://www.pathwren.workers.dev/security.html)
- [Services](https://www.pathwren.workers.dev/services)
- [Upstream status](https://www.pathwren.workers.dev/status.html)
- [Terms of use](https://www.pathwren.workers.dev/terms.html)
- [Trust](https://www.pathwren.workers.dev/trust)

## Machine copies of this page

- [HTML (canonical)](https://www.pathwren.workers.dev/mcp-transport.html)
- [JSON](https://www.pathwren.workers.dev/mcp-transport.json)
- [Markdown](https://www.pathwren.workers.dev/mcp-transport.md) — this document

This document is a markdown rendering of [https://www.pathwren.workers.dev/mcp-transport.html](https://www.pathwren.workers.dev/mcp-transport.html), generated from that page's own bytes in the same build. The HTML page is canonical.
