Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/x-mcp-header-reject-number.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@modelcontextprotocol/core-internal': patch
'@modelcontextprotocol/client': patch
'@modelcontextprotocol/server': patch
---

`x-mcp-header` on a `number`-typed tool parameter is now rejected, matching the 2026-07-28 spec ("Parameters with type `number` are not permitted"; clients MUST exclude such tools from `tools/list`). Previously `number` was accepted only to satisfy an older conformance fixture that has since been corrected. `integer`, `string` and `boolean` are unaffected.
15 changes: 6 additions & 9 deletions packages/core-internal/src/shared/mcpParamHeaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,13 @@ export type XMcpHeaderScanResult = { valid: true; declarations: readonly XMcpHea
const RFC9110_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;

/**
* JSON Schema `type` values the spec admits on an `x-mcp-header` property.
*
* The spec text names `integer`, `string`, `boolean` and explicitly excludes
* `number`. The published conformance referee at the pinned release ships its
* `http-custom-headers` scenario with two `type: "number"` `x-mcp-header`
* parameters and expects the client to mirror them, so `number` is accepted
* here so that the conformance gate passes; the discrepancy is tracked
* upstream. Everything else (`object`, `array`, `null`, absent) is rejected.
* JSON Schema `type` values the spec admits on an `x-mcp-header` property:
* `integer`, `string` and `boolean`. `number` is explicitly not permitted
* (2026-07-28 Streamable HTTP, "Schema Extension"), so a tool that annotates a
* `number`-typed property is rejected like any other invalid declaration.
* Everything else (`object`, `array`, `null`, absent) is rejected too.
*/
const PERMITTED_X_MCP_HEADER_TYPES: ReadonlySet<string> = new Set(['string', 'integer', 'boolean', 'number']);
const PERMITTED_X_MCP_HEADER_TYPES: ReadonlySet<string> = new Set(['string', 'integer', 'boolean']);

/**
* Scan a tool's JSON-serialized `inputSchema` for `x-mcp-header` declarations
Expand Down
4 changes: 4 additions & 0 deletions packages/core-internal/test/shared/mcpParamHeaders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ describe('scanXMcpHeaderDeclarations — constraint table', () => {
expect(invalid({ type: 'object', properties: { a: { type: 'array', [X_MCP_HEADER_KEY]: 'Items' } } })).toMatch(/primitive/);
});

test('number-typed property is rejected (only integer, string, boolean are permitted)', () => {
expect(invalid({ type: 'object', properties: { a: { type: 'number', [X_MCP_HEADER_KEY]: 'Score' } } })).toMatch(/primitive/);
});

test('null-typed property is rejected', () => {
expect(invalid({ type: 'object', properties: { a: { type: 'null', [X_MCP_HEADER_KEY]: 'Nil' } } })).toMatch(/primitive/);
});
Expand Down
Loading