SEP-2243: HTTP Header Standardization for Streamable HTTP Transport
seps/2243-http-standardization
History
seps/2243-http-standardization First recorded · 811 lines, first recorded
# SEP-2243: HTTP Header Standardization for Streamable HTTP Transport ## Abstract ## Motivation ## Specification ### Standard Headers #### Example: tools/call Request #### Example: resources/read Request #### Example: prompts/get Request #### Example: Other Request Methods #### Example: Notification ### Custom Headers from Tool Parameters #### Schema Extension #### Example: Geo-Distributed Database #### Example: Multi-Tenant SaaS Application #### Example: Priority-Based Request Handling ### Header Processing #### Value Encoding #### Client Behavior #### Server Behavior ## Rationale ### Headers vs Path ### Infrastructure Support ### Explicit Header Names in x-mcp-header ### Placement Within JSON Schema ### Scope: Tools Only ### No Specification-Level Header Size Limit ### Encoding Approach for Unsafe Values ## Backward Compatibility ### Standard Headers ### Custom Headers from Tool Parameters ## Security Implications ### Header Injection ### Header Spoofing ### Information Disclosure ### Trusting Header Values ## Conformance Test Cases ### Standard Header Edge Cases #### Case Sensitivity #### Header/Body Mismatch #### Special Characters in Values ### Custom Header Edge Cases #### x-mcp-header Name Conflicts #### Invalid x-mcp-header Values #### Value Encoding Edge Cases #### Type Restriction Violations ### Server Validation Edge Cases #### Base64 Decoding #### Null and Missing Values #### Missing Custom Header with Value in Body ## Reference Implementation ## Changes since SEP became Final
The first capture of this source. The page was already there, and this is what it said.
# SEP-2243: HTTP Header Standardization for Streamable HTTP Transport
> HTTP Header Standardization for Streamable HTTP Transport
<div className="flex items-center gap-2 mb-4">
<Badge color="green" shape="pill">
Final
</Badge>
<Badge color="gray" shape="pill">
Standards Track
</Badge>
</div>
<Note>
This SEP has reached Final status and is preserved as a historical record of
the design as accepted. Changes made to the protocol after finalization are
not reflected here. Refer to the [current
specification](/specification/latest) and its changelog for authoritative
requirements.
</Note>
| Field | Value |
| ------------- | ------------------------------------------------------------------------------- |
| **SEP** | 2243 |
| **Title** | HTTP Header Standardization for Streamable HTTP Transport |
| **Status** | Final |
| **Type** | Standards Track |
| **Created** | 2026-02-04 |
| **Author(s)** | MCP Transports Working Group |
| **Sponsor** | None |
| **PR** | [#2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243) |
***
## Abstract
This SEP proposes exposing critical routing and context information in standard HTTP header locations for the Streamable HTTP transport. By mirroring key fields from the JSON-RPC payload into HTTP headers, network intermediaries such as load balancers, proxies, and observability tools can route and process MCP traffic without deep packet inspection, reducing latency and computational overhead.
## Motivation
Current MCP implementations over HTTP bury all routing information within the JSON-RPC payload. This creates friction for network infrastructure:
* **Load balancers** must terminate TLS and parse the entire JSON body to extract routing information (e.g., region, tool name)
* **Proxies and gateways** cannot make routing decisions without deep packet inspection
* **Observability tools** have limited visibility into MCP traffic patterns
* **Rate limiters and WAFs** cannot apply policies based on MCP-specific fields
By exposing key fields in HTTP headers, we enable standard network infrastructure to work with MCP traffic using existing, well-supported mechanisms.
## Specification
### Standard Headers
The Streamable HTTP transport will require POST requests to include the following headers mirrored from the request body:
| Header Name | Source Field | Required For |
| ------------ | ----------------------------- | ------------------------------------------------------ |
| `Mcp-Method` | `method` | All requests and notifications |
| `Mcp-Name` | `params.name` or `params.uri` | `tools/call`, `resources/read`, `prompts/get` requests |
These headers are **required** for compliance with the MCP version in which they are introduced.
**Server Behavior**: Servers that process the request body MUST reject requests where the values specified in the headers do not match the values in the request body.
> **Rationale**: This requirement prevents potential security vulnerabilities and error conditions that could arise when different components in the network rely on different sources of truth. For example, a load balancer or gateway might use the header values to make routing decisions, while the MCP server uses the body values for execution. This requirement applies to any network intermediary that processes the message body, as well as the MCP server itself.
> **Implementation Note**: When validating integer parameter values, servers SHOULD compare the header value and the body value numerically rather than as strings (e.g., `42.0` and `42` are considered equal).
**Case Sensitivity**: Header names (called "field names" in [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-field-names)) are case-insensitive. Clients and servers MUST use case-insensitive comparisons for header names.
#### Example: tools/call Request
```http theme={null}
POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c
Mcp-Method: tools/call
Mcp-Name: get_weather
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"location": "Seattle, WA"
}
}
}
```
#### Example: resources/read Request
```http theme={null}
POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c
Mcp-Method: resources/read
Mcp-Name: file:///projects/myapp/config.json
{
"jsonrpc": "2.0",
"id": 2,
"method": "resources/read",
"params": {
"uri": "file:///projects/myapp/config.json"
}
}
```
#### Example: prompts/get Request
```http theme={null}
POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c
Mcp-Method: prompts/get
Mcp-Name: code_review
{
"jsonrpc": "2.0",
"id": 3,
"method": "prompts/get",
"params": {
"name": "code_review",
"arguments": {
"language": "python"
}
}
}
```
#### Example: Other Request Methods
For requests that don't involve tools, resources, or prompts, only the `Mcp-Method` header is required:
```http theme={null}
POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Method: initialize
{
"jsonrpc": "2.0",
"id": 4,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {
"name": "ExampleClient",
"version": "1.0.0"
}
}
}
```
#### Example: Notification
Notifications also require the `Mcp-Method` header:
```http theme={null}
POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c
Mcp-Method: notifications/initialized
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
```
### Custom Headers from Tool Parameters
MCP servers MAY designate specific tool parameters to be mirrored into HTTP headers using an `x-mcp-header` extension property in the parameter's schema within the tool's `inputSchema`.
**Client Requirement**: While the use of `x-mcp-header` is optional for servers, clients MUST support this feature. When a server's tool definition includes `x-mcp-header` annotations, conforming clients MUST mirror the designated parameter values into HTTP headers as specified in this document.
#### Schema Extension
The `x-mcp-header` property specifies the name portion used to construct the header name `Mcp-Param-{name}`.
**Constraints on `x-mcp-header` values**:
* MUST NOT be empty
* MUST match HTTP field-name token syntax (`1*tchar`, [RFC 9110 Section 5.1](https://datatracker.ietf.org/doc/html/rfc9110#section-5.1))
* MUST NOT contain control characters, including carriage return (CR, `\r`) or line feed (LF, `\n`)
* MUST be case-insensitively unique among all `x-mcp-header` values in the `inputSchema`
* MUST only be applied to parameters with primitive types (integer, string, boolean). Parameters with type `number` are not permitted. Integer values MUST be within the safe range for JavaScript (−2^53+1 to 2^53−1)
* MAY be applied to properties at any nesting depth within the `inputSchema`, not only top-level properties
Clients using the Streamable HTTP transport MUST reject tool definitions where any `x-mcp-header` value violates these constraints. Rejection means the client MUST exclude the invalid tool from the result of `tools/list`. Clients SHOULD log a warning when rejecting a tool definition, including the tool name and the reason for rejection. This behavior ensures that a single malformed tool definition does not prevent other valid tools from being used. Clients using other transports (e.g., stdio) MAY ignore `x-mcp-header` annotations entirely.
**Example Tool Definition**:
```json theme={null}
{
"name": "execute_sql",
"description": "Execute SQL on Google Cloud Spanner",
"inputSchema": {
"type": "object",
"properties": {
"region": {
"type": "string",
"description": "The region to execute the query in",
"x-mcp-header": "Region"
},
"query": {
"type": "string",
"description": "The SQL query to execute"
}
},
"required": ["region", "query"]
}
}
```
#### Example: Geo-Distributed Database
Consider a server exposing an `execute_sql` tool for Google Cloud Spanner, which requires a `region` parameter.
**Tool Definition**:
```json theme={null}
{
"name": "execute_sql",
"description": "Execute SQL on Google Cloud Spanner",
"inputSchema": {
"type": "object",
"properties": {
"region": {
"type": "string",
"description": "The region to execute the query in",
"x-mcp-header": "Region"
},
"query": {
"type": "string",
"description": "The SQL query to execute"
}
},
"required": ["region", "query"]
}
}
```
**Scenario**: A client requests to execute SQL in `us-west1`.
**Current Friction**: The global load balancer receives the request but must terminate TLS and parse the entire JSON body to find `"region": "us-west1"` before it knows whether to route the packet to the Oregon or Belgium cluster.
**With This Proposal**: The client detects the `x-mcp-header` annotation and automatically adds the header `Mcp-Param-Region: us-west1` to the HTTP request. The load balancer can now route based on the header without parsing the body.
**Request**:
```http theme={null}
POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c
Mcp-Method: tools/call
Mcp-Name: execute_sql
Mcp-Param-Region: us-west1
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "execute_sql",
"arguments": {
"region": "us-west1",
"query": "SELECT * FROM users"
}
}
}
```
#### Example: Multi-Tenant SaaS Application
A SaaS platform exposes tools that operate on different customer tenants. By exposing the tenant ID in a header, the platform can route requests to tenant-specific infrastructure.
**Tool Definition**:
```json theme={null}
{
"name": "query_analytics",
"description": "Query analytics data for a tenant",
"inputSchema": {
"type": "object",
"properties": {
"tenant_id": {
"type": "string",
"description": "The tenant identifier",
"x-mcp-header": "TenantId"
},
"metric": {
"type": "string",
"description": "The metric to query"
},
"start_date": {
Cut at 300 lines.