SEP-XXXX: Global Capability Scope for Per-Request Client Capabilities#31
SEP-XXXX: Global Capability Scope for Per-Request Client Capabilities#31gjz22 wants to merge 2 commits intomodelcontextprotocol:mainfrom
Conversation
Draft SEP specifying that per-request client capabilities (introduced in SEP-1442) should be treated as globally scoped, so servers can filter and adapt tool lists based on the capabilities the client will provide on subsequent tools/call requests.
pja-ant
left a comment
There was a problem hiding this comment.
This is great, thanks for putting it together!
Added some comments, mostly semantical things that would make it a bit more precise (in my opinion), but high-level I think this is the right framing of options. Would love to see a comparison matrix to help us debate trade-offs.
FWIW, I'm really not sure what the right thing is here. I was originally leaning towards your proposed option but can see pros/cons versus others as well.
| proposal does not specify the *scope* of those capabilities: whether a | ||
| capability declared on one request should be assumed to hold for other | ||
| requests, or whether each request's capabilities are independent. |
There was a problem hiding this comment.
A bit of a semantical nit, but I'm not sure we should use "scope" here. It's really not about scope and I find the "globally scoped" phrasing confusing (what does "global" mean?). With 1442 we have a stateless protocol, so the only scope that exists from MCP's perspective is the request.
What is being discussed is if/how servers should factor in capabilities sent on list/tools, which I think this SEP does a good job of exploring. I'd recommend dropping the "scope" verbiage.
There was a problem hiding this comment.
More broadly, I feel like we need to define what criteria should can be used to affect the outcome of things like tools/list, resources/list. The wording in SEP-2567 says:
tools/list,resources/list, andprompts/list: there is no longer a per-session or per-connection scope for their results to depend on. Lists can still change for other reasons — a server deploys a new version, a user's plan or granted scopes change — and this SEP does not enumerate or restrict those.
It seems like we need to clearly enumerate in which case the tools/list should be cached or shared. In what instances should clients refresh the list?
There was a problem hiding this comment.
Global here means what it means today in the protocol. That you have one set of capabilities and those capabilities apply globally to all requests as opposed to being able to specify per request what capabilities the client has for that request only.
| When a client sends per-request capabilities (as defined in SEP-1442), | ||
| the following rules apply: | ||
|
|
||
| 1. A client **SHOULD** send the same `clientCapabilities` object on every |
There was a problem hiding this comment.
This is where the scoping ("every") makes things confusing. What does "every" request mean? Are clients not allowed to change capabilities?
A more direct phrasing would be something like "A client should not assume that a tools list returned with one set of supplied capabilities is the same that another set of capabilities would return" -- with the implication being that if a client changes capabilities then it should probably re-fetch tools. It should also not cache tools across capability sets.
| 2. A server that receives capabilities on any request **SHOULD** assume | ||
| that those capabilities apply to all requests from that client. | ||
| Specifically, if a server receives capabilities on a `tools/list` | ||
| request, it **SHOULD** use those capabilities to determine which tools | ||
| to return, assuming the same capabilities will be present on | ||
| subsequent `tools/call` requests. |
There was a problem hiding this comment.
It should not assume that capabilities apply to subsequent requests: every request is independent. The capabilities may change on subsequent tools/call requests and the server will have to handle that (e.g. by rejecting the request).
| 3. If a client's capabilities change (e.g., a user enables or disables | ||
| a feature), the client **SHOULD** re-issue applicable list requests | ||
| (e.g., `tools/list`, `resources/list`) with the updated capabilities | ||
| rather than relying on cached results. This ensures the server can | ||
| return results that reflect the new capability set. | ||
|
|
||
| 4. A server **MUST NOT** assume capabilities are available if the client | ||
| has not declared them on any request. The absence of capabilities | ||
| is always a safe default. |
There was a problem hiding this comment.
These are good. I do think we're on the same page, but the language needs to be phrased in a more stateless way where every request is treated in isolation (since you have no other choice).
| request carries the full capability set, so any server instance can | ||
| make the correct decision without shared state. | ||
|
|
||
| ### Alternatives Considered |
There was a problem hiding this comment.
Thanks for including these in so much detail. I think it could be very useful to provide a comparison matrix across all options to give a high-level overview.
There was a problem hiding this comment.
Added (quickly but will revise). I included only the main option, and alternative 1 and alternative 2 because I consider these the main viable options.
|
|
||
| ## Abstract | ||
|
|
||
| [SEP-1442](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1442) |
There was a problem hiding this comment.
| [SEP-1442](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1442) | |
| [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2575) |
kurtisvg
left a comment
There was a problem hiding this comment.
At it's core, I think this issue should be reframed as "what information can be used to filter */list calls", and probably should explicitly call out auth/user identity in addition to capabilities.
It also needs to be refocused on defining client behavior more sot than server behavior, because in a stateless world the server doesn't differentiate between different clients, so it's up to the client to refetched cached info (and not the server to try and enforce that).
| proposal does not specify the *scope* of those capabilities: whether a | ||
| capability declared on one request should be assumed to hold for other | ||
| requests, or whether each request's capabilities are independent. |
There was a problem hiding this comment.
More broadly, I feel like we need to define what criteria should can be used to affect the outcome of things like tools/list, resources/list. The wording in SEP-2567 says:
tools/list,resources/list, andprompts/list: there is no longer a per-session or per-connection scope for their results to depend on. Lists can still change for other reasons — a server deploys a new version, a user's plan or granted scopes change — and this SEP does not enumerate or restrict those.
It seems like we need to clearly enumerate in which case the tools/list should be cached or shared. In what instances should clients refresh the list?
| capability declared on one request should be assumed to hold for other | ||
| requests, or whether each request's capabilities are independent. | ||
|
|
||
| This proposal specifies that per-request client capabilities SHOULD be |
There was a problem hiding this comment.
I'm not a big fan of this wording -- it feels like we should be giving the client guidance on when it should refresh tools/list, not what the server should assume. The client could change capabilities at any time, and it should be the clients responsibility to refresh the given tools/list accordingly.
There was a problem hiding this comment.
Wording aside, to be clear, the assumption here is that if a client declares capabilities on tools/list, unless the client changes capabilities (in which the client should refetch tools), the server should assume the client will have the same capabilities on tools/call
Compares the primary Global Scope proposal against Alternative 1 (Tool-Level Capability Annotations) and Alternative 2 (Related Request Capabilities) across the main goals motivating this SEP.
|
Firstly, I want to call out that I believe there's a significant rift in desire here from closed-system and public server perspectives. Since I've put a lot of thought into Alt 1 (e.g. [1],[2]), I'd like to weigh in on the differences between this and Alt 1, which I believe will have significant impact on this protocol going forward: Server-side filtering (vs Client-side): (assuming no room for filtering parameters on Substitution: This is filtering. Parameterized adaptation: I think this is a better approach to support this than forcing clients to disclose their full capability details. Again, a closed-system owner may not have the same privacy or security concerns here that client users calling public servers would. Say a user is operating a client with some kind of private capability. They may not want to share that with all the public servers they interact with. Simplicity for client implementers: I think the differences here are trivial compared to some of the problems being solved outside this discussion. I developed matching logic for my implementation example for #1385 very quickly. It's definitely worth it in order to maintain client discretion in exposing capabilities. Simplicity for server implementers: I disagree with the label of MEDIUM here for Alt 1. They wrote the tool. They know what's required for it to execute successfully. Transparency to the client: While I agree with the assessment, I think if you're going to have a row for this, you shouldn't leave out the row for the inverse: "Exposure (forcing transparency) of client to the server". It's important to remember that public servers advertise themselves and strive to be transparent in order to earn trust from clients, who have to be careful of what servers they reach out to (just like we're all careful of the websites we visit). True per-request capability scoping: Agree. Overall, I think there's much to be learned from the evolution of
So, unless we start limiting capability declaration (I worry about custom capabilities before they have their time here) in similar ways, we'd be on course to start from point 1. |
This is a draft SEP specifying the scope of per-request client capabilities introduced by SEP-1442.
Motivation and Context
SEP-1442 moves client capabilities from a single initialization handshake to per-request declarations in
_meta, but does not specify whether a capability declared on one request should be assumed to hold for other requests. This creates an ambiguity for requests liketools/list, which need to decide what to return based on what capabilities the client will provide on a futuretools/call.This SEP proposes that per-request capabilities should be treated as globally scoped: when a client declares capabilities on any request, the server should assume those same capabilities apply to all requests from that client. Without this, the server does not have enough information to know whether to return a tool with a required capability, and if it does return the tool, the client has no way to know whether it can actually execute it.
Alternatives are discussed in detail:
whenCapabilitiesUnavailablesubstitution attribute)relatedRequestCapabilitieskeyed by method name in_meta)tools/callfail)See SEP for details.
How Has This Been Tested?
Not tested yet, that will happen when we implement.
Breaking Changes
This proposal is additive to SEP-1442 and does not introduce breaking changes on its own. See SEP for details.
Types of changes
Checklist
Additional context
N/A