MCP Resources vs Tools(vs Prompts):違いと使い分け
MCP の Resources と Tools の違いを解説。各プリミティブを誰が制御するのか、Resource・Tool・Prompt の使い分け、TypeScript/Python の実装例、クライアントの実際の対応状況まで。
MCP において Tools(ツール) はモデルが呼び出しを判断する関数(モデル制御)であり、Resources(リソース) は URI で識別される読み取り専用のデータで、クライアントアプリケーションがコンテキストとして添付するかどうかを決めます(アプリケーション制御)。3 つ目のプリミティブである Prompts(プロンプト) は、ユーザーが明示的に選ぶテンプレートで、通常はスラッシュコマンドとして表示されます(ユーザー制御)。簡単な目安は、モデルが操作や計算をする必要があるなら Tool、ホストアプリがデータをコンテキストとして読み込めるようにしたいなら Resource、人が決まったワークフローを開始するなら Prompt です。
この一文の答えの裏には、実務で本当に重要な判断が隠れています。データを取り込む主導権を誰が持つべきか、そして対象のクライアントがそもそもそれをサポートしているか、です。本記事では両方を扱い、現行の TypeScript と Python の SDK(どちらも v2、2026-07-28 版仕様 を実装)のコードを示します。
制御モデル:誰が決めるのか?
MCP サーバー仕様 は、3 つのサーバープリミティブを制御主体によって分類しています:
| プリミティブ | 制御主体 | 典型的な UI | 例 |
|---|---|---|---|
| Prompts | ユーザー制御 | スラッシュコマンド、メニュー項目 | /review-code、「この PR を要約」 |
| Resources | アプリケーション制御 | @ メンション、添付/コンテキストピッカー、自動挿入 | ファイル内容、DB スキーマ、git 履歴 |
| Tools | モデル制御 | LLM が呼び出す(ユーザー承認付きが望ましい) | issue 作成、クエリ実行、メール送信 |
「制御」とは、そのプリミティブをいつ使うかを誰が決めるかであり、誰が書くかではありません。3 つともサーバーが作成します。また仕様では、これらはあくまで設計上の意図であり、クライアントはどのプリミティブも自由な方法で提示できるとされています。実際、この柔軟さこそがクライアントごとの対応状況に大きな差を生んでいます(後述)。
Tools:操作と計算
Tool は名前、説明、inputSchema(JSON Schema)、任意で outputSchema を持ちます。クライアントは tools/list でツールを一覧し、モデルは tools/call で呼び出しを要求します。次のような場合は Tool が適しています:
- 操作に副作用がある:ファイル書き込み、PR 作成、メッセージ投稿、決済。
- 結果がモデルが実行時に選ぶ引数に依存する:検索クエリ、フィルター、日付範囲。
- 処理が参照ではなく計算である:SQL 実行、グラフ描画、外部 API 呼び出し。
- タスクの途中でモデルが自ら判断して何かを必要とする。
Tool の結果にはテキスト、画像、音声、構造化 JSON(structuredContent)を含められ、さらにリソースも含められます。resource_link(クライアントが取得できる URI)か、中身をインラインで持つ埋め込み resource です。これが 2 つのプリミティブをつなぐ橋渡しになります。
TypeScript(@modelcontextprotocol/server v2):
import { McpServer } from '@modelcontextprotocol/server';
import * as z from 'zod/v4';
const server = new McpServer({ name: 'catalog', version: '1.0.0' });
server.registerTool(
'search_products',
{
description: 'Search the product catalog by name',
inputSchema: z.object({
query: z.string().describe('Substring to match against product names'),
limit: z.number().int().max(50).optional(),
}),
},
async ({ query, limit }) => {
const hits = await db.searchProducts(query, limit ?? 10);
return {
content: [{ type: 'text', text: hits.map((h) => h.name).join('\n') }],
};
}
);
Python(mcp v2、MCPServer):
from mcp.server import MCPServer
mcp = MCPServer("catalog")
@mcp.tool()
def search_products(query: str, limit: int = 10) -> list[str]:
"""Search the product catalog by name."""
return [p.name for p in db.search_products(query, limit)]
Python では型ヒントがそのままスキーマになり、docstring がモデルの見る説明になります。
Resources:アプリケーションが添付するデータ
Resource は URI・名前・MIME タイプを持つ読み取り専用のコンテンツです。クライアントは resources/list で発見し、resources/read で取得します。次のような場合は Resource が適しています:
- データが操作ではなくコンテキストである:設定ファイル、スキーマ、ドキュメント、レポート。
- コンテキストに何を入れるかを人間やホストアプリが選ぶべき(「ファイルを添付」パターン)。
- 同じ内容を複数ターンや複数の会話で再利用し、キャッシュできる。
- コンテンツをアドレス可能にして、Tool の結果や Prompt から URI で参照させたい。
TypeScript、静的リソース:
server.registerResource(
'db-schema',
'schema://main',
{
title: 'Database schema',
description: 'DDL for the main database',
mimeType: 'text/plain',
},
async (uri) => ({
contents: [{ uri: uri.href, text: await db.dumpSchema() }],
})
);
Python:
@mcp.resource("schema://main", mime_type="text/plain")
def db_schema() -> str:
"""DDL for the main database."""
return db.dump_schema()
読み取りコールバックは contents 配列を返します。各要素は URI を返し、text か base64 の blob を持つので、PNG や PDF などのバイナリファイルも扱えます。
リソーステンプレート
実際のデータの多くはパラメータを持ちます。リソーステンプレートは RFC 6570 の URI テンプレート で URI のパターン全体を公開し、クライアントは resources/templates/list でそれを発見します。引数は completion API で自動補完できます。
import { ResourceTemplate } from '@modelcontextprotocol/server';
server.registerResource(
'user-profile',
new ResourceTemplate('users://{userId}/profile', { list: undefined }),
{ title: 'User profile', mimeType: 'application/json' },
async (uri, { userId }) => ({
contents: [
{
uri: uri.href,
mimeType: 'application/json',
text: JSON.stringify(await getUser(userId)),
},
],
})
);
@mcp.resource("users://{user_id}/profile")
def user_profile(user_id: str) -> str:
"""Profile data for one user."""
return json.dumps(get_user(user_id))
TypeScript SDK では list は必須のキーです。インスタンスを列挙できない場合は undefined を渡し、resources/list に表示させたい場合は具体的な URI を返すコールバックを渡します。
サブスクリプションと変更通知
Resource は個々の項目の変更を追跡できる唯一のプリミティブで、この部分は最新仕様で変わりました:
notifications/resources/list_changedはリソースの集合が変わったことをクライアントに伝えます(listChanged: trueを宣言)。TS SDK はリソースの登録・削除時に自動で送信します。- 個別リソースの更新(
subscribe: trueを宣言)は、ある URI の内容が変わったことをnotifications/resources/updatedで伝えます。2025 年版の接続では、クライアントはresources/subscribeでオプトインします。2026-07-28 版の接続ではこのメソッドはなくなり、クライアントはsubscriptions/listenリクエストのresourceSubscriptionsフィルターにリソース URI を指定し、更新はそのストリームで届きます。
両方の版をサポートする場合の状態管理は TypeScript SDK のドキュメント に載っています。なお、サブスクリプションにはリクエストより長く生きる接続が必要なため、ステートレスな旧来の HTTP ハンドラーの背後では更新が届きません。
Prompts:ユーザーが開始するワークフロー
Prompt は名前と引数を持つテンプレートで、メッセージのリストを返します(prompts/list、prompts/get)。「このコード差分をうちのスタイルガイドに沿ってレビューして」「バージョン X のリリースノートを下書きして」のように、人間が繰り返し使うワークフローを起動する場合に使います。Prompt のメッセージにはリソースを埋め込めるので、スタイルガイドを URI で取り込むこともできます。
server.registerPrompt(
'review-code',
{
title: 'Code review',
description: 'Review code against our conventions',
argsSchema: z.object({ code: z.string().describe('The code to review') }),
},
({ code }) => ({
messages: [
{
role: 'user',
content: { type: 'text', text: `Review this code:\n\n${code}` },
},
],
})
);
@mcp.prompt(title="Code review")
def review_code(code: str) -> str:
"""Review code against our conventions."""
return f"Review this code:\n\n{code}"
Resources・Tools・Prompts の比較
| Tools | Resources | Prompts | |
|---|---|---|---|
| 制御主体 | モデル | アプリケーション/ホスト | ユーザー |
| 識別方法 | 名前 + 入力スキーマ | URI(または URI テンプレート) | 名前 + 引数 |
| 副作用 | 可(確認を取るべき) | なし、読み取り専用 | なし(メッセージを返す) |
| 発見 | tools/list | resources/list、resources/templates/list | prompts/list |
| 利用 | tools/call | resources/read | prompts/get |
| 変更追跡 | list_changed | list_changed + URI 単位の更新 | list_changed |
| リソース参照 | 可(resource_link、埋め込み) | 該当なし | 可(埋め込み、リンク) |
| 最適な用途 | 操作、実行時引数を伴うクエリ | コンテキスト、ドキュメント、スキーマ | 人が開始する定型ワークフロー |
クライアント対応:チュートリアルが飛ばす部分
仕様は判断材料の半分にすぎません。どれほどよく設計された Resource でも、Resource を無視するクライアントでは見えません。2026 年 10 月時点:
| クライアント | Tools | Resources | Prompts |
|---|---|---|---|
| Claude Code | 対応 | 対応:@server:protocol://path メンション、加えてリソースを一覧・読み取りする組み込みツール | 対応:/mcp__server__prompt スラッシュコマンド |
| Cursor | 対応 | 対応 | 対応 |
| VS Code(Copilot) | 対応 | 対応:Add Context → MCP Resources、MCP: Browse Resources | 対応:チャットのスラッシュコマンド |
| Claude API MCP connector | 対応 | 非対応(SDK のクライアント側ヘルパーを使う) | 非対応(同上) |
| OpenAI API/ChatGPT | 対応(search・fetch などのツールが中心) | アプリ用の UI リソースのみ | ドキュメントに記載なし |
ここから実務上の結論が 2 つ導かれます:
- モデルが自分でデータを見つける必要があるなら Tool を用意する。 Resource に対応したクライアントでも、多くはリソースをユーザーに(ピッカーや @ メンションで)見せるだけで、モデルに自由に閲覧させるわけではありません。Claude Code はモデルに一覧・読み取り用のヘルパーを与えている点で例外です。
- 重要なデータは二重に公開する。 よくあるパターンは、
read_docやget_schemaのような Tool を用意し、Resource として登録したのと同じ URI のresource_linkや埋め込みリソースを返すことです。Tool のみのクライアントでも動き、Resource 対応クライアントではキャッシュ、ピッカー、サブスクリプションの恩恵を受けられます。
クライアントがツールをどう選ぶのか、ツール一覧が巨大だとなぜ問題なのかは MCP のツールディスカバリー を参照してください。Resource と検索パイプラインのどちらにするか迷っているなら、MCP vs RAG がそのトレードオフを扱っています。
判断チェックリスト
- サーバーの外の何かを変更するか? → Tool。
- モデルが実行時に引数を選ぶ必要があるか(クエリ、タスク途中で見つけた ID など)? → Tool。必要に応じて
resource_linkを返す。 - ユーザーが「添付」するような安定したコンテキスト(ドキュメント、スキーマ、ファイル)か? → Resource。パラメータがあればテンプレートに。
- クライアントがいつ変わったかを知る必要があるか? →
subscribe/listChanged付きの Resource。 - 人が少しの入力で開始するワークフローか? → Prompt。必要に応じてリソースを埋め込む。
- 対象クライアントが Tool のみ対応(API コネクタ、多くのエージェントフレームワーク)か? → データを Tool からも公開する。
よくある間違い
- 読み取り専用データを Tool だけで公開する。 どこでも動きますが、キャッシュやユーザー主導の添付ができず、参照のたびにモデルのターンを消費します。Resource も追加しましょう。
- Resource の中で書き込みをする。
resources/readに副作用があってはいけません。クライアントは自由にプリフェッチや再読み込みをする可能性があります。 - テンプレート変数を検証しない。
file:///{path}のようなテンプレートはパスを解決して範囲内に制限する必要があります。仕様はサーバーに URI の検証と、ディレクトリトラバーサル対策のためのパスのサニタイズを求めています。 - 存在しないリソースに空の
contents配列を返す。 2026-07-28 版では JSON-RPC の-32602エラーを返します(クライアントは旧来の-32002も引き続き受け付けるべきとされています)。 - 特定のクライアントを前提にした Prompt。 すべてのクライアントが Prompt を表示するわけではないので、基盤となる機能は Tool や Resource からも使えるようにしておきます。
3 つのプリミティブは MCP Inspector で並べてテストしましょう。Tools、Resources(テンプレート含む)、Prompts それぞれにタブがあり、クライアントが実際に何を受け取るかを確認する最速の方法です。
次のステップ
既存のサーバーがインターフェースをどう分けているかを見てみましょう。たとえば Filesystem、GitHub、Postgres の各サーバーを Inspector で比較し、どのデータを Tool として、どれを Resource として公開しているかを確認します。そのうえで MCP.so のサーバーディレクトリ から自分のスタックに合うサーバーを探し、Claude Code の MCP 設定 を参考に、3 つすべてに対応したクライアントで @ リソースと / プロンプトを試してみてください。
よくある質問
MCP の Resources と Tools の違いは何ですか?
- Tools はモデルが呼び出しを判断する関数で副作用を持てます。一方 Resources は URI で識別される読み取り専用データで、クライアントアプリがコンテキストとして読み込むかを決めます。つまり Tools はモデル制御、Resources はアプリケーション制御です。
Tool ではなく MCP Resource を使うべきなのはいつですか?
- ファイル、スキーマ、ドキュメントなど、ユーザーやホストアプリが添付・キャッシュできるべき安定した読み取り専用のコンテキストには Resource を使います。モデルが実行時に引数を選ぶ、計算する、状態を変更する場合は Tool を使います。
MCP の Prompts は何に使いますか?
- Prompts はユーザーが制御するテンプレートで、メッセージのリストを返し、通常はスラッシュコマンドとして表示されます。コードレビューのような定型ワークフローをまとめ、コンテキストとしてリソースを埋め込むこともできます。
すべての MCP クライアントが Resources と Prompts に対応していますか?
- いいえ。Claude Code、Cursor、VS Code は Tools・Resources・Prompts に対応していますが、Claude API の MCP connector のような API レベルの連携は Tools のみを使います。どのクライアントでもモデルがデータに届くようにするには、Tool からも公開してください。