Skip to content

Commit ba22c4a

Browse files
committed
tests: align four interaction-manifest entries with the cross-SDK canonical wording
- client-auth:scope-selection:priority — behavior now states the spec's two-step chain; the SDK's extra AS-metadata fallback moves to a divergence note. - mcpserver:tool:naming-validation — behavior now states what the SDK does (warn + register); divergence dropped (spec is SHOULD-only). - client-transport:http:404-surfaces — split: this slug now pins the SDK's surface-as-error behaviour (source=sdk); the spec MUST is tracked at the new client-transport:http:session-404-reinitialize (deferred + divergence). - sampling:tool-use:result-balance — split: this slug now states the spec's client-side -32602 check (deferred + divergence); the SDK's server-side pre-send ValueError moves to the new sampling:tool-use:server-preflight, and the existing test moves with it. Net manifest delta: +2 entries, +2 deferred, +1 divergence.
1 parent 93cc828 commit ba22c4a

3 files changed

Lines changed: 47 additions & 17 deletions

File tree

tests/interaction/_requirements.py

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -689,13 +689,9 @@ def __post_init__(self) -> None:
689689
),
690690
"mcpserver:tool:naming-validation": Requirement(
691691
source="sdk",
692-
behavior="Tool names that violate the spec's naming rules are rejected at registration time.",
693-
divergence=Divergence(
694-
note=(
695-
"MCPServer runs the SEP-986 naming check at registration (validate_and_warn_tool_name at "
696-
"tools/base.py) and logs a warning for non-conforming names, but does not reject them; the "
697-
"bool result is discarded and registration proceeds."
698-
),
692+
behavior=(
693+
"Registering a tool whose name violates the spec's tool-naming conventions emits a warning; "
694+
"registration still succeeds."
699695
),
700696
),
701697
"mcpserver:tool:output-schema:model": Requirement(
@@ -1215,9 +1211,27 @@ def __post_init__(self) -> None:
12151211
"sampling:tool-use:result-balance": Requirement(
12161212
source=f"{SPEC_BASE_URL}/client/sampling#tool-use-and-result-balance",
12171213
behavior=(
1218-
"Every assistant tool_use block in a sampling request must be matched by a tool_result with "
1219-
"the same id in the following user message; an unmatched tool_use is rejected with a ValueError "
1220-
"before the request is sent."
1214+
"In a sampling/createMessage request, every assistant tool_use block in messages MUST be "
1215+
"matched by a tool_result with the same toolUseId in the immediately-following user message; "
1216+
"an unmatched tool_use is rejected with -32602 Invalid params."
1217+
),
1218+
divergence=Divergence(
1219+
note=(
1220+
"The client does not validate inbound tool_use/tool_result balance; the SDK enforces "
1221+
"the rule server-side instead, before the request leaves the server (see "
1222+
"sampling:tool-use:server-preflight)."
1223+
),
1224+
),
1225+
deferred=(
1226+
"Not implemented on the client receive path: validation runs only on the server send path "
1227+
"(pinned by sampling:tool-use:server-preflight)."
1228+
),
1229+
),
1230+
"sampling:tool-use:server-preflight": Requirement(
1231+
source="sdk",
1232+
behavior=(
1233+
"The server validates tool_use/tool_result balance before sending a sampling/createMessage "
1234+
"request; an unmatched tool_use raises ValueError and the request never reaches the wire."
12211235
),
12221236
),
12231237
"sampling:tools:server-gated-by-capability": Requirement(
@@ -2252,6 +2266,11 @@ def __post_init__(self) -> None:
22522266
# Client transport: streamable HTTP
22532267
# ═══════════════════════════════════════════════════════════════════════════
22542268
"client-transport:http:404-surfaces": Requirement(
2269+
source="sdk",
2270+
behavior="A 404 (session expired) on a request surfaces as an error to the caller.",
2271+
transports=("streamable-http",),
2272+
),
2273+
"client-transport:http:session-404-reinitialize": Requirement(
22552274
source=f"{SPEC_BASE_URL}/basic/transports#session-management",
22562275
behavior=(
22572276
"A 404 in response to a request carrying a session ID makes the client start a new session "
@@ -2264,6 +2283,10 @@ def __post_init__(self) -> None:
22642283
"session; the spec's MUST is not satisfied."
22652284
),
22662285
),
2286+
deferred=(
2287+
"Not implemented in the SDK: the client surfaces a Session terminated error instead of "
2288+
"re-initializing (the surfaced error is pinned by client-transport:http:404-surfaces)."
2289+
),
22672290
),
22682291
"client-transport:http:accept-header-get": Requirement(
22692292
source=f"{SPEC_BASE_URL}/basic/transports#listening-for-messages-from-the-server",
@@ -2585,11 +2608,17 @@ def __post_init__(self) -> None:
25852608
"client-auth:scope-selection:priority": Requirement(
25862609
source=f"{SPEC_BASE_URL}/basic/authorization#scope-selection-strategy",
25872610
behavior=(
2588-
"The client selects the requested scope from WWW-Authenticate when present, then from the "
2589-
"protected-resource metadata, then (as an SDK addition beyond the spec's chain) from the "
2590-
"AS metadata's scopes_supported, and otherwise omits scope."
2611+
"Client selects requested scope from the WWW-Authenticate scope param if present; otherwise "
2612+
"uses scopes_supported from the PRM document; otherwise omits scope."
25912613
),
25922614
transports=("streamable-http",),
2615+
divergence=Divergence(
2616+
note=(
2617+
"The SDK inserts an extra fallback step between PRM and omit: if the authorization "
2618+
"server metadata advertises scopes_supported, that list is used (client/auth/utils.py). "
2619+
"This is beyond the spec's two-step chain."
2620+
),
2621+
),
25932622
),
25942623
"client-auth:state:verify": Requirement(
25952624
source=f"{SPEC_BASE_URL}/basic/authorization#open-redirection",

tests/interaction/lowlevel/test_sampling.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,12 +589,13 @@ async def sampling_callback(
589589
)
590590

591591

592-
@requirement("sampling:tool-use:result-balance")
592+
@requirement("sampling:tool-use:server-preflight")
593593
async def test_create_message_with_mismatched_tool_use_and_result_ids_is_rejected(connect: Connect) -> None:
594594
"""A sampling request whose tool_result ids do not match the preceding tool_use ids never leaves the server.
595595
596596
The message-structure validation runs inside create_message before the request is sent, so the
597-
client callback is never invoked and the handler observes the ValueError directly.
597+
client callback is never invoked and the handler observes the ValueError directly. The spec's
598+
client-side -32602 check is tracked separately at sampling:tool-use:result-balance.
598599
"""
599600

600601
async def list_tools(

tests/interaction/transports/test_client_transport_http.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ async def test_a_404_mid_session_surfaces_as_a_session_terminated_error() -> Non
218218
"""A 404 in response to a request after initialization is reported to the caller as an MCP error.
219219
220220
The spec says the client MUST start a new session in this situation; the SDK instead surfaces a
221-
`Session terminated` error to the caller (see the divergence on the requirement). This test pins
222-
that current behaviour.
221+
`Session terminated` error to the caller. The spec's MUST is tracked at
222+
client-transport:http:session-404-reinitialize; this test pins the SDK's current behaviour.
223223
"""
224224
server = _tooled_server()
225225
real_app = server.streamable_http_app(transport_security=NO_DNS_REBINDING_PROTECTION)

0 commit comments

Comments
 (0)