@mcp-utils/pagination
v1.0.0
Published
Cursor-based pagination helpers for MCP tool responses — powered by vurb.
Maintainers
Readme
@mcp-utils/pagination
Your users.list tool returns 10,000 records. The LLM tries to process all of them at once. The context window overflows. The agent loses track. The user gets a broken response.
Dumping your entire database into a context window isn't a feature — it's a bug waiting to happen.
@mcp-utils/pagination gives you cursor-based pagination with a single function call:
import { paginate } from '@mcp-utils/pagination';
handler: async (ctx, args) => {
const all = await db.users.findAll();
return success(paginate(all, { cursor: args['cursor'], limit: 25 }));
// → { items, nextCursor, total, hasMore, limit, offset }
}The agent gets nextCursor in the response and knows exactly how to ask for the next page. No guessing, no overflows, no broken experiences.
Install
npm install @mcp-utils/paginationUsage
In-memory array pagination
For collections already in memory: config lists, processed results, filtered datasets.
import { paginate } from '@mcp-utils/pagination';
const listUsers = async (ctx, args) => {
const all = await db.users.findAll();
return success(paginate(all, {
cursor: args['cursor'] as string | undefined,
limit: args['limit'] as number | undefined ?? 20,
maxLimit: 100, // hard cap — no client can request more than 100
}));
};Database-native pagination (SQL offset/limit)
For large tables: let the database do the heavy lifting.
import { buildPage, decodeCursor } from '@mcp-utils/pagination';
const listOrders = async (ctx, args) => {
const offset = decodeCursor(args['cursor'] as string | undefined);
const limit = Math.min(args['limit'] as number ?? 25, 100);
const [rows, total] = await Promise.all([
db.orders.findMany({ skip: offset, take: limit }),
db.orders.count(),
]);
return success(buildPage(rows, total, { offset, limit }));
};What agents receive
Every response is consistent and predictable:
{
items: User[], // current page — never more than `limit`
nextCursor?: string, // pass as `cursor` to get the next page (absent on last page)
total: number, // full dataset size
hasMore: boolean,
limit: number,
offset: number,
}API
paginate(items, options)
| Option | Type | Default | Description |
|---|---|---|---|
| cursor | string \| undefined | — | Cursor from previous nextCursor |
| limit | number | 20 | Items per page |
| maxLimit | number | 100 | Hard cap on page size |
buildPage(slice, total, { offset, limit })
Constructs a PaginationResult from a pre-fetched database slice and total count.
encodeCursor(offset) / decodeCursor(cursor?)
Low-level cursor helpers. decodeCursor returns 0 for undefined or invalid input — always safe to use without null checks.
Part of @mcp-utils — production-grade utilities for MCP server development.
License
Apache-2.0
