Skip to content

Proposal: Add {Client,Server}Options.Capabilities, and deprecate Has{Tools,Prompts,Resources} #706

@findleyr

Description

@findleyr

In #135 we addressed a simple problem--that there was no way to force advertising unused capabilities--by adding the simplest possible API: ServerOptions.Has{Tools,Prompts,Resources} allowed server authors to advertise the relevant capability even if there were no features (yet) registered on the server.

However, as I work on the 2025-11-25 version of the spec, particularly #629, I am finding problems with this design.

Problem statement

  • What if the user wants to customize those capabilities, for example by disabling the listChanged feature?
  • Lack of discoverability, and confusing semantics: if I set HasTools: false, that doesn't actually mean that my server has no tools, it just means that it has no tools yet.
  • Explosion of API: as we require further customization of capabilities, we have to add more and more arbitrary fields to the {Client,Server}Options structs.

This was already a problem, but is becoming more noticeable with 2025-11-25 version of the spec, which added more capability configuration (URL elicitation and Tool sampling, to name two). For example, in #623 I added ClientOptions.ElicitationModes, but didn't like it. Fortunately, we've not yet had a release and so can change that API.

Proposal

I propose the following solution, which I believe is similar to what the typescript SDK does (CC @felixweinberger for any advice from typescript).

  1. Add {Client.Server}Options.Capabilities, an optional field that may be set by the API user to configure default capabilities. This overrides the current default capabilities, which are not currently empty (we always advertise "roots" from the client, and "logging" from the server).
  2. Whenever the user adds a new capability, for example by adding a tool or prompt, or by setting an ElicitationHandler, augment the advertised capabilities to reflect that new capability. So if the user sets ServerOptions.Capabilities to new(ServerCapabilities), but then adds a tool, we WILL advertise tool capabilities.
  3. Check listChanged capabilities before sending notifications, so that server behavior is consistent with its advertised capabilities.
  4. Deprecate Has{Tools,Prompts,Resources}, and remove ClientOptions.ElicitationModes (which has not yet been released).

That's it.

Notes

  • This means there's no way to e.g. add a tool but not advertise the tool capability (but why would you want to do that?).
  • I wish we didn't advertise the "roots" capability by default (see also How to check client capabilities and handle unsupported capability errors? #607), but this at least provides a way to disable the capability: setting ClientOptions.Capabilities to new(ClientCapabilities) disables advertisement of "roots" if no root is added.
  • We'll need to record the user-provided default capabilities explicitly, rather than just copying and mutating: if the users passes new(ToolCapabilities), then adds and removes a tool, we should not revert to not advertising the tool capability.
  • Unlike typescript, let's not provide any sort of RegisterCapability API. I think the configuration option is enough for now.

I believe this solves all the limitations listed above.

CC @jba @markus-kusano @SamMorrowDrums

Now that I've seen it, I think this proposal is obvious (but please let me know if you disagree!), so I'm going to send a PR now, which we can perhaps land while the proposal is still pending.

Metadata

Metadata

Assignees

Labels

proposalA proposal for an a new API or behavior. See CONTRIBUTING.md.proposal-acceptedProposals that have been accepted.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions