# MCPのページネーション設計——cursorの正しい扱い方

> MCPの一覧取得はnextCursorによる不透明カーソル方式で、ページサイズはサーバーが決める。tools/listなど4操作の実装ルールと不正カーソル時の-32602を整理する。

- Canonical: https://kuucorp.com/blog/mcp-pagination-cursor-list-operations-smb/
- Date: 2026-10-20
- Last modified: 2026-10-20
- Publisher: Kuu株式会社 (https://kuucorp.com)

---
社内システムをMCPサーバーとして公開すると、リソースやツールの数は想定より早く増えます。最初は数件だった `resources/list` が数百件になり、クライアントが応答を待ち続ける、あるいは先頭の一部しか取得できていないことに気づかない、という事故につながります。MCPには一覧取得のためのページネーション仕様があり、守るべきルールは多くありません。

## MCPのページネーションはどんな仕組みか

> MCPの一覧取得は、番号ページではなく不透明なcursor文字列で位置を表す方式で、1ページの件数はサーバーが決めます。

仕様上、cursor は結果セット内の位置を表す**不透明な文字列トークン**です。ページ番号やオフセットではありません。サーバーは応答に現在ページの結果と、続きがある場合のみ `nextCursor` を含めます。

```json
{
  "jsonrpc": "2.0",
  "id": "123",
  "result": {
    "resources": [],
    "nextCursor": "eyJwYWdlIjogM30="
  }
}
```

続きが必要なクライアントは、受け取った値を `params.cursor` に入れて同じメソッドを再度呼びます。`nextCursor` が無い応答が最終ページです。1ページの件数は固定ではなく、クライアントは件数を仮定してはなりません（MUST NOT）。

## どの操作がページネーション対象か

> ページネーションに対応するのは、resources/list・resources/templates/list・prompts/list・tools/list の4操作です。

仕様が挙げる対象は次の4つです。

- `resources/list`: リソース一覧
- `resources/templates/list`: リソーステンプレート一覧
- `prompts/list`: プロンプト一覧
- `tools/list`: ツール一覧

`tools/call` の結果にはこの仕組みは適用されません。大きな出力を返すツールは、件数指定や続きの取得をツール自身の引数と結果スキーマで設計する必要があります。

また、ページネーションは一覧の転送量を分割するだけです。取得した定義がモデルのコンテキストに載るコストは減りません。ツール数が多い場合は、取得そのものを分割する設計に加えて、ツールの数を絞る設計も別に必要です。ツール設計の考え方は[MCPサーバーのツール設計](/blog/mcp-server-implementation-tool-design/)を参照してください。

## サーバー実装では何を守るべきか

> サーバーは安定したcursorを返し、不正なcursorには-32602（Invalid params）のエラーで応答するのが仕様の推奨です。

サーバー側に求められるのは、仕様上 SHOULD の2点です。

1. **安定したcursorを提供する**: 同じ cursor を渡せば同じ位置から再開できるようにします。
2. **不正なcursorを適切に扱う**: 不正な cursor には `-32602`（Invalid params）を返します。

実装方針として、cursor は位置情報を Base64 などで符号化した文字列にする方法が仕様の例にも見られます。ただしクライアントには中身を解釈させないため、サーバー側で改ざん検知を付けるかどうかは自社で判断する範囲です。cursor に認可情報や個人情報を埋め込まないでください。

並び順が更新で変わる一覧では、cursor が指す位置が意味を失う場合があります。更新日時順ではなく作成順や ID 順にすると、取りこぼしと重複を減らせます。

## クライアント実装で守るべきルールは何か

> クライアントはnextCursorが無くなるまで取得を繰り返し、cursorの中身を解釈・加工せず、空文字列も有効な値として扱います。

クライアントの規則は仕様で次のように定められています。

- `nextCursor` が無い応答を結果の終端とみなす（SHOULD）
- ページネーションあり・なしの両方のサーバーに対応する（SHOULD）
- cursor を不透明トークンとして扱い、形式を仮定・解析・変更しない（MUST）

2025-11-25版では、cursor をセッションをまたいで保存しないことも MUST とされています。一方、draft 版の記述では、空文字列も有効な cursor であり終端と誤認してはならない、という趣旨の規則に置き換わっています。クライアントを実装するときは、準拠するプロトコルバージョンの本文を確認してください。

取得ループの骨格は次のとおりです。

```ts
const tools = [];
let cursor: string | undefined;
do {
  const page = await client.listTools({ cursor });
  tools.push(...page.tools);
  cursor = page.nextCursor;
} while (cursor !== undefined);
```

終了条件を `!cursor` と書くと、空文字列の cursor を終端と誤認します。`undefined` との比較にしておく点が重要です。

## 規模が小さいうちに何をしておくか

> 件数が少ないうちから、ループ取得と不正cursor時のエラー処理を実装しておくと、規模拡大時に作り直す必要がありません。

中小企業の MCP サーバーは、最初は一覧が1ページに収まります。しかしクライアントの取得ループを後から入れると、サーバー側の変更が連鎖します。次の3点だけは最初から入れておくことを勧めます。

1. クライアントは常に `nextCursor` のループで取得する
2. サーバーは不正な cursor に `-32602` を返す
3. 一覧の並び順は更新で変わりにくいキーにする

大規模な組織でのカタログ管理は、[エンタープライズ向けのMCPレジストリ設計](/blog/mcp-registry-namespace-verification-enterprise-trust/)が詳しいです。MCPサーバーの導入から運用定着までは、[Kuuの運用管理サービス](https://kuucorp.com/services/ai-ops/)で伴走しています。

なお、MCP仕様の変更は速く、機能によっては非推奨とされるものがあります。たとえばロギング機能は 2026-07-28 版で非推奨となりました。利用中の機能の状態は[MCPの機能ライフサイクルと非推奨の棚卸し](/blog/mcp-feature-lifecycle-deprecation-inventory-design/)で確認できます。

## 参考

- [Pagination（MCP仕様 2025-11-25）](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/pagination)
- [Pagination（MCP仕様 draft）](https://modelcontextprotocol.io/specification/draft/server/utilities/pagination)
- [Logging（MCP仕様 draft・非推奨の告知）](https://modelcontextprotocol.io/specification/draft/server/utilities/logging)

## まとめ

MCPの一覧取得は、不透明な cursor と `nextCursor` で続きを渡す方式です。クライアントは cursor を解析せず、`nextCursor` が無くなるまで取得を繰り返します。サーバーは安定した cursor を返し、不正な cursor には `-32602` で応答します。件数が少ないうちにこの2点を実装しておけば、規模が増えても作り直しが要りません。MCPサーバーの設計や運用でお困りの際は、[Kuuの運用管理サービス](https://kuucorp.com/services/ai-ops/)にご相談ください。
