MCPのResources仕様を読み解く — resources/readでの参照の仕方
MCPのResources仕様が定めるresources/list・resources/readの挙動と、Annotations・URIスキームの使い分けを仕様原文から読み解きます。
Resourcesは、MCPサーバーがファイルやDBスキーマのようなデータをモデルの文脈として差し出す仕組みです。仕様は「アプリケーション主導」と位置づけています。モデルが自動で呼び出すのではなく、ホストアプリ側がどのリソースを文脈に含めるかを決める前提です。2026-07-28版の仕様から、resources/listとresources/readの挙動、キャッシュと変更通知の組み合わせ方、Annotationsの使い方を、実装で迷う順に読み解きます。
Resourcesは何を決め、何を実装者に任せているか
定義されるメッセージは主に3つです。一覧を取るresources/list、中身を読むresources/read、パラメータ化されたリソースの雛形を取るresources/templates/listです。変更を知らせる通知も別に定義されています。
UIの形は縛りません。ツリーやリストで明示的に選ばせる、検索して絞り込ませる、ヒューリスティクスやモデルの選択で自動的に文脈へ入れる、といった例が挙げられていますが、どれを採るかは実装の自由です。仕様が決めるのは、メッセージのやり取りの形と、返るデータの構造までです。
resourcesの能力(capability)を宣言したサーバーは、resources/listに現在のクライアントへ見せられるリソースの集合を返します。集合は空でもよく、時間とともに変わってもかまいません。ただし、接続ごとに違ったり、同じ接続の別リクエストの副作用で変わったりしてはいけません。呼び出しごとの認可情報で見える範囲が変わるのは許されています(例は「付与されたスコープが許す分だけ返す」)。
一覧と中身はどんな形で返ってくるか
resources/listはページネーションに対応し、レスポンスにはnextCursorのほか、キャッシュ用のttlMsとcacheScopeが付きます。
{
"result": {
"resultType": "complete",
"resources": [
{
"uri": "file:///project/README.md",
"name": "README.md",
"mimeType": "text/markdown"
}
],
"nextCursor": "next-page-cursor",
"ttlMs": 300000,
"cacheScope": "public"
}
}1件の定義はuri(一意な識別子)・name・title・description・icons・mimeType・sizeで構成されます。必須なのはuriとnameで、titleは表示用の人間向けの名前、sizeはバイト数のヒントです。
中身を読むresources/readは、URIを1つ渡すだけのリクエストです。返るcontentsは配列で、ディレクトリのリソースを読んだときに配下の複数ファイルをまとめて返すこともできます。
テキストはtextに文字列をそのまま入れ、バイナリはbase64のblobに入れます。クライアントは、どちらのフィールドがあるかで判別できます。
{ "uri": "file:///example.txt", "mimeType": "text/plain", "text": "本文" }{ "uri": "file:///example.png", "mimeType": "image/png", "blob": "base64-encoded-data" }仕様の例では、リクエスト例から_meta(プロトコルのバージョンとクライアント情報など)が省かれています。実際のリクエストには必須のフィールドなので、例をそのままコピーして送ると仕様違反になります。
名前の似たものとの区別も要ります。Managed Agentsでファイルをエージェントに渡すときは、MCPのResourcesではなくFiles APIでアップロードしてセッションにマウントする経路を使います。実行中のセッションへのファイルの追加・一覧・ダウンロードも、専用のsession resources APIの領分で、resources/readとは別物です。
読み取りの前に追加の入力が要るとき、https://のとき
resources/readへの応答は、1回で終わるとは限りません。サーバーはInputRequiredResultを返して、読む前に追加の入力が必要だと伝えられます。クライアントはinputResponsesを添え、サーバーからrequestStateが渡されていればそれも付けて、同じリクエストを再送します。これが複数回の往復リクエスト(MRTR)で、2026-07-28版で新しく導入され、以前のサーバー起点のリクエスト方式に代わりました。tools/callとprompts/getも、同じ仕組みでInputRequiredResultを返せます。
実装で効くのは、requestStateの扱いです。サーバーはこれを攻撃者に操作されうる入力として扱い、認可・リソースへのアクセス・業務ロジックに影響するなら、HMACやAEADで完全性を守って検証に失敗したものを拒否しなければなりません。再試行で得られた結果はキャッシュされません。
もう1つの分岐が、URIがhttps://のときです。このときクライアントはサーバーを通さず、Webから直接取得してもかまいません。サーバー側の条件は後の「URIスキームの使い分け」で扱います。
ttlMsとcacheScopeをどう決めるか
resources/list・resources/templates/list・resources/readの結果(resultTypeがcompleteのもの)には、サーバーがキャッシュ用のヒントを必ず付けます。input_requiredの中間結果にはヒントが付かず、キャッシュもされません。
ttlMsは、クライアントが結果を新鮮とみなしてよい時間(ミリ秒)で、HTTPのCache-Control: max-ageに近い意味です。0なら即座に古い扱いで、取り直しても構いません。値はあくまでヒントで、期限内にデータが変わっていてもサーバーは違反になりません。クライアントも、TTLをポーリング間隔にして裏で取り直し続けないことが求められています。
cacheScopeは、誰がキャッシュを使い回してよいかを決めます。
cacheScopeの2つの値
public
ユーザー固有のデータを含みません。共有ゲートウェイやキャッシュプロキシが、どのユーザーにも返してかまいません。ツール・プロンプト・リソーステンプレートの一覧が全員共通のときに向きます。
private
呼び出し元に紐づくデータを含みます。同じ認可コンテキスト(同じアクセストークン)の範囲でだけ再利用でき、別のトークンの相手にキャッシュを共有してはいけません。認証ユーザーによって中身が変わるresources/readの結果に向きます。
キャッシュの鍵は、メソッドと結果に影響するパラメータの組です。resources/readならuri、ページ分けされた一覧ならcursorが入ります。仕様の例では、一覧がpublic・300000ms、読み取りがprivate・60000msでした。
変更の知らせ方は2種類ある
変更通知は、一覧の増減と、個々のリソースの中身の更新で能力が分かれています。両方を宣言しても、片方だけでも、どちらも宣言しなくてもかまいません。どちらも宣言しないサーバーは、resources: {}のように空オブジェクトで宣言できます。
listChangedとsubscribe
listChanged
リソースが増減したとき、サーバーはnotifications/resources/list_changedを送ります。仕様の例では通知はmethodだけで、新しい一覧は載っていません。
subscribe
クライアントがsubscriptions/listenで、見たいURIをnotifications.resourceSubscriptionsに並べます。対象が変わるたびにnotifications/resources/updatedが届きます。
subscribeの更新通知には中身が付きません。下の4ステップの最後で読み直します。
subscribeの1サイクル
- 1
購読を張る
subscriptions/listenにresourceSubscriptionsを付けて送ります。 - 2
受理を受け取る
サーバーが
notifications/subscriptions/acknowledgedを返します。 - 3
更新通知が届く
対象が変わると
notifications/resources/updatedが届きます。通知のparamsにあるのはuriと、購読を対応づけるIDだけです。 - 4
読み直す
クライアントが
resources/readを呼び、更新後の中身を取得します。
TTLと通知は両立します。listChangedとttlMsを両方使うと、TTLの間は無駄な取り直しを避けられ、通知が即時の無効化の合図になります。逆にlistChangedを宣言せずttlMsだけを返すサーバーでは、クライアントはTTLの鮮度判断だけに頼ります。
Resource Templatesは無限に近い集合をどう表すか
固定のURIではなく、file:///{path}のようなURIテンプレート(RFC 6570)でリソースの雛形を公開できます。resources/templates/listで取得し、レスポンスの各要素はuriTemplate・name・title・description・mimeTypeを持ちます。プロジェクト内の任意のファイルパスを受け付けるリソースのように、無限に近い集合を1つの定義で表したいときの仕組みです。
テンプレートの変数は、補完APIで入力候補を出せます。入力途中の絞り込みを実装したいときは、MCPのCompletion仕様が定めるオートコンプリートの出番です。
Annotationsは何をクライアントに伝えるか
リソース・リソーステンプレート・コンテンツブロックには、任意のannotationsを付けられます。仕様はこれをクライアントへのヒントと位置づけており、強制力はありません。
audience:"user"と"assistant"のどちらに向けた情報かを示す配列です。両方を並べることもできますpriority: 0.0〜1.0の重要度です。1は「実質必須」、0は「完全に省略可」を意味しますlastModified: ISO 8601形式の更新時刻です
{
"uri": "file:///project/README.md",
"name": "README.md",
"title": "Project Documentation",
"mimeType": "text/markdown",
"annotations": {
"audience": ["user"],
"priority": 0.8,
"lastModified": "2025-01-12T15:00:58Z"
}
}クライアントは、対象読者での絞り込み、文脈に入れる優先順位の決定、更新時刻での並べ替えに使えます。リソースが多いサーバーでは、priorityがクライアント側の取捨選択の手がかりになります。
同じ3つは、Toolsの結果コンテンツ(テキスト・画像・音声・リソースリンク・埋め込みリソース)にも共通して付けられます。
URIスキームはどう使い分けるか
仕様が例示する標準スキームは3つで、一覧は網羅ではありません。
https://: クライアントがサーバーを介さずWebから自力で取得できるときにだけ使います。サーバー自身がインターネットからダウンロードして返す場合は、https://ではなく別のスキームか独自スキームを使うよう求められていますfile://: ファイルシステムのように振る舞うリソースに使います。実際の物理ファイルシステムに対応していなくてもかまいません。ディレクトリのような通常でないものには、XDG MIMEタイプのinode/directoryなどを付けられますgit://: Gitのバージョン管理との統合用です
独自スキームも定義でき、RFC 3986に沿っていれば足ります。
存在しないリソースと、セキュリティ上の注意
存在しないリソースへのresources/readには、JSON-RPCエラーの-32602(Invalid Params)を必ず返します。内部エラーには-32603が推奨されています。クライアント側は、以前の版が使っていた-32002も同じ「見つからない」として受け入れるよう求められています。
{
"jsonrpc": "2.0",
"id": 5,
"error": {
"code": -32602,
"message": "Resource not found",
"data": { "uri": "file:///nonexistent.txt" }
}
}空のcontents配列で「存在しない」を表してはいけません。空配列は「あるが中身がない」とも「存在しない」とも読めて、区別が付かないためです。
セキュリティ考慮事項は5点です。
- すべてのリソースURIを検証する
- 機微なリソースにはアクセス制御を実装する
- バイナリデータは適切にエンコードする
- 操作の前にリソースの権限を確認する
file://を提供するときはパスをサニタイズして、ディレクトリトラバーサルを防ぐ
とくに5番目は見落としやすい点です。file:///{path}のテンプレートでパスをそのまま受けると、../../etc/passwdのような相対パスで意図しない場所へ抜けられます。任意のパスを受け付ける設計にするときほど、パスの正規化と許可範囲外の拒否が欠かせません。サーバーが持つ権限の範囲はMCPセキュリティガイドで扱っています。
ToolsのresourceリンクとResourcesの関係
ツールがresource_link型のコンテンツを返すと、そのURIは、クライアントが取得・購読できるものとして扱われます。ただし、ツールが返したresource_linkがresources/listの結果に現れるとは限りません。ツール呼び出しの副産物として生まれたリソースを、恒常的な一覧に載せないままクライアントへ渡せるという意味です。
ResourcesとToolsを両方持つサーバーなら、一覧に載せるリソースと、ツール結果からだけ辿れるリソースを意図的に分けられます。Toolsの往復設計や結果コンテンツの形はMCPのTools仕様を読み解くで扱っています。
まとめ
自作サーバーでは、読み取りの結果に付けるttlMsとcacheScope、更新通知をlistChangedとsubscribeのどちらで出すか、file://のパスの検証、の3点を最初に決めておくと後の手戻りが減ります。手順はMCPサーバー自作ガイドに、Claude Code側でresourcesを読む2つのツールはListMcpResourcesToolとReadMcpResourceToolにあります。