modelcontextprotocol / modelcontextprotocol/python-sdk

[Bug] check_capability does not validate elicitation sub-capabilities (form/url)

Open Beginner friendly
#2,965 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug needs confirmation P2 v2
Dominant language
Python
Stars
24.3k
Forks
4k
Avg merge
1d 1h
Merged PRs (30d)
31

Description

Summary

Connection.check_capability() returns True when a client has URL-mode elicitation but the caller checks for form-mode elicitation — the sub-capability (ElicitationCapability.form / .url) is never inspected.

Root cause

In src/mcp/server/connection.py:340-341:

if capability.elicitation is not None and have.elicitation is None:
    return False

This only returns False when the client has no elicitation at all. It does not check individual sub-capabilities (form / url). Compare with sampling, which correctly checks sub-capabilities:

if capability.sampling is not None:
    if have.sampling is None:
        return False
    if capability.sampling.context is not None and have.sampling.context is None:
        return False
    if capability.sampling.tools is not None and have.sampling.tools is None:
        return False

Reproduction

# Client supports URL elicitation only
have = ClientCapabilities(elicitation=ElicitationCapability(url=UrlElicitationCapability()))

# Check for form elicitation — should be False (client does not have form)
want = ClientCapabilities(elicitation=ElicitationCapability(form=FormElicitationCapability()))

Connection.from_envelope("2025-11-25", client_info, have).check_capability(want)  # Returns True (BUG)

Impact

  • check_capability is public API (via ServerSession.check_client_capability)
  • Currently no production callers use elicitation sub-capability checks — low immediate impact
  • But once someone relies on it (e.g., checking if the client supports form elicitation before calling elicit_form), it will return wrong results

Additional gaps (lower priority)

  • extensionsClientCapabilities.extensions is not checked at all
  • tasksClientCapabilities.tasks and sub-capabilities entirely unhandled

Proposed fix

Add form / url sub-capability checks, matching the sampling pattern:

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

AI assistance: Bug discovered and analyzed with AI assistance (opencode).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/mcp/server/connection.py around lines 340-341 and compare the elicitation handling with the existing sampling checks. Reproduce the URL-only versus form-only capability case from the issue, then verify that matching and mismatching form/url capabilities return the expected results in the relevant test suite.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.