Media - MCP Python SDK
https://py.sdk.modelcontextprotocol.io/servers/media/ • 72 KB fetched
Open original page
Media - MCP Python SDK
Skip to content
MCP Python SDK
Media
*
en - English
*
de - Deutsch
*
es - español
*
fr - français
*
hi - हिन्दी
*
ja - 日本語
*
ko - 한국어
*
pt - português (Brasil)
*
ru - русский язык
*
tr - Türkçe
*
uk - українська мова
*
zh - 简体中文
*
zh-hant - 繁體中文
Search
modelcontextprotocol/python-sdk
MCP Python SDK
modelcontextprotocol/python-sdk
*
MCP Python SDK
*
What's new in v2
*
Get started
Get started
*
Installation
*
First steps
*
Connect to a real host
*
Testing
*
Servers
Servers
*
Tools
*
Structured Output
*
Resources
*
URI templates
*
Prompts
*
Completions
*
Images, audio & icons
Images, audio & icons
On this page
*
Returning an image
*
Try it
*
Returning audio
*
Bytes or a file
*
Embedding a resource
*
Icons
*
Where a client sees them
*
Recap
*
Handling errors
*
Inside your handler
Inside your handler
*
The Context
*
Dependencies
*
Lifespan
*
Elicitation
*
Multi-round-trip requests
*
Sampling and roots
*
Progress
*
Logging
*
Subscriptions
*
Running your server
Running your server
*
Add to an existing app
*
Deploy & scale
*
Authorization
*
OpenTelemetry
*
Serving legacy clients
*
Clients
Clients
*
Callbacks
*
Transports
*
OAuth
*
Identity assertion
*
Multiple servers
*
Subscriptions
*
Caching
*
Protocol versions
*
Deprecated features
*
Advanced
Advanced
*
The low-level Server
*
Pagination
*
Middleware
*
Extensions
*
MCP Apps
*
Troubleshooting
*
Translations
*
Migration Guide
*
API Reference
API Reference
*
mcp
*
mcp_types
On this page
*
Returning an image
*
Try it
*
Returning audio
*
Bytes or a file
*
Embedding a resource
*
Icons
*
Where a client sees them
*
Recap
*
MCP Python SDK
*
Servers
Media
Text is not the only thing a tool can return.
The SDK ships two helpers for binary results ( Image and Audio ) and an Icon type for giving your server, tools, resources, and prompts a face in the client's UI.
Returning an image
Annotate the return type as Image , point it at a file, and return it:
server.py 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 takes exactly one of path (a file to read) or data (raw bytes).
* The MIME type the client sees is guessed from the suffix: logo.png is announced as image/png .
* Nothing here is special about logos. Any PNG next to server.py works: a chart your code rendered, a diagram, a photo.
Image is an SDK convenience, not a protocol type. On the wire your return value becomes an ImageContent block (the file's bytes base64-encoded, plus the MIME type):
result . content # [ImageContent(type="image", data="iVBORw0KGgoAAAANSUhEUg...", mime_type="image/png")]
result . structured_content # None
Two things to notice:
* data is base64. You never touched the bytes; the SDK read the file and did the encoding.
* structured_content is None . An Image is content for the model to look at, not data for the application to parse: there is no output schema. (Contrast Structured Output , where the return annotation is the schema.)
Info
ImageContent and AudioContent live in mcp.types , right next to the TextContent
that a plain str result becomes ( Tools ). A tool result is a list of content blocks; Image and Audio are
the shortest way to produce the two binary kinds.
Try it
Drop any PNG next to server.py , name it logo.png , and run:
uv run mcp dev server.py
Open the Tools tab and call logo . The result is not a string: it is an image content block, and the Inspector renders your picture. Everything between the file on disk and the pixels on screen was the SDK.
Returning audio
Audio is the same shape. Keep logo.png where it was, and put any WAV beside it as chime.wav :
server.py 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 )
The result is an AudioContent block:
result . content # [AudioContent(type="audio", data="UklGR...", mime_type="audio/wav")]
result . structured_content # None
Same deal: a file on disk in, base64 and a MIME type out, no output schema.
Bytes or a file
Both helpers also accept data= (raw bytes) instead of path= . That is the mode for bytes that never came from a file of their own — a database column, an HTTP response, something Pillow just drew:
server.py 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" )
With path= there is nothing to declare: the file is read when the result is built, and the MIME type is guessed from the suffix:
* Image : .png , .jpg , .jpeg , .gif , .webp .
* Audio : .wav , .mp3 , .ogg , .flac , .aac , .m4a .
A suffix it doesn't recognise falls back to application/octet-stream .
Check
With data= there is no filename, so there is nothing to guess from. Forget format= and
the SDK falls back to a default: image/png for images, audio/wav for audio. Build an
Audio from MP3 bytes that way and the client is told mime_type="audio/wav" , then
faithfully fails to decode it. When you pass data= , pass format= .
Embedding a resource
A tool can also return a document: some text or bytes together with the URI it lives at and a MIME type. That is an EmbeddedResource , another kind of content block. Unlike a plain str it tells the client what the content is, so the client can show it as an attachment or recognise a resource it already knows.
server.py from mcp.server import MCPServer
from mcp.types import EmbeddedResource , TextResourceContents
mcp = MCPServer ( "Brand kit" )
@mcp . resource ( "brand://guidelines" , mime_type = "text/markdown" )
def guidelines () -> str :
"""How to use the brand assets."""
return "# Brand guidelines \n\n Use the primary colour for calls to action. \n "
@mcp . tool ()
def brand_guidelines () -> EmbeddedResource :
"""The brand guidelines as a Markdown document."""
return EmbeddedResource (
resource = TextResourceContents ( uri = "brand://guidelines" , mime_type = "text/markdown" , text = guidelines ())
)
* brand://guidelines is an ordinary resource ( Resources covers those). The tool hands the same document to the model on request, and calling guidelines() directly keeps one source of truth.
* EmbeddedResource and TextResourceContents come from mcp.types . There is no helper as there is for images: the block you build goes into the result untouched, and there is no structured_content .
* Use the URI the resource is registered under, so a client can tell that the attachment and brand://guidelines are the same document. Any URI is legal, registered or not.
result . content # [EmbeddedResource(type="resource", resource=TextResourceContents(uri="brand://guidelines", mime_type="text/markdown", text="# Brand guidelines\n\n..."))]
For binary content, use BlobResourceContents(uri=..., mime_type=..., blob=...) with the bytes base64-encoded into blob , in place of TextResourceContents . To send only a pointer the client can resources/read later, return a ResourceLink(name=..., uri=...) instead; it is a content block too.
Icons
An Icon is metadata, not content. It doesn't carry the image; it points at one with a URI, and a client may fetch it and show it next to your server's name, a tool, a resource, or a prompt.
server.py 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 is a URI the client can resolve: https: , or a data: URI if you want the icon embedded with no extra fetch.
* mime_type and sizes ( "48x48" , or "any" for a scalable format) let the client pick the right one when you offer several.
* theme="light" or theme="dark" marks an icon for one colour scheme.
The same icons=[...] keyword is accepted by MCPServer(...) , @mcp.tool() , @mcp.resource() , and @mcp.prompt() .
Where a client sees them
Icons travel with whatever they decorate. The server's arrive when the client connects, on client.server_info (optional on 2026-era connections, so narrow it first):
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"])]
A tool's icons are on the Tool object from tools/list , a resource's on the Resource from resources/list , a prompt's on the Prompt from prompts/list . The field is always called icons .
Recap
* Return an Image or Audio from a tool and the client receives an ImageContent / AudioContent block: your bytes base64-encoded, with a MIME type.
* Build one from a path= and let the suffix decide the MIME type, or from in-memory data= plus an explicit format= .
* Return an EmbeddedResource to put a document (text or a base64 blob, with its URI and MIME type) in the result, or a ResourceLink to send just the pointer.
* Media results carry no structured_content and no output schema.
* An Icon is a pointer: a src URI plus optional mime_type , sizes , and theme .
* icons=[...] works on the server, on tools, on resources, and on prompts, and clients find them on the matching objects.
That is everything a tool can put into a result. What happens when a tool fails (and who should find out) is Handling errors .
Back to top
Previous
Completions
Next
Handling errors
Made with
Zensical
Links found on this page
- Skip to content [direct]
- en - English [direct]
- de - Deutsch [direct]
- es - español [direct]
- fr - français [direct]
- hi - हिन्दी [direct]
- ja - 日本語 [direct]
- ko - 한국어 [direct]
- pt - português (Brasil) [direct]
- ru - русский язык [direct]
- tr - Türkçe [direct]
- uk - українська мова [direct]
- zh - 简体中文 [direct]
- zh-hant - 繁體中文 [direct]
- modelcontextprotocol/python-sdk [direct]
- What's new in v2 [direct]
- Get started [direct]
- Installation [direct]
- First steps [direct]
- Connect to a real host [direct]
- Testing [direct]
- Servers [direct]
- Tools [direct]
- Structured Output [direct]
- Resources [direct]
- URI templates [direct]
- Prompts [direct]
- Completions [direct]
- Handling errors [direct]
- Inside your handler [direct]
- The Context [direct]
- Dependencies [direct]
- Lifespan [direct]
- Elicitation [direct]
- Multi-round-trip requests [direct]
- Sampling and roots [direct]
- Progress [direct]
- Logging [direct]
- Subscriptions [direct]
- Running your server [direct]
- Add to an existing app [direct]
- Deploy & scale [direct]
- Authorization [direct]
- OpenTelemetry [direct]
- Serving legacy clients [direct]
- Clients [direct]
- Callbacks [direct]
- Transports [direct]
- OAuth [direct]
- Identity assertion [direct]
- Multiple servers [direct]
- Subscriptions [direct]
- Caching [direct]
- Protocol versions [direct]
- Deprecated features [direct]
- Advanced [direct]
- The low-level Server [direct]
- Pagination [direct]
- Middleware [direct]
- Extensions [direct]
- MCP Apps [direct]
- Troubleshooting [direct]
- Translations [direct]
- Migration Guide [direct]
- mcp [direct]
- mcp_types [direct]
- Zensical [direct]