From c3dbc22d605dbb1de1d007af8755e30fd3de5ec0 Mon Sep 17 00:00:00 2001 From: Robin1987China <41602358+Robin1987China@users.noreply.github.com> Date: Thu, 25 Jun 2026 18:17:53 +0800 Subject: [PATCH] fix: check_capability now validates elicitation sub-capabilities (form/url) Schema has ElicitationCapability.form and .url sub-fields but check_capability only validated the top-level elicitation key. A client that supports URL elicitation but not form elicitation would incorrectly return True when the caller checks for form. Fix matches the existing sampling sub-capability pattern. --- src/mcp/server/connection.py | 9 +++++++-- tests/server/test_connection.py | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/mcp/server/connection.py b/src/mcp/server/connection.py index f5bfc18df..7786b5520 100644 --- a/src/mcp/server/connection.py +++ b/src/mcp/server/connection.py @@ -337,8 +337,13 @@ def check_capability(self, capability: ClientCapabilities) -> bool: return False if capability.sampling.tools is not None and have.sampling.tools is None: return False - if capability.elicitation is not None and have.elicitation is None: - return False + if capability.elicitation is not None: + if have.elicitation is None: + return False + if capability.elicitation.form is not None and have.elicitation.form is None: + return False + if capability.elicitation.url is not None and have.elicitation.url is None: + return False if capability.experimental is not None: if have.experimental is None: return False diff --git a/tests/server/test_connection.py b/tests/server/test_connection.py index 8ca1ae8a7..5b465b40e 100644 --- a/tests/server/test_connection.py +++ b/tests/server/test_connection.py @@ -27,6 +27,7 @@ CreateMessageRequestParams, ElicitationCapability, EmptyResult, + FormElicitationCapability, Implementation, ListRootsRequest, ListRootsResult, @@ -37,6 +38,7 @@ SamplingCapability, SamplingContextCapability, SamplingToolsCapability, + UrlElicitationCapability, ) _CLIENT_INFO = Implementation(name="t", version="0") @@ -365,6 +367,28 @@ def test_connection_check_capability_false_when_no_client_params_recorded(): (ClientCapabilities(experimental={"a": {}}), ClientCapabilities(experimental={"b": {}}), False), (ClientCapabilities(experimental={"a": {"x": 1}}), ClientCapabilities(experimental={"a": {"x": 2}}), False), (ClientCapabilities(experimental={"a": {}}), ClientCapabilities(experimental={"a": {}}), True), + # Elicitation sub-capability checks (form / url) + (ClientCapabilities(elicitation=None), ClientCapabilities(elicitation=ElicitationCapability()), False), + ( + ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability())), + ClientCapabilities(elicitation=ElicitationCapability(url=UrlElicitationCapability())), + False, + ), + ( + ClientCapabilities(elicitation=ElicitationCapability(url=UrlElicitationCapability())), + ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability())), + False, + ), + ( + ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability(), url=UrlElicitationCapability())), + ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability())), + True, + ), + ( + ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability())), + ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability())), + True, + ), ], ) def test_check_capability_per_field_branches(have: ClientCapabilities, want: ClientCapabilities, expected: bool):