Skip to content

Commit 33c0d76

Browse files
committed
Verify the conformance preview tarball; tidy mcp_param_headers typing
Address PR review: - conformance.yml pinned CONFORMANCE_PKG to a mutable pkg.pr.new URL run via npx without verification. Restore the repo's pre-#2974 supply-chain pattern: a CONFORMANCE_PKG_SHA256 digest plus a fetch-and-verify step that downloads, checks the sha256, and repoints CONFORMANCE_PKG at the verified local tarball (no-op for registry specs). Verified the scenario still passes 18/18 via the file: tarball. - mcp_param_headers carried an inline node: Any and a cast. Move the arbitrary-JSON path walk into a small _value_at_path helper so the main body is clean; the single remaining cast (strict-pyright narrowing of a bare Mapping) matches _walk_schema_positions in the same file.
1 parent 9a84462 commit 33c0d76

2 files changed

Lines changed: 45 additions & 10 deletions

File tree

.github/workflows/conformance.yml

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ env:
2121
#
2222
# Temporarily pinned to the pkg.pr.new preview build of conformance#371, which
2323
# 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).
24+
# x-mcp-header annotations. Because this is a mutable URL (not a registry
25+
# spec), CONFORMANCE_PKG_SHA256 pins the tarball and the fetch-and-verify step
26+
# below downloads, checks the digest, and repoints CONFORMANCE_PKG at the
27+
# verified local copy. Repin to the published release that includes #371 once
28+
# it ships, then drop CONFORMANCE_PKG_SHA256 and the fetch-and-verify steps.
2629
CONFORMANCE_PKG: "https://pkg.pr.new/@modelcontextprotocol/conformance@371"
30+
CONFORMANCE_PKG_SHA256: "9d8b25874d55e304b006cbaa066571773582f5828143c53a2b8a6830f203ca1d"
2731

2832
jobs:
2933
server-conformance:
@@ -39,6 +43,19 @@ jobs:
3943
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
4044
with:
4145
node-version: 24
46+
- name: Fetch and verify conformance harness
47+
# Only when CONFORMANCE_PKG is a URL: download, check the recorded
48+
# sha256, and re-point CONFORMANCE_PKG at the verified local tarball.
49+
# When CONFORMANCE_PKG is a registry spec, this step is a no-op (npm's
50+
# own integrity check applies).
51+
run: |
52+
case "$CONFORMANCE_PKG" in
53+
https://*)
54+
curl -fsSL "$CONFORMANCE_PKG" -o /tmp/conformance.tgz
55+
echo "$CONFORMANCE_PKG_SHA256 /tmp/conformance.tgz" | sha256sum -c -
56+
echo "CONFORMANCE_PKG=file:/tmp/conformance.tgz" >> "$GITHUB_ENV"
57+
;;
58+
esac
4259
- run: uv sync --frozen --all-extras --package mcp-everything-server
4360
- name: Run server conformance (active suite)
4461
run: >-
@@ -70,6 +87,19 @@ jobs:
7087
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
7188
with:
7289
node-version: 24
90+
- name: Fetch and verify conformance harness
91+
# Only when CONFORMANCE_PKG is a URL: download, check the recorded
92+
# sha256, and re-point CONFORMANCE_PKG at the verified local tarball.
93+
# When CONFORMANCE_PKG is a registry spec, this step is a no-op (npm's
94+
# own integrity check applies).
95+
run: |
96+
case "$CONFORMANCE_PKG" in
97+
https://*)
98+
curl -fsSL "$CONFORMANCE_PKG" -o /tmp/conformance.tgz
99+
echo "$CONFORMANCE_PKG_SHA256 /tmp/conformance.tgz" | sha256sum -c -
100+
echo "CONFORMANCE_PKG=file:/tmp/conformance.tgz" >> "$GITHUB_ENV"
101+
;;
102+
esac
73103
- run: uv sync --frozen --all-extras --package mcp
74104
- name: Run client conformance (all suite)
75105
# The harness runs all scenarios via unbounded Promise.all; with 40

src/mcp/shared/inbound.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -241,19 +241,24 @@ def mcp_param_headers(header_map: Mapping[tuple[str, ...], str], arguments: Mapp
241241
"""
242242
headers: dict[str, str] = {}
243243
for path, token in header_map.items():
244-
node: Any = arguments
245-
for key in path:
246-
if not isinstance(node, Mapping):
247-
node = None
248-
break
249-
node = cast("Mapping[str, Any]", node).get(key)
250-
if node is None:
244+
value = _value_at_path(arguments, path)
245+
if value is None:
251246
continue
252-
rendered = ("true" if node else "false") if isinstance(node, bool) else str(node)
247+
rendered = ("true" if value else "false") if isinstance(value, bool) else str(value)
253248
headers[f"{MCP_PARAM_HEADER_PREFIX}{token}"] = encode_header_value(rendered)
254249
return headers
255250

256251

252+
def _value_at_path(arguments: Mapping[str, Any], path: tuple[str, ...]) -> Any:
253+
"""Read the value at a `properties`-key path in `arguments`, or `None` if any step is missing or non-mapping."""
254+
node: Any = arguments
255+
for key in path:
256+
if not isinstance(node, Mapping):
257+
return None
258+
node = cast("Mapping[str, Any]", node).get(key)
259+
return node
260+
261+
257262
# INTERNAL_ERROR is deliberately unmapped (→ HTTP 200): the spec assigns no status to
258263
# -32603, and whether handler-origin errors get 5xx is an open S4 question — see TODO(L66).
259264
ERROR_CODE_HTTP_STATUS: Final[Mapping[int, int]] = MappingProxyType(

0 commit comments

Comments
 (0)