ankuro.dev
← ブログ一覧に戻る
【CCA Foundations対策 / MCP編 #3】MCPのリソースとプロンプト——データ公開とテンプレート管理
2026-04-03#Claude#MCP#Python#生成AI#入門#Claude Certified Architect

【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_promptsget_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の理解度チェック