FinalStandards Track
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
Specification
Standard Headers
The Streamable HTTP transport will require POST requests to include the following headers mirrored from the request body:
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.,Case Sensitivity: Header names (called “field names” in RFC 9110) are case-insensitive. Clients and servers MUST use case-insensitive comparisons for header names.42.0and42are considered equal).
Example: tools/call Request
Example: resources/read Request
Example: prompts/get Request
Example: Other Request Methods
For requests that don’t involve tools, resources, or prompts, only theMcp-Method header is required:
Example: Notification
Notifications also require theMcp-Method header:
Custom Headers from Tool Parameters
MCP servers MAY designate specific tool parameters to be mirrored into HTTP headers using anx-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
Thex-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) - MUST NOT contain control characters, including carriage return (CR,
\r) or line feed (LF,\n) - MUST be case-insensitively unique among all
x-mcp-headervalues in theinputSchema - MUST only be applied to parameters with primitive types (integer, string, boolean). Parameters with type
numberare 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
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:
Example: Geo-Distributed Database
Consider a server exposing anexecute_sql tool for Google Cloud Spanner, which requires a region parameter.
Tool Definition:
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:
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:Example: Priority-Based Request Handling
A server can expose a priority parameter to allow infrastructure to prioritize certain requests. Tool Definition:Header Processing
Value Encoding
Clients MUST encode parameter values before including them in HTTP headers to ensure safe transmission and prevent injection attacks. Character Restrictions Per RFC 9110, HTTP header field values must consist of visible ASCII characters (0x21-0x7E), space (0x20), and horizontal tab (0x09). The following characters are explicitly prohibited:- Carriage return (
\r, 0x0D) - Line feed (
\n, 0x0A) - Null character (
\0, 0x00) - Any character outside the ASCII range (> 0x7F)
- Starts with a space (0x20) or horizontal tab (0x09)
- Ends with a space (0x20) or horizontal tab (0x09)
-
Type conversion: Convert the parameter value to its string representation:
string: Use the value as-isinteger: Convert to decimal string representation (e.g.,42,-7)boolean: Convert to lowercase"true"or"false"
-
Whitespace check: If the string starts or ends with whitespace (space or tab):
- Apply Base64 encoding (see below)
-
ASCII validation: Check if the string contains only valid ASCII characters (0x20-0x7E):
- If valid, proceed to step 4
- If invalid (contains non-ASCII characters), apply Base64 encoding (see below)
-
Control character check: If the string contains any control characters (0x00-0x1F or 0x7F):
- Apply Base64 encoding (see below)
=?base64? and suffix ?= indicate that the value is Base64-encoded. These markers are case-sensitive and MUST appear exactly as shown (lowercase). Servers and intermediaries that need to inspect these values MUST decode them accordingly.
To avoid ambiguity, clients MUST also Base64-encode any plain-ASCII value that matches the sentinel pattern (i.e., starts with =?base64? and ends with ?=).
Examples:
Client Behavior
When constructing atools/call request via HTTP transport, the client MUST:
- Extract the values for any standard headers from the request body (e.g.,
method,params.name,params.uri) - Append the
Mcp-Methodheader and, if applicable,Mcp-Nameheader to the request - Inspect the tool’s
inputSchemafor properties marked withx-mcp-headerand extract the value for each parameter - Encode the values according to the rules in Value Encoding
- Append a
Mcp-Param-{Name}: {Value}header to the request:
Implementation Note: Clients MUST constructMcp-Param-*headers using the most recently obtainedinputSchemafor the tool. A client that has never obtained the tool’sinputSchemaSHOULD send the request withoutMcp-Param-*headers. If the server rejects the request because requiredMcp-Param-*headers are missing or do not match the body, the client SHOULD calltools/listto obtain the currentinputSchema, then retry the original request with the appropriate headers. Clients MAY pre-load tool definitions via other means (e.g., from a previous session or configuration) to enable header emission without a priortools/listcall.
Server Behavior
When receiving a request, the server MUST reject requests withMcp-Param-{Name} headers that contain invalid characters (see “Character Restrictions” in the Value Encoding section).
Any server that processes the message body (not simply forwarding it) MUST validate that encoded header values, after decoding if Base64-encoded, match the corresponding values in the request body. Servers MUST reject requests with a 400 Bad Request HTTP status if any validation fails.
Error Code
When rejecting a request due to header validation failure, servers MUST return a JSON-RPC error response with the following error code:
This error code is in the JSON-RPC implementation-defined server error range (
-32000 to -32099).
Error Response Format:
- A required standard header (
Mcp-Method,Mcp-Name, etc.) is missing - A header value does not match the request body value
- A Base64-encoded value cannot be decoded
- A header value contains invalid characters
Note: Intermediaries MUST return an appropriate HTTP error status (e.g., 400 Bad Request) for validation failures but are not required to return a JSON-RPC error response.
Note: Intermediaries that enforce policy based on mirrored headers (e.g., routing or rate-limiting by tenant) SHOULD verify that the MCP-Protocol-Version header indicates a version that requires header–body validation. If the version is older or the header is absent, the intermediary SHOULD reject the request rather than trusting unvalidated header values.
Custom Header Handling:
Custom headers (those defined via x-mcp-header) follow the same validation rules as standard headers:
When rejecting requests due to missing or invalid custom headers, the server MUST return HTTP status
400 Bad Request with JSON-RPC error code -32001 (HeaderMismatch).
Rationale
Headers vs Path
This proposal mirrors request data into headers rather than encoding it in the URL path. Advantages of Headers:- Simplicity: All widely-used network load balancers support routing based on HTTP headers
- Multi-version support: Easier to support multiple MCP versions in clients and servers
- Compatibility: Headers work with the existing Streamable HTTP transport design without changing the endpoint structure
- Unlimited values: Header values can contain characters that would require encoding in URLs (e.g.,
/,?,#) - No URL length limits: Very long values can be transmitted without hitting URL length restrictions
- Framework simplicity: Many web frameworks (Flask, Express, Django, Rails) have built-in support for path-based routing with minimal configuration
- Logging: URL paths are typically logged by default, making debugging easier
For frameworks like Flask that strongly favor path-based routing, implementing header-based routing requires additional code:
- Backwards Compatibility introducing path based routing would require all existing MCP Servers to take a major update, and potentially support two sets of endpoints to support multiple versions. Even if the SDKs can paper over this additional operational concerns like testing, metrics, etc would need to happen. Header based routing requires minimal client side changes. And clients which don’t opt in will still function correctly.
- Infrastructure benefits outweigh framework complexity: The primary goal is enabling network infrastructure (load balancers, proxies, WAFs) to route and process requests without body parsing. This benefit applies regardless of the server framework.
Infrastructure Support
HTTP header-based routing and processing is supported by:- Load Balancers: All major load balancers (HAProxy, NGINX, Cloudflare, F5, Envoy/Istio)
- Rate Limiting: 9 of 11 popular rate-limiting solutions
- Authorization: Kong, Tyk, AWS API Gateway, Google Cloud Apigee, Azure API Gateway, NGINX, Apache APISIX, Istio/Envoy
- Web Application Firewalls: Cloudflare WAF, AWS WAF, Azure WAF, F5 Advanced WAF, FortiWeb, Imperva WAF, Barracuda WAF, ModSecurity, Akamai, Wallarm
- Observability: Most observability solutions can extract data from HTTP headers
Explicit Header Names in x-mcp-header
The design uses an explicit name value inx-mcp-header rather than deriving the header name from the parameter name because:
- Case sensitivity mismatch: Header names are case-insensitive, but JSON Schema property names are case-sensitive
- Character set constraints: Header names are limited to ASCII characters, but tool parameter names may contain arbitrary Unicode
- Simplicity: No complex scheme needed for constructing header names from nested properties
Placement Within JSON Schema
Thex-mcp-header extension is placed directly within the JSON Schema of the property to be mirrored, rather than in a separate metadata field outside the schema. This design choice offers several advantages:
- Co-location: The header mapping is defined alongside the property it affects, making it immediately clear which parameter will be mirrored. Developers don’t need to cross-reference between the schema and a separate metadata structure.
-
Established pattern: JSON Schema explicitly supports extension keywords (properties starting with
x-), and this pattern is widely used in ecosystems like OpenAPI. Tool authors and SDK developers are already familiar with this approach. -
Schema composability: When schemas are composed, extended, or referenced using
$ref, thex-mcp-headerannotation travels with the property definition. A separate metadata structure would require complex synchronization logic to maintain consistency. -
Tooling compatibility: Existing JSON Schema validators ignore unknown keywords by default, so adding
x-mcp-headerdoesn’t break existing schema validation. Tools that don’t understand this extension simply skip it. - Reduced complexity: A separate metadata structure would require defining a mapping mechanism (e.g., JSON Pointer or property paths) to associate headers with properties, adding implementation complexity and potential for errors.
Scope: Tools Only
Thex-mcp-header mechanism currently applies only to tools/call requests because tools are the only MCP primitive with an inputSchema that supports JSON Schema extension keywords. Resources and prompts lack an equivalent schema structure: resources/read takes only a uri (already exposed via Mcp-Name), and prompts/get defines arguments as a simple {name, description, required} array without JSON Schema extensibility. Generalizing custom header mapping to these primitives would require adding inputSchema-style definitions to resources and prompts, which is a larger specification change. This is noted as a potential future extension.
No Specification-Level Header Size Limit
This specification intentionally does not define limits on individual header value length, total MCP header size, or number of custom headers. Headers are solely an HTTP concept, and HTTP itself (RFC 9110) does not specify header size limits. Common HTTP infrastructure imposes its own limits — ranging from 4–8 KB on some servers (e.g., Apache at ~8190 bytes) to 128 KB on others (e.g., Cloudflare) — but the appropriate limit depends on the deployment environment, which only the service operator can determine. Defining a specification-level limit (such as “omit headers exceeding 8192 bytes”) would introduce problems:- Arbitrary threshold: Any chosen value would be too low for some deployments and irrelevant for others. The “right” limit varies by infrastructure.
- Counterproductive omission: If a client omits a header because it exceeds a spec-defined limit, servers and intermediaries that rely on that header for routing must either parse the body or reject the request — undermining the core purpose of exposing values in headers.
- Unnecessary SDK burden: SDK maintainers would need to implement and test limit-checking logic for a constraint that rarely applies in practice.
- Redundant with HTTP: Servers and intermediaries already reject oversized headers using standard HTTP status codes (
413 Request Entity Too Large,431 Request Header Fields Too Large), which clients must handle regardless.
Note to implementers: Servers, intermediaries, and clients MAY independently impose limits on individual header size, total MCP header size, or number of custom headers as appropriate for their deployment environment. Servers SHOULD document any limits they impose. Clients SHOULD gracefully handle413 Request Entity Too Largeor431 Request Header Fields Too Largeresponses. Tool authors SHOULD limitx-mcp-headerannotations to parameters that provide clear infrastructure benefits.
Encoding Approach for Unsafe Values
Four approaches were considered for encoding parameter values that cannot be safely represented as plain ASCII header values (non-ASCII characters, leading/trailing whitespace, control characters):-
Sentinel wrapping (chosen approach): Use the
=?base64?{value}?=prefix/suffix within the sameMcp-Param-{Name}header to signal Base64-encoded values. -
Separate header name: Use a distinct header name for encoded values, e.g.
Mcp-ParamEncoded-{Name}, so the encoding is indicated by the header name rather than the value format. -
Implicit encoding: Let the parser infer encoding from the tool schema, e.g. via a
"x-mcp-header-encoding": "base64"annotation in the tool definition. -
Always encode: Base64-encode every
Mcp-Param-{Name}value unconditionally.
Conclusion: The sentinel wrapping approach provides the best trade-off. The primary use case for custom headers is enabling intermediaries to route and filter on simple, readable values like region names and tenant IDs — these are invariably plain ASCII and never trigger Base64 encoding. Option 4 makes all values opaque to intermediaries. Option 3 leaves intermediaries unable to distinguish encoded from literal values without access to the tool schema. Option 2 eliminates in-band ambiguity but doubles the header namespace, requiring intermediaries to check two possible header names per parameter and adding a conflict rule when both are present. The theoretical collision risk of the sentinel in Option 1 is negligible since
=?base64?...?= is an unlikely literal parameter value in practice.
Backward Compatibility
Standard Headers
Existing clients and SDKs will be required to include the standard headers when using the new MCP version. This is a minor addition since clients already include headers likeMcp-Protocol-Version, adding only one or two new headers per message.
Servers implementing the new version MUST reject requests missing required headers. Servers MAY support older clients by accepting requests without headers when negotiating an older protocol version.
Custom Headers from Tool Parameters
Thex-mcp-header extension is optional for servers. Existing tools without this property continue to work unchanged. However, clients implementing the MCP version that includes this specification MUST support the feature. Older clients that do not support x-mcp-header will still function but will not provide the header-based routing benefits that servers may depend on.
Security Implications
Header Injection
Header injection attacks occur when malicious values containing control characters (especially\r\n) are included in headers, potentially allowing attackers to inject additional headers or terminate the header section early.
Clients MUST follow the Value Encoding rules defined in this specification. These rules ensure that:
- Control characters are never included in header values
- Non-ASCII values are safely encoded using Base64
- Values exceeding safe length limits are omitted
Header Spoofing
Servers MUST validate that header values match the corresponding values in the request body. This prevents clients from sending mismatched headers to manipulate routing while executing different operations. For example, a malicious client could attempt to:- Route a request to a less-secured region while executing operations intended for a high-security region
- Bypass rate limits by spoofing tenant identifiers
- Evade security policies by misrepresenting the operation being performed
Information Disclosure
Tool parameter values designated for headers will be visible to network intermediaries (load balancers, proxies, logging systems). Server developers:- SHOULD NOT mark sensitive parameters (passwords, API keys, tokens, PII) with
x-mcp-header - SHOULD document which parameters are exposed as headers
- SHOULD consider that Base64 encoding provides no confidentiality—it is merely an encoding, not encryption
Trusting Header Values
Header values originate from tool call arguments, which may be influenced by an LLM or a malicious client. Intermediaries and servers MUST NOT treat these values as trusted input for security-sensitive decisions. In particular:- Header values that imply access to specific resources (e.g., tenant IDs, region names) MUST be independently verified against the authenticated user’s permissions before granting access to those resources.
- Header values MUST NOT be used as the sole basis for granting elevated privileges without server-side enforcement of rate limits and quotas.
- Deployments SHOULD reject requests with oversized or excessive headers early in the pipeline — before performing Base64 decoding or body parsing — to mitigate denial-of-service risks from crafted payloads.
Conformance Test Cases
This section defines edge cases that conformance tests MUST cover to ensure interoperability between implementations.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
To be provided before this SEP reaches Final status. Implementation requirements:- Server SDKs: Provide a mechanism (attribute/decorator) for marking parameters with
x-mcp-header - Client SDKs: Implement the client behavior for extracting and encoding header values
- Validation: Both sides must validate header/body consistency