Skip to content

Pagination and cursors

Section 12 · From docs/MCP.md on GitHub

There are two independent kinds of paging.

Protocol lists (tools/list, resources/list, resources/templates/list, prompts/list) are paged by the native server only, --page-size items at a time (100 by default; 0 disables paging). The cursor is opaque base64 JSON that names the list kind and offset:

page_size=30: tools/list -> 30 tools, nextCursor "eyJrIjogInRvb2xzIiwgIm8iOiAzMH0"
              tools/list {cursor: ...} -> 30 tools, nextCursor "eyJrIjogInRvb2xzIiwgIm8iOiA2MH0"
              tools/list {cursor: "bogus"} -> {"code": -32602, "message": "Invalid cursor"}

The SDK and fastmcp adapters return cloudg's entries in one page. When the host server pages its own list, cloudg's entries are appended to the host's last page.

Tool results are paged by the catalog tools themselves. List-style tools take limit (1 to 500, default 50) and cursor, and return total, offset, returned, next_cursor and truncated:

{"dataset": "prod", "total": 11, "offset": 0, "returned": 2, "next_cursor": "c2.e3e466fc0ea8",
 "truncated": true, "severity_breakdown": {"CRITICAL": 2, "HIGH": 5, "MEDIUM": 2, "LOW": 1, "INFO": 1},
 "items": ["..."]}

The cursor is c<offset>.<12 hex characters>. The hex part fingerprints the dataset name, the dataset version, an id of that particular load of the dataset and the tool's filter arguments (not limit, so the page size may change between calls). Passing it back with the same query continues at the offset (the next page above started at offset 2). Changing the filters, any change to the dataset, or loading a new dataset under the same name makes the old cursor stale:

Stale cursor: the query arguments or the dataset changed since it was issued. Repeat the
call without cursor to start again.

That arrives as an isError result with code -32602. The short format is deliberate: the privacy profiles redact long base64-looking tokens as possible secrets, and these cursors survive redaction.