Skip to content

Commit 5a3bb27

Browse files
committed
Add the first MRTR interaction tests: 16 tests across six files
The multi-round-trip request pattern (SEP-2322, the 2026-07-28 replacement for server-initiated requests) gets its first end-to-end coverage: - lowlevel/test_mrtr.py (new): the write-once roundtrip (byte-exact requestState echo, opacity via a non-parseable state, fresh JSON-RPC id on retry), state-only retry, omit-when-absent, and parallel-call isolation via a symmetric rendezvous that provably holds both loops mid-flight. - test_elicitation.py: form-mode elicitation over MRTR (basic, decline, cancel, schema primitives) and the capability gate, which pins the current un-gated embed behaviour with a recorded divergence. - test_resources.py / test_prompts.py: the resources/read and prompts/get MRTR origins (lowlevel-only; MCPServer admits InputRequiredResult on the tools path only). - test_sampling.py / test_roots.py: sampling/createMessage and roots/list embedded as MRTR input requests, with model preferences, system prompt, and context-inclusion pass-through. Seventeen requirement entries flip from deferred to tested; five entries are minted (the request-state client obligations and the two non-tools origins). 830 -> 859 collected cells, every new cell accounted; suite green three consecutive runs; 100% line and branch on the new file with no coverage pragmas.
1 parent 75754b8 commit 5a3bb27

7 files changed

Lines changed: 1061 additions & 50 deletions

File tree

‎tests/interaction/_requirements.py‎

