- MCP pagination uses opaque cursors for tools, resources, resource templates, and prompts.
- Clients should detect repeated cursors, bound page walks, and report incomplete catalogs clearly.
- Changing catalogs and cache freshness can create gaps or duplicates across pages.
- Server-boundary telemetry can show what was requested, but not what a host displayed or considered.
MCP pagination is a cursor-based protocol utility for list operations that may return more data than one response should carry. A client requests a list such as tools/list, resources/list, resources/templates/list, or prompts/list. The server returns the current page and may include nextCursor. If nextCursor is present, the client sends another list request with that exact value in cursor. The client continues until the server omits nextCursor.
This article is verified against the MCP 2025-11-25 pagination specification and the 2026-07-28 tools specification on September 12, 2026. The pagination concept is shared across protocol eras, but modern list results also include cache hints and modern clients can use different discovery and transport envelopes.
What MCP pagination covers
MCP defines pagination for four list operations: tools/list, resources/list, resources/templates/list, and prompts/list. The server chooses the page size. The client cannot assume that the first page contains a particular number of tools, that page sizes are stable between requests, or that a cursor encodes an offset.
- tools/list returns the tools currently available to the requesting client.
- resources/list returns concrete resources the server can provide.
- resources/templates/list returns URI templates for dynamic resources.
- prompts/list returns the prompt templates exposed by the server.
The wire flow
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": { "cursor": "opaque-cursor-from-a-previous-page" }
}The response contains the page collection and, when more results are available, nextCursor. The name and encoding of the cursor belong to the server. It might be an encoded database position, a signed continuation token, or a value backed by a short-lived snapshot. A client must not need to know which.
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"resultType": "complete",
"tools": [{ "name": "search", "description": "Search records", "inputSchema": { "type": "object" } }],
"nextCursor": "opaque-cursor-for-the-next-page",
"ttlMs": 300000,
"cacheScope": "public"
}
}Why cursors are opaque
An offset-based client assumes that the server has a stable ordered array and that a page number has the same meaning later. Production lists often come from a database query, an authorization-filtered catalog, or a changing remote service. An opaque cursor lets the server choose a safe continuation strategy without making its storage design part of the protocol.
The client should only make one decision from the value: whether the response contains a nextCursor that the server expects to be sent back. It should not decode a base64-looking string, add one to a numeric string, sort cursors, or store one as a durable bookmark across unrelated sessions.
The empty cursor edge case
Older documentation and implementations sometimes treat an empty string as if it meant that pagination is finished. That is unsafe for an opaque protocol value. The robust rule is to distinguish absence from presence. A missing or null nextCursor means there is no next page. If a server emits an empty string as a cursor, the client should preserve it and send it back, unless the implementation has a documented defensive limit and reports the malformed server behavior.
A bounded client loop
let cursor: string | undefined;
const seen = new Set<string>();
const allTools = [];
for (let pageNumber = 0; pageNumber < 64; pageNumber += 1) {
const page = await client.listTools(cursor ? { cursor } : undefined);
allTools.push(...page.tools);
if (page.nextCursor === undefined) break;
if (seen.has(page.nextCursor)) {
throw new Error("MCP server repeated a pagination cursor");
}
seen.add(page.nextCursor);
cursor = page.nextCursor;
}The page cap is a safety boundary, not a protocol rule. Choose it from the largest catalog your client is meant to handle, the response-size budget, and the time available during discovery. A client should return a clear incomplete-catalog error when the limit is reached. Silently presenting only the first 64 pages makes missing tools look like a server configuration problem.
Server implementation rules
- Return a stable order while the underlying collection is unchanged.
- Validate cursors and return a JSON-RPC invalid-params error for an unknown, expired, or malformed cursor.
- Keep a cursor bound to the query, authorization context, and any snapshot assumptions it depends on.
- Never place secrets, raw customer data, or authorization material directly in a cursor unless it is protected and bounded.
- Make the final page omit nextCursor instead of inventing a sentinel such as done or null-as-a-string.
- Define what happens when the collection changes between page requests. Duplicates and gaps may be unavoidable without a snapshot.
Pagination and changing catalogs
A tools/list response is a view of a catalog at a moment in time. If tools are added or removed while a client is walking pages, the client may observe duplicates, gaps, or a cursor that is no longer valid. That is not solved by parsing the cursor. It is solved by a server choosing an appropriate snapshot or by a client detecting that the catalog changed and restarting from the beginning.
The 2026-07-28 protocol also gives list results ttlMs and cacheScope hints. These fields tell a client how long a response may be considered fresh and whether it may be shared. They do not create a consistent snapshot across pages. Each page has its own freshness and authorization considerations.
Pagination versus streaming
Pagination is client-pulled and discrete. The client decides when to request the next page and can process or discard a page before continuing. Streaming is a delivery mechanism for an ongoing flow. A large tool result may need its own application-level design, but the standard MCP pagination utility does not automatically add nextCursor to arbitrary tools/call results.
How to debug missing tools
- Capture the first tools/list response and check whether nextCursor is present.
- Confirm that the client sends the exact cursor in the next request and does not transform it.
- Compare the number of requested pages with the number of pages returned.
- Check for repeated cursors, invalid-params responses, response-size limits, and client page caps.
- Check whether authorization changes between pages and whether the server binds cursors to the correct caller.
- Record the protocol revision because a 2026 client may also expect cache hints and modern request metadata.
What TrackMCP can and cannot tell you
TrackMCP can help an MCP server team inspect server-boundary discovery events, page counts, observed durations, repeated-cursor findings, and truncation when the instrumented server emits those signals. That evidence can show that a server returned a nextCursor or that a client requested only the first page.
It cannot prove that a client displayed every tool, that a model considered every tool, or that a client used a cursor correctly after the request left the server. Those are separate host and client behaviors. Keeping that boundary explicit is the difference between useful telemetry and an invented end-to-end claim.
Does MCP pagination use page numbers?
No. MCP uses opaque cursor tokens. The server chooses the cursor format and page size, while the client sends the nextCursor value back as cursor.
What does a missing nextCursor mean?
It means the server did not advertise another page. A client should stop. It should not guess that a page is complete from the number of items returned.
Does pagination apply to tools/call results?
The standard MCP pagination utility applies to list operations. A tool that returns a large dataset can design its own cursor argument and result contract, but that is an application-level tool design unless a future protocol extension standardizes it.
About the publisher
TrackMCP, also written Track MCP
TrackMCP helps teams understand which clients connect to their MCP servers, which tools agents use, and where workflows fail. Learn more about Track MCP.
See this on your own server
TrackMCP turns your MCP server's calls into adoption, workflows, and outcomes. One line to install.