Skip to content

Commit b8a88af

Browse files
committed
Pass http-custom-headers conformance via the #371 fixture-fix preview
The conformance handler now replays the harness-supplied toolCalls verbatim (including the null and Base64-edge-case values) instead of synthesizing arguments from the schema, so every per-parameter check is exercised: the null-omission and Base64-unsafe checks now fire correctly. Pins CONFORMANCE_PKG to the pkg.pr.new preview of conformance#371, which fixes the fixture's spec-forbidden number-typed x-mcp-header annotations, and removes http-custom-headers from both expected-failures baselines (it now passes 18/18 on both the default and 2026-07-28 legs). Repin to the published release once #371 ships.
1 parent c895823 commit b8a88af

4 files changed

Lines changed: 25 additions & 44 deletions

File tree

.github/actions/conformance/client.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -335,41 +335,32 @@ async def run_http_invalid_tool_headers(server_url: str) -> None:
335335
logger.exception(f"call_tool({tool.name!r}) failed")
336336

337337

338-
def _stub_args_for_custom_headers(input_schema: dict[str, Any]) -> dict[str, Any]:
339-
"""Arguments exercising every `x-mcp-header`-annotated property in a tool schema.
340-
341-
Each annotated property gets a type-appropriate value so the SDK mirrors it into an
342-
`Mcp-Param-*` header; required properties without an annotation get a placeholder so
343-
the call is well-formed.
344-
"""
345-
by_type: dict[str, Any] = {"string": "us-west1", "integer": 42, "boolean": False, "number": 3.14}
346-
properties: dict[str, Any] = input_schema.get("properties", {})
347-
arguments: dict[str, Any] = {}
348-
for name, schema in properties.items():
349-
if "x-mcp-header" in schema:
350-
arguments[name] = by_type.get(schema.get("type"), "x")
351-
for name in input_schema.get("required", []):
352-
arguments.setdefault(name, by_type.get(properties.get(name, {}).get("type"), "x"))
353-
return arguments
354-
355-
356338
@register("http-custom-headers")
357339
async def run_http_custom_headers(server_url: str) -> None:
358-
"""List tools, then call each surfaced tool so its `x-mcp-header` args mirror into headers (SEP-2243).
340+
"""List tools, then replay the harness's `toolCalls` so x-mcp-header args mirror into headers (SEP-2243).
359341
360-
A conforming client drops tools with invalid annotations during `list_tools` (e.g. the
361-
harness's `number`-typed properties, which the spec forbids), so the loop only calls tools
362-
whose annotations are valid; for those, the SDK emits the `Mcp-Param-*` headers the scenario
363-
checks. Per-call failures are logged and skipped rather than aborting the run.
342+
The scenario supplies the exact arguments to send (including the null/edge-case values that
343+
exercise omission and Base64 encoding) via the context `toolCalls`; using them verbatim is
344+
what drives every per-parameter check. `list_tools` first so the SDK caches each tool's
345+
annotations; a tool the SDK dropped (invalid annotations) is skipped. Per-call failures are
346+
logged and skipped rather than aborting the run.
364347
"""
348+
tool_calls: list[dict[str, Any]] = []
349+
if os.environ.get("MCP_CONFORMANCE_CONTEXT"):
350+
tool_calls = get_conformance_context().get("toolCalls", [])
365351
async with Client(server_url, mode=client_mode()) as client:
366352
listed = await client.list_tools()
367-
logger.debug(f"Surfaced tools: {[t.name for t in listed.tools]}")
368-
for tool in listed.tools:
353+
surfaced = {tool.name for tool in listed.tools}
354+
logger.debug(f"Surfaced tools: {sorted(surfaced)}")
355+
for call in tool_calls:
356+
name = call["name"]
357+
if name not in surfaced:
358+
logger.debug(f"skipping {name!r}: not surfaced by list_tools")
359+
continue
369360
try:
370-
await client.call_tool(tool.name, _stub_args_for_custom_headers(tool.input_schema))
361+
await client.call_tool(name, call.get("arguments") or {})
371362
except Exception:
372-
logger.exception(f"call_tool({tool.name!r}) failed")
363+
logger.exception(f"call_tool({name!r}) failed")
373364

374365

375366
@register("elicitation-sep1034-client-defaults")

.github/actions/conformance/expected-failures.2026-07-28.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,7 @@
2121
# milestone.
2222

2323
client:
24-
# SEP-2243 (HTTP standardization): the client now mirrors x-mcp-header args into
25-
# Mcp-Param-* headers (S8), but the harness fixture annotates `number`-typed
26-
# properties, which the spec forbids ("Parameters with type number are not
27-
# permitted"). The SDK drops those tools per spec, so the scenario's
28-
# ClientSupportsCustomHeaders check (which requires the tool to be called)
29-
# cannot pass until the harness fixture is corrected.
30-
- http-custom-headers
24+
[]
3125
# auth/enterprise-managed-authorization (SEP-990) is in the 2025 baseline but
3226
# NOT here: the harness skips it as inapplicable at --spec-version 2026-07-28
3327
# (it is an extension scenario not carried into the 2026 wire), so it is

.github/actions/conformance/expected-failures.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,6 @@
1111
# on stale entries), so the baseline burns down per milestone.
1212

1313
client:
14-
# --- Draft-spec scenarios (in `--suite draft`, also part of `--suite all`) ---
15-
# SEP-2243 (HTTP standardization): the client now mirrors x-mcp-header args into
16-
# Mcp-Param-* headers (S8), but the harness fixture annotates `number`-typed
17-
# properties, which the spec forbids ("Parameters with type number are not
18-
# permitted"). The SDK drops those tools per spec, so the scenario's
19-
# ClientSupportsCustomHeaders check (which requires the tool to be called)
20-
# cannot pass until the harness fixture is corrected.
21-
- http-custom-headers
22-
2314
# --- Pre-existing scenarios that fail on checks added after conformance 0.1.15 ---
2415
# SEP-990 (enterprise-managed authorization extension): no fixture handler /
2516
# client support for the token-exchange + JWT bearer flow.

.github/workflows/conformance.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ env:
1818
# Use a published version, e.g. @modelcontextprotocol/conformance@0.2.0-alpha.7.
1919
# Bump deliberately and reconcile both
2020
# .github/actions/conformance/expected-failures*.yml files in the same change.
21-
CONFORMANCE_PKG: "@modelcontextprotocol/conformance@0.2.0-alpha.7"
21+
#
22+
# Temporarily pinned to the pkg.pr.new preview build of conformance#371, which
23+
# fixes the http-custom-headers fixture's spec-forbidden `number`-typed
24+
# x-mcp-header annotations. Repin to the published release that includes #371
25+
# once it ships (the preview URL is ephemeral).
26+
CONFORMANCE_PKG: "https://pkg.pr.new/@modelcontextprotocol/conformance@371"
2227

2328
jobs:
2429
server-conformance:

0 commit comments

Comments
 (0)