Add a resources_list_handler for context-dependent resource lists
## Motivation and Context `resources/read` can be taken over with `resources_read_handler`, but `resources/list` had no equivalent: it was bound to the internal `list_resources`, which paginates the constructor-provided `resources` array, and `define_custom_method` refuses to rebind a method the server already handles. An application whose visible resources depend on the request - authenticated versus anonymous sessions, or an OAuth-scoped connector that must see a scope-filtered set - had to build a fresh `Server` per request or override a private method. Both reference SDKs let the list vary per request. The TypeScript SDK exposes `resources/list` through `setRequestHandler` and gives resource templates a per-context `listCallback(ctx)`; the Python SDK takes an `on_list_resources` handler. This adds the Ruby equivalent. `resources_list_handler(&block)` registers a block that returns the resource collection to serve. It feeds `list_resources` rather than replacing it, so the framework still paginates the returned array and stamps the SEP-2549 cache hints; the block returns only the array, matching how `resources_read_handler` returns only the contents and the framework wraps them. A block that declares a `server_context:` keyword receives an `MCP::ServerContext`, reusing the same opt-in rule the read handler uses, so the list can be filtered by the authenticated principal or granted scope. The block is invoked once per page and must return a stable ordering across the pages of one query, since the cursor is a positional offset. When no handler is set, the constructor-provided resources are served unchanged. The `resources` capability is advertised whether or not the constructor array is populated, so a server that provides only a `resources_list_handler` still answers `resources/list`. Fixes #507. ## How Has This Been Tested? New tests in `test/mcp/server_test.rb` cover a handler replacing the served collection, a handler receiving `server_context:` and returning different sets for authenticated and anonymous requests, pagination and cache hints still applying to the handler-provided array, and a server with no constructor-provided resources served entirely by the handler. The existing `resources/list` test, which exercises the no-handler default path, is unchanged. ## Breaking Changes None. When no `resources_list_handler` is set, `resources/list` serves the constructor-provided resources exactly as before.
This commit is contained in:
parent
b195a21410
commit
27044b0047
1 changed files with 98 additions and 3 deletions
16
README.md
16
README.md
|
|
@ -1244,6 +1244,20 @@ end
|
|||
otherwise `resources/read` requests will be a no-op. Note that a `resources_read_handler` fully replaces
|
||||
the default `resources/read` handling, including the automatic routing to class-based resources described above.
|
||||
|
||||
To make the resource *list* depend on the request, register a `resources_list_handler`. The block returns the resource collection to serve,
|
||||
so the visible resources can vary by the authenticated principal or the granted scope. The framework paginates the returned array
|
||||
and stamps the same cache hints it applies to the constructor-provided resources, so the block returns only the array.
|
||||
A block that declares `server_context:` receives it:
|
||||
|
||||
```ruby
|
||||
server.resources_list_handler do |params, server_context:|
|
||||
server_context[:authenticated] ? real_resources : demo_resources
|
||||
end
|
||||
```
|
||||
|
||||
The block is invoked once per page, so it must return a stable ordering across the pages of one query; the cursor is a positional offset
|
||||
into the returned collection. When no handler is set, the resources passed to `MCP::Server.new` are served unchanged.
|
||||
|
||||
For unknown URIs, raise `MCP::Server::ResourceNotFoundError` from the handler.
|
||||
Per SEP-2164, the server then responds with the standard JSON-RPC Invalid Params error (`-32602`)
|
||||
carrying the requested URI in the error `data` member:
|
||||
|
|
@ -1613,7 +1627,7 @@ Client-initiated cancellation is also supported: see [Client-Side: Cancelling an
|
|||
#### Server-Side: Handlers that Check for Cancellation
|
||||
|
||||
Any handler that opts in to `server_context:` - tools (`Tool.call`), prompt templates,
|
||||
`resources_read_handler`, `completion_handler`, `resources_subscribe_handler`,
|
||||
`resources_read_handler`, `resources_list_handler`, `completion_handler`, `resources_subscribe_handler`,
|
||||
`resources_unsubscribe_handler`, and `define_custom_method` blocks - receives
|
||||
an `MCP::ServerContext` wired to the in-flight request's cancellation token.
|
||||
Handlers check `cancelled?` in their work loop, or call `raise_if_cancelled!` to raise
|
||||
|
|
|
|||
Loading…
Reference in a new issue