Skip to main content
DraftStandards Track
FieldValue
SEP2243
TitleHTTP Header Standardization for Streamable HTTP Transport
StatusDraft
TypeStandards Track
Created2026-02-04
Author(s)MCP Transports Working Group
SponsorNone
PR#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 NameSource FieldRequired For
Mcp-MethodmethodAll requests and notifications
Mcp-Nameparams.name or params.uritools/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.
Case Sensitivity: Header names (called “field names” in RFC 9110) are case-insensitive. Clients and servers MUST use case-insensitive comparisons for header names.

Example: tools/call Request

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

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

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:
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:
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 contain only ASCII characters (excluding space and :)
  • MUST be case-insensitively unique among all x-mcp-header values in the inputSchema
  • MUST only be applied to parameters with primitive types (number, string, boolean)
Clients 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. Example Tool Definition:
{
  "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:
{
  "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:
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:
{
  "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": {
        "type": "string",
        "description": "Start date for the query range"
      },
      "end_date": {
        "type": "string",
        "description": "End date for the query range"
      }
    },
    "required": ["tenant_id", "metric", "start_date", "end_date"]
  }
}
Request:
POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c
Mcp-Method: tools/call
Mcp-Name: query_analytics
Mcp-Param-TenantId: acme-corp

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "query_analytics",
    "arguments": {
      "tenant_id": "acme-corp",
      "metric": "page_views",
      "start_date": "2026-01-01",
      "end_date": "2026-01-31"
    }
  }
}

Example: Priority-Based Request Handling

A server can expose a priority parameter to allow infrastructure to prioritize certain requests. Tool Definition:
{
  "name": "generate_report",
  "description": "Generate a complex report",
  "inputSchema": {
    "type": "object",
    "properties": {
      "report_type": {
        "type": "string",
        "description": "Type of report to generate"
      },
      "priority": {
        "type": "string",
        "description": "Request priority: low, normal, or high",
        "x-mcp-header": "Priority"
      }
    },
    "required": ["report_type"]
  }
}
Request:
POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Session-Id: 1f3a4b5c-6d7e-8f9a-0b1c-2d3e4f5a6b7c
Mcp-Method: tools/call
Mcp-Name: generate_report
Mcp-Param-Priority: high

{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "tools/call",
  "params": {
    "name": "generate_report",
    "arguments": {
      "report_type": "quarterly_summary",
      "priority": "high"
    }
  }
}

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)
Whitespace Handling HTTP parsers typically trim leading and trailing whitespace from header values. To preserve leading and trailing spaces in parameter values, clients MUST use Base64 encoding when the value:
  • Starts with a space (0x20) or horizontal tab (0x09)
  • Ends with a space (0x20) or horizontal tab (0x09)
Encoding Rules Clients MUST apply the following encoding rules in order:
  1. Type conversion: Convert the parameter value to its string representation:
    • string: Use the value as-is
    • number: Convert to decimal string representation (e.g., 42, 3.14)
    • boolean: Convert to lowercase "true" or "false"
  2. Whitespace check: If the string starts or ends with whitespace (space or tab):
    • Apply Base64 encoding (see below)
  3. 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)
  4. Control character check: If the string contains any control characters (0x00-0x1F or 0x7F):
    • Apply Base64 encoding (see below)
Base64 Encoding for Unsafe Values When a value cannot be safely represented as a plain ASCII header value, clients MUST use Base64 encoding of the UTF-8 representation of the value with the following format:
Mcp-Param-{Name}: =?base64?{Base64EncodedValue}?=
The prefix =?base64? and suffix ?= indicate that the value is Base64-encoded. Servers and intermediaries that need to inspect these values MUST decode them accordingly. Examples:
Original ValueReasonEncoded Header Value
"us-west1"Plain ASCIIMcp-Param-Region: us-west1
"Hello, 世界"Contains non-ASCIIMcp-Param-Greeting: =?base64?SGVsbG8sIOS4lueVjA==?=
" padded "Leading/trailing spacesMcp-Param-Text: =?base64?IHBhZGRlZCA=?=
"line1\nline2"Contains newlineMcp-Param-Text: =?base64?bGluZTEKbGluZTI=?=

Client Behavior

When constructing a tools/call request via HTTP transport, the client MUST:
  1. Extract the values for any standard headers from the request body (e.g., method, params.name, params.uri)
  2. Append the Mcp-Method header and, if applicable, Mcp-Name header to the request
  3. Inspect the tool’s inputSchema for properties marked with x-mcp-header and extract the value for each parameter
  4. Encode the values according to the rules in Value Encoding
  5. Append a Mcp-Param-{Name}: {Value} header to the request:

Server Behavior

When receiving a request, the server MUST reject requests with Mcp-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:
CodeNameDescription
-32001HeaderMismatchThe HTTP headers do not match the corresponding values in the request body, or required headers are missing/malformed.
This error code is in the JSON-RPC implementation-defined server error range (-32000 to -32099). Error Response Format:
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32001,
    "message": "Header mismatch: Mcp-Name header value 'foo' does not match body value 'bar'"
  }
}
Validation Failure Conditions:
  • 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.
