約 4 分で読めます

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

社内システムを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サーバーのツール設計を参照してください。

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

サーバーは安定した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レジストリ設計が詳しいです。MCPサーバーの導入から運用定着までは、Kuuの運用管理サービスで伴走しています。

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

参考

まとめ

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

関連記事

MCPサーバーカードの仕組みと公開時の設計判断Skills over MCPとは何か——配信と検証の設計MCPBで中小企業のMCPサーバー配布を統制するMCP新設計:subscriptions/listenの要点