Skip to content
Open
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
76 changes: 76 additions & 0 deletions src/scenarios/client/http-custom-headers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ describe('HttpCustomHeadersScenario (SEP-2243) check IDs', () => {
arguments: {
region: 'us-west1',
priority: 42,
unsafe_integer_val: 9007199254740992,
non_ascii_val: nonAscii,
query: 'SELECT 1'
}
Expand Down Expand Up @@ -188,6 +189,7 @@ describe('HttpCustomHeadersScenario (SEP-2243) check IDs', () => {
arguments: {
region: 'us-west1',
priority: 42,
unsafe_integer_val: 9007199254740992,
non_ascii_val: nonAscii,
query: 'SELECT 1'
}
Expand Down Expand Up @@ -240,6 +242,80 @@ describe('HttpCustomHeadersScenario (SEP-2243) check IDs', () => {
await scenario.stop();
}
});

it('FAILs safe-integer-range when a client mirrors an out-of-range integer header', async () => {
const scenario = new HttpCustomHeadersScenario();
const { serverUrl } = await scenario.start(testScenarioContext());
try {
await post(
serverUrl,
{
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: {
name: 'test_custom_headers',
arguments: {
region: 'us-west1',
priority: 42,
unsafe_integer_val: 9007199254740992,
query: 'SELECT 1'
}
}
},
{
'Mcp-Method': 'tools/call',
'Mcp-Name': 'test_custom_headers',
'Mcp-Param-Region': 'us-west1',
'Mcp-Param-Priority': '42',
'Mcp-Param-UnsafeInteger': '9007199254740992'
}
);
const checks = scenario.getChecks();
expect(
statusesFor(checks, 'sep-2243-x-mcp-header-integer-safe-range')
).toContain('FAILURE');
} finally {
await scenario.stop();
}
});

it('PASSes safe-integer-range when a client omits the out-of-range integer header', async () => {
const scenario = new HttpCustomHeadersScenario();
const { serverUrl } = await scenario.start(testScenarioContext());
try {
await post(
serverUrl,
{
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: {
name: 'test_custom_headers',
arguments: {
region: 'us-west1',
priority: 42,
unsafe_integer_val: 9007199254740992,
query: 'SELECT 1'
}
}
},
{
'Mcp-Method': 'tools/call',
'Mcp-Name': 'test_custom_headers',
'Mcp-Param-Region': 'us-west1',
'Mcp-Param-Priority': '42'
// Mcp-Param-UnsafeInteger is deliberately omitted
}
);
const checks = scenario.getChecks();
expect(
statusesFor(checks, 'sep-2243-x-mcp-header-integer-safe-range')
).toContain('SUCCESS');
} finally {
await scenario.stop();
}
});
});

describe('HttpInvalidToolHeadersScenario (SEP-2243) check IDs', () => {
Expand Down
41 changes: 40 additions & 1 deletion src/scenarios/client/http-custom-headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export const CUSTOM_HEADERS_DECLARED_CHECK_IDS = [
'sep-2243-client-mirrors-designated-params',
'sep-2243-client-encode-values',
'sep-2243-client-base64-unsafe',
'sep-2243-client-omit-null'
'sep-2243-client-omit-null',
'sep-2243-x-mcp-header-integer-safe-range'
] as const;

/**
Expand Down Expand Up @@ -199,6 +200,7 @@ export class HttpCustomHeadersScenario extends BaseHttpScenario {
arguments: {
region: 'us-west1',
priority: 42,
unsafe_integer_val: 9007199254740992,
verbose: false,
debug: true,
empty_val: '',
Expand Down Expand Up @@ -298,6 +300,12 @@ export class HttpCustomHeadersScenario extends BaseHttpScenario {
description: 'Integer numeric value',
'x-mcp-header': 'Priority'
},
unsafe_integer_val: {
type: 'integer',
description:
'Integer value outside IEEE754 safe range (-2^53+1 to 2^53-1) — MUST NOT be mirrored to an HTTP header',
'x-mcp-header': 'UnsafeInteger'
},
verbose: {
type: 'boolean',
description: 'Boolean value',
Expand Down Expand Up @@ -453,6 +461,37 @@ export class HttpCustomHeadersScenario extends BaseHttpScenario {
// Check Mcp-Param-Priority header (integer)
this.checkParamHeader(req, 'Priority', args.priority, 'integer');

// Check Mcp-Param-UnsafeInteger header:
// SEP-2243: "Integer values MUST be within the safe range for integers
// represented using IEEE754 double-precision floating point numbers (-2^53+1 to 2^53-1)"
// An out-of-range integer argument MUST NOT be mirrored into an HTTP header.
if (
args.unsafe_integer_val !== undefined &&
args.unsafe_integer_val !== null
) {
const unsafeIntegerHeader = req.headers['mcp-param-unsafeinteger'] as
| string
| undefined;
this.checks.push({
id: 'sep-2243-x-mcp-header-integer-safe-range',
name: 'ClientCustomHeaderSafeIntegerRange',
description:
'Integer values outside IEEE754 safe range (-2^53+1 to 2^53-1) MUST NOT be mirrored into Mcp-Param headers',
status: unsafeIntegerHeader === undefined ? 'SUCCESS' : 'FAILURE',
timestamp: new Date().toISOString(),
errorMessage:
unsafeIntegerHeader !== undefined
? `Client mirrored unsafe integer value '${unsafeIntegerHeader}' into Mcp-Param-UnsafeInteger header. Integer values MUST be within the safe range (-2^53+1 to 2^53-1).`
: undefined,
specReferences: [SPEC_REFERENCE_TOOL_DEF, SPEC_REFERENCE_CUSTOM],
details: {
headerName: 'Mcp-Param-UnsafeInteger',
rawHeaderValue: unsafeIntegerHeader,
bodyValue: args.unsafe_integer_val
}
});
}

// Check Mcp-Param-Verbose header (boolean value)
// checkParamHeader already FAILs on missing header, so this also covers
// "optional parameter present → client MUST include header" without a
Expand Down
3 changes: 3 additions & 0 deletions src/seps/sep-2243.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ requirements:
- check: sep-2243-x-mcp-header-primitive-only
text: 'x-mcp-header MUST only be applied to parameters with primitive types (integer, string, boolean). Parameters with type `number` are not permitted.'
url: https://modelcontextprotocol.io/specification/draft/server/tools#custom-headers
- check: sep-2243-x-mcp-header-integer-safe-range
text: 'Integer values MUST be within the safe range for integers represented using IEEE754 double-precision floating point numbers (−2^53+1 to 2^53−1).'
url: https://modelcontextprotocol.io/specification/draft/server/tools#custom-headers
- check: sep-2243-client-reject-invalid-tool
text: '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 set of tools returned by tools/list.'
url: https://modelcontextprotocol.io/specification/draft/server/tools#custom-headers
Expand Down
8 changes: 7 additions & 1 deletion src/seps/traceability.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@
"text": "x-mcp-header MUST only be applied to parameters with primitive types (integer, string, boolean). Parameters with type `number` are not permitted.",
"url": "https://modelcontextprotocol.io/specification/draft/server/tools#custom-headers"
},
{
"check": "sep-2243-x-mcp-header-integer-safe-range",
"status": "tested",
"text": "Integer values MUST be within the safe range for integers represented using IEEE754 double-precision floating point numbers (−2^53+1 to 2^53−1).",
"url": "https://modelcontextprotocol.io/specification/draft/server/tools#custom-headers"
},
{
"check": "sep-2243-client-reject-invalid-tool",
"status": "tested",
Expand Down Expand Up @@ -289,7 +295,7 @@
"sep-2243-server-no-xmcp-tool"
],
"summary": {
"tested": 18,
"tested": 19,
"untested": 2,
"excluded": 4,
"untracked": 3,
Expand Down