For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Streamable HTTP
Verified : Code examples on this page have been automatically tested and verified.Connect to MCP servers via streamable HTTP with automatic session management
Connect to an MCP server via streamable HTTP.
Important
Want to use agentgateway in a Kubernetes environment with the Gateway API? Check out the agentgateway on Kubernetes docs.
About streamable HTTP
Agentgateway automatically manages stateful MCP sessions when using HTTP-based transports. The session state (including backend pinning) is encoded in the session ID and persisted across requests, ensuring that subsequent tool calls in the same session are routed to the same backend server.
sequenceDiagram
participant Client
participant Agentgateway
participant MCP Server
Client->>Agentgateway: initialize (no session)
Agentgateway->>MCP Server: initialize
MCP Server-->>Agentgateway: initialized
Note over Agentgateway: Pin session to backend<br/>Encode state into session ID
Agentgateway-->>Client: Mcp-Session-Id: encrypted-state-abc123
Client->>Agentgateway: call_tool (with session ID)
Note over Agentgateway: Decode session ID<br/>Route to pinned backend
Agentgateway->>MCP Server: call_tool (same server)
MCP Server-->>Agentgateway: tool result
Agentgateway-->>Client: result
- Session initialization: When a client sends an
initializerequest, agentgateway creates a session and returns a session ID - Backend pinning: The session is pinned to a specific backend server (important when using multiple targets)
- State encoding: The session state is encoded into the session ID and encrypted with AES-256-GCM. For the key that agentgateway uses, see Session key.
- Session resumption: Subsequent requests with the same session ID are automatically routed to the same backend
If a client sends a session ID that the proxy cannot decode, the proxy returns HTTP 404, and the MCP specification requires the client to start a new session. A session ID cannot be decoded after the session key changes. For example, a session ID fails to decode after you restart the proxy with a different key, or when proxy instances behind one load balancer use different keys.
When a client closes a stateful session with DELETE /mcp, the proxy decodes the session ID before it sends close requests to upstream targets. Any replica with the same session key can close the upstream sessions. Deletion is best effort. Upstream close failures still return HTTP 202, and those upstream sessions stay open until the target’s idle timeout. If the proxy cannot rebuild a target connection from the session ID, the client receives the rebuild error.
Client POST requests to the MCP endpoint must list both application/json and text/event-stream in the Accept header. Clients can send the two media types as one comma-separated value or as separate Accept header lines. If either media type is missing, the request fails with HTTP 406.
Session key
Agentgateway encrypts session IDs with an AES-256-GCM key, which is 32 random bytes encoded as 64 hexadecimal characters. Agentgateway uses the first key that it finds in the following order.
- The
SESSION_KEYenvironment variable. - The
config.session.keyfield in your configuration file. - A key that agentgateway generates at startup, if it has a writable place to save the key. Where agentgateway saves the key depends on the storage mode.
In
hybridmode, agentgateway stores the key in the database. Every instance that shares the database uses the same key.In
filemode, agentgateway writes the key to theconfig.session.keyfield of your configuration file, and reads it from there on the next start. The generated entry looks like the following example.config: session: # Auto-generated by agentgateway to encode session IDs. Changing it invalidates existing sessions. key: 5dbc5a7ac4111369ccfb9c0d94273079033d0d40c6cb4d1a114a0e813a051b7f
If agentgateway has nowhere to save a generated key, it leaves session IDs unencrypted and logs a warning at startup. This happens in the following cases.
- You use
readOnlystorage mode. - Agentgateway cannot write to the configuration file, such as a read-only Docker mount or the ConfigMap of a Helm installation.
- You pass the configuration inline with
--config.
An unencrypted session ID is only base64-encoded, so a client can read the target names and upstream session IDs in it.
no session key configured; MCP session IDs are not encrypted. Set SESSION_KEY or config.session.keyTo set your own key, generate one and pass it in the SESSION_KEY environment variable. Set your own key when you keep your configuration file in version control, so that agentgateway does not write a secret into it. Also set your own key when more than one agentgateway instance serves the same clients without a shared database, so that every instance can decode the session IDs that the others issue.
export SESSION_KEY=$(openssl rand -hex 32)To leave session IDs unencrypted on purpose and stop the warning, set SESSION_KEY or config.session.key to disabled.
Stateless sessions
By default, agentgateway proxies streamable HTTP in stateful mode, as described in the previous section. You can instead run in stateless mode with the statefulMode field, so that agentgateway does not create a session or return an Mcp-Session-Id header. Each request is treated independently, and the client must send the full context that the request needs. This mode suits stateless agents, or MCP servers where the client handles state directly.
Note
The statefulMode field controls how agentgateway proxies session-based servers. It is separate from the newer, inherently sessionless 2026-07-28 MCP protocol, which agentgateway supports automatically through version negotiation. For more information, see MCP spec compatibility.
To use stateless mode, set statefulMode to stateless on the MCP configuration.
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
mcp:
port: 3000
statefulMode: stateless
targets:
- name: mcp
mcp:
host: http://localhost:3005/mcp/When you send an initialize request through agentgateway in stateless mode, the response returns HTTP 200 with no Mcp-Session-Id header. In the default stateful mode, the same request returns an Mcp-Session-Id header that pins the session to a backend.
Request body limits
The MCP listener applies the gateway HTTP body buffer limit to JSON-RPC request bodies from streamable HTTP and legacy Server-Sent Events (SSE) POST requests. If a request body exceeds the configured limit, the client receives HTTP 413 and a response body that includes the configured byte limit. Malformed JSON that stays within the limit receives HTTP 400.
DNS rebinding protection
A browser page on any origin can resolve a hostname it controls to 127.0.0.1 and then send requests to a locally bound server. This is a DNS rebinding attack, and it is why the MCP specification requires a server that listens on localhost to reject a Host or Origin header that does not name a loopback address.
Agentgateway is usually a proxy in front of other servers rather than a browser-facing localhost server, so this check is off by default. Turn it on with dnsRebindingProtection when you run agentgateway on a developer machine and a browser can reach the MCP port.
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
mcp:
port: 3000
dnsRebindingProtection: true
targets:
- name: mcp
mcp:
host: http://localhost:3005/mcp/When the check is on, agentgateway applies the following rules.
| Rule | Behavior |
|---|---|
| Host must be loopback | The request authority must be localhost, 127.0.0.1, or [::1]. A port is allowed. |
| Origin must be loopback | If the request carries an Origin header, it must name one of the same three hosts. |
| A missing Origin is allowed | A non-browser client and a same-origin request often omit the header, so agentgateway accepts a request that has no Origin. |
| A duplicate Origin is rejected | Origin is a singleton header. Agentgateway rejects a request that carries more than one, rather than validating only the first. |
A request that fails any rule receives a 403 response with the body MCP DNS rebinding protection: Host/Origin must be localhost, 127.0.0.1, or [::1].
Warning
Do not turn this on for an agentgateway that serves MCP to clients over the network. Every request from a remote client names a non-loopback host, so agentgateway rejects all of them.
Before you begin
Install theagentgateway binary.Configure the agentgateway
Spin up an MCP server that uses streamable HTTP.
PORT=3005 npx -y @modelcontextprotocol/server-everything streamableHttpCreate a configuration for your agentgateway to connect to your MCP server. Make sure to expose the
Mcp-Session-Idheader in the CORS configuration for session persistence.cat <<EOF > config.yaml # yaml-language-server: $schema=https://agentgateway.dev/schema/config mcp: port: 3000 policies: cors: allowOrigins: - "*" allowHeaders: - "*" exposeHeaders: - "Mcp-Session-Id" targets: - name: mcp mcp: host: http://localhost:3005/mcp/ EOFRun the agentgateway.
agentgateway -f config.yaml
Verify access to tools
Open the agentgateway UI to view your listener and backend configuration.
Connect to the MCP test server with the agentgateway UI playground.
From the navigation menu under MCP, click Tool Playground.
If you see a Browser access is not allowed notice, click Apply CORS so the playground can call the MCP listener from the UI.
Click Initialize to open an MCP session. The agentgateway UI connects to the target that you configured and lists the tools that are exposed on the target.


Verify access to a tool.
From the Tool list, select the
echotool.In the Message field, enter any string, such as
This is my first agentgateway setup., and click Call tool.Verify that the Result card shows an
HTTP 200response with your message echoed back.
