はじめに
前回、MCPを実装理解!Python + FastAPIでAIアシスタントを開発するチュートリアル で Todo アプリを題材に MCP の Tools を実装しました。
ただ、実は MCP には Tools 以外にも重要な機能が2つあります。
- Resources : AI に「読ませる」データを公開する仕組み
- Prompts : よく使う定型操作をテンプレ化する仕組み
「Tools だけでなんとなく動いてたけど、Resources と Prompts って結局何が違うの?」を、前回の Todo MCP サーバーに機能追加しながら体感していきます。
対象者
- 前回の記事で Tools は実装した人
- MCP の Resources / Prompts がいまいちわからない人
- Tools・Resources・Prompts の使い分けを理解したい人
Tools / Resources / Prompts の違い
| 要素 | 役割 | イメージ |
|---|---|---|
| Tools | AI が呼び出して実行するアクション | 関数呼び出し |
| Resources | AI が読み込んで参照するデータ | ファイル・DB の中身 |
| Prompts | ユーザーが選んで実行する定型操作テンプレ | スラッシュコマンド |
Tools は「何かを実行する」ためのものなので、AI が自分の判断で叩きます。
一方 Resources は「コンテキストとして渡すデータ」です。AI が毎回ツールを呼んで一覧を取りに行かなくても、最初からコンテキストに乗せておける、という違いがあります。
Prompts はさらに毛色が違っていて、AI ではなくユーザー側が選んで使う定型文です。「今日のタスクまとめて」のような、よく使う操作をサーバー側でテンプレ化しておけます。
今回追加する構成
前回の MCP サーバー(mcp-todos/mcp-server)に、Resources と Prompts を追加します。API サーバー側の変更はありません。
MCP サーバー(Python)
├── Tools : addTodoItem / deleteTodoItem / updateTodoItem(前回実装済み)
├── Resources : todos://list(今回追加)
└── Prompts : summarize_today(今回追加)
1. Resources を実装する
Resource を登録する
mcp-server/main.py に以下を追記します。
@mcp.resource("todos://list") # 単数形 resource が正しい(resources だとエラー)
async def get_todos_resource() -> str:
async with httpx.AsyncClient() as client:
res = await client.get(f"{API_BASE}/todos")
todos = res.json()
if not todos:
return "TODO はまだ登録されていません。"
lines = []
for t in todos:
status = "✅" if t["completed"] else "⬜"
lines.append(f"{status} [{t['id']}] {t['title']}")
return "\n".join(lines)
@mcp.tool が @mcp.resource("URIスキーム") に変わっただけで、書き方自体は Tools とほぼ同じです。違うのは呼ばれ方で、AI が能動的に call するのではなく、クライアント側が「このリソースを読んで」とリクエストする形になります。
テストクライアントで確認する
test-client/main.py のメニューに Resource 読み込みを追加します。
print("5. Resource を読む(todos 一覧)")
elif choice == "5":
result = await session.read_resource("todos://list")
print("\n--- todos://list の中身 ---")
print(result.contents[0].text)
print()
実行するとこうなります。
番号を入力: 5
--- todos://list の中身 ---
⬜ [2] 掃除をする
✅ [3] 買い物に行く
Tool の addTodoItem のように「何かを変更する」のではなく、あくまで現在の状態を読み取るだけなのがポイントです。
2. Prompts を実装する
Prompt を登録する
続けて Prompts も追加します。
from mcp.server.fastmcp.prompts import base
@mcp.prompt(description="今日やるべき TODO をまとめて報告してもらう")
def summarize_today() -> list[base.Message]:
return [
base.UserMessage(
"todos://list を参照して、未完了の TODO だけを箇条書きでまとめてください。"
"優先度が高そうなものがあれば一番上に出してください。"
)
]
Prompt は「AI に投げるメッセージのテンプレート」を返す関数です。中で Resource を参照するよう指示を書いておくことで、Tools・Resources・Prompts を連携させられます。
テストクライアントで確認する
print("6. Prompt を実行する(今日のまとめ)")
elif choice == "6":
result = await session.get_prompt("summarize_today")
print("\n--- summarize_today の中身 ---")
for msg in result.messages:
print(msg.content.text)
print()
番号を入力: 6
--- summarize_today の中身 ---
todos://list を参照して、未完了の TODO だけを箇条書きでまとめてください。優先度が高そうなものがあれば一番上に出してください。
このテストクライアント自体は AI ではないので、Prompt の中身(テンプレ文)が返ってくるだけです。実際の AI クライアント(Claude Desktop など)から呼ぶと、このテンプレ文がそのまま AI への指示として渡り、Resource を読みに行って要約してくれる、という流れになります。
Tools と組み合わせるとどうなるか
実際のクライアントから一連の流れを見ると、こうなります。
ユーザー:「summarize_today プロンプトを実行して」
↓
MCP サーバー:テンプレ文を返す
↓
AI:テンプレ文を読んで「todos://list を見ればいいんだな」と判断
↓
AI:Resource (todos://list) を読み込む
↓
AI:未完了の TODO をまとめて回答
ここで「TODO を追加して」と言われたら Tools の addTodoItem が呼ばれる、という形で、3つの要素は競合せず役割分担します。
まとめ
| 要素 | 今回実装したもの | 呼ばれ方 |
|---|---|---|
| Tools | addTodoItem など(前回) | AI が能動的に実行 |
| Resources | todos://list | AI・クライアントが読み込む |
| Prompts | summarize_today | ユーザーが選んで実行 |
Tools だけだと「AI に何かをやらせる」用途に閉じがちですが、Resources を足すことで「AI に状況を把握させる」、Prompts を足すことで「よく使う操作を型化する」ができるようになります。
次は認証周り、もしくは複数 API を1つの MCP サーバーに集約する話を書く予定です。

