# `GenMCP.Transport.StreamableHTTP.V2511`
[🔗](https://github.com/lud/gen_mcp/blob/main/lib/gen_mcp/transport/streamable_http/v2511.ex#L34)

HTTP plug serving MCP clients that speak the `2025-06-18` or `2025-11-25`
protocol, on top of the stateless `2026-07-28` core.

This is a migration shim, not a second implementation. A 2025 request is
translated at this boundary into the same per-request contract
`GenMCP.Transport.StreamableHTTP` drives, so `GenMCP.Suite`, your tools, and
every other provider stay written once against the `2026-07-28` vocabulary and
never learn that a 2025 client exists.

Mount it alongside the 2026 transport, on its own path:

    scope "/mcp" do
      forward "/", GenMCP.Transport.StreamableHTTP,
        server_name: "My App",
        server_version: "1.0.0",
        tools: [MyApp.AddTool]

      forward "/2025", GenMCP.Transport.StreamableHTTP.V2511,
        server_name: "My App",
        server_version: "1.0.0",
        tools: [MyApp.AddTool]
    end

### Served methods

The surface is what a migrating client needs to keep calling tools, not
feature parity with the 2025 spec:

* `initialize` and `notifications/initialized` — the handshake.
* `ping`.
* `tools/list` and `tools/call`, including progress and log notifications on
  the POST's own SSE response.
* `GET` — the server-to-client notification stream, served by the Suite's
  subscription handler (see below).

Any other method is answered with a JSON-RPC `-32601`.

### Sessions

The 2025 protocol is stateful: `initialize` returns an `Mcp-Session-Id` that
every later request must carry. The default `GenMCP.SessionController.Token`
seals the session into the id itself, so no server-side state is introduced.
Pass `:session_controller` to store sessions yourself — see
`GenMCP.SessionController`.

A request with no `Mcp-Session-Id` after the handshake is answered `400`, and
one whose id is unknown or expired is answered `404`, which is the 2025
signal for the client to start a new session.

### The GET stream

A 2025 client opens a long-lived `GET` to receive server-initiated
notifications. That maps onto the 2026 `subscriptions/listen` request, so the
stream is served by the Suite's configured `:subscription_handler` and the
2026 subscription vocabulary is translated away on the wire.

    forward "/2025", GenMCP.Transport.StreamableHTTP.V2511,
      server_name: "My App",
      server_version: "1.0.0",
      tools: [MyApp.AddTool],
      subscription_handler: MyApp.ToolChanges

Without a subscription handler, `GET` is answered `405 Method Not Allowed`,
which the 2025 spec permits for a server that offers no server-initiated
stream.

### Options

* `:assigns` (`t:map/0`) - A map of assigns to define to the channel passed to tools. The default value is `%{}`.

* `:copy_assigns` (list of `t:atom/0`) - A list of assigns keys that will be copied from the conn to the channel. Those will overwrite the assigns from the `:assigns` option above. The default value is `[]`.

* `:allowed_origins` - Origin allowlist for DNS-rebinding protection. A request carrying an `Origin` header not in the list is rejected with 403 Forbidden. Requests without an `Origin` header (non-browser clients) are always accepted. Use `:any` to disable the check. The default value is `[]`.

* `:session_controller` - The `GenMCP.SessionController` implementation that mints and reads back session ids. The default seals the session into the id itself and stores nothing server-side. The default value is `GenMCP.SessionController.Token`.

Every other option is forwarded to the server implementation, exactly as for
`GenMCP.Transport.StreamableHTTP`.

# `call`

# `defplug`
*macro* 

Defines a named plug module delegating to this transport.

The 2025 counterpart of `GenMCP.Transport.StreamableHTTP.defplug/1`, for
routers that allow a module to be forwarded only once.

# `init`

Initializes the plug, returning the prepared transport configuration.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
