特に戻り値の設定の仕方についての検討を行いました。
MCP 2025-06-18 で追加された structured tool output を試す
MCP の Structured tool output を試してみる
了解です、孝幸さん。Python で MCP サーバを書く場合、**戻り値は「辞書(dict)で content を返す」**という形になります。
Node 版と同じ構造ですが、Python ではよりシンプルに書けます。
ここからは、実際に動く最小の Python 実装例を使って、戻り値の書き方をはっきり示します。
🐍 Python MCP サーバの戻り値はこう書く
MCP のツールは、Python の関数が dict を返すだけで OK です。
基本形:
return {
"content": [
{
"type": "text",
"text": "ここに返したい文字列"
}
]
}
📘 最小の MCP サーバ(Python)
from mcp.server import Server
from mcp.types import TextContent
server = Server(name="example-python-server", version="1.0.0")
@server.tool()
def hello(name: str):
return {
"content": [
TextContent(
type="text",
text=f"こんにちは、{name} さん!"
)
]
}
if __name__ == "__main__":
server.run()
ポイント:
@server.tool()でツールを定義- 戻り値は content: [TextContent(...)]
TextContentを使うと型安全で便利
🧪 もう少し複雑な戻り値(複数の content)
return {
"content": [
{"type": "text", "text": "処理が完了しました"},
{"type": "text", "text": "追加情報: OK"}
]
}
❗ エラーを返したい場合
Python の MCP SDK では、例外を投げるだけで JSON-RPC の error に変換されます。
from mcp.server import Server, ToolError
@server.tool()
def divide(a: float, b: float):
if b == 0:
raise ToolError("ゼロ除算はできません")
return {
"content": [
{"type": "text", "text": str(a / b)}
]
}
🎯 まとめ
Python MCP サーバの戻り値は:
- dict を返すだけでよい
contentは 配列- 各要素は
{"type": "text", "text": "..."} - エラーは
ToolErrorを投げる
必要なら:
- Python でのストリーミング返却
- JSON 以外(画像など)の返し方
- FastAPI や asyncio との組み合わせ
なども詳しく書けますよ。どこまで作りたいか教えてください。
Python で MCP のストリーミング返却(partial response) を行う方法を、実際に動くコード付きで分かりやすくまとめます。
結論から言うと:
🎯 Python MCP のストリーミング返却は「yield を使う」
MCP の Python SDK では、ツール関数を async generator(非同期ジェネレータ) にすると、
yield した内容が 部分レスポンス(partial response) としてクライアントに送られます。
🧪 最小のストリーミング例(Python)
from mcp.server import Server
from mcp.types import TextContent
import asyncio
server = Server(name="stream-example", version="1.0.0")
@server.tool()
async def stream_test():
# 1つ目の部分レスポンス
yield {
"content": [
TextContent(type="text", text="処理を開始します…")
]
}
await asyncio.sleep(1)
# 2つ目の部分レスポンス
yield {
"content": [
TextContent(type="text", text="まだ処理中です…")
]
}
await asyncio.sleep(1)
# 最終レスポンス(return)
return {
"content": [
TextContent(type="text", text="完了しました!")
]
}
if __name__ == "__main__":
server.run()
🧩 仕組みのポイント
✔ yield → partial response
クライアント(例:Copilot)は、yield された内容を 逐次受け取る。
✔ return → final response
最後に return した内容が 最終レスポンス になる。
✔ 非同期ジェネレータである必要がある
async def + yield の組み合わせ。
📡 クライアント側の受け取りイメージ
クライアントはこんな順番で受け取る:
- 「処理を開始します…」
- 「まだ処理中です…」
- 「完了しました!」
Copilot や VS Code の MCP クライアントは、これをリアルタイムに表示できる。
🔍 もう少し実用的な例:長い処理の進捗を送る
@server.tool()
async def long_task():
for i in range(5):
yield {
"content": [
TextContent(type="text", text=f"進捗: {i+1}/5")
]
}
await asyncio.sleep(0.5)
return {
"content": [
TextContent(type="text", text="すべて完了しました!")
]
}
🎁 まとめ
Python MCP のストリーミング返却は:
async defでツールを定義yieldで部分レスポンスを送るreturnで最終レスポンスを返すcontentは通常と同じ構造(TextContent など)
必要なら:
- ストリーミングで画像を返す方法
- 大規模処理の進捗バーを送る方法
- FastAPI や asyncio タスクとの連携
なども作れますよ。
Q.たとえば、get_constraintsに対して、どのように結果を返せばよいですか?スケジュールナース内の定義をどのように教えればよいですか?
ここは誤解しやすいポイントなので、丁寧に整理しますね。
結論:MCP サーバは “Python のデータ構造をそのまま返す” のではなく、MCP の content 形式に変換して返す必要があります。
つまり、get_constraints がどんな構造の制約を返したいかを あなたが定義し、それを content に載せて返す という形になります。
🎯 まず前提:MCP は「任意の構造体」を返せる
MCP のツールは、次のような構造で返します:
{
"content": [
{
"type": "text",
"text": "..."
}
]
}
つまり、返したいデータは text(または json)として content に入れる必要があります。
🧩 では、get_constraints の戻り値はどうする?
あなたの「スケジュールナース(Schedule Nurse)」の制約定義が、例えば Python でこうだとします:
constraints = {
"max_shifts_per_day": 2,
"max_consecutive_days": 5,
"required_rest_hours": 8,
"forbidden_pairs": [
["Aさん", "Bさん"],
["Cさん", "Dさん"]
]
}
これを MCP の返却形式にすると:
import json
from mcp.types import TextContent
@server.tool()
def get_constraints():
constraints = {
"max_shifts_per_day": 2,
"max_consecutive_days": 5,
"required_rest_hours": 8,
"forbidden_pairs": [
["Aさん", "Bさん"],
["Cさん", "Dさん"]
]
}
return {
"content": [
TextContent(
type="text",
text=json.dumps(constraints, ensure_ascii=False, indent=2)
)
]
}
📦 JSON で返すのがベスト
MCP は type="json" もサポートしています。
Schedule Nurse のように構造化データを返すなら、こちらの方が自然です。
from mcp.types import JsonContent
@server.tool()
def get_constraints():
constraints = {
"max_shifts_per_day": 2,
"max_consecutive_days": 5,
"required_rest_hours": 8,
"forbidden_pairs": [
["Aさん", "Bさん"],
["Cさん", "Dさん"]
]
}
return {
"content": [
JsonContent(
type="json",
json=constraints
)
]
}
🧠 「スケジュールナース内の定義をどう教える?」の答え
あなたが Schedule Nurse 内で持っている制約定義(Python の dict、クラス、DB など)は:
- MCP サーバ内で読み込む
- 必要な形に整形する
- MCP の
contentとして返す
という流れになります。
つまり、Schedule Nurse の内部構造をそのまま返すのではなく、MCP クライアントが理解できる JSON 形式に変換して返すのが正解です。
🎁 さらに実用的な例:制約をストリーミングで返す
制約が大量なら、ストリーミングも可能です。
@server.tool()
async def get_constraints():
yield {
"content": [
TextContent(type="text", text="基本制約を読み込み中…")
]
}
await asyncio.sleep(0.5)
constraints = load_constraints_from_schedule_nurse()
yield {
"content": [
JsonContent(type="json", json=constraints)
]
}
return {
"content": [
TextContent(type="text", text="完了しました")
]
}
🎯 まとめ
- MCP の返却形式は content 配列
get_constraintsは JSON を返すのが最適- Schedule Nurse の内部定義は Python で読み込み → JSON に変換 → content に載せる
- ストリーミング返却も可能
必要なら:
- あなたの Schedule Nurse の制約定義(Python コード)を MCP 用に最適化する方法
- 制約の種類ごとにツールを分ける設計
- 制約の更新 API(set_constraints)の作り方
なども一緒に作れますよ。
0 件のコメント:
コメントを投稿