Source Intelligence
Sweep 28 Aug 2026 · 00:00Z Build v2.1.250 478 read Stable v2.1.236 Latest v2.1.250 Next v2.1.251 Feeds RSS JSON llms.txt
Reading a new release v2.1.251 Analysing changes · 2/5 Deeper second pass · 0/5 agents 427 findings $12.75 so far

DisclaimerUnofficial, and not affiliated with Anthropic. Nearly all of this is read straight out of what ships: npm bundles, captured prompts, published docs. Anthropic's own notes go in verbatim, marked as theirs. The rest is my reading, and every entry carries the strings behind it. If one looks wrong, vote it down and say why.

Page history

Streamable HTTP

specification/draft/basic/transports/streamable-http

1 recorded change 731 lines First seen Last changed Upstream

History

specification/draft/basic/transports/streamable-http New page · 731 lines, new page

# Streamable HTTP ## Security & Endpoint ## Sending Messages ## Receiving Messages ## Message Flow ## Cancellation ## Request Metadata ### Protocol Version Header ### Standard Request Headers ### Custom Headers from Tool Parameters #### Schema Extension #### Value Encoding #### Client Behavior #### Server Behavior for Custom Headers ### Case Sensitivity ### Server Validation ## Backward Compatibility ### Earlier Streamable HTTP Revisions ### HTTP+SSE Transport (2024-11-05)

A whole new page. There's nothing to diff it against, so here is what it says.

# Streamable HTTP

<div id="enable-section-numbers" />

<Info>
  Streamable HTTP was introduced in protocol version 2025-03-26 as a replacement
  for the [HTTP+SSE transport][http-sse] from protocol version 2024-11-05.
</Info>

