Issue
The http-custom-headers scenario serves tools/list with "ttlMs": 0 (immediately stale per SEP-2549) and then asserts the very next tools/call carries Mcp-Param-* headers derived from the just-returned schema. SEP-2243 explicitly tells the client to omit Mcp-Param-* "if the cached schema is stale (e.g., its TTL has expired)", so a strict reading of the two SEPs together makes this scenario unpassable.
Spec text
SEP-2243 (docs/specification/draft/basic/transports.mdx, "Custom headers from tool parameters"):
If the client does not have the tool's inputSchema (e.g., tools/list has not yet been called) or the cached schema is stale (e.g., its TTL has expired), the client SHOULD send the request without custom Mcp-Param-* headers. If the server rejects the request because required custom headers are missing, the client SHOULD call tools/list to obtain the current inputSchema, then retry the original request with the appropriate headers.
SEP-2549 (docs/specification/draft/server/utilities/caching.mdx):
ttlMs value |
Client behavior |
0 |
"The response SHOULD be considered immediately stale, The Client MAY re-fetch every time the result is needed." |
| missing |
"clients SHOULD assume a default ttlMs of 0 (immediately stale)" |
Composing the two: a server that publishes tools/list with ttlMs: 0 is telling the client "my schema is stale the moment you receive it," and SEP-2243 then tells the client to omit Mcp-Param-* until either a fresh refetch arrives or the server rejects with -32001 HeaderMismatch and the client retries.
What the scenario does
@modelcontextprotocol/conformance@0.2.0-alpha.4, scenario http-custom-headers:
- Serves
tools/list → { resultType:"complete", ttlMs:0, cacheScope:"private", tools:[…test_custom_headers…] }.
- Has the harness immediately call
tools/call test_custom_headers with a fixed argument set seeded via configCtx.toolCalls.
- Asserts (via
ClientSupportsCustomHeaders etc.) that the call carried Mcp-Param-Region, Mcp-Param-Verbose, …
Step 1 declares the schema immediately stale; step 3 requires headers derived from that same stale schema. No -32001 HeaderMismatch rejection path is exercised — the harness's handleToolsCall always returns success regardless of headers, so a SEP-2243-compliant "retry on -32001" loop never fires either.
(Note: there is a separate issue, #344, where the same scenario's test_custom_headers.priority and .float_val declare x-mcp-header on type:"number", which SEP-2243 forbids. A SEP-2243-conformant client correctly excludes the whole tool from tools/list, which produces the same observable failure for an additional reason. Even after #344 is fixed, the SEP-2243/SEP-2549 stale-schema interaction reported here remains and continues to fail the scenario for SDKs that take both SEPs strictly.)
Related
Issue
The
http-custom-headersscenario servestools/listwith"ttlMs": 0(immediately stale per SEP-2549) and then asserts the very nexttools/callcarriesMcp-Param-*headers derived from the just-returned schema. SEP-2243 explicitly tells the client to omitMcp-Param-*"if the cached schema is stale (e.g., its TTL has expired)", so a strict reading of the two SEPs together makes this scenario unpassable.Spec text
SEP-2243 (
docs/specification/draft/basic/transports.mdx, "Custom headers from tool parameters"):SEP-2549 (
docs/specification/draft/server/utilities/caching.mdx):ttlMsvalue0ttlMsof 0 (immediately stale)"Composing the two: a server that publishes
tools/listwithttlMs: 0is telling the client "my schema is stale the moment you receive it," and SEP-2243 then tells the client to omitMcp-Param-*until either a fresh refetch arrives or the server rejects with-32001 HeaderMismatchand the client retries.What the scenario does
@modelcontextprotocol/conformance@0.2.0-alpha.4, scenariohttp-custom-headers:tools/list→{ resultType:"complete", ttlMs:0, cacheScope:"private", tools:[…test_custom_headers…] }.tools/call test_custom_headerswith a fixed argument set seeded viaconfigCtx.toolCalls.ClientSupportsCustomHeadersetc.) that the call carriedMcp-Param-Region,Mcp-Param-Verbose, …Step 1 declares the schema immediately stale; step 3 requires headers derived from that same stale schema. No
-32001 HeaderMismatchrejection path is exercised — the harness'shandleToolsCallalways returns success regardless of headers, so a SEP-2243-compliant "retry on-32001" loop never fires either.(Note: there is a separate issue, #344, where the same scenario's
test_custom_headers.priorityand.float_valdeclarex-mcp-headerontype:"number", which SEP-2243 forbids. A SEP-2243-conformant client correctly excludes the whole tool fromtools/list, which produces the same observable failure for an additional reason. Even after #344 is fixed, the SEP-2243/SEP-2549 stale-schema interaction reported here remains and continues to fail the scenario for SDKs that take both SEPs strictly.)Related
type:"number"params (forbidden by SEP-2243) #344 —type:"number"annotations in the same scenario fixture (independent blocker; fixing http-custom-headers scenario puts x-mcp-header ontype:"number"params (forbidden by SEP-2243) #344 alone does not fix this issue for strict SDKs).