メディア
ツールが返せるのはテキストだけではありません。
SDK には、バイナリの結果を扱うヘルパーが 2 つ(Image と Audio)と、サーバー、ツール、リソース、プロンプトにクライアントの UI 上での「顔」を与える Icon 型が用意されています。
画像を返す
戻り値の型を Image と注釈し、ファイルを指定して返します。
from pathlib import Path
from mcp.server import MCPServer
from mcp.server.mcpserver import Image
mcp = MCPServer("Brand kit")
LOGO_FILE = Path(__file__).parent / "logo.png" # or the path to your file on disk
@mcp.tool()
def logo() -> Image:
"""The brand logo as a PNG."""
return Image(path=LOGO_FILE)
Imageはpath(読み込むファイル)かdata(生のバイト列)のどちらか一方だけを取ります。- クライアントに見える MIME タイプは拡張子から推測されます。
logo.pngはimage/pngとして通知されます。 - ロゴだからといって特別なことは何もありません。
server.pyの隣にある PNG なら何でも使えます。コードが描画したグラフでも、図でも、写真でもかまいません。
Image は SDK の便利機能であって、プロトコルの型ではありません。実際に送受信されるときには、戻り値は ImageContent ブロック(ファイルのバイト列を base64 エンコードしたものと MIME タイプ)になります。
result.content # [ImageContent(type="image", data="iVBORw0KGgoAAAANSUhEUg...", mime_type="image/png")]
result.structured_content # None
注目すべき点が 2 つあります。
dataは base64 です。バイト列には一切触れていません。ファイルを読み込んでエンコードしたのは SDK です。structured_contentはNoneです。Imageはモデルが見るためのコンテンツであり、アプリケーションが解析するためのデータではありません。出力スキーマはありません。(戻り値の注釈そのものがスキーマになる 構造化出力 と比べてみてください。)
Info
ImageContent と AudioContent は mcp.types にあり、単純な str の結果が変換される TextContent のすぐ隣に並んでいます(ツール)。ツールの結果はコンテンツブロックのリストです。Image と Audio は、2 種類のバイナリブロックを作る最短の方法です。
試してみる
任意の PNG を server.py の隣に置いて logo.png という名前にし、次を実行してください。
uv run mcp dev server.py
Tools タブを開いて logo を呼び出します。結果は文字列ではありません。image コンテンツブロックであり、Inspector が画像を描画します。ディスク上のファイルから画面上のピクセルまでの間は、すべて SDK が処理しました。
音声を返す
Audio も同じ形です。logo.png はそのままにして、任意の WAV を chime.wav として隣に置いてください。
from pathlib import Path
from mcp.server import MCPServer
from mcp.server.mcpserver import Audio, Image
mcp = MCPServer("Brand kit")
LOGO_FILE = Path(__file__).parent / "logo.png"
CHIME_FILE = Path(__file__).parent / "chime.wav"
@mcp.tool()
def logo() -> Image:
"""The brand logo as a PNG."""
return Image(path=LOGO_FILE)
@mcp.tool()
def chime() -> Audio:
"""The notification chime as a WAV."""
return Audio(path=CHIME_FILE)
結果は AudioContent ブロックです。
result.content # [AudioContent(type="audio", data="UklGR...", mime_type="audio/wav")]
result.structured_content # None
仕組みは同じです。ディスク上のファイルが入力で、base64 と MIME タイプが出力、出力スキーマはありません。
バイト列かファイルか
どちらのヘルパーも path= の代わりに data=(生のバイト列)を受け付けます。これは、そもそもファイルとして存在したことのないバイト列のためのモードです。データベースのカラム、HTTP のレスポンス、Pillow が描いたばかりの画像などです。
from pathlib import Path
from mcp.server import MCPServer
from mcp.server.mcpserver import Image
mcp = MCPServer("Brand kit")
LOGO_FILE = Path(__file__).parent / "logo.png"
@mcp.tool()
def logo_from_bytes() -> Image:
"""The brand logo as a PNG."""
png = LOGO_FILE.read_bytes() # a database read, an HTTP response, Pillow output...
return Image(data=png, format="png")
path= なら宣言するものは何もありません。ファイルは結果を組み立てるときに読み込まれ、MIME タイプは拡張子から推測されます。
Image:.png、.jpg、.jpeg、.gif、.webp。Audio:.wav、.mp3、.ogg、.flac、.aac、.m4a。
認識できない拡張子は application/octet-stream にフォールバックします。
Check
data= の場合はファイル名がないので、推測する材料がありません。format= を忘れると、SDK はデフォルトにフォールバックします。画像なら image/png、音声なら audio/wav です。この方法で MP3 のバイト列から Audio を作ると、クライアントには mime_type="audio/wav" と伝えられ、それを忠実に信じてデコードに失敗します。data= を渡すときは format= も渡してください。
アイコン
Icon はメタデータであって、コンテンツではありません。画像そのものは運ばず、URI で画像を指し示します。クライアントはそれを取得して、サーバーの名前やツール、リソース、プロンプトの横に表示することがあります。
from mcp.server import MCPServer
from mcp.types import Icon
LOGO = Icon(src="https://example.com/brand-kit.png", mime_type="image/png", sizes=["48x48"])
PALETTE = Icon(src="https://example.com/palette.svg", mime_type="image/svg+xml", sizes=["any"])
mcp = MCPServer("Brand kit", icons=[LOGO])
@mcp.tool(icons=[PALETTE])
def palette() -> list[str]:
"""The brand colour palette as hex codes."""
return ["#1d4ed8", "#f59e0b", "#10b981"]
@mcp.resource("brand://guidelines", icons=[LOGO])
def guidelines() -> str:
"""How to use the brand assets."""
return "Use the primary colour for calls to action."
srcはクライアントが解決できる URI です。https:か、追加の取得なしでアイコンを埋め込みたければdata:URI です。mime_typeとsizes("48x48"、スケーラブルな形式なら"any")を指定すると、複数のアイコンを提供したときにクライアントが適切なものを選べます。theme="light"またはtheme="dark"で、アイコンを一方の配色向けとして印を付けます。
同じ icons=[...] キーワードは MCPServer(...)、@mcp.tool()、@mcp.resource()、@mcp.prompt() のいずれでも受け付けられます。
クライアントからはどこに見えるか
アイコンは、それが飾る対象と一緒に送られます。サーバーのアイコンはクライアントの接続時に client.server_info に届きます(2026 年世代の接続では省略可能なので、まず絞り込んでください)。
assert client.server_info is not None # python-sdk servers identify themselves by default
client.server_info.icons # [Icon(src="https://example.com/brand-kit.png", mime_type="image/png", sizes=["48x48"])]
ツールのアイコンは tools/list の Tool オブジェクトに、リソースのアイコンは resources/list の Resource に、プロンプトのアイコンは prompts/list の Prompt にあります。フィールド名は常に icons です。
まとめ
- ツールから
ImageまたはAudioを返すと、クライアントはImageContent/AudioContentブロックを受け取ります。バイト列が base64 エンコードされ、MIME タイプが付きます。 path=から作って拡張子に MIME タイプを決めさせるか、メモリ上のdata=に明示的なformat=を添えて作ります。- メディアの結果には
structured_contentも出力スキーマもありません。 Iconはポインターです。srcURI に、省略可能なmime_type、sizes、themeを加えたものです。icons=[...]はサーバー、ツール、リソース、プロンプトのどれにも使え、クライアントは対応するオブジェクト上でそれらを見つけます。
ツールが結果に「入れられる」ものはこれですべてです。ツールが「失敗した」ときに何が起こるか(そして誰がそれを知るべきか)は エラーの処理 で扱います。