Connect by JBRH Open Connect

Streamable HTTP

Streamable HTTP is MCP's remote transport: a single endpoint that accepts POST, carries one JSON-RPC message per request, and may answer either with a JSON body or with an event stream on that same response. Since revision 2026-07-28 there is no session and no separate GET stream.

Status
Reference What this means
Audience
developer
Last verified
Product version
6.3.2

One endpoint, one method#

The whole transport is a URL that accepts POST. A request carries Content-Type: application/json and exactly one JSON-RPC message; the response carries the reply. Anything a client needs to say about the conversation travels with the message rather than being remembered between them, which is what makes the endpoint horizontally scalable — any process can answer any request.

The name says 'streamable' rather than 'streaming' for a reason. A server that needs to send progress notifications before its result may answer a POST with text/event-stream and write several messages on that one response. A server whose work completes immediately answers with a JSON body. Both are the same transport; the client must be prepared for either and should look at the response's content type rather than assuming.

MethodAnswer
POSTThe JSON-RPC exchange — this is the transport
GET405 with Allow: POST; the standalone stream was removed
DELETE405; there is no session to delete

The three headers, and why they must match the body#

MCP-Protocol-Version
The revision this message is written to. Mirrors params._meta["io.modelcontextprotocol/protocolVersion"].
Mcp-Method
The JSON-RPC method name, copied out of the body so a proxy can route on it without parsing.
Mcp-Name
The tool, resource or prompt being addressed. Non-ASCII values are wrapped in a =?base64?…?= sentinel, because a header field is not a good place for arbitrary text.

If a header is present and disagrees with the body, the server answers -32020 with HTTP 400. This is not fussiness. The headers exist so that middleboxes — load balancers, gateways, rate limiters, audit collectors — can act on a request without reading its body. The moment the header and the body can differ, the component that decided where a request went and the component that executed it saw two different requests, and every log downstream is describing something that did not happen.

A client that sends no headers at all is accepted: the body is authoritative. A client that sends them must send them accurately.

Cancellation, retries and idempotency#

Without sessions, cancellation is the transport's own: close the connection. There is no cancel handshake to complete and no session to tidy up afterwards. A notifications/cancelled message is accepted and answered with 202 and no body, like any notification.

Retrying is the client's decision and depends on what the call does. Every tool on Connect's public endpoint is read-only and idempotent, so a retry is safe and cheap. That is a property of these particular tools, not of the transport — a transport cannot make a write idempotent, and a client should not assume one is.

  • 429 — back off. The window is a rolling minute; retrying immediately just consumes the next slot.
  • 413 — do not retry. Send less.
  • 400 with -32020 — fix the header, then retry.
  • -32602 — do not retry. The arguments will be equally wrong.
  • A dropped connection mid-response — retry is safe here because these tools read files; for a writing endpoint you would need an idempotency key.

Does Connect use Streamable HTTP?#

Yes — it is the only transport Connect's MCP server offers, at POST /mcp. There is no stdio build and no other endpoint.

In practice every response is a JSON body rather than an event stream. The public tools read a generated documentation corpus and complete in one step, so there is no progress to report and nothing to stream. A client that handles both response shapes is still correct; it will simply never see the second one here.

Everything the revision removed is genuinely absent: no session is minted or required, Mcp-Session-Id and Last-Event-ID are ignored, and GET and DELETE answer 405 with an explanation rather than silence. Batching is refused, so one message per request is not a convention here but a rule.

POST /mcp HTTP/1.1
Host: connectbyjbrh.com
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/list

{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}

HTTP/1.1 200 OK
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"result":{"tools":[ ... ]}}

Questions#

Do I need to handle server-sent events to use Connect's MCP endpoint?

Not in practice — every response here is a JSON body. Handling both is still the correct way to write a general MCP client, because another server may answer a POST with a stream when it has progress to report.

How do I cancel a call in flight?

Close the connection. There are no sessions in this revision, so there is nothing to release afterwards. notifications/cancelled is accepted and answered with 202, but the connection closing is what actually stops the work.

Why does my proxy see 400 with code -32020?

One of MCP-Protocol-Version, Mcp-Method or Mcp-Name does not match the body. Usually something rewrote a header, or a client set Mcp-Method once and reused the connection for a different method. Send the headers that match each message, or send none.