Mirror x-mcp-header tool arguments into Mcp-Param-* request headers (SEP-2243) (#2990)
This commit is contained in:
parent
3a8da8c0c3
commit
5b2713d40c
4 changed files with 393 additions and 14 deletions
30
.github/actions/conformance/client.py
vendored
30
.github/actions/conformance/client.py
vendored
|
|
@ -335,6 +335,34 @@ async def run_http_invalid_tool_headers(server_url: str) -> None:
|
|||
logger.exception(f"call_tool({tool.name!r}) failed")
|
||||
|
||||
|
||||
@register("http-custom-headers")
|
||||
async def run_http_custom_headers(server_url: str) -> None:
|
||||
"""List tools, then replay the harness's `toolCalls` so x-mcp-header args mirror into headers (SEP-2243).
|
||||
|
||||
The scenario supplies the exact arguments to send (including the null/edge-case values that
|
||||
exercise omission and Base64 encoding) via the context `toolCalls`; using them verbatim is
|
||||
what drives every per-parameter check. `list_tools` first so the SDK caches each tool's
|
||||
annotations; a tool the SDK dropped (invalid annotations) is skipped. Per-call failures are
|
||||
logged and skipped rather than aborting the run.
|
||||
"""
|
||||
tool_calls: list[dict[str, Any]] = []
|
||||
if os.environ.get("MCP_CONFORMANCE_CONTEXT"):
|
||||
tool_calls = get_conformance_context().get("toolCalls", [])
|
||||
async with Client(server_url, mode=client_mode()) as client:
|
||||
listed = await client.list_tools()
|
||||
surfaced = {tool.name for tool in listed.tools}
|
||||
logger.debug(f"Surfaced tools: {sorted(surfaced)}")
|
||||
for call in tool_calls:
|
||||
name = call["name"]
|
||||
if name not in surfaced:
|
||||
logger.debug(f"skipping {name!r}: not surfaced by list_tools")
|
||||
continue
|
||||
try:
|
||||
await client.call_tool(name, call.get("arguments") or {})
|
||||
except Exception:
|
||||
logger.exception(f"call_tool({name!r}) failed")
|
||||
|
||||
|
||||
@register("elicitation-sep1034-client-defaults")
|
||||
async def run_elicitation_defaults(server_url: str) -> None:
|
||||
"""Connect with elicitation callback that applies schema defaults."""
|
||||
|
|
@ -526,8 +554,6 @@ def main() -> None:
|
|||
elif scenario.startswith("auth/"):
|
||||
asyncio.run(run_auth_code_client(server_url))
|
||||
else:
|
||||
# Unhandled scenarios:
|
||||
# - http-custom-headers (SEP-2243 / S8: Mcp-Param-* emission)
|
||||
print(f"Unknown scenario: {scenario}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -21,9 +21,7 @@
|
|||
# milestone.
|
||||
|
||||
client:
|
||||
# SEP-2243 (HTTP standardization): no client Mcp-Param-* support yet — needs the
|
||||
# tool-schema-cache vs per-call tool_definition design (S8).
|
||||
- http-custom-headers
|
||||
[]
|
||||
# auth/enterprise-managed-authorization (SEP-990) is in the 2025 baseline but
|
||||
# NOT here: the harness skips it as inapplicable at --spec-version 2026-07-28
|
||||
# (it is an extension scenario not carried into the 2026 wire), so it is
|
||||
|
|
|
|||
|
|
@ -11,11 +11,6 @@
|
|||
# on stale entries), so the baseline burns down per milestone.
|
||||
|
||||
client:
|
||||
# --- Draft-spec scenarios (in `--suite draft`, also part of `--suite all`) ---
|
||||
# SEP-2243 (HTTP standardization): no client Mcp-Param-* support yet — needs the
|
||||
# tool-schema-cache vs per-call tool_definition design (S8).
|
||||
- http-custom-headers
|
||||
|
||||
# --- Pre-existing scenarios that fail on checks added after conformance 0.1.15 ---
|
||||
# SEP-990 (enterprise-managed authorization extension): no fixture handler /
|
||||
# client support for the token-exchange + JWT bearer flow.
|
||||
|
|
|
|||
37
.github/workflows/conformance.yml
vendored
37
.github/workflows/conformance.yml
vendored
|
|
@ -18,7 +18,16 @@ env:
|
|||
# Use a published version, e.g. @modelcontextprotocol/conformance@0.2.0-alpha.7.
|
||||
# Bump deliberately and reconcile both
|
||||
# .github/actions/conformance/expected-failures*.yml files in the same change.
|
||||
CONFORMANCE_PKG: "@modelcontextprotocol/conformance@0.2.0-alpha.7"
|
||||
#
|
||||
# Temporarily pinned to the pkg.pr.new preview build of conformance#371, which
|
||||
# fixes the http-custom-headers fixture's spec-forbidden `number`-typed
|
||||
# x-mcp-header annotations. Because this is a mutable URL (not a registry
|
||||
# spec), CONFORMANCE_PKG_SHA256 pins the tarball and the fetch-and-verify step
|
||||
# below downloads, checks the digest, and repoints CONFORMANCE_PKG at the
|
||||
# verified local copy. Repin to the published release that includes #371 once
|
||||
# it ships, then drop CONFORMANCE_PKG_SHA256 and the fetch-and-verify steps.
|
||||
CONFORMANCE_PKG: "https://pkg.pr.new/@modelcontextprotocol/conformance@371"
|
||||
CONFORMANCE_PKG_SHA256: "9d8b25874d55e304b006cbaa066571773582f5828143c53a2b8a6830f203ca1d"
|
||||
|
||||
jobs:
|
||||
server-conformance:
|
||||
|
|
@ -34,6 +43,19 @@ jobs:
|
|||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
- name: Fetch and verify conformance harness
|
||||
# Only when CONFORMANCE_PKG is a URL: download, check the recorded
|
||||
# sha256, and re-point CONFORMANCE_PKG at the verified local tarball.
|
||||
# When CONFORMANCE_PKG is a registry spec, this step is a no-op (npm's
|
||||
# own integrity check applies).
|
||||
run: |
|
||||
case "$CONFORMANCE_PKG" in
|
||||
https://*)
|
||||
curl -fsSL "$CONFORMANCE_PKG" -o /tmp/conformance.tgz
|
||||
echo "$CONFORMANCE_PKG_SHA256 /tmp/conformance.tgz" | sha256sum -c -
|
||||
echo "CONFORMANCE_PKG=file:/tmp/conformance.tgz" >> "$GITHUB_ENV"
|
||||
;;
|
||||
esac
|
||||
- run: uv sync --frozen --all-extras --package mcp-everything-server
|
||||
- name: Run server conformance (active suite)
|
||||
run: >-
|
||||
|
|
@ -65,6 +87,19 @@ jobs:
|
|||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
- name: Fetch and verify conformance harness
|
||||
# Only when CONFORMANCE_PKG is a URL: download, check the recorded
|
||||
# sha256, and re-point CONFORMANCE_PKG at the verified local tarball.
|
||||
# When CONFORMANCE_PKG is a registry spec, this step is a no-op (npm's
|
||||
# own integrity check applies).
|
||||
run: |
|
||||
case "$CONFORMANCE_PKG" in
|
||||
https://*)
|
||||
curl -fsSL "$CONFORMANCE_PKG" -o /tmp/conformance.tgz
|
||||
echo "$CONFORMANCE_PKG_SHA256 /tmp/conformance.tgz" | sha256sum -c -
|
||||
echo "CONFORMANCE_PKG=file:/tmp/conformance.tgz" >> "$GITHUB_ENV"
|
||||
;;
|
||||
esac
|
||||
- run: uv sync --frozen --all-extras --package mcp
|
||||
- name: Run client conformance (all suite)
|
||||
# The harness runs all scenarios via unbounded Promise.all; with 40
|
||||
|
|
|
|||
Loading…
Reference in a new issue