"""Pure-function tests of :mod:`mcp.shared.inbound`. Independent verifier of the classifier: every ladder rung is exercised pass+fail with no `mcp.server` / transport imports and no inlined error-code or protocol-version literals — all facts are imported from their one source. """ import dataclasses from collections.abc import Iterator, Mapping from typing import Any import pytest from mcp_types import ( CLIENT_CAPABILITIES_META_KEY, CLIENT_INFO_META_KEY, PROTOCOL_VERSION_META_KEY, ) from mcp_types.jsonrpc import ( HEADER_MISMATCH, INVALID_PARAMS, INVALID_REQUEST, METHOD_NOT_FOUND, MISSING_REQUIRED_CLIENT_CAPABILITY, PARSE_ERROR, UNSUPPORTED_PROTOCOL_VERSION, ) from mcp_types.version import LATEST_HANDSHAKE_VERSION, LATEST_MODERN_VERSION, MODERN_PROTOCOL_VERSIONS from mcp.shared.inbound import ( _SUBSCHEMA_LIST, _SUBSCHEMA_MAP, _SUBSCHEMA_SINGLE, ERROR_CODE_HTTP_STATUS, MCP_METHOD_HEADER, MCP_NAME_HEADER, MCP_PROTOCOL_VERSION_HEADER, NAME_BEARING_METHODS, InboundLadderRejection, InboundModernRoute, classify_inbound_request, decode_header_value, encode_header_value, find_duplicated_routing_header, find_invalid_x_mcp_header, mcp_param_headers, validate_mcp_param_headers, x_mcp_header_map, ) CLIENT_INFO = {"name": "t", "version": "0"} CLIENT_CAPS: dict[str, Any] = {} def envelope( method: str = "tools/list", *, version: str = LATEST_MODERN_VERSION, drop: frozenset[str] = frozenset(), extra_params: dict[str, Any] | None = None, ) -> dict[str, Any]: """Build a JSON-RPC body carrying a complete modern `_meta` envelope. `drop` removes named envelope keys so rung-1 failures are driven from one table instead of repeating reserved-key constants per call site. """ meta: dict[str, Any] = { PROTOCOL_VERSION_META_KEY: version, CLIENT_INFO_META_KEY: CLIENT_INFO, CLIENT_CAPABILITIES_META_KEY: CLIENT_CAPS, } for key in drop: del meta[key] params: dict[str, Any] = {"_meta": meta} if extra_params: params.update(extra_params) return {"jsonrpc": "2.0", "id": 1, "method": method, "params": params} def matching_headers(body: dict[str, Any]) -> dict[str, str]: """The minimal lowercase HTTP header set that agrees with `body` for rung 2.""" headers = { MCP_PROTOCOL_VERSION_HEADER: body["params"]["_meta"][PROTOCOL_VERSION_META_KEY], MCP_METHOD_HEADER: body["method"], } name_key = NAME_BEARING_METHODS.get(body["method"]) if name_key is not None and name_key in body["params"]: headers[MCP_NAME_HEADER] = encode_header_value(body["params"][name_key]) return headers def assert_rejected(result: object, code: int) -> InboundLadderRejection: assert isinstance(result, InboundLadderRejection) assert result.code == code return result # --- rung 1: envelope-three-keys ----------------------------------------------- @pytest.mark.parametrize( "body", [ pytest.param({"jsonrpc": "2.0", "id": 1, "method": "tools/list"}, id="no-params"), pytest.param({"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}, id="no-meta"), pytest.param(envelope(drop=frozenset({PROTOCOL_VERSION_META_KEY})), id="meta-missing-version"), pytest.param(envelope(drop=frozenset({CLIENT_INFO_META_KEY})), id="meta-missing-client-info"), pytest.param(envelope(drop=frozenset({CLIENT_CAPABILITIES_META_KEY})), id="meta-missing-client-caps"), ], ) def test_envelope_rung_rejects_missing_keys(body: dict[str, Any]) -> None: """Spec-mandated: a modern request lacking any of the three reserved `_meta` keys is rejected INVALID_PARAMS.""" rejection = assert_rejected(classify_inbound_request(body), INVALID_PARAMS) assert rejection.data is None @pytest.mark.parametrize( "body", [ pytest.param({"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": None}, id="params-none"), pytest.param({"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {"_meta": None}}, id="meta-none"), pytest.param( {"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {"_meta": 0}}, id="meta-non-mapping" ), ], ) def test_envelope_rung_rejects_non_mapping_shapes(body: dict[str, Any]) -> None: """Spec-mandated: non-mapping `params` / `_meta` cannot carry the envelope and reject INVALID_PARAMS.""" assert_rejected(classify_inbound_request(body), INVALID_PARAMS) @pytest.mark.parametrize("version", [7, None, ["2026-07-28"]], ids=["int", "null", "list"]) def test_envelope_rung_rejects_non_string_protocol_version(version: Any) -> None: """A present-but-non-string protocol version is a shape defect, rejected INVALID_PARAMS: it must never become -32022 (the one code auto-negotiating clients do not fall back from), and must not escape as a ValidationError from the version rung's own typed payload (`requested` is a `str` field).""" body = envelope() body["params"]["_meta"][PROTOCOL_VERSION_META_KEY] = version rejection = assert_rejected(classify_inbound_request(body), INVALID_PARAMS) assert "string" in rejection.message def test_non_string_protocol_version_over_http_still_rejects_at_the_header_rung() -> None: """SDK-defined: the non-string guard sits after the header rung, so over HTTP a present version header (a string, which can never equal a non-string body value) keeps producing HEADER_MISMATCH - the guard's wire delta is confined to header-less transports.""" body = envelope() headers = matching_headers(body) body["params"]["_meta"][PROTOCOL_VERSION_META_KEY] = 7 assert_rejected(classify_inbound_request(body, headers=headers), HEADER_MISMATCH) @pytest.mark.parametrize("version", [7, None], ids=["int", "null"]) def test_absent_version_header_rejects_before_the_string_guard(version: Any) -> None: """SDK-defined: the version header must be PRESENT, not merely equal - a null body version would otherwise slip the equality check (None == None) - so an absent header is HEADER_MISMATCH for every body value and the string guard stays reachable only on header-less transports.""" body = envelope() headers = matching_headers(body) del headers[MCP_PROTOCOL_VERSION_HEADER] body["params"]["_meta"][PROTOCOL_VERSION_META_KEY] = version assert_rejected(classify_inbound_request(body, headers=headers), HEADER_MISMATCH) # --- rung 2: protocol-version-supported ---------------------------------------- def test_version_rung_rejects_unsupported_with_data_shape() -> None: """Spec-mandated: an envelope version outside the modern set rejects with the `supported`/`requested` data.""" rejection = assert_rejected( classify_inbound_request(envelope(version=LATEST_HANDSHAKE_VERSION)), UNSUPPORTED_PROTOCOL_VERSION, ) assert rejection.data == { "supported": list(MODERN_PROTOCOL_VERSIONS), "requested": LATEST_HANDSHAKE_VERSION, } def test_version_rung_data_reflects_supplied_supported_list() -> None: """SDK-defined: the caller-supplied `supported_modern_versions` is what rejection `data.supported` echoes.""" custom = (LATEST_HANDSHAKE_VERSION,) rejection = assert_rejected( classify_inbound_request(envelope(), supported_modern_versions=custom), UNSUPPORTED_PROTOCOL_VERSION, ) assert rejection.data == {"supported": list(custom), "requested": LATEST_MODERN_VERSION} # --- rung 3: header ↔ envelope agreement --------------------------------------- def test_header_rung_does_not_reject_when_headers_arg_is_none() -> None: """SDK-defined: `headers=None` (non-HTTP transports) means rung 3 has nothing to check and the ladder proceeds.""" result = classify_inbound_request(envelope(), headers=None) assert isinstance(result, InboundModernRoute) def test_header_rung_passes_when_header_matches_envelope() -> None: """Spec-mandated: an HTTP version header equal to the envelope version passes rung 3.""" body = envelope() result = classify_inbound_request(body, headers=matching_headers(body)) assert isinstance(result, InboundModernRoute) @pytest.mark.parametrize( "headers", [ pytest.param({MCP_PROTOCOL_VERSION_HEADER: LATEST_HANDSHAKE_VERSION}, id="mismatch"), pytest.param({}, id="header-absent"), ], ) def test_header_rung_rejects_on_disagreement(headers: dict[str, str]) -> None: """Spec-mandated: an absent or mismatched HTTP version header rejects HEADER_MISMATCH.""" assert_rejected(classify_inbound_request(envelope(), headers=headers), HEADER_MISMATCH) @pytest.mark.parametrize( "override", [ pytest.param({MCP_METHOD_HEADER: "prompts/list"}, id="method-mismatch"), pytest.param({MCP_METHOD_HEADER: "TOOLS/LIST"}, id="method-case-mismatch"), ], ) def test_header_rung_rejects_method_header_disagreement(override: dict[str, str]) -> None: """Spec-mandated: `Mcp-Method` must equal `body.method` exactly (case-sensitive) → else HEADER_MISMATCH.""" body = envelope() rejection = assert_rejected( classify_inbound_request(body, headers=matching_headers(body) | override), HEADER_MISMATCH ) assert MCP_METHOD_HEADER in rejection.message def test_header_rung_rejects_missing_method_header() -> None: """Spec-mandated: an HTTP request on the modern path without `Mcp-Method` is HEADER_MISMATCH.""" body = envelope() headers = matching_headers(body) del headers[MCP_METHOD_HEADER] assert_rejected(classify_inbound_request(body, headers=headers), HEADER_MISMATCH) @pytest.mark.parametrize( ("method", "name_key"), [(m, k) for m, k in NAME_BEARING_METHODS.items()], ) def test_header_rung_rejects_missing_or_mismatched_name_header_for_name_bearing_methods( method: str, name_key: str ) -> None: """Spec-mandated: when the body carries the named param, `Mcp-Name` must be present and equal it.""" body = envelope(method, extra_params={name_key: "expected"}) headers = matching_headers(body) # Mismatch assert_rejected(classify_inbound_request(body, headers=headers | {MCP_NAME_HEADER: "wrong"}), HEADER_MISMATCH) # Absent del headers[MCP_NAME_HEADER] assert_rejected(classify_inbound_request(body, headers=headers), HEADER_MISMATCH) def test_header_rung_decodes_base64_sentinel_before_comparing_name() -> None: """Spec-mandated: servers MUST decode the `=?base64?...?=` sentinel before comparing `Mcp-Name`.""" body = envelope("tools/call", extra_params={"name": "résumé"}) headers = matching_headers(body) assert headers[MCP_NAME_HEADER].startswith("=?base64?") result = classify_inbound_request(body, headers=headers) assert isinstance(result, InboundModernRoute) def test_header_rung_does_not_require_name_header_for_non_name_bearing_method() -> None: """SDK-defined: a method outside `NAME_BEARING_METHODS` ignores `Mcp-Name` entirely.""" body = envelope("tools/list") result = classify_inbound_request(body, headers=matching_headers(body) | {MCP_NAME_HEADER: "anything"}) assert isinstance(result, InboundModernRoute) def test_header_rung_does_not_require_name_header_when_body_omits_the_named_param() -> None: """SDK-defined: a name-bearing method whose body lacks the named param skips the `Mcp-Name` check — the param's absence is INVALID_PARAMS later, not HEADER_MISMATCH here.""" body = envelope("tools/call") result = classify_inbound_request(body, headers=matching_headers(body)) assert isinstance(result, InboundModernRoute) # --- all rungs pass ------------------------------------------------------------ def test_all_rungs_pass_yields_route() -> None: """Spec-mandated: a complete envelope at a supported version with agreeing header routes, surfacing the envelope.""" body = envelope() result = classify_inbound_request(body, headers=matching_headers(body)) assert isinstance(result, InboundModernRoute) assert result.protocol_version == LATEST_MODERN_VERSION assert result.client_info == CLIENT_INFO assert result.client_capabilities == CLIENT_CAPS @pytest.mark.parametrize("method", ["initialize", "myorg/custom", "does/not/exist"]) def test_classifier_passes_unknown_method_through_to_route(method: str) -> None: """SDK-defined: the classifier does not gate on method — kernel dispatch is the single owner of that decision.""" body = envelope(method) result = classify_inbound_request(body, headers=matching_headers(body)) assert isinstance(result, InboundModernRoute) def test_ladder_first_failure_wins() -> None: """Spec-mandated: rungs evaluate in order — header-mismatch and version-unsupported would both fail; the header rung fires first so an inconsistent client is told it disagrees with itself rather than that its body version is unsupported.""" body = envelope(version=LATEST_HANDSHAKE_VERSION) result = classify_inbound_request(body, headers={MCP_PROTOCOL_VERSION_HEADER: LATEST_MODERN_VERSION}) assert_rejected(result, HEADER_MISMATCH) # --- ERROR_CODE_HTTP_STATUS ---------------------------------------------------- @pytest.mark.parametrize( ("code", "status"), [ (PARSE_ERROR, 400), (INVALID_REQUEST, 400), (INVALID_PARAMS, 400), (HEADER_MISMATCH, 400), (MISSING_REQUIRED_CLIENT_CAPABILITY, 400), (UNSUPPORTED_PROTOCOL_VERSION, 400), (METHOD_NOT_FOUND, 404), ], ) def test_error_code_http_status_table(code: int, status: int) -> None: """SDK-defined: pins the JSON-RPC error code → HTTP status mapping the streamable transport reads.""" assert ERROR_CODE_HTTP_STATUS[code] == status def test_error_code_http_status_covers_every_ladder_code() -> None: """SDK-defined: every code the ladder can emit has an HTTP-status entry, so the transport never has to default.""" ladder_codes = {INVALID_PARAMS, UNSUPPORTED_PROTOCOL_VERSION, HEADER_MISMATCH} assert ladder_codes <= ERROR_CODE_HTTP_STATUS.keys() # --- shape invariants ---------------------------------------------------------- def test_verdict_dataclasses_are_frozen() -> None: """SDK-defined: both verdict dataclasses are frozen so a route/rejection cannot be mutated after classification.""" route = classify_inbound_request(envelope()) assert isinstance(route, InboundModernRoute) rejection = InboundLadderRejection(code=METHOD_NOT_FOUND, message="m") for verdict in (route, rejection): with pytest.raises(dataclasses.FrozenInstanceError): setattr(verdict, "message", "mutated") # --- header-value codec -------------------------------------------------------- @pytest.mark.parametrize( "raw", ["plain", "with internal space", "", " edge-ws ", "résumé", "a\r\nb", "=?base64?Zm9v?="], ) def test_decode_header_value_round_trips_encode(raw: str) -> None: """SDK-defined: `decode_header_value` is the exact inverse of `encode_header_value` over the full input domain.""" assert decode_header_value(encode_header_value(raw)) == raw def test_decode_header_value_passes_none_and_plain_through() -> None: """SDK-defined: `None` in → `None` out so callers can pass `headers.get(...)` directly; plain stays verbatim.""" assert decode_header_value(None) is None assert decode_header_value("plain") == "plain" @pytest.mark.parametrize("bad", ["=?base64?not base64!?=", "=?base64?gA==?="]) def test_decode_header_value_returns_none_for_malformed_sentinel(bad: str) -> None: """SDK-defined: a sentinel with bad base64 or bad UTF-8 decodes to `None`, so it can never match a body value.""" assert decode_header_value(bad) is None # --- NAME_BEARING_METHODS ------------------------------------------------------ def test_name_bearing_methods_table_matches_spec() -> None: """Spec-mandated: pins the method → name-param table the client emit and server validate share.""" assert NAME_BEARING_METHODS == {"tools/call": "name", "prompts/get": "name", "resources/read": "uri"} # --- find_invalid_x_mcp_header ------------------------------------------------- def _schema(**props: Any) -> dict[str, Any]: return {"type": "object", "properties": props} @pytest.mark.parametrize( "input_schema", [ pytest.param(None, id="none"), pytest.param("not-a-mapping", id="non-mapping"), pytest.param({"type": "object"}, id="no-properties"), pytest.param({"type": "object", "properties": "not-a-mapping"}, id="properties-non-mapping"), pytest.param(_schema(a={"type": "string"}), id="no-annotation"), pytest.param(_schema(a={"type": "string", "x-mcp-header": "Region"}), id="valid-string"), pytest.param(_schema(a={"type": "integer", "x-mcp-header": "Count"}), id="valid-integer"), pytest.param(_schema(a={"type": "boolean", "x-mcp-header": "Flag"}), id="valid-boolean"), pytest.param( _schema(a={"type": "string", "x-mcp-header": "A"}, b={"type": "string", "x-mcp-header": "B"}), id="two-distinct", ), pytest.param(_schema(a="not-a-mapping", b={"type": "string", "x-mcp-header": "B"}), id="non-mapping-prop"), pytest.param( _schema(outer={"type": "object", "properties": {"r": {"type": "string", "x-mcp-header": "R"}}}), id="nested-on-properties-chain", ), pytest.param( _schema(a={"type": "string", "default": {"x-mcp-header": "ignored"}}), id="annotation-lookalike-in-default-is-data", ), pytest.param( _schema(a={"type": "string", "examples": [{"x-mcp-header": "ignored"}]}), id="annotation-lookalike-in-examples-is-data", ), pytest.param( _schema(a={"type": "string", "const": {"x-mcp-header": "ignored"}}), id="annotation-lookalike-in-const-is-data", ), pytest.param( _schema(a={"type": "string", "enum": [{"x-mcp-header": "ignored"}]}), id="annotation-lookalike-in-enum-is-data", ), pytest.param( {"properties": {"a": {"type": "string", "x-mcp-header": "R"}}, "$ref": "#/$defs/loop"}, id="ref-is-not-dereferenced", ), pytest.param( {"type": "object", "allOf": 0, "anyOf": [], "$defs": 0, "patternProperties": {}}, id="malformed-or-empty-applicators-ignored", ), ], ) def test_find_invalid_x_mcp_header_accepts_valid_or_absent_annotations(input_schema: Any) -> None: """Spec-mandated: a schema without annotations, or with annotations that are RFC 9110 tokens on integer/string/boolean properties reachable via a pure `properties` chain and case-insensitively unique across the whole schema, is valid.""" assert find_invalid_x_mcp_header(input_schema) is None @pytest.mark.parametrize( "input_schema", [ pytest.param(_schema(a={"type": "string", "x-mcp-header": ""}), id="empty"), pytest.param(_schema(a={"type": "string", "x-mcp-header": "My Region"}), id="space"), pytest.param(_schema(a={"type": "string", "x-mcp-header": "Region:Primary"}), id="colon"), pytest.param(_schema(a={"type": "string", "x-mcp-header": "Région"}), id="non-ascii"), pytest.param(_schema(a={"type": "string", "x-mcp-header": "Region\t1"}), id="control-char"), pytest.param(_schema(a={"type": "string", "x-mcp-header": 42}), id="non-string"), pytest.param(_schema(a={"type": "string", "x-mcp-header": 10**5000}), id="oversized-int-header"), pytest.param(_schema(a={"type": "object", "x-mcp-header": "Data"}), id="on-object"), pytest.param(_schema(a={"type": "array", "x-mcp-header": "Items"}), id="on-array"), pytest.param(_schema(a={"type": "null", "x-mcp-header": "Nil"}), id="on-null"), pytest.param(_schema(a={"type": "number", "x-mcp-header": "Ratio"}), id="on-number"), pytest.param(_schema(a={"type": ["string", "null"], "x-mcp-header": "Maybe"}), id="array-type"), pytest.param(_schema(a={"type": {"not": "valid"}, "x-mcp-header": "Bad"}), id="dict-type"), pytest.param(_schema(a={"type": 10**5000, "x-mcp-header": "Big"}), id="oversized-int-type"), pytest.param(_schema(a={"x-mcp-header": "NoType"}), id="missing-type"), pytest.param( _schema(a={"type": "string", "x-mcp-header": "Region"}, b={"type": "string", "x-mcp-header": "Region"}), id="duplicate-same-case", ), pytest.param( _schema(a={"type": "string", "x-mcp-header": "MyField"}, b={"type": "string", "x-mcp-header": "myfield"}), id="duplicate-diff-case", ), pytest.param( {"allOf": [{"type": "object", "properties": {"a": {"type": "string", "x-mcp-header": "X"}}}]}, id="properties-chain-not-restored-below-an-applicator", ), pytest.param( {"type": "string", "x-mcp-header": "X"}, id="on-root-schema", ), pytest.param( _schema( a={"type": "string", "x-mcp-header": "Region"}, o={"type": "object", "properties": {"b": {"type": "string", "x-mcp-header": "region"}}}, ), id="duplicate-across-nesting-levels", ), pytest.param( _schema(outer={"type": "object", "properties": {"r": {"type": "string", "x-mcp-header": "bad name"}}}), id="nested-bad-token", ), pytest.param( _schema(outer={"type": "object", "properties": {"r": {"type": "object", "x-mcp-header": "R"}}}), id="nested-non-primitive", ), ], ) def test_find_invalid_x_mcp_header_rejects_malformed_annotations(input_schema: dict[str, Any]) -> None: """Spec-mandated: empty / non-token / non-primitive / off-chain / duplicate `x-mcp-header` annotations yield a reason string.""" assert isinstance(find_invalid_x_mcp_header(input_schema), str) # Keyword → a value of that keyword's own JSON Schema shape carrying an annotated subschema. # Deliberately a literal table, independent of the `_SUBSCHEMA_*` sets in `inbound.py`: # dropping a keyword from the walk must FAIL its case here, not shrink the parametrization. _ANNOTATED = {"type": "string", "x-mcp-header": "Region"} _APPLICATOR_CASES: dict[str, Any] = { "$defs": {"T": _ANNOTATED}, "additionalProperties": _ANNOTATED, "allOf": [_ANNOTATED], "anyOf": [_ANNOTATED], "contains": _ANNOTATED, "contentSchema": _ANNOTATED, "definitions": {"T": _ANNOTATED}, "dependentSchemas": {"k": _ANNOTATED}, "else": _ANNOTATED, "if": _ANNOTATED, "items": _ANNOTATED, "not": _ANNOTATED, "oneOf": [_ANNOTATED], "patternProperties": {"^a": _ANNOTATED}, "prefixItems": [_ANNOTATED], "propertyNames": _ANNOTATED, "then": _ANNOTATED, "unevaluatedItems": _ANNOTATED, "unevaluatedProperties": _ANNOTATED, } @pytest.mark.parametrize("keyword", sorted(_APPLICATOR_CASES)) def test_find_invalid_x_mcp_header_rejects_annotations_under_every_non_properties_applicator(keyword: str) -> None: """Spec-mandated: a property reached through any applicator other than `properties` is not statically reachable, so its annotation invalidates the whole tool definition.""" schema = _schema(ok={"type": "string"}) | {keyword: _APPLICATOR_CASES[keyword]} assert isinstance(find_invalid_x_mcp_header(schema), str) def test_schema_walk_applicator_keywords_match_the_pinned_reject_cases() -> None: """SDK-defined: a keyword added to the walk must gain a literal reject case above (a removed keyword already fails its case there).""" assert _SUBSCHEMA_LIST | _SUBSCHEMA_MAP | _SUBSCHEMA_SINGLE == set(_APPLICATOR_CASES) def test_find_invalid_x_mcp_header_reports_dotted_path_for_nested_property() -> None: """SDK-defined: the reason string names the nested property by its dotted `properties` path.""" schema = _schema(outer={"type": "object", "properties": {"r": {"type": "object", "x-mcp-header": "R"}}}) reason = find_invalid_x_mcp_header(schema) assert reason is not None and "'outer.r'" in reason # --- x_mcp_header_map ---------------------------------------------------------- def test_x_mcp_header_map_keys_top_level_and_nested_properties_by_path() -> None: """Each annotated property maps to its token under its full `properties` path; unannotated props are absent.""" schema = _schema( region={"type": "string", "x-mcp-header": "Region"}, query={"type": "string"}, outer={"type": "object", "properties": {"inner": {"type": "string", "x-mcp-header": "Inner"}}}, ) assert x_mcp_header_map(schema) == {("region",): "Region", ("outer", "inner"): "Inner"} @pytest.mark.parametrize("input_schema", [None, "not-a-mapping", {"type": "object"}]) def test_x_mcp_header_map_empty_for_schemas_without_annotations(input_schema: Any) -> None: assert x_mcp_header_map(input_schema) == {} # --- mcp_param_headers --------------------------------------------------------- def test_mcp_param_headers_renders_primitive_types_per_spec() -> None: """String verbatim, integer as decimal, boolean as lowercase `true`/`false`, header named `Mcp-Param-`.""" header_map = {("region",): "Region", ("priority",): "Priority", ("verbose",): "Verbose", ("debug",): "Debug"} arguments = {"region": "us-west1", "priority": 42, "verbose": False, "debug": True} assert mcp_param_headers(header_map, arguments) == { "Mcp-Param-Region": "us-west1", "Mcp-Param-Priority": "42", "Mcp-Param-Verbose": "false", "Mcp-Param-Debug": "true", } @pytest.mark.parametrize( ("value", "encoded"), [ pytest.param("us-west1", "us-west1", id="plain-ascii"), pytest.param("Hello, 世界", "=?base64?SGVsbG8sIOS4lueVjA==?=", id="non-ascii"), pytest.param(" padded ", "=?base64?IHBhZGRlZCA=?=", id="edge-whitespace"), pytest.param("line1\nline2", "=?base64?bGluZTEKbGluZTI=?=", id="control-char"), pytest.param("=?base64?literal?=", "=?base64?PT9iYXNlNjQ/bGl0ZXJhbD89?=", id="sentinel-lookalike"), ], ) def test_mcp_param_headers_base64_wraps_header_unsafe_strings(value: str, encoded: str) -> None: """Matches the spec's Value Encoding table: a non-header-safe string is base64-sentinel wrapped.""" assert mcp_param_headers({("v",): "Val"}, {"v": value}) == {"Mcp-Param-Val": encoded} def test_mcp_param_headers_omits_absent_or_null_arguments() -> None: """A path that hits a missing key or a `None` value emits no header (spec: omit when no value is present).""" header_map = {("present",): "Present", ("missing",): "Missing", ("nulled",): "Nulled"} assert mcp_param_headers(header_map, {"present": "x", "nulled": None}) == {"Mcp-Param-Present": "x"} def test_mcp_param_headers_reads_nested_argument_path() -> None: """A nested annotated property reads its value at the matching nested `arguments` path.""" headers = mcp_param_headers({("outer", "inner"): "Inner"}, {"outer": {"inner": "deep"}}) assert headers == {"Mcp-Param-Inner": "deep"} def test_mcp_param_headers_omits_when_nested_path_is_broken() -> None: """A nested path through a non-mapping or missing intermediate node yields no header.""" header_map = {("outer", "inner"): "Inner"} assert mcp_param_headers(header_map, {"outer": "not-a-mapping"}) == {} assert mcp_param_headers(header_map, {}) == {} # --- validate_mcp_param_headers -------------------------------------------- REGION_SCHEMA: dict[str, Any] = { "type": "object", "properties": {"region": {"type": "string", "x-mcp-header": "Region"}}, } @pytest.mark.parametrize( ("argument", "header"), [ pytest.param("Hello", "Hello", id="plain-literal"), pytest.param("Hello", "=?base64?SGVsbG8=?=", id="valid-sentinel"), pytest.param("", "=?base64??=", id="empty-sentinel"), pytest.param("SGVsbG8=", "SGVsbG8=", id="missing-prefix-is-literal"), pytest.param("=?base64?SGVsbG8=", "=?base64?SGVsbG8=", id="missing-suffix-is-literal"), ], ) def test_validate_mcp_param_headers_accepts_agreeing_header_and_argument(argument: str, header: str) -> None: """Spec Value Encoding: a fully-wrapped sentinel decodes before comparison; anything else is a literal.""" assert validate_mcp_param_headers(REGION_SCHEMA, {"region": argument}, {"Mcp-Param-Region": header}) is None @pytest.mark.parametrize( "header", [ pytest.param("=?base64?SGVsbG8?=", id="missing-padding"), pytest.param("=?base64?SGVs!!!bG8=?=", id="non-alphabet-chars"), pytest.param("=?base64?SGVsbG9=?=", id="non-canonical-trailing-bits"), pytest.param("=?base64?gA==?=", id="invalid-utf8"), ], ) def test_validate_mcp_param_headers_rejects_malformed_sentinel(header: str) -> None: """Spec: servers MUST reject a recognized header whose sentinel cannot be strictly decoded — not a literal.""" rejection = assert_rejected( validate_mcp_param_headers(REGION_SCHEMA, {"region": "Hello"}, {"Mcp-Param-Region": header}), HEADER_MISMATCH, ) assert "malformed base64" in rejection.message def test_validate_mcp_param_headers_rejects_missing_header_for_present_argument() -> None: """Spec table: client omits the header but the value is in the body → server MUST reject.""" rejection = assert_rejected( validate_mcp_param_headers(REGION_SCHEMA, {"region": "test-value"}, {}), HEADER_MISMATCH ) assert "missing" in rejection.message @pytest.mark.parametrize( "arguments", [pytest.param({}, id="absent"), pytest.param({"region": None}, id="null")], ) def test_validate_mcp_param_headers_rejects_orphan_header_for_absent_or_null_argument( arguments: dict[str, Any], ) -> None: """SDK-defined posture on a spec gap: an orphan header is the routing-spoof case; go rejects too, ts skips.""" rejection = assert_rejected( validate_mcp_param_headers(REGION_SCHEMA, arguments, {"Mcp-Param-Region": "eu"}), HEADER_MISMATCH ) assert "absent" in rejection.message def test_validate_mcp_param_headers_rejects_value_mismatch() -> None: rejection = assert_rejected( validate_mcp_param_headers(REGION_SCHEMA, {"region": "us"}, {"Mcp-Param-Region": "eu"}), HEADER_MISMATCH ) assert "does not match" in rejection.message def test_validate_mcp_param_headers_accepts_absent_argument_with_no_header() -> None: """Spec table: parameter not in arguments / null → client MUST omit, server MUST NOT expect.""" assert validate_mcp_param_headers(REGION_SCHEMA, {}, {}) is None assert validate_mcp_param_headers(REGION_SCHEMA, {"region": None}, {}) is None def test_validate_mcp_param_headers_matches_header_names_case_insensitively() -> None: """Spec Case Sensitivity: header-name comparison MUST be case-insensitive.""" assert validate_mcp_param_headers(REGION_SCHEMA, {"region": "eu"}, {"MCP-PARAM-REGION": "eu"}) is None rejection = validate_mcp_param_headers(REGION_SCHEMA, {"region": "us"}, {"MCP-PARAM-REGION": "eu"}) assert_rejected(rejection, HEADER_MISMATCH) def test_validate_mcp_param_headers_ignores_undeclared_mcp_param_headers() -> None: """Spec: an undeclared `Mcp-Param-*` header is unrecognized — forwarded and ignored, never a failure.""" headers = {"Mcp-Param-Region": "eu", "Mcp-Param-Undeclared": "=?base64?not even base64?="} assert validate_mcp_param_headers(REGION_SCHEMA, {"region": "eu"}, headers) is None def test_validate_mcp_param_headers_validates_nothing_for_an_invalid_annotation_schema() -> None: """Spec gives definition rejection to clients (drop the tool), so an invalid schema recognizes no headers.""" invalid = { "type": "object", "properties": { "region": {"type": "string", "x-mcp-header": "Region"}, "dupe": {"type": "string", "x-mcp-header": "region"}, }, } assert find_invalid_x_mcp_header(invalid) is not None assert validate_mcp_param_headers(invalid, {"region": "us"}, {"Mcp-Param-Region": "eu"}) is None def test_validate_mcp_param_headers_reads_nested_argument_paths() -> None: """A nested annotated property compares against the matching nested `arguments` path; a broken path is absent.""" schema = { "type": "object", "properties": { "outer": {"type": "object", "properties": {"inner": {"type": "string", "x-mcp-header": "Inner"}}} }, } assert validate_mcp_param_headers(schema, {"outer": {"inner": "deep"}}, {"Mcp-Param-Inner": "deep"}) is None rejection = validate_mcp_param_headers(schema, {"outer": {"inner": "deep"}}, {"Mcp-Param-Inner": "other"}) assert_rejected(rejection, HEADER_MISMATCH) assert validate_mcp_param_headers(schema, {"outer": "not-a-mapping"}, {}) is None def test_validate_mcp_param_headers_compares_booleans_against_true_false_rendering() -> None: """Booleans compare against the lowercase `true`/`false` rendering the client emits.""" schema = {"type": "object", "properties": {"flag": {"type": "boolean", "x-mcp-header": "Flag"}}} assert validate_mcp_param_headers(schema, {"flag": True}, {"Mcp-Param-Flag": "true"}) is None assert validate_mcp_param_headers(schema, {"flag": False}, {"Mcp-Param-Flag": "false"}) is None rejection = validate_mcp_param_headers(schema, {"flag": True}, {"Mcp-Param-Flag": "True"}) assert_rejected(rejection, HEADER_MISMATCH) INTEGER_SCHEMA: dict[str, Any] = { "type": "object", "properties": {"n": {"type": "integer", "x-mcp-header": "N"}}, } @pytest.mark.parametrize( ("body_value", "header", "matches"), [ pytest.param(42, "42", True, id="exact"), pytest.param(42, "42.0", True, id="trailing-zero-fraction"), pytest.param(42, "42.000", True, id="long-zero-fraction"), pytest.param(42, "42.5", False, id="real-fraction"), pytest.param(42, "1e2", False, id="scientific-notation-never-numeric"), pytest.param(42, "43", False, id="different-value"), pytest.param(-7, "-7.0", True, id="negative"), pytest.param(9007199254740993, "9007199254740993", True, id="beyond-ieee754-safe-range-exact"), pytest.param(9007199254740993, "9007199254740992", False, id="beyond-ieee754-safe-range-off-by-one"), ], ) def test_validate_mcp_param_headers_compares_integers_numerically(body_value: int, header: str, matches: bool) -> None: """Spec SHOULD: integers compare numerically (`42` == `42.0`) — gated to canonical decimals, compared exactly.""" result = validate_mcp_param_headers(INTEGER_SCHEMA, {"n": body_value}, {"Mcp-Param-N": header}) if matches: assert result is None else: assert_rejected(result, HEADER_MISMATCH) def test_validate_mcp_param_headers_non_primitive_body_value_rejects_only_when_a_header_claims_it() -> None: """A header claiming a non-primitive argument is a mismatch; without one, rejection is params validation's job.""" rejection = assert_rejected( validate_mcp_param_headers(REGION_SCHEMA, {"region": {"k": "v"}}, {"Mcp-Param-Region": "x"}), HEADER_MISMATCH, ) assert "does not match" in rejection.message assert validate_mcp_param_headers(REGION_SCHEMA, {"region": {"k": "v"}}, {}) is None assert validate_mcp_param_headers(REGION_SCHEMA, {"region": [1, 2]}, {}) is None class _RepeatedHeaders(Mapping[str, str]): """A header carrier whose `items()` yields duplicate names, like a raw HTTP header list.""" def __init__(self, pairs: list[tuple[str, str]]) -> None: self._pairs = pairs def __getitem__(self, key: str) -> str: return next(value for name, value in self._pairs if name == key) def __iter__(self) -> Iterator[str]: return (name for name, _ in self._pairs) def __len__(self) -> int: return len(self._pairs) def items(self) -> Any: return list(self._pairs) def test_validate_mcp_param_headers_rejects_a_recognized_header_supplied_more_than_once() -> None: """Duplicate recognized headers reject even when one matches: first-copy readers and last-wins checks diverge.""" headers = _RepeatedHeaders([("Mcp-Param-Region", "spoofed"), ("mcp-param-region", "eu")]) # The carrier behaves like a raw header list: first-wins lookup, every line iterated. assert headers["Mcp-Param-Region"] == "spoofed" assert len(headers) == len(list(headers)) == 2 rejection = assert_rejected(validate_mcp_param_headers(REGION_SCHEMA, {"region": "eu"}, headers), HEADER_MISMATCH) assert "more than once" in rejection.message noisy = _RepeatedHeaders([("Mcp-Param-Region", "eu"), ("Mcp-Param-Other", "a"), ("mcp-param-other", "b")]) assert validate_mcp_param_headers(REGION_SCHEMA, {"region": "eu"}, noisy) is None def test_validate_mcp_param_headers_rejects_a_header_exceeding_the_int_conversion_limit() -> None: """A canonical-decimal header beyond CPython's int-conversion digit limit is a clean mismatch, never an error.""" rejection = validate_mcp_param_headers(INTEGER_SCHEMA, {"n": 1}, {"Mcp-Param-N": "1" * 5000}) assert_rejected(rejection, HEADER_MISMATCH) def test_validate_mcp_param_headers_compares_integral_float_bodies_numerically() -> None: """JSON Schema admits `42.0` as an integer; the numeric SHOULD applies in both directions.""" assert validate_mcp_param_headers(INTEGER_SCHEMA, {"n": 42.0}, {"Mcp-Param-N": "42"}) is None assert validate_mcp_param_headers(INTEGER_SCHEMA, {"n": 42.0}, {"Mcp-Param-N": "42.0"}) is None assert_rejected(validate_mcp_param_headers(INTEGER_SCHEMA, {"n": 42.0}, {"Mcp-Param-N": "43"}), HEADER_MISMATCH) # A genuinely fractional body value falls back to the exact string rendering. assert validate_mcp_param_headers(INTEGER_SCHEMA, {"n": 42.5}, {"Mcp-Param-N": "42.5"}) is None @pytest.mark.parametrize( "value", [ pytest.param("=?base64?SGVsbG9=?=", id="non-canonical-trailing-bits"), pytest.param("=?base64?SGVsbG8?=", id="missing-padding"), ], ) def test_decode_header_value_returns_none_for_non_canonical_base64(value: str) -> None: """Canonical base64 only: a payload that decodes but does not re-encode byte-identically is malformed.""" assert decode_header_value(value) is None def test_validate_mcp_param_headers_union_typed_annotation_invalidates_the_whole_tool() -> None: """A union-typed annotation fails the integer/string/boolean-only rule, so the whole schema validates nothing.""" union_schema = { "type": "object", "properties": {"n": {"type": ["integer", "null"], "x-mcp-header": "N"}}, } assert find_invalid_x_mcp_header(union_schema) is not None assert validate_mcp_param_headers(union_schema, {"n": 42}, {"Mcp-Param-N": "999"}) is None assert validate_mcp_param_headers(union_schema, {"n": 42}, {}) is None def test_validate_mcp_param_headers_accepts_the_clients_own_rendering_of_large_integral_floats() -> None: """A non-canonical-decimal header falls back to rendered comparison, so the client's own mirroring round-trips.""" emitted = mcp_param_headers(x_mcp_header_map(INTEGER_SCHEMA), {"n": 1e16}) assert emitted == {"Mcp-Param-N": "1e+16"} assert validate_mcp_param_headers(INTEGER_SCHEMA, {"n": 1e16}, emitted) is None assert_rejected(validate_mcp_param_headers(INTEGER_SCHEMA, {"n": 42}, {"Mcp-Param-N": "1e2"}), HEADER_MISMATCH) def test_validate_mcp_param_headers_handles_unrenderable_huge_integer_bodies_without_raising() -> None: """An integer beyond CPython's int-to-str digit limit has no rendering: claimed → mismatch, unclaimed → fine.""" huge = 10**5000 rejection = validate_mcp_param_headers(REGION_SCHEMA, {"region": huge}, {"Mcp-Param-Region": "x"}) assert_rejected(rejection, HEADER_MISMATCH) assert validate_mcp_param_headers(REGION_SCHEMA, {"region": huge}, {}) is None assert validate_mcp_param_headers(INTEGER_SCHEMA, {"n": huge}, {}) is None def test_mcp_param_headers_omits_values_with_no_scalar_rendering() -> None: """Objects, arrays, and over-limit integers have no scalar rendering, so the client omits the header.""" header_map = {("v",): "Val"} assert mcp_param_headers(header_map, {"v": {"k": 1}}) == {} assert mcp_param_headers(header_map, {"v": [1, 2]}) == {} assert mcp_param_headers(header_map, {"v": 10**5000}) == {} def test_find_duplicated_routing_header_detects_repeats_of_routing_headers_only() -> None: """Repeated routing headers report case-insensitively; `Mcp-Param-*` or unrelated repeats are ignored.""" assert find_duplicated_routing_header([("Mcp-Name", "a"), ("mcp-name", "b")]) == MCP_NAME_HEADER assert find_duplicated_routing_header([("MCP-Protocol-Version", "x"), ("mcp-protocol-version", "x")]) == ( MCP_PROTOCOL_VERSION_HEADER ) assert find_duplicated_routing_header([("Mcp-Method", "a"), ("Mcp-Method", "a")]) == MCP_METHOD_HEADER assert find_duplicated_routing_header([("Mcp-Name", "a"), ("Mcp-Param-X", "1"), ("Mcp-Param-X", "2")]) is None assert find_duplicated_routing_header([("accept", "a"), ("accept", "b")]) is None