<Info>
  Revision 2026-07-28 changed the behavior of Streamable HTTP. Clients must
  ensure they handle backwards compatibility correctly. Changes included:

  * Removal of the GET stream endpoint.
  * Removal of protocol-level sessions.

  See the [changelog](/specification/draft/changelog) and
  [Backward Compatibility](#backward-compatibility) below.
</Info>

In the **Streamable HTTP** transport, the server operates as an independent
process that can handle multiple client connections. At a glance:

* The server exposes a single HTTP endpoint (the **MCP endpoint**) that
  accepts POST.
* The client sends every JSON-RPC request or notification as its own HTTP
  POST.
* The server answers each request with either a single JSON object or a
  [Server-Sent Events][sse] (SSE) stream scoped to that request, carrying
  request-related notifications followed by the final response.
* Server-to-client interactions (sampling, elicitation, roots) are embedded
  in results as input requests per
  [Multi Round-Trip Requests (MRTR)][mrtr] ([SEP-2322][sep-2322]).
* Long-lived change notifications (such as list changes and resource updates)
  are delivered on the response stream of a
  [`subscriptions/listen`][subscriptions-listen] request.

See [Message Flow](#message-flow) for sequence diagrams of these
interactions.

The server **MUST** provide a single HTTP endpoint path (hereafter referred to
as the **MCP endpoint**) that supports POST. For example, this could be a URL
like `https://example.com/mcp`.

[http-sse]: /specification/2024-11-05/basic/transports#http-with-sse

[sse]: https://en.wikipedia.org/wiki/Server-sent_events

## Security & Endpoint

When implementing Streamable HTTP transport:

1. Servers **MUST** validate the `Origin` header on all incoming connections
   to prevent DNS rebinding attacks.
   * If the `Origin` header is present and invalid, servers **MUST** respond
     with HTTP 403 Forbidden. The HTTP response body **MAY** comprise a
     JSON-RPC *error response* that has no `id`.
2. When running locally, servers **SHOULD** bind only to localhost
   (127.0.0.1) rather than all network interfaces (0.0.0.0).
3. Servers **SHOULD** implement proper authentication for all connections.

Without these protections, attackers could use DNS rebinding to interact with
local MCP servers from remote websites.

## Sending Messages

Every JSON-RPC message sent from the client **MUST** be a new HTTP POST
request to the MCP endpoint.

1. The client **MUST** use HTTP POST to send JSON-RPC messages.
2. The client **MUST** include an `Accept` header listing both
   `application/json` and `text/event-stream` as supported content types.
3. The client **MUST** include the [request metadata headers](#request-metadata)
   on each POST request.
4. The body of the HTTP POST **MUST** be a single JSON-RPC *request* or
   *notification*. The client **MUST NOT** send JSON-RPC *responses*.
5. If the body is a JSON-RPC *notification*:
   * If the server accepts it, the server **MUST** return HTTP status code
     `202 Accepted` with no body.
   * If the server cannot accept it, it **MUST** return an HTTP error status
     code (e.g., `400 Bad Request`). The HTTP response body **MAY** comprise
     a JSON-RPC *error response* that has no `id`.
6. If the body is a JSON-RPC *request*, the server **MUST** return either
   `Content-Type: application/json` (a single JSON object) or
   `Content-Type: text/event-stream` (an SSE response stream). The client
   **MUST** support both.

<Note>
  This revision of the core protocol defines no client-to-server
  *notifications* over Streamable HTTP. The only client-sent notification in
  the core protocol, `notifications/cancelled`, is used only on the
  [stdio](/specification/draft/basic/transports/stdio) transport; on
  Streamable HTTP, closing the SSE response stream is itself the cancellation
  signal and no `notifications/cancelled` message is expected (see
  [Cancellation][cancellation]). The notification rules above describe the
  transport mechanics for a notification POST; header requirements for
  notification POSTs are not defined by this revision.
</Note>

## Receiving Messages

When the server returns an SSE response stream
(`Content-Type: text/event-stream`):

* The server **MAY** send JSON-RPC *notifications* — for example,
  [`notifications/progress`][notifications-progress]
  or [`notifications/message`][notifications-message] —
  before the final response. These notifications **MUST** relate to the
  originating client request.
* The server **MUST NOT** send independent JSON-RPC *requests* on this stream.
  Server-to-client interactions (sampling, elicitation, list-roots) are
  embedded as input requests inside an
  [`InputRequiredResult`][input-required-result] per
  [MRTR][mrtr] ([SEP-2322][sep-2322]), not delivered as separate requests on
  this or any other stream. This is a change from Streamable HTTP in protocol
  versions `2025-03-26` through `2025-11-25`, where servers could send such
  requests on SSE streams.
* The final JSON-RPC *response* **SHOULD** terminate the stream.

Long-lived notification streams are obtained by sending a
[`subscriptions/listen`][subscriptions-listen]
request. The server's response is itself an SSE stream that stays open and
delivers the change notifications the client opted in to (such as
`notifications/tools/list_changed` or `notifications/resources/updated`).
Request-scoped notifications like `notifications/progress` and
`notifications/message` are **not** delivered on the listen stream — they
flow only on the response stream of the request they relate to.

When initiating an SSE stream, servers **SHOULD** include the
`X-Accel-Buffering: no` header in the HTTP response. This instructs reverse
proxies (such as nginx) to disable response buffering, ensuring that SSE
events are delivered to clients immediately rather than being held in a
buffer. Without this header, proxies may accumulate messages before sending
them to the client, introducing unwanted latency and potentially breaking the
real-time nature of SSE communication.

<Note>
  For long-lived streams — in particular the
  [`subscriptions/listen`][subscriptions-listen] response stream — servers are
  encouraged to periodically emit an SSE comment line (a line beginning with a
  colon, e.g. `:\r\n`) as a keep-alive. This keeps the connection from being
  closed by intermediaries or client idle timeouts during quiet periods when no
  notifications are flowing. Per the [SSE specification][sse], any line beginning
  with a colon is a comment that carries no event data; clients must ignore such
  lines and must not treat them as malformed input.
</Note>

Resumable SSE streams via `Last-Event-ID` are not supported.

[notifications-progress]: /specification/draft/basic/patterns/progress

[notifications-message]: /specification/draft/server/utilities/logging

[input-required-result]: /specification/draft/schema#inputrequiredresult

[mrtr]: /specification/draft/basic/patterns/mrtr

[sep-2322]: /seps/2322-MRTR

[subscriptions-listen]: /specification/draft/basic/patterns/subscriptions

## Message Flow

The following diagrams illustrate the message flows on a single MCP endpoint.

**Requests and responses.** Each request is its own POST; the server chooses
per request whether to respond with a single JSON object or an SSE stream:

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Server

    note over Client,Server: Simple response
    Client->>Server: POST tools/call (JSON-RPC request)
    Server-->>Client: 200 OK, application/json<br/>JSON-RPC response

    note over Client,Server: Streaming response
    Client->>Server: POST tools/call (JSON-RPC request)
    note over Server: Opens SSE stream<br/>scoped to this request
    Server-->>Client: SSE: notifications/progress
    Server-->>Client: SSE: notifications/progress
    Server-->>Client: SSE: JSON-RPC response
    note over Client,Server: Stream closes

    note over Client,Server: Notification
    Client->>Server: POST (JSON-RPC notification)
    Server-->>Client: 202 Accepted
```

**Server-to-client interactions (MRTR).** When the server needs input from
the client — sampling, elicitation, or roots — it does not send its own
JSON-RPC request. It returns an
[`InputRequiredResult`][input-required-result] containing `inputRequests`,
and the client retries the original request with the matching
`inputResponses` (see [Multi Round-Trip Requests][mrtr]):

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Server

    Client->>Server: POST tools/call (id: 1)
    note over Server: Needs user input or<br/>an LLM completion
    Server-->>Client: InputRequiredResult<br/>(inputRequests: elicitation/create)
    note over Client: Gathers the requested input
    Client->>Server: POST tools/call (id: 2)<br/>(original params + inputResponses)
    Server-->>Client: Final result
```

**Change notifications.** Clients that want server-initiated change
notifications open a long-lived stream with
[`subscriptions/listen`][subscriptions-listen]; the response stream stays
open and carries only the notification types the client opted in to:

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Server

    Client->>Server: POST subscriptions/listen<br/>(notification filter)
    Server-->>Client: SSE: notifications/subscriptions/acknowledged
    note over Client,Server: Stream stays open
    Server-->>Client: SSE: notifications/tools/list_changed
    Server-->>Client: SSE: notifications/resources/updated
    note over Client,Server: Until the client or server closes the stream
```

## Cancellation

Closing the SSE response stream **MUST** be treated by the server as
cancellation of that request. Because each request has its own response
stream, the transport-level disconnect is unambiguous. The server **SHOULD**
stop work on the cancelled request as soon as practical and **MUST NOT** send
any further messages for it. See
[Cancellation][cancellation] for the full rules.

[cancellation]: /specification/draft/basic/patterns/cancellation

## Request Metadata

The Streamable HTTP transport mirrors selected JSON-RPC body fields into HTTP
headers so that intermediaries (load balancers, gateways, observability
tooling) can route and inspect requests without parsing the body.

### Protocol Version Header

Every POST request to the MCP endpoint **MUST** include an
`MCP-Protocol-Version` header.

For example: `MCP-Protocol-Version: 2026-07-28`

The header value **MUST** match the
`io.modelcontextprotocol/protocolVersion` field carried in the request body's
`_meta`. If the values do not match, the server **MUST** reject the request
with `400 Bad Request` and a `HeaderMismatch` JSON-RPC error
(see [Server Validation](#server-validation)).

If the server does not implement the requested protocol version (whether the
version is unknown to the server, or is a known version the server has chosen
not to support), it **MUST** respond with `400 Bad Request` and an
[`UnsupportedProtocolVersionError`][unsupported-version]
listing its supported versions. See
[Versioning: Protocol Version Negotiation][lifecycle-version]
for the negotiation flow.

If the server does not implement the requested RPC method, it **MUST** respond
with `404 Not Found` and a JSON-RPC error with code `-32601`
(`Method not found`). The JSON-RPC error body distinguishes this case from a
`404` returned by a legacy [HTTP+SSE][http-sse] server that does not host the
modern MCP endpoint (see [Backward Compatibility](#backward-compatibility)).

A server that supports clients implementing protocol versions earlier than
`2025-06-18` (which did not define the `MCP-Protocol-Version` header) **MAY**
treat a request that omits the header as protocol version `2025-03-26`. A
server that does not support such clients **MUST** reject a request without
the header per [Server Validation](#server-validation).

[unsupported-version]: /specification/draft/schema#unsupportedprotocolversionerror

[lifecycle-version]: /specification/draft/basic/versioning#protocol-version-negotiation

### Standard Request Headers

| Header Name  | Source Field                  | Required For                                           |
| ------------ | ----------------------------- | ------------------------------------------------------ |
| `Mcp-Method` | `method`                      | All requests                                           |
| `Mcp-Name`   | `params.name` or `params.uri` | `tools/call`, `resources/read`, `prompts/get` requests |

These headers are **REQUIRED** for compliance.

If the `Mcp-Name` source value cannot be safely represented as a plain ASCII
header value, clients **MUST** encode it using the Base64 sentinel format
described in [Value Encoding](#value-encoding).

**`tools/call` request:**

```http theme={null}
POST /mcp HTTP/1.1
Content-Type: application/json

Cut at 300 lines.