@@ -219,6 +219,7 @@ def initialize(
219219 @resources = resources
220220 @resource_templates = resource_templates
221221 @resource_index = index_resources_by_uri ( resources )
222+ @resources_list_handler = nil
222223 @server_context = server_context
223224 self . page_size = page_size
224225 self . ttl_ms = ttl_ms
@@ -426,6 +427,22 @@ def roots_list_changed_handler(&block)
426427 @handlers [ Methods ::NOTIFICATIONS_ROOTS_LIST_CHANGED ] = block
427428 end
428429
430+ # Sets a custom handler for `resources/list` requests, letting the visible list depend on request context such as
431+ # the authenticated principal or granted scope. The block returns the resource collection to serve;
432+ # the framework paginates it and stamps SEP-2549 cache hints exactly as it does for the constructor-provided resources,
433+ # so the block returns only the array, not the paginated result.
434+ # A block that declares a `server_context:` keyword receives an `MCP::ServerContext`. When no handler is set,
435+ # the constructor-provided `resources` array is served unchanged.
436+ #
437+ # The block is invoked once per page, so it must return a stable ordering across the pages of one logical query;
438+ # the cursor is a positional offset into the returned collection.
439+ #
440+ # @yield [params, server_context:] The request params, and an `MCP::ServerContext` when declared.
441+ # @yieldreturn [Array<MCP::Resource>] The resources to paginate.
442+ def resources_list_handler ( &block )
443+ @resources_list_handler = block
444+ end
445+
429446 # Sets a custom handler for `resources/read` requests.
430447 # The block receives the parsed request params and should return resource
431448 # contents. The return value is set as the `contents` field of the response.
@@ -1292,12 +1309,28 @@ def get_prompt(request, session: nil, related_request_id: nil, cancellation: nil
12921309 call_prompt_template_with_args ( prompt , prompt_args , server_context )
12931310 end
12941311
1295- def list_resources ( request )
1296- page = paginate ( @resources , cursor : cursor_from ( request ) , page_size : @page_size , request : request , &:to_h )
1312+ def list_resources ( request , server_context : nil )
1313+ resources = if @resources_list_handler
1314+ invoke_resources_list_handler ( request , server_context )
1315+ else
1316+ @resources
1317+ end
1318+
1319+ page = paginate ( resources , cursor : cursor_from ( request ) , page_size : @page_size , request : request , &:to_h )
12971320
12981321 apply_cache_metadata ( { resources : page [ :items ] , nextCursor : page [ :next_cursor ] } . compact )
12991322 end
13001323
1324+ # Calls the `resources_list_handler` block, forwarding `server_context:` only when the block opts in
1325+ # by declaring the keyword (the same rule `dispatch_optional_context_handler` applies).
1326+ def invoke_resources_list_handler ( request , server_context )
1327+ if handler_declares_server_context? ( @resources_list_handler )
1328+ @resources_list_handler . call ( request , server_context : server_context )
1329+ else
1330+ @resources_list_handler . call ( request )
1331+ end
1332+ end
1333+
13011334 # Default `resources/read` handler: routes to class-based resources and resource templates.
13021335 # Fully replaced when `resources_read_handler` is set. When no class-based resource or template is registered,
13031336 # unknown URIs keep the historical no-op `[]` response instead of raising.
0 commit comments