Skip to content

Enforce declared client capabilities before a handler runs #1198

Description

@chemicL

SEP: SEP-2575: Missing Required Capabilities
Spec: Overview: _meta, "Per-request protocol fields"

A server MUST NOT rely on capabilities the client has not declared. If processing a request requires a capability the client did not include in io.modelcontextprotocol/clientCapabilities, the server MUST return a MissingRequiredClientCapabilityError (-32021) whose data.requiredCapabilities lists the missing capabilities. On HTTP, the response status MUST be 400 Bad Request.

Current behavior

Only handlers enforce this, by throwing McpException.missingClientCapability(...) themselves. The framework passes through an InputRequired result containing, say, an elicitation request even when the client never declared elicitation. The only manual check today is in the modern conformance servlet.

Problem

To fail such a request reliably, the framework has to know a primitive's required capabilities before its handler runs and produces an McpAsyncResponse. That means encoding them in the user-provided handling logic exposed through the repositories (tools, prompts, resources), so a request can be rejected before any work starts.

When the decision is made matters as much as the check itself. On HTTP, a Streaming response commits 200 OK and text/event-stream before its body runs. After that, the error can only be sent in-band, and the spec requires 400 for -32021. Checking the outgoing InputRequired result afterwards is also too late, because the handler has already done its work and may have had side effects. So the check has to happen before headers and status are sent.

Open questions

  • Where the required capabilities are declared: on the primitive's definition, on the repository lookup, or somewhere else.
  • Whether requirements can depend on the request's arguments, or are fixed per primitive.
  • How this fits with handlers that need a capability only on some code paths, which today can still throw -32021 themselves as long as they haven't started streaming.

Activity

  1. added this to the 2.2.0-M2 milestone on Oct 20, 2026
  2. added
    enhancementNew feature or request
    P2Moderate issues affecting some users, edge cases, potentially valuable feature
    on Oct 20, 2026
  3. AbhishekShukla1289 commented on Oct 21, 2026

    @AbhishekShukla1289

    Happy to pick this up.

    On where to declare it, I'd keep it out of the Tool schema: a default requiredClientCapabilities(ctx, request) on the repositories (empty by default), which the framework checks right before call(...). That runs before anything streams, so the 400 still works, and since it sees the request it handles argument-dependent requirements too. Handlers that only need a capability on some paths can keep throwing -32021 themselves before streaming.

    Does that work for you, or would you rather put it on the primitive definition?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Moderate issues affecting some users, edge cases, potentially valuable featurearea/serverenhancementNew feature or request

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions