在 python-sdk 的 MCP 服务器中返回图片、音频与资源:Image、Audio、EmbeddedResource 与 Icon 实战指南
2026/9/20 23:43:21 网站建设 项目流程
  • 人工智能
  • MCP 服务
  • MCP Clients

【免费下载链接】python-sdk

The official Python SDK for Model Context Protocol servers and clients

项目地址:https://gitcode.com/gh_mirrors/pythonsd/python-sdk
点击查看免费下载

文本不是工具(tool)唯一能返回的东西。在 Model Context Protocol 的世界里,一个工具的结果是一组**内容块(content block)**的列表——除了普通的字符串文本,还可以是图片、音频、被嵌入的文档资源,甚至是带图标的富元数据。本指南聚焦 python-sdk(Model Context Protocol 的官方 Python SDK)服务器端媒体处理能力:如何使用ImageAudio两个辅助类型在工具中返回二进制结果,如何使用EmbeddedResource把文档资源嵌入到工具结果中,以及如何使用Icon为服务器、工具、资源和提示词在客户端界面中“配上一张脸”。读完本文,你将能够用最短的代码写出返回图片、音频和文档的 MCP 工具,并理解这些返回值在线缆上究竟以什么形式传输。

内容块机制:工具结果的本质

在 MCP 协议中,工具调用的结果(CallToolResult)由一个内容块列表构成。普通字符串结果会被包装为TextContent,而二进制内容则对应ImageContentAudioContent两种块。这两个类型位于mcp.types中,与TextContent并列,见 src/mcp/server/mcpserver/utilities/types.py 中从mcp_types的导入。

python-sdk 的工具返回转换逻辑(见 src/mcp/server/mcpserver/utilities/func_metadata.py)会按以下顺序处理返回值:

  • 返回None→ 空列表;
  • 返回ContentBlock→ 原样包装进结果列表;
  • 返回Image→ 调用to_image_content()转为ImageContent
  • 返回Audio→ 调用to_audio_content()转为AudioContent
  • 返回list/tuple→ 逐项递归转换后拼接(这意味着一个工具可以同时返回文本与图片的混合列表);
  • 返回其他类型 → 走结构化输出等路径序列化。

也就是说,ImageAudio是 SDK 提供的便捷包装器(helper),而不是协议原生类型——它们在返回时被转换成真正的协议类型ImageContent/AudioContent

返回一张图片

在工具函数上把返回类型标注为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)

对应源码见 docs_src/media/tutorial001.py。需要理解的关键点:

  • Image恰好接收path(要读取的文件)与data(原始字节)二者之一。构造时校验逻辑在 src/mcp/server/mcpserver/utilities/types.py:两者都缺或都传都会抛出ValueError
  • 客户端看到的 MIME 类型由文件后缀推断logo.png会被宣告为image/png。推断表见 types.py:.pngimage/png.jpg/.jpegimage/jpeg.gifimage/gif.webpimage/webp,无法识别的后缀回退到application/octet-stream
  • 这里对 logo 没有任何特殊要求。放在server.py旁边的任何 PNG 都可以:你的代码渲染出的图表、示意图、照片皆可。

在线缆上的形态:ImageContent

Image是 SDK 的便利设施,而非协议类型。在网络上,你的返回值会变成ImageContent块——文件的字节经 base64 编码,再加上 MIME 类型:

result.content # [ImageContent(type="image", data="iVBORw0KGgoAAAANSUhEUg...", mime_type="image/png")] result.structured_content # None

Image.to_image_content()的实现(见 types.py)用base64.b64encode读取文件字节并完成编码——你从未接触过原始字节,SDK 替你读文件并处理了编码

两件值得注意的事:

  • data是 base64 字符串;
  • structured_contentNoneImage是供模型“看”的内容,而不是供应用程序“解析”的数据,因此没有输出 schema。(与之对比的是结构化输出,那里的返回注解本身就是 schema。)

信息:ImageContentAudioContent位于mcp.types,紧挨着普通str结果所变成的TextContent(见工具)。工具结果是一组内容块的列表,ImageAudio是产出两种二进制块的最短路径。

动手试一下

server.py旁边放任意一个 PNG,命名为logo.png,然后运行:

uv run mcp dev server.py

打开Tools标签页并调用logo。结果不是字符串,而是一个image内容块,Inspector 会渲染出你的图片。从磁盘上的文件到屏幕上的像素,中间发生的一切都由 SDK 完成。

返回一段音频

AudioImage形状完全相同。保留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)

对应源码见 docs_src/media/tutorial002.py。结果是AudioContent块:

result.content # [AudioContent(type="audio", data="UklGR...", mime_type="audio/wav")] result.structured_content # None

同样的套路:磁盘文件进,base64 与 MIME 类型出,没有输出 schema。音频后缀推断表见 types.py:.wavaudio/wav.mp3audio/mpeg.oggaudio/ogg.flacaudio/flac.aacaudio/aac.m4aaudio/mp4,未识别后缀同样回退application/octet-stream

字节还是文件:path 与 data 两种模式

两个辅助类型也都接受data=(原始字节)来替代path=。这是为那些“从未有过自己的文件”的字节准备的模式——数据库列、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")

对应源码见 docs_src/media/tutorial003.py。

使用path=时无需额外声明:文件在结果构建的那一刻被读取(open(self.path, "rb")发生在to_image_content()中,见 types.py),MIME 类型由后缀推断:

  • Image.png.jpg.jpeg.gif.webp
  • Audio.wav.mp3.ogg.flac.aac.m4a

