Repository navigation
server: defaultPageSize is 0 everywhere, so mcpkit never paginates any list method #1356
Description
What
server/pagination.go sets defaultPageSize = 0, which paginate reads as "return every item, emit no cursor". Every list method takes it:
| Method | Call site |
|---|---|
tools/list |
server/dispatch.go:567 |
resources/list |
server/dispatch.go:758 |
resources/templates/list |
server/dispatch.go:834 |
prompts/list |
server/dispatch.go:900 |
So an mcpkit server with thousands of tools returns all of them in one response, and there is no way for an operator to change that short of forking. ext/skills had the same shape until #1333 follow-up work added WithSkillsListPageSize and WithDirectoryReadPageSize (1e1fdb11); the four base-protocol methods still have no override.
Why it matters here rather than later
This is protocol-surface behavior, which is what this tracker says 1.0 commits to. Changing a server's default page size after the freeze is a breaking wire change. Before it, it is a default.
Not a conformance bug
The spec treats pagination as optional for servers: the request accepts a cursor, and a response carrying no nextCursor is a complete listing. Single-page is conformant, and mcpkit passes every conformance suite today. This is a scale and API-hygiene issue, not a spec violation.
The part that is a defect
The machinery exists and nothing can reach it. paginate is generic and correct, the wire types carry nextCursor, the handlers accept a cursor they can never issue. ext/skills had five green unit tests calling paginateDirectoryRead(items, cursor, 2) directly with a page size no caller could produce, and one wire test asserting NextCursor == "", which pinned the gap as the contract. Coverage looked healthy over a path production never took.
That combination surfaced concretely on 2026-09-04: the SEP-2640 conformance scenarios read a single page of resources/directory/read and every child-inspection check below saw only that page. Invisible against mcpkit, because mcpkit never emits a cursor. Found by Sam Bloomberg running the suite against the Go SDK, which does paginate.
Recommendation
Adopt a non-zero server-wide default, set high, with per-primitive overrides.
The tempting framing is 0 versus 1, and that is a false choice. The real risk of paginating by default is not round trips, it is silent truncation: a client that ignores nextCursor does not error, it quietly treats the first page as the whole catalog. That is a correctness failure that looks like normal operation, and it is worse than a large response, which at least works.
The threshold decides the blast radius:
| Default | Small catalogs | Large catalogs | Truncation risk |
|---|---|---|---|
0 (today) |
fine | broken | none |
1-50 |
behavior changes for everyone | fixed | wide |
250-500 |
unchanged, never paginates | fixed | only servers already broken |
A high default gets most of the safety with almost none of the risk. Servers under the threshold behave identically to today so no client breaks, and servers over it are already shipping a multi-megabyte response, so for them pagination is strictly an improvement.
Two secondary benefits: it retires the dead-path problem permanently, since any large deployment exercises paging continuously rather than only when someone opts in. And it surfaces cursor-ignoring clients against our own servers rather than in someone else's production, which is exactly how the conformance bug above came to light in reverse.
Decision needed
- Non-zero default, or keep 0 and only add the overrides?
- If non-zero, what value? Suggest 250, erring high.
- One server-wide default with per-primitive overrides, rather than four independent knobs, so the decision is made once.
Work
- Decide the above
-
WithDefaultPageSizeserver option, plus per-primitive overrides where they earn their keep - Wire through the four
dispatch.gocall sites - End-to-end tests that page over the wire, not unit tests of
paginatewith hand-passed sizes - Audit existing tests for assertions like
NextCursor == ""that would pin the old behavior -
VERSIONING.mdnote if the default changes, since it alters wire behavior
Refs #1240.
Activity