
【CCA Foundations対策 / MCP編 #3】MCPのリソースとプロンプト——データ公開とテンプレート管理
Anthropic Academyの「Introduction to Model Context Protocol」コースをもとに解説しています。
#1でMCPのアーキテクチャを、#2でToolsの実装を解説した。今回はResourcesとPrompts——残りの2つのプリミティブを実装し、3つの使い分けを整理する。
この記事でわかること:
- Tools / Resources / Promptsの役割と制御者の違い
@mcp.resourceデコレータによるDirect URI / Templated URIの定義read_resourceでリソースを取得する実装- content catalogでツール呼び出しを減らす設計
@mcp.promptによるサーバーサイドテンプレート管理list_prompts/get_promptでクライアントからプロンプトを呼ぶ
3つのプリミティブの使い分け
MCPサーバーは3種類のプリミティブを持つ。それぞれの役割と「誰が呼び出しタイミングを制御するか」が異なる。
| プリミティブ | 役割 | 制御者 |
|---|---|---|
| Tools | 外部APIを呼ぶなどのアクションを実行 | Claudeが必要と判断したときに呼ぶ |
| Resources | データを読み取り専用で公開 | アプリケーションコードが取得タイミングを決める |
| Prompts | 事前に用意したプロンプトテンプレート | ユーザーが/コマンドなどで明示的に呼ぶ |
ポイントは制御者の違い。ToolsはClaudeが自律的に呼び出す。Resourcesはアプリが取得タイミングを決める(Claudeが自動で取得するわけではない)。Promptsはユーザーが明示的に選んで使う。
Resourcesを定義する
Resourcesはデータを読み取り専用で公開するプリミティブ。@mcp.resource デコレータで定義する。
URIの形式には2種類ある。
Direct URI:特定のリソースを直接指定
@mcp.resource("tasks://all")
def get_all_tasks() -> str:
"""タスク一覧を返す"""
result = []
for task_id, content in tasks.items():
result.append(f"{task_id}: {content}")
return "\n".join(result)
tasks://all という固定URIでアクセスするたびに、その時点のタスク一覧を返す。
Templated URI:パラメータつきで特定のリソースを取得
@mcp.resource("tasks://{task_id}")
def get_task_resource(task_id: str) -> str:
"""タスクIDを指定してタスク内容を返す"""
if task_id not in tasks:
raise ValueError(f"タスク {task_id} が見つかりません")
return tasks[task_id]
tasks://task-001 のようにURIにIDを埋め込む。{task_id} の部分がパラメータになる。
クライアントからリソースを取得する
クライアント側では read_resource でリソースを取得する。
async def read_resource(self, uri: str) -> str:
result = await self.session().read_resource(uri)
# リソースの内容はMIMEタイプによって異なる
for content in result.contents:
if hasattr(content, "text"):
return content.text
elif hasattr(content, "blob"):
# バイナリデータの場合はbase64エンコードされている
return content.blob
return ""
result.contents にリソースの内容が入る。テキストデータは content.text、バイナリデータ(画像・PDFなど)は content.blob(base64エンコード)で取得する。
Direct URIなら "tasks://all" を、Templated URIなら "tasks://task-001" のように組み立てて渡す。
content catalogでツール呼び出しを減らす
Resourcesの重要な使い方が content catalog——利用可能なリソースの目録を返すリソースを定義するパターン。
@mcp.resource("tasks://catalog")
def get_task_catalog() -> str:
"""利用可能なタスクリソースの一覧をURIとともに返す"""
lines = ["利用可能なタスクリソース一覧:"]
for task_id in tasks.keys():
lines.append(f"- tasks://{task_id} # {tasks[task_id][:20]}...")
return "\n".join(lines)
アプリがClaudeにリクエストを送る前に tasks://catalog を取得してコンテキストに含めておくと、Claudeは「どのタスクがあるか」を事前に把握できる。
これにより、Claudeが「タスク一覧を取得するためにlist_tasksツールを呼ぶ」という余分なツール呼び出しを減らせる。アプリがリソース取得のタイミングを制御するからこそ、こういった最適化ができる。
📋 試験ガイドより
公式試験ガイドのIn-Scope Topicsに「MCP resources for content catalogs, URI patterns (direct vs templated), MIME type handling, reducing exploratory tool calls」が明記されている。content catalogパターンでexploratoryなツール呼び出しを減らせる設計が、Resourcesを使う主な理由として取り上げられている。
Promptsを定義する
Promptsはサーバーサイドで管理するプロンプトテンプレート。@mcp.prompt デコレータで定義する。
@mcp.prompt()
def task_review_prompt(task_id: str) -> str:
"""タスクのレビュー依頼プロンプトを生成する"""
if task_id not in tasks:
raise ValueError(f"タスク {task_id} が見つかりません")
task_content = tasks[task_id]
return f"""以下のタスクの進捗をレビューしてください。
タスクID: {task_id}
タスク内容: {task_content}
確認してほしいこと:
1. タスクの説明は明確か
2. 完了条件が定義されているか
3. 次のアクションは何か"""
@mcp.prompt()
def task_summary_prompt() -> str:
"""全タスクのサマリー生成プロンプトを返す"""
task_list = "\n".join(
[f"- {tid}: {content}" for tid, content in tasks.items()]
)
return f"""以下のタスク一覧を分析し、優先度順に並べ替えてください。
{task_list}
出力形式:
1. [優先度高] タスクID: 理由
2. [優先度中] タスクID: 理由
..."""
なぜサーバーサイドで定義するのか。アプリのコードにプロンプトを書くと、複数のアプリが同じMCPサーバーを使うときに各アプリがテンプレートを個別に管理することになる。MCPサーバー側に置けば、プロンプトを一箇所で管理・更新できる。
クライアントからプロンプトを呼ぶ
クライアント側での実装は list_prompts と get_prompt の2つ。
list_prompts:利用可能なプロンプトを取得
async def list_prompts(self) -> list:
result = await self.session().list_prompts()
return result.prompts
Toolsの list_tools と同じパターン。利用可能なプロンプトの名前・説明・引数一覧が返る。
get_prompt:プロンプトを取得して実行に使う
async def get_prompt(self, name: str, arguments: dict) -> str:
result = await self.session().get_prompt(name, arguments)
# プロンプトはメッセージのリストとして返る
messages = result.messages
# テキストコンテンツを抽出
for msg in messages:
if hasattr(msg.content, "text"):
return msg.content.text
return ""
name にプロンプト名、arguments にパラメータを渡す。返り値はメッセージのリスト。テキストを取り出してClaudeへのリクエストに組み込む。
よくある誤解まとめ
| 誤解 | 実際 |
|---|---|
| ResourcesはClaudeが自動で取得する | アプリケーションコードが取得タイミングを決める。Claudeが自動で取得するわけではない |
| ToolsとResourcesは同じ用途で使える | Toolsはアクション実行、Resourcesはデータ読み取り専用。副作用の有無で使い分ける |
| content catalogはオプションの最適化 | exploratoryなツール呼び出しを構造的に減らせる設計パターン。リソース数が多い場合に有効 |
| Promptsはアプリ側で管理するほうが柔軟 | 複数アプリが同じサーバーを使う場合、サーバー側で一元管理するほうが更新コストが下がる |
| Templated URIのパラメータは型変換が必要 | MCPのSDKが文字列として受け取る。バリデーションはサーバー関数内で行う |
設計の判断基準
| 場面 | やりがちな選択 | 正しい選択 | 判断の根拠 |
|---|---|---|---|
| Claudeにデータを渡したい | データ取得ツールを定義してClaudeに呼ばせる | Resourcesで公開してアプリが取得タイミングを制御する | データ取得に副作用がなく、タイミングをアプリが管理すべき場合はResourcesが適切 |
| 複数アプリで同じプロンプトテンプレートを使う | 各アプリのコードにプロンプトを書く | MCPサーバーのPromptsに定義して一元管理する | サーバー側に置くことで更新が一箇所に集約される |
| Claudeがどのリソースを使えるか把握させたい | list_tasksツールをClaudeに呼ばせる | content catalog(tasks://catalog)をアプリが事前取得してコンテキストに含める |
exploratoryなツール呼び出しを削減できる。アプリが取得タイミングを制御するResourcesの利点を活かせる |
まとめ
- MCPの3プリミティブは制御者の違いで使い分ける。ToolsはClaude、Resourcesはアプリ、Promptsはユーザーが制御する
@mcp.resourceデコレータでDirect URI(固定)とTemplated URI(パラメータつき)の2種類を定義できる- クライアントは
read_resource(uri)でリソースを取得。テキストは.text、バイナリは.blobで取り出す - content catalogパターンでClaudeの探索的なツール呼び出しを減らせる
@mcp.promptでプロンプトテンプレートをサーバーサイドで管理。list_prompts/get_promptでクライアントから呼ぶ
← #2:MCPでツールを定義する——サーバー実装とクライアント接続 | 【振り返りクイズ】MCP編——#1〜#3の理解度チェック →