无法识别的后缀回退为application/octet-stream

注意:使用data=时没有文件名,也就没有可供推断的依据。如果忘记传format=,SDK 会回退到默认值:图片默认image/png,音频默认audio/wav。用这种方式从 MP3 字节构造Audio,客户端收到的mime_type会是"audio/wav",然后忠实地解码失败。当你传data=时,务必同时传format=从实现上看,format会被直接拼进 MIME 类型:f"image/{self._format.lower()}"f"audio/{self._format.lower()}"(见 types.py),所以传format="png"就得到image/png,传format="jpeg"就得到image/jpeg,以此类推。

嵌入一个资源:EmbeddedResource

工具还可以返回一份文档:文本或字节,附带它所在的 URI 与 MIME 类型。这就是EmbeddedResource,另一种内容块。与普通str不同,它告诉客户端内容“是什么”,因此客户端可以把它当作附件展示,或识别出它已知的资源。

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\nUse 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()) )

对应源码见 docs_src/media/tutorial005.py。要点:

  • brand://guidelines是一个普通资源(资源一章专门讲解这类资源)。工具在模型请求时把同一份文档交给模型,而直接调用guidelines()保持了单一事实来源(single source of truth)。
  • EmbeddedResourceTextResourceContents都来自mcp.types。与图片不同,这里没有便捷辅助类型:你构建的块原样进入结果,也不存在structured_content
  • 使用资源注册所用的 URI,这样客户端才能判断“附件”与brand://guidelines是同一份文档。任何 URI 都是合法的,无论是否已注册。

线缆上的结果形态:

result.content # [EmbeddedResource(type="resource", resource=TextResourceContents(uri="brand://guidelines", mime_type="text/markdown", text="# Brand guidelines\n\n..."))]

对于二进制内容,改用BlobResourceContents(uri=..., mime_type=..., blob=...),把字节 base64 编码后放进blob字段,替代TextResourceContents。如果只想发送一个指针、让客户端稍后通过resources/read读取,则返回ResourceLink(name=..., uri=...)——它同样是一种内容块。

图标(Icon):给服务器一个“脸”

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."

对应源码见 docs_src/media/tutorial004.py。Icon的字段语义:

  • src是客户端可以解析的 URI:https:,或者如果希望图标内嵌、无需额外请求,则用data:URI;
  • mime_typesizes"48x48",或矢量格式用"any")让客户端在你提供多个图标时选出合适的那一个;
  • theme="light"theme="dark"把某个图标限定给一种配色方案。

同一个icons=[...]关键字被MCPServer(...)@mcp.tool()@mcp.resource()@mcp.prompt()接受。在底层,服务器的icons参数贯穿到低层Serverlist_server_info装配逻辑(见 src/mcp/server/lowlevel/server.py),工具、资源和提示词对象也各自带icons字段(例如提示词定义在 src/mcp/server/mcpserver/prompts/base.py)。仓库中的测试也覆盖了这些用法,例如 tests/server/mcpserver/test_server.py 在构造服务器时传入icons=[Icon(src="https://example.com/icon.png", ...)]

客户端在哪里看到它们

图标与它所装饰的对象一起传输。服务器的图标在客户端连接时到达,位于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

内容块转换与验证:源码与测试的佐证

除了func_metadata.py中工具返回值的自动转换,Prompt的消息构造同样会处理ImageAudio辅助类型:UserMessage/Message的构造函数会把str包装成TextContent,把Imageto_image_content()Audioto_audio_content(),或原样接收现成的内容块(见 src/mcp/server/mcpserver/prompts/base.py)。这意味着你在提示词(prompt)消息里也可以直接使用Image(data=..., format=...)这类便捷写法。

仓库测试对上述行为提供了直接验证:

  • tests/server/mcpserver/prompts/test_base.py 断言Image(data=b"img", format="png")Audio(data=b"snd", format="wav")分别被转换为ImageContent(type="image", data="aW1n", mime_type="image/png")AudioContent(type="audio", data="c25k", mime_type="audio/wav")——注意data就是b"img"的 base64 编码aW1n
  • 同文件 test_base.py 覆盖了路径不存在时抛出ValueErrorImage(path=tmp_path / "missing.png"))的错误路径;
  • tests/server/mcpserver/test_server.py 验证了Image(path)Audio(path)作为工具返回值的接线方式。

这些测试同时印证了“辅助类型在转换时读取文件/编码字节”与“MIME 由format或后缀决定”两条核心行为。

小结

  • 从工具返回ImageAudio,客户端会收到ImageContent/AudioContent块:你的字节经 base64 编码,并带有一个 MIME 类型。
  • path=构建并让后缀决定 MIME 类型,或用内存中的data=加显式format=构建。
  • 返回EmbeddedResource把一份文档(文本或 base64 blob,含 URI 与 MIME 类型)放进结果,或返回ResourceLink只发送指针。
  • 媒体结果不带structured_content,也没有输出 schema。
  • Icon是一个指针:一个srcURI 加可选的mime_typesizestheme
  • icons=[...]在服务器、工具、资源和提示词上都可用,客户端在对应的对象上找到它们。

以上就是工具能放进结果里的全部内容。工具失败时会发生什么(以及该让谁知道),见处理错误。

  • 人工智能
  • MCP 服务
  • MCP Clients

【免费下载链接】python-sdk

The official Python SDK for Model Context Protocol servers and clients

项目地址:https://gitcode.com/gh_mirrors/pythonsd/python-sdk
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询