news 2026/5/24 0:21:43

FastMCP之Tools

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FastMCP之Tools

在MCP中,Tools是一个函数,它可以被用于执行动作或者访问外部系统。

一、如何创建一个Tools?

最简单的方法就是用@mcp.tool注解一个python的函数。

服务端

fromfastmcpimportFastMCP mcp=FastMCP(name="CalculatorServer")@mcp.tooldefadd(a:int,b:int)->int:"""Adds two integer numbers together."""returna+bif__name__=="__main__":mcp.run(transport="http",# 使用HTTP传输host="0.0.0.0",# 允许外部访问port=8000# 端口)

带有 * args 或 **kwargs 的函数不支持作为工具。存在这一限制是因为 FastMCP 需要为 MCP 协议生成完整的参数 schema,而这对于可变参数列表来说是无法实现的。

客户端

importasynciofromfastmcpimportClient client=Client("http://localhost:8000/mcp")asyncdefcall_tool(name:str):asyncwithclient:result=awaitclient.call_tool(name,{"a":2,"b":3})print(result)asyncio.run(call_tool("add"))

装饰器参数说明

  • name str | None 设置通过 MCP 暴露的明确工具名称。如果未提供,则使用函数名称
  • description str | None 提供通过 MCP 公开的描述。如果设置了此描述,那么函数的文档字符串将为此目的而被忽略。
  • tags set[str] | None 一组用于对工具进行分类的字符串。服务器以及在某些情况下的客户端可以使用这些字符串来筛选或分组可用的工具。
  • enabled bool default:“True” 一个用于启用或禁用该工具的布尔值。

二、同步与异步

FastMCP 是一个优先支持异步的框架,它能无缝支持异步(async def)和同步(def)函数作为工具。对于 I/O 密集型操作,异步工具是更优选择,可保持服务器的响应性。
虽然同步工具在 FastMCP 中能无缝运行,但在执行过程中可能会阻塞事件循环。对于 CPU 密集型或可能存在阻塞的同步操作,可考虑其他策略。一种方法是使用 anyio(FastMCP 内部已在使用)将它们包装为异步函数。

同步示例在第一章节已经有了,这里举一个异步的例子

服务端

importasyncioimportanyiofromfastmcpimportFastMCP mcp=FastMCP()defcpu_intensive_task(data:str)->str:# Some heavy computation that could block the event loopreturndata@mcp.toolasyncdefwrapped_cpu_task(data:str)->str:"""CPU-intensive task wrapped to prevent blocking."""returnawaitanyio.to_thread.run_sync(cpu_intensive_task,data)# 服务启动入口asyncdefmain():# 方式1:启动 FastMCP 本地服务(默认基于 HTTP 或 WebSocket,取决于 FastMCP 版本)# 如需自定义端口/地址,可传入参数,例如:host="0.0.0.0", port=8000awaitmcp.run_http_async(host="127.0.0.1",# 绑定本地地址,外部访问可改为 0.0.0.0port=8000,# 服务端口)if__name__=="__main__":asyncio.run(main())

客户端

importasynciofromfastmcpimportClient client=Client("http://localhost:8000/mcp")asyncdefcall_tool(name:str):asyncwithclient:result=awaitclient.call_tool("wrapped_cpu_task",{"data":name})print(result)if__name__=="__main__":asyncio.run(call_tool("apple"))

三、类型声明

FastMCP 支持多种类型注解,包括所有的 Pydantic 类型, 在定义Tool函数的时候,建议添加上类型:
比如:name: str, [str] 就是类型声明。

Type AnnotationExampleDescription
Basic typesint, float, str, boolSimple scalar values
Binary databytesBinary content (raw strings, not auto-decoded base64)
Date and Timedatetime, date, timedeltaDate and time objects (ISO format strings)
Collection typeslist[str], dict[str, int], set[int]Collections of items
Optional typesfloatNone, Optional[float]
Union typesstrint, Union[str, int]
Constrained typesLiteral[“A”, “B”], EnumParameters with specific allowed values
PathsPathFile system paths (auto-converted from strings)
UUIDsUUIDUniversally unique identifiers (auto-converted from strings)
Pydantic modelsUserDataComplex structured data with validation

四、验证模式

在LLM调用Tools的时候,需要传参,默认情况下,FastMCP 采用 Pydantic 的灵活验证机制,会将兼容的输入强制转换为与类型注解匹配的形式。这提高了与大型语言模型客户端的兼容性,这些客户端可能会发送值的字符串表示形式(例如,对于整数参数发送 “10”)。
如果需要更严格的验证以拒绝任何类型不匹配的情况,您可以启用严格输入验证。严格模式使用 MCP SDK 内置的 JSON 模式验证,在将输入传递给函数之前,根据精确的模式对其进行验证:

4.1 宽松的验证

Tools定义的类型是int, 在调用的时候,传str类型,MCP自动转换int类型。

服务端

fromfastmcpimportFastMCP mcp=FastMCP("StrictServer",strict_input_validation=False)@mcp.tooldefadd_numbers(a:int,b:int)->int:"""Add two numbers."""returna+bif__name__=="__main__":mcp.run(transport="http",# 使用HTTP传输host="0.0.0.0",# 允许外部访问port=8000# 端口)

客户端

importasynciofromfastmcpimportClient client=Client("http://localhost:8000/mcp")asyncdefcall_tool(name:str):asyncwithclient:result=awaitclient.call_tool(name,{"a":"2","b":"3"})print(result)asyncio.run(call_tool("add_numbers"))

4.2 严格的验证

服务端

fromfastmcpimportFastMCP mcp=FastMCP("StrictServer",strict_input_validation=True)@mcp.tooldefadd_numbers(a:int,b:int)->int:"""Add two numbers."""returna+bif__name__=="__main__":mcp.run(transport="http",# 使用HTTP传输host="0.0.0.0",# 允许外部访问port=8000# 端口)

客户端

importasynciofromfastmcpimportClient client=Client("http://localhost:8000/mcp")asyncdefcall_tool(name:str):asyncwithclient:result=awaitclient.call_tool(name,{"a":"2","b":"3"})print(result)asyncio.run(call_tool("add_numbers"))

调用的时候,将会报异常:

Traceback(most recent call last):File"D:\code\mcp-demo\tools\flexible_client.py",line17,in<module>asyncio.run(call_tool("add_numbers"))~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^File"D:\Python\Python313\Lib\asyncio\runners.py",line195,inrunreturnrunner.run(main)~~~~~~~~~~^^^^^^File"D:\Python\Python313\Lib\asyncio\runners.py",line118,inrunreturnself._loop.run_until_complete(task)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^File"D:\Python\Python313\Lib\asyncio\base_events.py",line725,inrun_until_completereturnfuture.result()~~~~~~~~~~~~~^^File"D:\code\mcp-demo\tools\flexible_client.py",line14,incall_tool result=awaitclient.call_tool(name,{"a":"2","b":"3"})^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^File"D:\code\mcp-demo\.venv\Lib\site-packages\fastmcp\client\client.py",line969,incall_toolraiseToolError(msg)fastmcp.exceptions.ToolError:Input validation error:'3'isnotoftype'integer'

五、参数元数据

可以通过多种方式提供有关参数的额外元数据:

5.1 简单字符串描述

fromtypingimportAnnotated@mcp.tooldefprocess_image(image_url:Annotated[str,"URL of the image to process"],resize:Annotated[bool,"Whether to resize the image"]=False,width:Annotated[int,"Target width in pixels"]=800,format:Annotated[str,"Output image format"]="jpeg")->dict:"""Process an image with optional resizing."""# Implementation...

5.2 使用Field进行描述

方式一: 在Annotated中使用Field

fromtypingimportAnnotatedfrompydanticimportField@mcp.tooldefprocess_image(image_url:Annotated[str,Field(description="URL of the image to process")],resize:Annotated[bool,Field(description="Whether to resize the image")]=False,width:Annotated[int,Field(description="Target width in pixels",ge=1,le=2000)]=800,format:Annotated[Literal["jpeg","png","webp"],Field(description="Output image format")]="jpeg")->dict:"""Process an image with optional resizing."""# Implementation...

方式二:将 Field 用作默认值,不过更推荐使用 Annotated 方法:

@mcp.tooldefsearch_database(query:str=Field(description="Search query string"),limit:int=Field(10,description="Maximum number of results",ge=1,le=100))->list:"""Search the database with the provided query."""# Implementation...

5.3 对LLM隐藏参数

要在运行时注入值而不将其暴露给 LLM(例如用户 ID、凭据或数据库连接),请使用带有 Depends () 的依赖注入。使用 Depends () 的参数会自动从工具架构中排除:

fromfastmcpimportFastMCPfromfastmcp.dependenciesimportDepends mcp=FastMCP()defget_user_id()->str:return"user_123"# Injected at runtime@mcp.tooldefget_user_details(user_id:str=Depends(get_user_id))->str:# user_id is injected by the server, not provided by the LLMreturnf"Details for{user_id}"

六、返回值

FastMCP 工具可以以两种互补的格式返回数据:传统内容块(如文本和图像)和结构化输出(机器可读取的 JSON)。当你添加返回类型注释时,FastMCP 会自动生成输出模式来验证结构化数据,并使客户端能够将结果反序列化为 Python 对象。

6.1 Content

FastMCP 会自动将工具返回值转换为适当的 MCP 内容块:

返回类型转换后的MCP内容块
strSent as TextContent
bytesBase64 encoded and sent as BlobResourceContents (within an EmbeddedResource)
fastmcp.utilities.types.ImageSent as ImageContent
fastmcp.utilities.types.AudioSent as AudioContent
fastmcp.utilities.types.FileSent as base64-encoded EmbeddedResource

示例

fromfastmcp.utilities.typesimportImage,Audio,File@mcp.tooldefget_chart()->Image:"""Generate a chart image."""returnImage(path="chart.png")@mcp.tooldefget_multiple_charts()->list[Image]:"""Return multiple charts."""return[Image(path="chart1.png"),Image(path="chart2.png")]

注意事项(以上类型转换的条件)

  1. 直接返回
  2. 作为List的一部分返回
  3. 其他情况需要手动转换
# ✅ Automatic conversionreturnImage(path="chart.png")return[Image(path="chart1.png"),"text content"]# ❌ Will not be automatically convertedreturn{"image":Image(path="chart.png")}# ✅ Manual conversion for nested usereturn{"image":Image(path="chart.png").to_image_content()}

6.2 结构化输出

当你的工具返回具有 JSON 对象表示形式的数据时,FastMCP 会自动创建与传统内容并存的结构化输出。这提供了机器可读取的 JSON 数据,客户端可以将其反序列化为 Python 对象。

自动结构化内容规则:

  • 类对象结果(dict、Pydantic 模型、dataclasses)→ 始终成为结构化内容(即使没有输出模式)
  • 非对象结果(int、str、list)→ 只有存在用于验证 / 序列化它们的输出模式时,才会成为结构化内容
  • 所有结果 → 为了向后兼容,始终成为传统内容块
6.2.1 返回类对象(dict、Pydantic 模型、dataclasses)

当你的工具返回字典、数据类或 Pydantic 模型时,FastMCP 会自动从中创建结构化内容。这种结构化内容包含实际的对象数据,便于客户端反序列化为原生对象。

返回

@mcp.tooldefget_user_data(user_id:str)->dict:"""Get user data."""return{"name":"Alice","age":30,"active":True}

结构化返回

{"content":[{"type":"text","text":"{\n \"name\": \"Alice\",\n \"age\": 30,\n \"active\": true\n}"}],"structuredContent":{"name":"Alice","age":30,"active":true}}
6.2.2返回非对象结果(int、str、list)
6.2.2.1 不带类型注解(没有说明函数返回的类型)

返回

@mcp.tooldefcalculate_sum(a:int,b:int):"""Calculate sum without return annotation."""returna+b# Returns 8

实际结果

CallToolResult(content=[TextContent(type='text',text='5',annotations=None,meta=None)],structured_content=None,meta=None,data=None,is_error=False)
6.2.2.2 带类型注解(没有说明函数返回的类型)

返回

@mcp.tooldefcalculate_sum(a:int,b:int)->int:"""Calculate sum without return annotation."""returna+b# Returns 8

实际结果

CallToolResult(content=[TextContent(type='text',text='5',annotations=None,meta=None)],structured_content={'result':5},meta=None,data=5,is_error=False)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/24 0:08:56

PyTorch Lightning是否适用于Qwen-Image训练复现?

PyTorch Lightning 是否适用于 Qwen-Image 训练复现&#xff1f; 在生成式 AI 快速演进的今天&#xff0c;文生图模型已从实验性玩具走向工业级内容生产的核心引擎。以 Qwen-Image 为代表的 200 亿参数 MMDiT 架构模型&#xff0c;不仅对算力提出了极限挑战&#xff0c;更对训练…

作者头像 李华
网站建设 2026/5/21 4:27:47

通信系统仿真:数字调制与解调技术_(30).误码率BER分析

误码率BER分析 误码率&#xff08;Bit Error Rate, BER&#xff09;是通信系统中衡量数据传输可靠性的重要指标。BER定义为接收到的比特中错误比特的比例&#xff0c;通常以一个很小的数值表示。在数字通信系统中&#xff0c;误码率的分析和测量可以帮助我们了解系统的性能&…

作者头像 李华
网站建设 2026/5/21 23:55:24

GitHub上最受欢迎的Qwen3-8B微调项目TOP5盘点

GitHub上最受欢迎的Qwen3-8B微调项目TOP5盘点 在AI模型日益“军备竞赛”的今天&#xff0c;千亿参数大模型固然耀眼&#xff0c;但真正能落地到中小企业、个人开发者甚至边缘设备上的&#xff0c;往往是那些性能与成本兼得的“轻量级选手”。通义千问系列中的 Qwen3-8B 正是这样…

作者头像 李华
网站建设 2026/5/24 12:37:48

数据一多就卡?别急,先把“数据入口”修好

数据一多就卡&#xff1f;别急&#xff0c;先把“数据入口”修好 ——聊聊如何构建高吞吐、低延迟的数据接入层&#xff08;Kafka / Pulsar&#xff09; 咱先说一句大实话&#xff1a; 很多系统慢&#xff0c;不是算不动&#xff0c;而是数据进得太慢、太乱。 我见过太多项目&a…

作者头像 李华
网站建设 2026/5/22 12:28:42

算法题 自除数

自除数 问题描述 自除数 是指可以被它包含的每一位数整除的正整数。 例如&#xff0c;128 是一个自除数&#xff0c;因为 128 % 1 0&#xff0c;128 % 2 0&#xff0c;128 % 8 0。 注意&#xff1a;自除数不允许包含 0&#xff0c;因为任何数除以 0 都是未定义的。 给定两个…

作者头像 李华
网站建设 2026/5/22 7:26:25

深度解析 Flutter 自定义组件封装:从基础封装到高性能复用

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net)&#xff0c;一起共建开源鸿蒙跨平台生态。在 Flutter 开发中&#xff0c;“组件化” 是提升开发效率、保证代码可维护性的核心抓手。原生组件虽能满足基础需求&#xff0c;但实际业务中&am…

作者头像 李华