Custom Header Handling: Custom headers (those defined via x-mcp-header) follow the same validation rules as standard headers:
ScenarioClient BehaviorServer Behavior
Parameter value providedClient MUST include the headerServer MUST validate header matches body
Parameter value is nullClient MUST omit the headerServer MUST NOT expect the header
Parameter not in argumentsClient MUST omit the headerServer MUST NOT expect the header
Client omits header but value is in bodyNon-conforming clientServer MUST reject the request
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:
  1. Simplicity: All widely-used network load balancers support routing based on HTTP headers
  2. Multi-version support: Easier to support multiple MCP versions in clients and servers
  3. Compatibility: Headers work with the existing Streamable HTTP transport design without changing the endpoint structure
  4. Unlimited values: Header values can contain characters that would require encoding in URLs (e.g., /, ?, #)
  5. No URL length limits: Very long values can be transmitted without hitting URL length restrictions
Advantages of Path-based Routing:
  1. Framework simplicity: Many web frameworks (Flask, Express, Django, Rails) have built-in support for path-based routing with minimal configuration
  2. Logging: URL paths are typically logged by default, making debugging easier
Trade-offs and Framework Considerations:
FrameworkHeader-based RoutingPath-based Routing
Flask (Python)Requires middleware or decorators to extract headers before routingNative support via @app.route('/mcp/<method>')
Express (Node.js)Easy via req.headers but requires custom routing logicNative support via app.post('/mcp/:method')
Django (Python)Requires custom middlewareNative URL patterns
Go (net/http)Easy via r.Header.Get()Native via path patterns
ASP.NET CoreEasy via [FromHeader] attributeNative via route templates
For frameworks like Flask that strongly favor path-based routing, implementing header-based routing requires additional code:
# Flask example: Header-based routing requires manual dispatch
@app.route('/mcp', methods=['POST'])
def mcp_handler():
    method = request.headers.get('Mcp-Method')
    if method == 'tools/call':
        return handle_tools_call(request)
    elif method == 'resources/read':
        return handle_resources_read(request)
    # ... etc
Despite this additional complexity in some frameworks, header-based routing was chosen because:
  1. 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.
  2. 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 in x-mcp-header rather than deriving the header name from the parameter name because:
  1. Case sensitivity mismatch: Header names are case-insensitive, but JSON Schema property names are case-sensitive
  2. Character set constraints: Header names are limited to ASCII characters, but tool parameter names may contain arbitrary Unicode
  3. Simplicity: No complex scheme needed for constructing header names from nested properties

Placement Within JSON Schema

The x-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:
  1. 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.
  2. 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.
  3. Schema composability: When schemas are composed, extended, or referenced using $ref, the x-mcp-header annotation travels with the property definition. A separate metadata structure would require complex synchronization logic to maintain consistency.
  4. Tooling compatibility: Existing JSON Schema validators ignore unknown keywords by default, so adding x-mcp-header doesn’t break existing schema validation. Tools that don’t understand this extension simply skip it.
  5. 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

The x-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:
  1. Arbitrary threshold: Any chosen value would be too low for some deployments and irrelevant for others. The “right” limit varies by infrastructure.
  2. 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.
  3. Unnecessary SDK burden: SDK maintainers would need to implement and test limit-checking logic for a constraint that rarely applies in practice.
  4. 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 handle 413 Request Entity Too Large or 431 Request Header Fields Too Large responses. Tool authors SHOULD limit x-mcp-header annotations 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):
  1. Sentinel wrapping (chosen approach): Use the =?base64?{value}?= prefix/suffix within the same Mcp-Param-{Name} header to signal Base64-encoded values.
  2. 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.
  3. 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.
  4. Always encode: Base64-encode every Mcp-Param-{Name} value unconditionally.
ApproachProsCons
Sentinel wrappingSingle header name per parameter; common case (plain ASCII) is human-readable; intermediaries can route on plain values without decodingIn-band signaling can theoretically collide with literal values; every reader must check for the prefix
Separate header nameNo in-band ambiguity; encoding is self-documenting from the header nameDoubles the header namespace; every intermediary must check two header names per parameter; needs a conflict rule if both are present
Implicit encodingSimplest wire format; no sentinels or extra headersIntermediaries need access to the tool schema to know whether to decode — defeats the purpose of exposing values in headers; static per-parameter decision doesn’t handle the mixed case well
Always encodeSimplest rules; no conditional logic or ambiguityPlain ASCII values become unreadable; intermediaries must decode Base64 to inspect any value, significantly undermining the core motivation of this SEP
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 like Mcp-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

The x-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

Test CaseInputExpected Behavior
Header name case variationmcp-method: tools/callServer MUST accept (header names are case-insensitive)
Header name mixed caseMCP-METHOD: tools/callServer MUST accept
Method value caseMcp-Method: TOOLS/CALLServer MUST reject (method values are case-sensitive)

Header/Body Mismatch

