Skip to content

Commit 2456c27

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 2ac6e90 commit 2456c27

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
# ═══════════════════════════════════════════════════════════════════════════
@@ -2176,10 +2200,6 @@ def __post_init__(self) -> None:
21762200
),
21772201
added_in="2026-07-28",
21782202
supersedes=("sampling:create:basic", "tools:call:sampling-roundtrip"),
2179-
deferred=(
2180-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2181-
"the behaviour is implemented and drivable (phase-4 verdict)."
2182-
),
21832203
),
21842204
"sampling:mrtr:create:include-context": Requirement(
21852205
source=f"{SPEC_2026_BASE_URL}/client/sampling#context-inclusion",
@@ -2189,10 +2209,6 @@ def __post_init__(self) -> None:
21892209
),
21902210
added_in="2026-07-28",
21912211
supersedes=("sampling:create:include-context",),
2192-
deferred=(
2193-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2194-
"the behaviour is implemented and drivable (phase-4 verdict)."
2195-
),
21962212
),
21972213
"sampling:mrtr:create:model-preferences": Requirement(
21982214
source=f"{SPEC_2026_BASE_URL}/client/sampling#model-preferences",
@@ -2202,10 +2218,6 @@ def __post_init__(self) -> None:
22022218
),
22032219
added_in="2026-07-28",
22042220
supersedes=("sampling:create:model-preferences",),
2205-
deferred=(
2206-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2207-
"the behaviour is implemented and drivable (phase-4 verdict)."
2208-
),
22092221
),
22102222
"sampling:mrtr:create:system-prompt": Requirement(
22112223
source=f"{SPEC_2026_BASE_URL}/client/sampling#system-prompt",
@@ -2215,10 +2227,6 @@ def __post_init__(self) -> None:
22152227
),
22162228
added_in="2026-07-28",
22172229
supersedes=("sampling:create:system-prompt",),
2218-
deferred=(
2219-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2220-
"the behaviour is implemented and drivable (phase-4 verdict)."
2221-
),
22222230
),
22232231
"sampling:mrtr:create:audio-content": Requirement(
22242232
source=f"{SPEC_2026_BASE_URL}/client/sampling#audio-content",
@@ -2565,10 +2573,6 @@ def __post_init__(self) -> None:
25652573
"elicitation:form:action:accept",
25662574
"transport:streamable-http:server-to-client",
25672575
),
2568-
deferred=(
2569-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2570-
"the behaviour is implemented and drivable (phase-4 verdict)."
2571-
),
25722576
),
25732577
"elicitation:mrtr:form:action:cancel": Requirement(
25742578
source=f"{SPEC_2026_BASE_URL}/client/elicitation#response-actions",
@@ -2578,10 +2582,6 @@ def __post_init__(self) -> None:
25782582
),
25792583
added_in="2026-07-28",
25802584
supersedes=("elicitation:form:action:cancel",),
2581-
deferred=(
2582-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2583-
"the behaviour is implemented and drivable (phase-4 verdict)."
2584-
),
25852585
),
25862586
"elicitation:mrtr:form:action:decline": Requirement(
25872587
source=f"{SPEC_2026_BASE_URL}/client/elicitation#response-actions",
@@ -2591,10 +2591,6 @@ def __post_init__(self) -> None:
25912591
),
25922592
added_in="2026-07-28",
25932593
supersedes=("elicitation:form:action:decline",),
2594-
deferred=(
2595-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2596-
"the behaviour is implemented and drivable (phase-4 verdict)."
2597-
),
25982594
),
25992595
"elicitation:mrtr:form:schema:primitives": Requirement(
26002596
source=f"{SPEC_2026_BASE_URL}/client/elicitation#requested-schema",
@@ -2604,10 +2600,6 @@ def __post_init__(self) -> None:
26042600
),
26052601
added_in="2026-07-28",
26062602
supersedes=("elicitation:form:schema:primitives",),
2607-
deferred=(
2608-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2609-
"the behaviour is implemented and drivable (phase-4 verdict)."
2610-
),
26112603
),
26122604
"elicitation:mrtr:form:schema:enum-variants": Requirement(
26132605
source=f"{SPEC_2026_BASE_URL}/client/elicitation#requested-schema",
@@ -2642,13 +2634,18 @@ def __post_init__(self) -> None:
26422634
"inputRequests for a client whose declared capabilities do not support it (including "
26432635
"mode-level support: form vs url)."
26442636
),
2637+
divergence=Divergence(
2638+
note=(
2639+
"The server does not gate input_required input requests against the client's declared "
2640+
"capabilities: an elicitation/create is embedded and sent as-is to a client whose request "
2641+
"envelope declared no elicitation capability. The mode-level half of the same MUST NOT "
2642+
"(form vs url) is equally ungated and additionally unpinned -- a configured elicitation "
2643+
"callback always declares both modes, so a form-only client is unproducible through the "
2644+
"public API."
2645+
),
2646+
),
26452647
added_in="2026-07-28",
26462648
supersedes=("elicitation:form:not-supported", "elicitation:capability:server-respects-mode"),
2647-
deferred=(
2648-
"Not implemented in the SDK: the server does not gate input_required input requests against the "
2649-
"client's declared capabilities -- a handler can embed an elicitation/create request for a "
2650-
"client that never declared the matching capability or mode and it is sent as-is."
2651-
),
26522649
),
26532650
"elicitation:mrtr:url:action-no-content": Requirement(
26542651
source=f"{SPEC_2026_BASE_URL}/client/elicitation#response-actions",
@@ -2713,10 +2710,34 @@ def __post_init__(self) -> None:
27132710
),
27142711
added_in="2026-07-28",
27152712
supersedes=("tools:call:elicitation-roundtrip", "mcpserver:context:elicit-from-handler"),
2716-
deferred=(
2717-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2718-
"the behaviour is implemented and drivable (phase-4 verdict)."
2713+
),
2714+
"mrtr:request-state-only:retry": Requirement(
2715+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#client-requirements-basic-workflow",
2716+
behavior=(
2717+
"An InputRequiredResult carrying only requestState (no inputRequests) is retried by the "
2718+
"client driver with no inputResponses and the requestState echoed verbatim."
27192719
),
2720+
added_in="2026-07-28",
2721+
note=(
2722+
"The spec's 'MAY retry the original request immediately' is permission; the SDK paces "
2723+
"state-only retries with an internal 50 ms exponential backoff as its chosen pacing."
2724+
),
2725+
),
2726+
"mrtr:request-state:omitted-when-absent": Requirement(
2727+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#client-requirements-basic-workflow",
2728+
behavior=(
2729+
"When an InputRequiredResult carries no requestState field, the client does not include "
2730+
"a requestState key in the serialized retry."
2731+
),
2732+
added_in="2026-07-28",
2733+
),
2734+
"mrtr:request-state:scoped-to-originating-request": Requirement(
2735+
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#client-requirements-basic-workflow",
2736+
behavior=(
2737+
"inputRequests and requestState affect only the client's retry of the originating "
2738+
"request; they never appear on any other request the client sends in parallel."
2739+
),
2740+
added_in="2026-07-28",
27202741
),
27212742
"mrtr:multi-round:complete": Requirement(
27222743
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#server-requirements-basic-workflow",
@@ -2813,10 +2834,6 @@ def __post_init__(self) -> None:
28132834
),
28142835
added_in="2026-07-28",
28152836
supersedes=("roots:list:basic",),
2816-
deferred=(
2817-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2818-
"the behaviour is implemented and drivable (phase-4 verdict)."
2819-
),
28202837
),
28212838
"roots:mrtr:list:empty": Requirement(
28222839
source=f"{SPEC_2026_BASE_URL}/client/roots#listing-roots",
@@ -2826,10 +2843,6 @@ def __post_init__(self) -> None:
28262843
),
28272844
added_in="2026-07-28",
28282845
supersedes=("roots:list:empty",),
2829-
deferred=(
2830-
"Not yet covered here: 2026-07-28 successor entry registered by the era pass ahead of its test; "
2831-
"the behaviour is implemented and drivable (phase-4 verdict)."
2832-
),
28332846
),
28342847
"roots:mrtr:capability:not-declared": Requirement(
28352848
source=f"{SPEC_2026_BASE_URL}/basic/patterns/mrtr#server-requirements-basic-workflow",

0 commit comments

Comments
 (0)