Skip to content

Commit 1cec2d6

Browse files
authored
Relax monolith ElicitRequestURLParams.elicitation_id for 2026-07-28 (#2913)
1 parent 510832a commit 1cec2d6

3 files changed

Lines changed: 34 additions & 4 deletions

File tree

src/mcp/types/_types.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1868,7 +1868,9 @@ class CancelledNotificationParams(NotificationParams):
18681868
The ID of the request to cancel.
18691869
18701870
This MUST correspond to the ID of a request previously issued in the same direction.
1871-
Required on the wire through 2025-06-18; optional from 2025-11-25.
1871+
Required on the wire through 2025-06-18; optional at 2025-11-25; required again from
1872+
2026-07-28, where it must name a request the client previously issued (servers send
1873+
this notification only to terminate a `subscriptions/listen` stream).
18721874
"""
18731875
reason: str | None = None
18741876
"""An optional string describing the reason for the cancellation."""
@@ -1956,10 +1958,11 @@ class ElicitRequestURLParams(RequestParams):
19561958
url: str
19571959
"""The URL that the user should navigate to."""
19581960

1959-
elicitation_id: str
1961+
elicitation_id: str | None = None
19601962
"""The ID of the elicitation, which must be unique within the context of the server.
19611963
1962-
The client MUST treat this ID as an opaque value.
1964+
The client MUST treat this ID as an opaque value. Required on the wire at
1965+
2025-11-25; removed at 2026-07-28.
19631966
"""
19641967

19651968
task: TaskMetadata | None = None

tests/interaction/lowlevel/test_elicitation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ async def test_elicitation_complete_notification_carries_the_elicited_id_back_to
309309
returns; the same ordering already holds on in-memory and SSE transports.
310310
"""
311311
elicitation_id = "auth-001"
312-
elicited_ids: list[str] = []
312+
elicited_ids: list[str | None] = []
313313
received: list[IncomingMessage] = []
314314

315315
async def collect(message: IncomingMessage) -> None:

tests/types/test_methods.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,33 @@ def test_embedded_input_request_entries_without_method_reject_at_the_surface_ste
674674
methods.parse_server_result("tools/call", "2026-07-28", body)
675675

676676

677+
def test_input_required_url_elicit_without_elicitation_id_parses_at_2026():
678+
"""A 2026-07-28 `InputRequiredResult` embedding a URL-mode elicitation parses
679+
through both the surface and monolith steps without `elicitationId`.
680+
681+
Spec-mandated: the field is required at 2025-11-25 only and removed at
682+
2026-07-28; the monolith model carries it as optional so the superset can
683+
accept both versions.
684+
"""
685+
body = {
686+
"resultType": "input_required",
687+
"inputRequests": {
688+
"r1": {
689+
"method": "elicitation/create",
690+
"params": {"mode": "url", "message": "Please sign in", "url": "https://example.com/auth"},
691+
}
692+
},
693+
}
694+
parsed = methods.parse_server_result("tools/call", "2026-07-28", body)
695+
assert isinstance(parsed, types.InputRequiredResult)
696+
assert parsed.input_requests is not None
697+
request = parsed.input_requests["r1"]
698+
assert isinstance(request, types.ElicitRequest)
699+
assert isinstance(request.params, types.ElicitRequestURLParams)
700+
assert request.params.url == "https://example.com/auth"
701+
assert request.params.elicitation_id is None
702+
703+
677704
def test_none_params_omit_the_key_so_required_params_reject():
678705
with pytest.raises(pydantic.ValidationError) as excinfo:
679706
methods.parse_client_request("tools/call", "2025-11-25", None)

0 commit comments

Comments
 (0)