Test CaseHeader ValueBody ValueExpected Behavior
Method mismatchMcp-Method: tools/call"method": "prompts/get"Server MUST reject with 400 and error code -32001
Tool name mismatchMcp-Name: foo"params": {"name": "bar"}Server MUST reject with 400 and error code -32001
Missing required header(no Mcp-Method)Valid bodyServer MUST reject with 400 and error code -32001
Extra whitespace in headerMcp-Name: foo "params": {"name": "foo"}Server MUST accept (trim whitespace per HTTP spec)

Special Characters in Values

Test CaseValueExpected Behavior
Tool name with hyphenmy-tool-nameClient sends as-is; server accepts
Tool name with underscoremy_tool_nameClient sends as-is; server accepts
Resource URI with special charsfile:///path/to/file%20name.txtClient sends as-is; server accepts
Resource URI with query stringhttps://example.com/resource?id=123Client sends as-is; server accepts

Custom Header Edge Cases

x-mcp-header Name Conflicts

Test CaseSchemaExpected Behavior
Duplicate header names (same case)Two properties with "x-mcp-header": "Region"Client MUST reject tool definition
Duplicate header names (different case)"x-mcp-header": "Region" and "x-mcp-header": "REGION"Client MUST reject tool definition (case-insensitive uniqueness)
Header name matches standard header"x-mcp-header": "Method"Allowed (produces Mcp-Param-Method, not Mcp-Method)
Empty header name"x-mcp-header": ""Client MUST reject tool definition

Invalid x-mcp-header Values

Test Casex-mcp-header ValueExpected Behavior
Contains space"x-mcp-header": "My Region"Client MUST reject tool definition
Contains colon"x-mcp-header": "Region:Primary"Client MUST reject tool definition
Contains non-ASCII"x-mcp-header": "Région"Client MUST reject tool definition
Contains control character"x-mcp-header": "Region\t1"Client MUST reject tool definition

Value Encoding Edge Cases

Test CaseParameter ValueExpected Header Value
Plain ASCII string"us-west1"Mcp-Param-Region: us-west1
String with leading space" us-west1"Mcp-Param-Region: =?base64?IHVzLXdlc3Qx?=
String with trailing space"us-west1 "Mcp-Param-Region: =?base64?dXMtd2VzdDEg?=
String with leading/trailing spaces" us-west1 "Mcp-Param-Region: =?base64?IHVzLXdlc3QxIA==?=
String with internal spaces only"us west 1"Mcp-Param-Region: us west 1
Boolean truetrueMcp-Param-Flag: true
Boolean falsefalseMcp-Param-Flag: false
Integer42Mcp-Param-Count: 42
Floating point3.14159Mcp-Param-Value: 3.14159
Non-ASCII characters"日本語"Mcp-Param-Text: =?base64?5pel5pys6Kqe?=
String with newline"line1\nline2"Mcp-Param-Text: =?base64?bGluZTEKbGluZTI=?=
String with carriage return"line1\r\nline2"Mcp-Param-Text: =?base64?bGluZTENCmxpbmUy?=
String with leading tab"\tindented"Mcp-Param-Text: =?base64?CWluZGVudGVk?=
Empty string""Mcp-Param-Name: (empty value)

Type Restriction Violations

Test CaseProperty Typex-mcp-header PresentExpected Behavior
Array type"type": "array"YesServer MUST reject tool definition
Object type"type": "object"YesServer MUST reject tool definition
Null type"type": "null"YesServer MUST reject tool definition
Nested propertyProperty inside objectYesServer MUST reject tool definition

Server Validation Edge Cases

Base64 Decoding

Test CaseHeader ValueExpected Behavior
Valid Base64=?base64?SGVsbG8=?=Server decodes to "Hello" and validates
Invalid Base64 padding=?base64?SGVsbG8?=Server MUST reject with 400 and error code -32001; Intermediary MAY reject with 400 status code
Invalid Base64 characters=?base64?SGVs!!!bG8=?=Server MUST reject with 400 and error code -32001; Intermediary MAY reject with 400 status code
Missing prefixSGVsbG8=Server treats as literal value, not Base64
Missing suffix=?base64?SGVsbG8=Server treats as literal value, not Base64
Malformed wrapper=?BASE64?SGVsbG8=?=Server MUST accept (case-insensitive prefix)

Null and Missing Values

Test CaseScenarioExpected Behavior
Parameter with x-mcp-header is null"region": nullClient MUST omit header
Parameter with x-mcp-header is missingParameter not in argumentsClient MUST omit header
Optional parameter presentOptional parameter providedClient MUST include header

Missing Custom Header with Value in Body

Test CaseHeader PresentBody ValueExpected Behavior
Standard header omitted, value in bodyNo Mcp-Name"params": {"name": "foo"}Server MUST reject with 400 and error code -32001; Intermediary MAY reject with 400 status code
Custom header omitted, value in bodyNo Mcp-Param-Region"region": "us-west1"Server MUST reject with 400 and error code -32001; Intermediary MAY reject with 400 status code

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