Skip to content

Commit 4d83d3f

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 3cc521d commit 4d83d3f

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
@@ -1359,6 +1359,18 @@ def __post_init__(self) -> None:
13591359
source=f"{SPEC_BASE_URL}/server/utilities/pagination#operations-supporting-pagination",
13601360
behavior="resources/list supports cursor pagination.",
13611361
),
1362+
"resources:mrtr:read:basic": Requirement(
1363+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#supported-requests",
1364+
behavior=(
1365+
"A resources/read may be answered with an input_required result; the client fulfils the "
1366+
"embedded request and the retried resources/read completes with the resource contents."
1367+
),
1368+
added_in="2026-07-28",
1369+
note=(
1370+
"Low-level Server only: MCPServer returns InputRequiredResult from tools alone, so the "
1371+
"resources/read MRTR leg has no mcpserver mirror."
1372+
),
1373+
),
13621374
"resources:read:blob": Requirement(
13631375
source=f"{SPEC_BASE_URL}/server/resources#reading-resources",
13641376
behavior="resources/read returns binary contents base64-encoded in blob.",
@@ -1684,6 +1696,18 @@ def __post_init__(self) -> None:
16841696
source=f"{SPEC_BASE_URL}/server/utilities/pagination#operations-supporting-pagination",
16851697
behavior="prompts/list supports cursor pagination.",
16861698
),
1699+
"prompts:mrtr:get:basic": Requirement(
1700+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#supported-requests",
1701+
behavior=(
1702+
"A prompts/get may be answered with an input_required result; the client fulfils the "
1703+
"embedded request and the retried prompts/get completes with the prompt messages."
1704+
),
1705+
added_in="2026-07-28",
1706+
note=(
1707+
"Low-level Server only: MCPServer returns InputRequiredResult from tools alone, so the "
1708+
"prompts/get MRTR leg has no mcpserver mirror."
1709+
),
1710+
),
16871711
# ═══════════════════════════════════════════════════════════════════════════
16881712
# Prompts: SDK guarantees
16891713
# ═══════════════════════════════════════════════════════════════════════════
@@ -2173,10 +2197,6 @@ def __post_init__(self) -> None:
21732197
),
21742198
added_in="2026-07-28",
21752199
supersedes=("sampling:create:basic", "tools:call:sampling-roundtrip"),
2176-
deferred=(
2177-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2178-
"the behaviour is implemented and drivable (phase-4 verdict)."
2179-
),
21802200
),
21812201
"sampling:mrtr:create:include-context": Requirement(
21822202
source=f"{SPEC_2026_BASE_URL}/client/sampling#context-inclusion",
@@ -2186,10 +2206,6 @@ def __post_init__(self) -> None:
21862206
),
21872207
added_in="2026-07-28",
21882208
supersedes=("sampling:create:include-context",),
2189-
deferred=(
2190-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2191-
"the behaviour is implemented and drivable (phase-4 verdict)."
2192-
),
21932209
),
21942210
"sampling:mrtr:create:model-preferences": Requirement(
21952211
source=f"{SPEC_2026_BASE_URL}/client/sampling#model-preferences",
@@ -2199,10 +2215,6 @@ def __post_init__(self) -> None:
21992215
),
22002216
added_in="2026-07-28",
22012217
supersedes=("sampling:create:model-preferences",),
2202-
deferred=(
2203-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2204-
"the behaviour is implemented and drivable (phase-4 verdict)."
2205-
),
22062218
),
22072219
"sampling:mrtr:create:system-prompt": Requirement(
22082220
source=f"{SPEC_2026_BASE_URL}/client/sampling#system-prompt",
@@ -2212,10 +2224,6 @@ def __post_init__(self) -> None:
22122224
),
22132225
added_in="2026-07-28",
22142226
supersedes=("sampling:create:system-prompt",),
2215-
deferred=(
2216-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2217-
"the behaviour is implemented and drivable (phase-4 verdict)."
2218-
),
22192227
),
22202228
"sampling:mrtr:create:audio-content": Requirement(
22212229
source=f"{SPEC_2026_BASE_URL}/client/sampling#audio-content",
@@ -2562,10 +2570,6 @@ def __post_init__(self) -> None:
25622570
"elicitation:form:action:accept",
25632571
"transport:streamable-http:server-to-client",
25642572
),
2565-
deferred=(
2566-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2567-
"the behaviour is implemented and drivable (phase-4 verdict)."
2568-
),
25692573
),
25702574
"elicitation:mrtr:form:action:cancel": Requirement(
25712575
source=f"{SPEC_2026_BASE_URL}/client/elicitation#response-actions",
@@ -2575,10 +2579,6 @@ def __post_init__(self) -> None:
25752579
),
25762580
added_in="2026-07-28",
25772581
supersedes=("elicitation:form:action:cancel",),
2578-
deferred=(
2579-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2580-
"the behaviour is implemented and drivable (phase-4 verdict)."
2581-
),
25822582
),
25832583
"elicitation:mrtr:form:action:decline": Requirement(
25842584
source=f"{SPEC_2026_BASE_URL}/client/elicitation#response-actions",
@@ -2588,10 +2588,6 @@ def __post_init__(self) -> None:
25882588
),
25892589
added_in="2026-07-28",
25902590
supersedes=("elicitation:form:action:decline",),
2591-
deferred=(
2592-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2593-
"the behaviour is implemented and drivable (phase-4 verdict)."
2594-
),
25952591
),
25962592
"elicitation:mrtr:form:schema:primitives": Requirement(
25972593
source=f"{SPEC_2026_BASE_URL}/client/elicitation#requested-schema",
@@ -2601,10 +2597,6 @@ def __post_init__(self) -> None:
26012597
),
26022598
added_in="2026-07-28",
26032599
supersedes=("elicitation:form:schema:primitives",),
2604-
deferred=(
2605-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2606-
"the behaviour is implemented and drivable (phase-4 verdict)."
2607-
),
26082600
),
26092601
"elicitation:mrtr:form:schema:enum-variants": Requirement(
26102602
source=f"{SPEC_2026_BASE_URL}/client/elicitation#requested-schema",
@@ -2639,13 +2631,18 @@ def __post_init__(self) -> None:
26392631
"inputRequests for a client whose declared capabilities do not support it (including "
26402632
"mode-level support: form vs url)."
26412633
),
2634+
divergence=Divergence(
2635+
note=(
2636+
"The server does not gate input_required input requests against the client's declared "
2637+
"capabilities: an elicitation/create is embedded and sent as-is to a client whose request "
2638+
"envelope declared no elicitation capability. The mode-level half of the same MUST NOT "
2639+
"(form vs url) is equally ungated and additionally unpinned -- a configured elicitation "
2640+
"callback always declares both modes, so a form-only client is unproducible through the "
2641+
"public API."
2642+
),
2643+
),
26422644
added_in="2026-07-28",
26432645
supersedes=("elicitation:form:not-supported", "elicitation:capability:server-respects-mode"),
2644-
deferred=(
2645-
"Not implemented in the SDK: the server does not gate input_required input requests against the "
2646-
"client's declared capabilities -- a handler can embed an elicitation/create request for a "
2647-
"client that never declared the matching capability or mode and it is sent as-is."
2648-
),
26492646
),
26502647
"elicitation:mrtr:url:action-no-content": Requirement(
26512648
source=f"{SPEC_2026_BASE_URL}/client/elicitation#response-actions",
@@ -2710,10 +2707,34 @@ def __post_init__(self) -> None:
27102707
),
27112708
added_in="2026-07-28",
27122709
supersedes=("tools:call:elicitation-roundtrip", "mcpserver:context:elicit-from-handler"),
2713-
deferred=(
2714-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2715-
"the behaviour is implemented and drivable (phase-4 verdict)."
2710+
),
2711+
"mrtr:request-state-only:retry": Requirement(
2712+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#client-requirements-basic-workflow",
2713+
behavior=(
2714+
"An InputRequiredResult carrying only requestState (no inputRequests) is retried by the "
2715+
"client driver with no inputResponses and the requestState echoed verbatim."
27162716
),
2717+
added_in="2026-07-28",
2718+
note=(
2719+
"The spec's 'MAY retry the original request immediately' is permission; the SDK paces "
2720+
"state-only retries with an internal 50 ms exponential backoff as its chosen pacing."
2721+
),
2722+
),
2723+
"mrtr:request-state:omitted-when-absent": Requirement(
2724+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#client-requirements-basic-workflow",
2725+
behavior=(
2726+
"When an InputRequiredResult carries no requestState field, the client does not include "
2727+
"a requestState key in the serialized retry."
2728+
),
2729+
added_in="2026-07-28",
2730+
),
2731+
"mrtr:request-state:scoped-to-originating-request": Requirement(
2732+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#client-requirements-basic-workflow",
2733+
behavior=(
2734+
"inputRequests and requestState affect only the client's retry of the originating "
2735+
"request; they never appear on any other request the client sends in parallel."
2736+
),
2737+
added_in="2026-07-28",
27172738
),
27182739
"mrtr:multi-round:complete": Requirement(
27192740
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#server-requirements-basic-workflow",
@@ -2810,10 +2831,6 @@ def __post_init__(self) -> None:
28102831
),
28112832
added_in="2026-07-28",
28122833
supersedes=("roots:list:basic",),
2813-
deferred=(
2814-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2815-
"the behaviour is implemented and drivable (phase-4 verdict)."
2816-
),
28172834
),
28182835
"roots:mrtr:list:empty": Requirement(
28192836
source=f"{SPEC_2026_BASE_URL}/client/roots#listing-roots",
@@ -2823,10 +2840,6 @@ def __post_init__(self) -> None:
28232840
),
28242841
added_in="2026-07-28",
28252842
supersedes=("roots:list:empty",),
2826-
deferred=(
2827-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2828-
"the behaviour is implemented and drivable (phase-4 verdict)."
2829-
),
28302843
),
28312844
"roots:mrtr:capability:not-declared": Requirement(
28322845
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#server-requirements-basic-workflow",

0 commit comments

Comments
 (0)