Lines changed: 61 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,18 @@ def __post_init__(self) -> None:
13611361
source=f"{SPEC_BASE_URL}/server/utilities/pagination#operations-supporting-pagination",
13621362
behavior="resources/list supports cursor pagination.",
13631363
),
1364+
"resources:mrtr:read:basic": Requirement(
1365+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#supported-requests",
1366+
behavior=(
1367+
"A resources/read may be answered with an input_required result; the client fulfils the "
1368+
"embedded request and the retried resources/read completes with the resource contents."
1369+
),
1370+
added_in="2026-07-28",
1371+
note=(
1372+
"Low-level Server only: MCPServer returns InputRequiredResult from tools alone, so the "
1373+
"resources/read MRTR leg has no mcpserver mirror."
1374+
),
1375+
),
13641376
"resources:read:blob": Requirement(
13651377
source=f"{SPEC_BASE_URL}/server/resources#reading-resources",
13661378
behavior="resources/read returns binary contents base64-encoded in blob.",
@@ -1686,6 +1698,18 @@ def __post_init__(self) -> None:
16861698
source=f"{SPEC_BASE_URL}/server/utilities/pagination#operations-supporting-pagination",
16871699
behavior="prompts/list supports cursor pagination.",
16881700
),
1701+
"prompts:mrtr:get:basic": Requirement(
1702+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#supported-requests",
1703+
behavior=(
1704+
"A prompts/get may be answered with an input_required result; the client fulfils the "
1705+
"embedded request and the retried prompts/get completes with the prompt messages."
1706+
),
1707+
added_in="2026-07-28",
1708+
note=(
1709+
"Low-level Server only: MCPServer returns InputRequiredResult from tools alone, so the "
1710+
"prompts/get MRTR leg has no mcpserver mirror."
1711+
),
1712+
),
16891713
# ═══════════════════════════════════════════════════════════════════════════
16901714
# Prompts: SDK guarantees
16911715
# ═══════════════════════════════════════════════════════════════════════════
@@ -2175,10 +2199,6 @@ def __post_init__(self) -> None:
21752199
),
21762200
added_in="2026-07-28",
21772201
supersedes=("sampling:create:basic", "tools:call:sampling-roundtrip"),
2178-
deferred=(
2179-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2180-
"the behaviour is implemented and drivable (phase-4 verdict)."
2181-
),
21822202
),
21832203
"sampling:mrtr:create:include-context": Requirement(
21842204
source=f"{SPEC_2026_BASE_URL}/client/sampling#context-inclusion",
@@ -2188,10 +2208,6 @@ def __post_init__(self) -> None:
21882208
),
21892209
added_in="2026-07-28",
21902210
supersedes=("sampling:create:include-context",),
2191-
deferred=(
2192-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2193-
"the behaviour is implemented and drivable (phase-4 verdict)."
2194-
),
21952211
),
21962212
"sampling:mrtr:create:model-preferences": Requirement(
21972213
source=f"{SPEC_2026_BASE_URL}/client/sampling#model-preferences",
@@ -2201,10 +2217,6 @@ def __post_init__(self) -> None:
22012217
),
22022218
added_in="2026-07-28",
22032219
supersedes=("sampling:create:model-preferences",),
2204-
deferred=(
2205-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2206-
"the behaviour is implemented and drivable (phase-4 verdict)."
2207-
),
22082220
),
22092221
"sampling:mrtr:create:system-prompt": Requirement(
22102222
source=f"{SPEC_2026_BASE_URL}/client/sampling#system-prompt",
@@ -2214,10 +2226,6 @@ def __post_init__(self) -> None:
22142226
),
22152227
added_in="2026-07-28",
22162228
supersedes=("sampling:create:system-prompt",),
2217-
deferred=(
2218-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2219-
"the behaviour is implemented and drivable (phase-4 verdict)."
2220-
),
22212229
),
22222230
"sampling:mrtr:create:audio-content": Requirement(
22232231
source=f"{SPEC_2026_BASE_URL}/client/sampling#audio-content",
@@ -2564,10 +2572,6 @@ def __post_init__(self) -> None:
25642572
"elicitation:form:action:accept",
25652573
"transport:streamable-http:server-to-client",
25662574
),
2567-
deferred=(
2568-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2569-
"the behaviour is implemented and drivable (phase-4 verdict)."
2570-
),
25712575
),
25722576
"elicitation:mrtr:form:action:cancel": Requirement(
25732577
source=f"{SPEC_2026_BASE_URL}/client/elicitation#response-actions",
@@ -2577,10 +2581,6 @@ def __post_init__(self) -> None:
25772581
),
25782582
added_in="2026-07-28",
25792583
supersedes=("elicitation:form:action:cancel",),
2580-
deferred=(
2581-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2582-
"the behaviour is implemented and drivable (phase-4 verdict)."
2583-
),
25842584
),
25852585
"elicitation:mrtr:form:action:decline": Requirement(
25862586
source=f"{SPEC_2026_BASE_URL}/client/elicitation#response-actions",
@@ -2590,10 +2590,6 @@ def __post_init__(self) -> None:
25902590
),
25912591
added_in="2026-07-28",
25922592
supersedes=("elicitation:form:action:decline",),
2593-
deferred=(
2594-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2595-
"the behaviour is implemented and drivable (phase-4 verdict)."
2596-
),
25972593
),
25982594
"elicitation:mrtr:form:schema:primitives": Requirement(
25992595
source=f"{SPEC_2026_BASE_URL}/client/elicitation#requested-schema",
@@ -2603,10 +2599,6 @@ def __post_init__(self) -> None:
26032599
),
26042600
added_in="2026-07-28",
26052601
supersedes=("elicitation:form:schema:primitives",),
2606-
deferred=(
2607-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2608-
"the behaviour is implemented and drivable (phase-4 verdict)."
2609-
),
26102602
),
26112603
"elicitation:mrtr:form:schema:enum-variants": Requirement(
26122604
source=f"{SPEC_2026_BASE_URL}/client/elicitation#requested-schema",
@@ -2641,13 +2633,18 @@ def __post_init__(self) -> None:
26412633
"inputRequests for a client whose declared capabilities do not support it (including "
26422634
"mode-level support: form vs url)."
26432635
),
2636+
divergence=Divergence(
2637+
note=(
2638+
"The server does not gate input_required input requests against the client's declared "
2639+
"capabilities: an elicitation/create is embedded and sent as-is to a client whose request "
2640+
"envelope declared no elicitation capability. The mode-level half of the same MUST NOT "
2641+
"(form vs url) is equally ungated and additionally unpinned -- a configured elicitation "
2642+
"callback always declares both modes, so a form-only client is unproducible through the "
2643+
"public API."
2644+
),
2645+
),
26442646
added_in="2026-07-28",
26452647
supersedes=("elicitation:form:not-supported", "elicitation:capability:server-respects-mode"),
2646-
deferred=(
2647-
"Not implemented in the SDK: the server does not gate input_required input requests against the "
2648-
"client's declared capabilities -- a handler can embed an elicitation/create request for a "
2649-
"client that never declared the matching capability or mode and it is sent as-is."
2650-
),
26512648
),
26522649
"elicitation:mrtr:url:action-no-content": Requirement(
26532650
source=f"{SPEC_2026_BASE_URL}/client/elicitation#response-actions",
@@ -2712,10 +2709,34 @@ def __post_init__(self) -> None:
27122709
),
27132710
added_in="2026-07-28",
27142711
supersedes=("tools:call:elicitation-roundtrip", "mcpserver:context:elicit-from-handler"),
2715-
deferred=(
2716-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2717-
"the behaviour is implemented and drivable (phase-4 verdict)."
2712+
),
2713+
"mrtr:request-state-only:retry": Requirement(
2714+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#client-requirements-basic-workflow",
2715+
behavior=(
2716+
"An InputRequiredResult carrying only requestState (no inputRequests) is retried by the "
2717+
"client driver with no inputResponses and the requestState echoed verbatim."
27182718
),
2719+
added_in="2026-07-28",
2720+
note=(
2721+
"The spec's 'MAY retry the original request immediately' is permission; the SDK paces "
2722+
"state-only retries with an internal 50 ms exponential backoff as its chosen pacing."
2723+
),
2724+
),
2725+
"mrtr:request-state:omitted-when-absent": Requirement(
2726+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#client-requirements-basic-workflow",
2727+
behavior=(
2728+
"When an InputRequiredResult carries no requestState field, the client does not include "
2729+
"a requestState key in the serialized retry."
2730+
),
2731+
added_in="2026-07-28",
2732+
),
2733+
"mrtr:request-state:scoped-to-originating-request": Requirement(
2734+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#client-requirements-basic-workflow",
2735+
behavior=(
2736+
"inputRequests and requestState affect only the client's retry of the originating "
2737+
"request; they never appear on any other request the client sends in parallel."
2738+
),
2739+
added_in="2026-07-28",
27192740
),
27202741
"mrtr:multi-round:complete": Requirement(
27212742
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#server-requirements-basic-workflow",
@@ -2812,10 +2833,6 @@ def __post_init__(self) -> None:
28122833
),
28132834
added_in="2026-07-28",
28142835
supersedes=("roots:list:basic",),
2815-
deferred=(
2816-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2817-
"the behaviour is implemented and drivable (phase-4 verdict)."
2818-
),
28192836
),
28202837
"roots:mrtr:list:empty": Requirement(
28212838
source=f"{SPEC_2026_BASE_URL}/client/roots#listing-roots",
@@ -2825,10 +2842,6 @@ def __post_init__(self) -> None:
28252842
),
28262843
added_in="2026-07-28",
28272844
supersedes=("roots:list:empty",),
2828-
deferred=(
2829-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2830-
"the behaviour is implemented and drivable (phase-4 verdict)."
2831-
),
28322845
),
28332846
"roots:mrtr:capability:not-declared": Requirement(
28342847
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#server-requirements-basic-workflow",

0 commit comments

Comments
 (0)