Skip to content

fix(sep-2243): reject x-mcp-header on number-typed params - #444

Open
halter73 wants to merge 1 commit into
modelcontextprotocol:mainfrom
halter73:halter73/issue-344-http-custom-headers-scenario-puts-x-mcp-3b453b
Open

fix(sep-2243): reject x-mcp-header on number-typed params#444
halter73 wants to merge 1 commit into
modelcontextprotocol:mainfrom
halter73:halter73/issue-344-http-custom-headers-scenario-puts-x-mcp-3b453b

Conversation

@halter73

@halter73 halter73 commented Aug 5, 2026

Copy link
Copy Markdown

Closes #344

Why this is still open after #371

SEP-2243 permits x-mcp-header only on integer, string and boolean parameters. The released 2026-07-28 spec is explicit:

x-mcp-header MUST only be applied to parameters with primitive types (integer, string, boolean). Parameters with type number are not permitted.
server/tools.mdx

and the value-encoding table in basic/transports/streamable-http.mdx lists only string / integer / boolean.

#371 fixed the positive fixture (priority is now integer, float_val is served unannotated and asserted not mirrored) but left the two items #344 listed as optional, which is why the issue stayed open. This finishes them.

Changes

1. Negative case for the number rule. HttpInvalidToolHeadersScenario served invalid tools for every other x-mcp-header constraint — empty / object / array / null / duplicate / space / colon / non-ascii / control-char — but had no number-typed one. sep-2243-x-mcp-header-primitive-only was only ever exercised via object/array/null, so nothing verified the rule the issue was actually about. Adds an invalid_number_header tool with score: { type: 'number', 'x-mcp-header': 'Score' }, mapped to the existing requirement.

No check IDs added or removed — INVALID_TOOL_DECLARED_CHECK_IDS is unchanged.

2. Requirement text corrected. sep-2243.yaml still described the permitted set as "(number, string, or boolean)" and said "number values MUST be converted to their decimal string representation" — both contradict the released spec. traceability.json embeds this text verbatim, so its two text fields are updated in place; check IDs and status are untouched.

Verification

  • npm run typecheck, npm run lint, npm run build clean; npm test 505 passed / 43 files.
  • Mutation-tested: removing the INVALID_TOOL_CONSTRAINT_IDS mapping makes the new test fail (expected [ 'SUCCESS', 'SUCCESS', 'SUCCESS' ] to include 'FAILURE'), so it isn't vacuous.
  • Ran both scenarios against typescript-sdk@main: positive http-custom-headers still 18/18.

The new check finds a real SDK bug

http-invalid-tool-headers against typescript-sdk@main goes 11/12 — sep-2243-x-mcp-header-primitive-only fails on invalid_number_header. The cause is in the SDK, not the harness:

// packages/core-internal/src/shared/mcpParamHeaders.ts
const PERMITTED_X_MCP_HEADER_TYPES = new Set(['string', 'integer', 'boolean', 'number']);

number was allow-listed there specifically to tolerate this fixture bug, with a comment noting the discrepancy is "tracked upstream." With #371 having fixed the fixture, that workaround can be dropped. Worth a follow-up on the SDK side.

This won't red-light CI: ci.yml runs only npm ci / check / build / test. The typescript-sdk run lives in traceability.yml, which is manual/scheduled, uses || true, and per AGENTS.md is not a PR gate.

Out of scope

#354 (positive scenario's ttlMs: 0 vs. SEP-2549 stale-schema) is a separate defect owned by #358. The positive scenario's TTL is untouched here, and #358's scope note confirms it doesn't modify the invalid-tool scenario — so these shouldn't collide beyond a trivial rebase.

SEP-2243 permits `x-mcp-header` only on integer, string and boolean
parameters — "Parameters with type `number` are not permitted" — but the
harness never verified the `number` case and its requirement text still
described `number` as permitted.

PR modelcontextprotocol#371 fixed the positive `http-custom-headers` fixture (priority is now
`integer`, `float_val` is served unannotated) but left the two remaining
items from modelcontextprotocol#344:

- `HttpInvalidToolHeadersScenario` serves invalid tools for every other
  x-mcp-header constraint but had no number-typed case, so nothing checked
  that a client excludes such a tool.
- `sep-2243.yaml` claimed primitive types are "(number, string, or
  boolean)" and that "number values" get decimal-string encoding, both of
  which contradict the released 2026-07-28 spec.

Add an `invalid_number_header` tool mapped to the existing
`sep-2243-x-mcp-header-primitive-only` requirement (no new check IDs) and
correct both requirement texts. traceability.json is updated in place
since it embeds the yaml text verbatim; check IDs and statuses are
unchanged.

Verified against typescript-sdk@main: the new check fails there because
the SDK deliberately allow-lists `number` to satisfy the old fixture — a
workaround its own source notes as "tracked upstream" and can now drop.
The positive scenario still passes 18/18.

Closes modelcontextprotocol#344

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pkg-pr-new

pkg-pr-new Bot commented Aug 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

npx https://pkg.pr.new/@modelcontextprotocol/conformance@444

commit: 7881434

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

http-custom-headers scenario puts x-mcp-header on type:"number" params (forbidden by SEP-2243)

1 participant