Follow Discord
Sweep 29 Sep 2026 · 18:10Z Build v2.1.285 506 read Stable v2.1.280 Latest v2.1.285 Next v2.1.285 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · mcp

SEP-2243: HTTP Header Standardization for Streamable HTTP Transport changedseps/2243-http-standardization

Upstream edited this page at 29 Jul 2026 22:46 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 28 Sep 2026 22:07 UTC.

Upstream edited
Recorded here
Lines+107added
Lines−107removed
From line 20 where the diff opens
First seen 14 Aug 2026 this site's first read of the page
Recorded edits2to this page, all time

The whole hunk

from line 20, old and new numbered
/
lines
from line 20
2020 requirements.
2121</Note>
2222 
23| Field | Value |
24| ------------- | ------------------------------------------------------------------------------- |
25| **SEP** | 2243 |
26| **Title** | HTTP Header Standardization for Streamable HTTP Transport |
27| **Status** | Final |
28| **Type** | Standards Track |
29| **Created** | 2026-02-04 |
30| **Author(s)** | MCP Transports Working Group |
31| **Sponsor** | None |
32| **PR** | [#2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243) |
23| Field | Value |
24| - | - |
25| **SEP** | 2243 |
26| **Title** | HTTP Header Standardization for Streamable HTTP Transport |
27| **Status** | Final |
28| **Type** | Standards Track |
29| **Created** | 2026-02-04 |
30| **Author(s)** | MCP Transports Working Group |
31| **Sponsor** | None |
32| **PR** | [#2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243) |
3333 
3434***
3535 
from line 54
5454 
5555The Streamable HTTP transport will require POST requests to include the following headers mirrored from the request body:
5656 
57| Header Name | Source Field | Required For |
58| ------------ | ----------------------------- | ------------------------------------------------------ |
59| `Mcp-Method` | `method` | All requests and notifications |
60| `Mcp-Name` | `params.name` or `params.uri` | `tools/call`, `resources/read`, `prompts/get` requests |
57| Header Name | Source Field | Required For |
58| - | - | - |
59| `Mcp-Method` | `method` | All requests and notifications |
60| `Mcp-Name` | `params.name` or `params.uri` | `tools/call`, `resources/read`, `prompts/get` requests |
6161 
6262These headers are **required** for compliance with the MCP version in which they are introduced.
6363 
from line 444
444444 
445445**Examples**:
446446 
447| Original Value | Reason | Encoded Header Value |
448| ---------------------- | ------------------------ | ----------------------------------------------------- |
449| `"us-west1"` | Plain ASCII | `Mcp-Param-Region: us-west1` |
450| `"Hello, 世界"` | Contains non-ASCII | `Mcp-Param-Greeting: =?base64?SGVsbG8sIOS4lueVjA==?=` |
451| `" padded "` | Leading/trailing spaces | `Mcp-Param-Text: =?base64?IHBhZGRlZCA=?=` |
452| `"line1\nline2"` | Contains newline | `Mcp-Param-Text: =?base64?bGluZTEKbGluZTI=?=` |
453| `"=?base64?literal?="` | Matches sentinel pattern | `Mcp-Param-Val: =?base64?PT9iYXNlNjQ/bGl0ZXJhbD89?=` |
447| Original Value | Reason | Encoded Header Value |
448| - | - | - |
449| `"us-west1"` | Plain ASCII | `Mcp-Param-Region: us-west1` |
450| `"Hello, 世界"` | Contains non-ASCII | `Mcp-Param-Greeting: =?base64?SGVsbG8sIOS4lueVjA==?=` |
451| `" padded "` | Leading/trailing spaces | `Mcp-Param-Text: =?base64?IHBhZGRlZCA=?=` |
452| `"line1\nline2"` | Contains newline | `Mcp-Param-Text: =?base64?bGluZTEKbGluZTI=?=` |
453| `"=?base64?literal?="` | Matches sentinel pattern | `Mcp-Param-Val: =?base64?PT9iYXNlNjQ/bGl0ZXJhbD89?=` |
454454 
455455#### Client Behavior
456456 
from line 474
474474 
475475When rejecting a request due to header validation failure, servers MUST return a JSON-RPC error response with the following error code:
476476 
477| Code | Name | Description |
478| -------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------- |
477| Code | Name | Description |
478| - | - | - |
479479| `-32001` | `HeaderMismatch` | The HTTP headers do not match the corresponding values in the request body, or required headers are missing/malformed. |
480480 
481481This error code is in the JSON-RPC implementation-defined server error range (`-32000` to `-32099`).
from line 508
508508 
509509Custom headers (those defined via `x-mcp-header`) follow the same validation rules as standard headers:
510510 
511| Scenario | Client Behavior | Server Behavior |
512| ---------------------------------------- | ------------------------------ | ---------------------------------------- |
513| Parameter value provided | Client MUST include the header | Server MUST validate header matches body |
514| Parameter value is `null` | Client MUST omit the header | Server MUST NOT expect the header |
515| Parameter not in arguments | Client MUST omit the header | Server MUST NOT expect the header |
516| Client omits header but value is in body | Non-conforming client | Server MUST reject the request |
511| Scenario | Client Behavior | Server Behavior |
512| - | - | - |
513| Parameter value provided | Client MUST include the header | Server MUST validate header matches body |
514| Parameter value is `null` | Client MUST omit the header | Server MUST NOT expect the header |
515| Parameter not in arguments | Client MUST omit the header | Server MUST NOT expect the header |
516| Client omits header but value is in body | Non-conforming client | Server MUST reject the request |
517517 
518518When 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`).
519519 
from line 538
538538 
539539**Trade-offs and Framework Considerations**:
540540 
541| Framework | Header-based Routing | Path-based Routing |
542| ----------------- | ------------------------------------------------------------------- | ------------------------------------------------ |
543| Flask (Python) | Requires middleware or decorators to extract headers before routing | Native support via `@app.route('/mcp/<method>')` |
544| Express (Node.js) | Easy via `req.headers` but requires custom routing logic | Native support via `app.post('/mcp/:method')` |
545| Django (Python) | Requires custom middleware | Native URL patterns |
546| Go (net/http) | Easy via `r.Header.Get()` | Native via path patterns |
547| ASP.NET Core | Easy via `[FromHeader]` attribute | Native via route templates |
541| Framework | Header-based Routing | Path-based Routing |
542| - | - | - |
543| Flask (Python) | Requires middleware or decorators to extract headers before routing | Native support via `@app.route('/mcp/<method>')` |
544| Express (Node.js) | Easy via `req.headers` but requires custom routing logic | Native support via `app.post('/mcp/:method')` |
545| Django (Python) | Requires custom middleware | Native URL patterns |
546| Go (net/http) | Easy via `r.Header.Get()` | Native via path patterns |
547| ASP.NET Core | Easy via `[FromHeader]` attribute | Native via route templates |
548548 
549549For frameworks like Flask that strongly favor path-based routing, implementing header-based routing requires additional code:
550550 
from line 627
627627 
6286284. **Always encode**: Base64-encode every `Mcp-Param-{Name}` value unconditionally.
629629 
630| Approach | Pros | Cons |
631| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
632| Sentinel wrapping | Single header name per parameter; common case (plain ASCII) is human-readable; intermediaries can route on plain values without decoding | In-band signaling can theoretically collide with literal values; every reader must check for the prefix |
633| Separate header name | No in-band ambiguity; encoding is self-documenting from the header name | Doubles the header namespace; every intermediary must check two header names per parameter; needs a conflict rule if both are present |
634| Implicit encoding | Simplest wire format; no sentinels or extra headers | Intermediaries 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 |
635| Always encode | Simplest rules; no conditional logic or ambiguity | Plain ASCII values become unreadable; intermediaries must decode Base64 to inspect any value, significantly undermining the core motivation of this SEP |
630| Approach | Pros | Cons |
631| - | - | - |
632| Sentinel wrapping | Single header name per parameter; common case (plain ASCII) is human-readable; intermediaries can route on plain values without decoding | In-band signaling can theoretically collide with literal values; every reader must check for the prefix |
633| Separate header name | No in-band ambiguity; encoding is self-documenting from the header name | Doubles the header namespace; every intermediary must check two header names per parameter; needs a conflict rule if both are present |
634| Implicit encoding | Simplest wire format; no sentinels or extra headers | Intermediaries 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 |
635| Always encode | Simplest rules; no conditional logic or ambiguity | Plain ASCII values become unreadable; intermediaries must decode Base64 to inspect any value, significantly undermining the core motivation of this SEP |
636636 
637637**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.
638638 
from line 694
694694 
695695#### Case Sensitivity
696696 
697| Test Case | Input | Expected Behavior |
698| -------------------------- | ------------------------ | ------------------------------------------------------ |
697| Test Case | Input | Expected Behavior |
698| - | - | - |
699699| Header name case variation | `mcp-method: tools/call` | Server MUST accept (header names are case-insensitive) |
700| Header name mixed case | `MCP-METHOD: tools/call` | Server MUST accept |
701| Method value case | `Mcp-Method: TOOLS/CALL` | Server MUST reject (method values are case-sensitive) |
700| Header name mixed case | `MCP-METHOD: tools/call` | Server MUST accept |
701| Method value case | `Mcp-Method: TOOLS/CALL` | Server MUST reject (method values are case-sensitive) |
702702 
703703#### Header/Body Mismatch
704704 
705| Test Case | Header Value | Body Value | Expected Behavior |
706| -------------------------- | ------------------------ | --------------------------- | --------------------------------------------------- |
707| Method mismatch | `Mcp-Method: tools/call` | `"method": "prompts/get"` | Server MUST reject with 400 and error code `-32001` |
708| Tool name mismatch | `Mcp-Name: foo` | `"params": {"name": "bar"}` | Server MUST reject with 400 and error code `-32001` |
709| Missing required header | (no `Mcp-Method`) | Valid body | Server MUST reject with 400 and error code `-32001` |
710| Extra whitespace in header | `Mcp-Name: foo ` | `"params": {"name": "foo"}` | Server MUST accept (trim whitespace per HTTP spec) |
705| Test Case | Header Value | Body Value | Expected Behavior |
706| - | - | - | - |
707| Method mismatch | `Mcp-Method: tools/call` | `"method": "prompts/get"` | Server MUST reject with 400 and error code `-32001` |
708| Tool name mismatch | `Mcp-Name: foo` | `"params": {"name": "bar"}` | Server MUST reject with 400 and error code `-32001` |
709| Missing required header | (no `Mcp-Method`) | Valid body | Server MUST reject with 400 and error code `-32001` |
710| Extra whitespace in header | `Mcp-Name: foo ` | `"params": {"name": "foo"}` | Server MUST accept (trim whitespace per HTTP spec) |
711711 
712712#### Special Characters in Values
713713 
714| Test Case | Value | Expected Behavior |
715| ------------------------------- | ------------------------------------- | ---------------------------------- |
716| Tool name with hyphen | `my-tool-name` | Client sends as-is; server accepts |
717| Tool name with underscore | `my_tool_name` | Client sends as-is; server accepts |
718| Resource URI with special chars | `file:///path/to/file%20name.txt` | Client sends as-is; server accepts |
719| Resource URI with query string | `https://example.com/resource?id=123` | Client sends as-is; server accepts |
714| Test Case | Value | Expected Behavior |
715| - | - | - |
716| Tool name with hyphen | `my-tool-name` | Client sends as-is; server accepts |
717| Tool name with underscore | `my_tool_name` | Client sends as-is; server accepts |
718| Resource URI with special chars | `file:///path/to/file%20name.txt` | Client sends as-is; server accepts |
719| Resource URI with query string | `https://example.com/resource?id=123` | Client sends as-is; server accepts |
720720 
721721### Custom Header Edge Cases
722722 
723723#### x-mcp-header Name Conflicts
724724 
725| Test Case | Schema | Expected Behavior |
726| --------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------------------- |
727| Duplicate header names (same case) | Two properties with `"x-mcp-header": "Region"` | Client MUST reject tool definition |
725| Test Case | Schema | Expected Behavior |
726| - | - | - |
727| Duplicate header names (same case) | Two properties with `"x-mcp-header": "Region"` | Client MUST reject tool definition |
728728| Duplicate header names (different case) | `"x-mcp-header": "Region"` and `"x-mcp-header": "REGION"` | Client MUST reject tool definition (case-insensitive uniqueness) |
729| Header name matches standard header | `"x-mcp-header": "Method"` | Allowed (produces `Mcp-Param-Method`, not `Mcp-Method`) |
730| Empty header name | `"x-mcp-header": ""` | Client MUST reject tool definition |
729| Header name matches standard header | `"x-mcp-header": "Method"` | Allowed (produces `Mcp-Param-Method`, not `Mcp-Method`) |
730| Empty header name | `"x-mcp-header": ""` | Client MUST reject tool definition |
731731 
732732#### Invalid x-mcp-header Values
733733 
734| Test Case | x-mcp-header Value | Expected Behavior |
735| -------------------------- | ---------------------------------- | ---------------------------------- |
736| Contains space | `"x-mcp-header": "My Region"` | Client MUST reject tool definition |
737| Contains colon | `"x-mcp-header": "Region:Primary"` | Client MUST reject tool definition |
738| Contains non-ASCII | `"x-mcp-header": "Région"` | Client MUST reject tool definition |
739| Contains control character | `"x-mcp-header": "Region\t1"` | Client MUST reject tool definition |
734| Test Case | x-mcp-header Value | Expected Behavior |
735| - | - | - |
736| Contains space | `"x-mcp-header": "My Region"` | Client MUST reject tool definition |
737| Contains colon | `"x-mcp-header": "Region:Primary"` | Client MUST reject tool definition |
738| Contains non-ASCII | `"x-mcp-header": "Région"` | Client MUST reject tool definition |
739| Contains control character | `"x-mcp-header": "Region\t1"` | Client MUST reject tool definition |
740740 
741741#### Value Encoding Edge Cases
742742 
743| Test Case | Parameter Value | Expected Header Value |
744| ----------------------------------- | ------------------ | ----------------------------------------------- |
745| Plain ASCII string | `"us-west1"` | `Mcp-Param-Region: us-west1` |
746| String with leading space | `" us-west1"` | `Mcp-Param-Region: =?base64?IHVzLXdlc3Qx?=` |
747| String with trailing space | `"us-west1 "` | `Mcp-Param-Region: =?base64?dXMtd2VzdDEg?=` |
748| String with leading/trailing spaces | `" us-west1 "` | `Mcp-Param-Region: =?base64?IHVzLXdlc3QxIA==?=` |
749| String with internal spaces only | `"us west 1"` | `Mcp-Param-Region: us west 1` |
750| Boolean true | `true` | `Mcp-Param-Flag: true` |
751| Boolean false | `false` | `Mcp-Param-Flag: false` |
752| Integer | `42` | `Mcp-Param-Count: 42` |
753| Floating point | `3.14159` | `Mcp-Param-Value: 3.14159` |
754| Non-ASCII characters | `"日本語"` | `Mcp-Param-Text: =?base64?5pel5pys6Kqe?=` |
755| String with newline | `"line1\nline2"` | `Mcp-Param-Text: =?base64?bGluZTEKbGluZTI=?=` |
756| String with carriage return | `"line1\r\nline2"` | `Mcp-Param-Text: =?base64?bGluZTENCmxpbmUy?=` |
757| String with leading tab | `"\tindented"` | `Mcp-Param-Text: =?base64?CWluZGVudGVk?=` |
758| Empty string | `""` | `Mcp-Param-Name: ` (empty value) |
743| Test Case | Parameter Value | Expected Header Value |
744| - | - | - |
745| Plain ASCII string | `"us-west1"` | `Mcp-Param-Region: us-west1` |
746| String with leading space | `" us-west1"` | `Mcp-Param-Region: =?base64?IHVzLXdlc3Qx?=` |
747| String with trailing space | `"us-west1 "` | `Mcp-Param-Region: =?base64?dXMtd2VzdDEg?=` |
748| String with leading/trailing spaces | `" us-west1 "` | `Mcp-Param-Region: =?base64?IHVzLXdlc3QxIA==?=` |
749| String with internal spaces only | `"us west 1"` | `Mcp-Param-Region: us west 1` |
750| Boolean true | `true` | `Mcp-Param-Flag: true` |
751| Boolean false | `false` | `Mcp-Param-Flag: false` |
752| Integer | `42` | `Mcp-Param-Count: 42` |
753| Floating point | `3.14159` | `Mcp-Param-Value: 3.14159` |
754| Non-ASCII characters | `"日本語"` | `Mcp-Param-Text: =?base64?5pel5pys6Kqe?=` |
755| String with newline | `"line1\nline2"` | `Mcp-Param-Text: =?base64?bGluZTEKbGluZTI=?=` |
756| String with carriage return | `"line1\r\nline2"` | `Mcp-Param-Text: =?base64?bGluZTENCmxpbmUy?=` |
757| String with leading tab | `"\tindented"` | `Mcp-Param-Text: =?base64?CWluZGVudGVk?=` |
758| Empty string | `""` | `Mcp-Param-Name: ` (empty value) |
759759 
760760#### Type Restriction Violations
761761 
762| Test Case | Property Type | x-mcp-header Present | Expected Behavior |
763| --------------- | ---------------------- | -------------------- | ---------------------------------- |
764| Array type | `"type": "array"` | Yes | Server MUST reject tool definition |
765| Object type | `"type": "object"` | Yes | Server MUST reject tool definition |
766| Null type | `"type": "null"` | Yes | Server MUST reject tool definition |
767| Nested property | Property inside object | Yes | Server MUST reject tool definition |
762| Test Case | Property Type | x-mcp-header Present | Expected Behavior |
763| - | - | - | - |
764| Array type | `"type": "array"` | Yes | Server MUST reject tool definition |
765| Object type | `"type": "object"` | Yes | Server MUST reject tool definition |
766| Null type | `"type": "null"` | Yes | Server MUST reject tool definition |
767| Nested property | Property inside object | Yes | Server MUST reject tool definition |
768768 
769769### Server Validation Edge Cases
770770 
771771#### Base64 Decoding
772772 
773| Test Case | Header Value | Expected Behavior |
774| ------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------- |
775| Valid Base64 | `=?base64?SGVsbG8=?=` | Server decodes to `"Hello"` and validates |
776| Invalid Base64 padding | `=?base64?SGVsbG8?=` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code |
773| Test Case | Header Value | Expected Behavior |
774| - | - | - |
775| Valid Base64 | `=?base64?SGVsbG8=?=` | Server decodes to `"Hello"` and validates |
776| Invalid Base64 padding | `=?base64?SGVsbG8?=` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code |
777777| Invalid Base64 characters | `=?base64?SGVs!!!bG8=?=` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code |
778| Missing prefix | `SGVsbG8=` | Server treats as literal value, not Base64 |
779| Missing suffix | `=?base64?SGVsbG8=` | Server treats as literal value, not Base64 |
780| Non-lowercase prefix | `=?BASE64?SGVsbG8=?=` | Server treats as literal value, not Base64 |
778| Missing prefix | `SGVsbG8=` | Server treats as literal value, not Base64 |
779| Missing suffix | `=?base64?SGVsbG8=` | Server treats as literal value, not Base64 |
780| Non-lowercase prefix | `=?BASE64?SGVsbG8=?=` | Server treats as literal value, not Base64 |
781781 
782782#### Null and Missing Values
783783 
784| Test Case | Scenario | Expected Behavior |
785| -------------------------------------- | --------------------------- | -------------------------- |
786| Parameter with x-mcp-header is null | `"region": null` | Client MUST omit header |
787| Parameter with x-mcp-header is missing | Parameter not in arguments | Client MUST omit header |
788| Optional parameter present | Optional parameter provided | Client MUST include header |
784| Test Case | Scenario | Expected Behavior |
785| - | - | - |
786| Parameter with x-mcp-header is null | `"region": null` | Client MUST omit header |
787| Parameter with x-mcp-header is missing | Parameter not in arguments | Client MUST omit header |
788| Optional parameter present | Optional parameter provided | Client MUST include header |
789789 
790790#### Missing Custom Header with Value in Body
791791 
792| Test Case | Header Present | Body Value | Expected Behavior |
793| -------------------------------------- | --------------------- | --------------------------- | ------------------------------------------------------------------------------------------------- |
794| Standard header omitted, value in body | No `Mcp-Name` | `"params": {"name": "foo"}` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code |
795| Custom header omitted, value in body | No `Mcp-Param-Region` | `"region": "us-west1"` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code |
792| Test Case | Header Present | Body Value | Expected Behavior |
793| - | - | - | - |
794| Standard header omitted, value in body | No `Mcp-Name` | `"params": {"name": "foo"}` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code |
795| Custom header omitted, value in body | No `Mcp-Param-Region` | `"region": "us-west1"` | Server MUST reject with 400 and error code `-32001`; Intermediary MAY reject with 400 status code |
796796 
797797## Reference Implementation
798798 
Feedback