Skip to content

Commit a349f2a

Browse files
committed
Validate elicitation sub-capabilities in check_capability
check_capability only rejected a requested elicitation capability when the client declared no elicitation support at all, ignoring the form/url sub-capabilities. A client that advertised only URL-mode elicitation would incorrectly pass a check for form-mode elicitation (and vice versa). Mirror the existing sampling sub-capability handling: when a specific elicitation mode is requested, require the client to declare that mode. A bare elicitation request is still satisfied by any elicitation support. Fixes #2965
1 parent 53117cb commit a349f2a

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

src/mcp/server/connection.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,8 +388,13 @@ def check_capability(self, capability: ClientCapabilities) -> bool:
388388
return False
389389
if capability.sampling.tools is not None and have.sampling.tools is None:
390390
return False
391-
if capability.elicitation is not None and have.elicitation is None:
392-
return False
391+
if capability.elicitation is not None:
392+
if have.elicitation is None:
393+
return False
394+
if capability.elicitation.form is not None and have.elicitation.form is None:
395+
return False
396+
if capability.elicitation.url is not None and have.elicitation.url is None:
397+
return False
393398
if capability.experimental is not None:
394399
if have.experimental is None:
395400
return False

tests/server/test_connection.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
CreateMessageRequestParams,
2222
ElicitationCapability,
2323
EmptyResult,
24+
FormElicitationCapability,
2425
Implementation,
2526
ListRootsRequest,
2627
ListRootsResult,
@@ -31,6 +32,7 @@
3132
SamplingCapability,
3233
SamplingContextCapability,
3334
SamplingToolsCapability,
35+
UrlElicitationCapability,
3436
)
3537
from mcp_types.version import LATEST_HANDSHAKE_VERSION, LATEST_MODERN_VERSION
3638
from pydantic import BaseModel, ValidationError
@@ -364,6 +366,35 @@ def test_connection_check_capability_false_when_no_client_params_recorded():
364366
(ClientCapabilities(experimental={"a": {}}), ClientCapabilities(experimental={"b": {}}), False),
365367
(ClientCapabilities(experimental={"a": {"x": 1}}), ClientCapabilities(experimental={"a": {"x": 2}}), False),
366368
(ClientCapabilities(experimental={"a": {}}), ClientCapabilities(experimental={"a": {}}), True),
369+
(ClientCapabilities(elicitation=None), ClientCapabilities(elicitation=ElicitationCapability()), False),
370+
# The client offers only URL-mode elicitation, but form mode is requested.
371+
(
372+
ClientCapabilities(elicitation=ElicitationCapability(url=UrlElicitationCapability())),
373+
ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability())),
374+
False,
375+
),
376+
# The client offers only form-mode elicitation, but URL mode is requested.
377+
(
378+
ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability())),
379+
ClientCapabilities(elicitation=ElicitationCapability(url=UrlElicitationCapability())),
380+
False,
381+
),
382+
(
383+
ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability())),
384+
ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability())),
385+
True,
386+
),
387+
(
388+
ClientCapabilities(elicitation=ElicitationCapability(url=UrlElicitationCapability())),
389+
ClientCapabilities(elicitation=ElicitationCapability(url=UrlElicitationCapability())),
390+
True,
391+
),
392+
# A bare elicitation request (no sub-capability) is satisfied by any elicitation support.
393+
(
394+
ClientCapabilities(elicitation=ElicitationCapability(url=UrlElicitationCapability())),
395+
ClientCapabilities(elicitation=ElicitationCapability()),
396+
True,
397+
),
367398
],
368399
)
369400
def test_check_capability_per_field_branches(have: ClientCapabilities, want: ClientCapabilities, expected: bool):

0 commit comments

Comments
 (0)