Repository navigation
Server list handlers ignore cursor and never set nextCursor, and cannot be replaced #1128
Description
Version: 2.0.1 (latest at the time of writing)
What happens
All four server-side list handlers in McpAsyncServer build their result the same way — read the whole registry, map, done:
toolsListRequestHandler()—tools.stream().map(...).toList()→ListToolsResult.builder(list).build()promptsListRequestHandler()—prompts.values().stream()...→ListPromptsResult.builder(list).build()resourcesListRequestHandler()—resources.values().stream()...→ListResourcesResult.builder(list).build()resourceTemplateListRequestHandler()— same shape
None of them reads the cursor from the request, and none sets nextCursor on the result — although every one of those result records already carries a nextCursor component. The data model is there; the handlers never use it.
Why this is worth reporting
To be clear about what is not being claimed: returning every entry with no nextCursor is a complete, valid response. The spec says these operations support pagination, not that a server must paginate, and a client that receives no cursor correctly stops after one page. This is a missing capability, not a protocol violation.
What does seem worth fixing:
-
A server author cannot implement it either.
McpServer.AsyncSpecification/SyncSpecificationexposetools,resources,resourceTemplates,prompts,completions,capabilities,instructions,requestTimeout,jsonMapper,jsonSchemaValidator,rootsChangeHandler(s)— and no way to supply or replace a request handler. A server with a large catalog has no option inside the SDK. -
The client half is implemented.
McpAsyncClientexposeslistResources(String cursor),listTools(String cursor)and their siblings, so the SDK can send a cursor and follow anextCursor— one that no SDK-based server will ever produce. -
A
cursorsent by a client is silently ignored rather than rejected: the request succeeds and returns page one again. Harmless in practice — without anextCursorno well-behaved client sends one — but it is an argument accepted and dropped.
That it is identical across all four handlers suggests a design state rather than an oversight in one place, which is why this is filed against the set rather than against resources/list.
Where the earlier request went
#746 asked for cursor-based pagination (alongside a tools repository) and was closed as a duplicate of #578. #578 is about pluggable repository APIs for dynamic, per-client content — a different concern, and its description does not cover pagination. The pagination half appears to have been lost in the deduplication.
Concrete case
RESTHeart exposes MCP resources derived from MongoDB collections: one entry per collection an operator opts in, plus per-collection entries for a single document and for the document count. A deployment that opts in broadly produces a resources/list response that grows linearly with the database, returned in one message with no way to page it.
We are not blocked — the catalog size is under the operator's control — and we are not asking for a workaround. We would rather not re-slice the SDK's response in our own transport, which is why we are reporting it instead.
What would help
Either the handlers reading cursor and setting nextCursor with a server-configurable page size, or a hook that lets a server provide its own list handler. Either one closes it; the second also covers per-client filtering, which is what #578 is about.
Activity
ujibang commentedon Sep 9, 2026
A follow-up, for the record, because we have since done the thing this issue says we would rather not do.
We needed
resources/listandresources/templates/listto be filtered per caller: a client authorized to reach our MCP endpoint could enumerate every MCP-enabled resource on the server — URIs, action names including the writing ones, parameter names, and each resource's human-written description, which is prose whose whole purpose is to explain what the data is. Reads were already authorized individually; the listing was not.With no way to supply a list handler, and #578 (per-caller repositories) targeted at 3.0, we ended up filtering the SDK's result in our own transport after all. So the sentence above — "we would rather not re-slice the SDK's response in our own transport" — no longer describes what we do, and we would rather say so than leave a contradiction sitting in an open issue.
What changed our mind is that pagination and confidentiality are not the same trade. Returning a whole catalog in one message is a performance question and we can live with it. Returning a catalog that discloses what the caller cannot read is not something we can leave until 3.0.
Two things we would flag for anyone weighing the same workaround:
None of this changes the request. A hook for supplying a list handler — or #578's repositories — would let us delete the workaround, and the second one would cover the pagination half of this issue at the same time.
Kehrlann commentedon Sep 10, 2026
Duplicate of #967 re: pagination with fixed page size
Repositories, as mentioned in #578 , will be implemented for the new version of the spec