news 2026/6/21 16:21:56

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/6/21 2:07:11

【ros2】从零认识URDF:机器人模型描述的“说明书”

文章目录 从零认识URDF:机器人模型描述的“说明书” 一、URDF到底是什么?能做什么? 1. 核心定位:机器人的“数字孪生说明书” 2. URDF的四大核心功能 二、URDF的基本结构:XML格式的“零件清单” 1. 根节点:`<robot>` 2. 核心组件一:`<link>`——机器人的“零…

作者头像 李华
网站建设 2026/6/20 16:51:04

sm2加密算法

Sm2背景SM2算法全称是SM2椭圆曲线公钥密码算法(SM是商用密码的拼音缩写&#xff09;,是一种基于“椭圆曲线”的密码ECC(Elliptic CurveCryptography)。2016年,SM2成为中国国家密码标准。在商用密码体系中,SM2主要用于替换RSA加密算法。SM2算法是中国国家密码局推出的国产化算法…

作者头像 李华
网站建设 2026/6/19 7:57:26

介观交通流仿真软件:Aimsun Next_(2).AimsunNext软件介绍及安装

AimsunNext软件介绍及安装 软件介绍 Aimsun Next 是一款功能强大的介观交通流仿真软件&#xff0c;广泛应用于交通规划、管理和研究领域。它能够模拟城市交通网络中的各种交通现象&#xff0c;包括车辆行驶、交通信号控制、公共交通运营等。Aimsun Next 提供了丰富的仿真模型和…

作者头像 李华
网站建设 2026/6/21 7:33:01

介观交通流仿真软件:Aimsun Next_(3).AimsunNext的用户界面和基本操作

AimsunNext的用户界面和基本操作 用户界面概述 Aimsun Next 是一款功能强大的交通流仿真软件&#xff0c;其用户界面设计直观且用户友好&#xff0c;旨在帮助用户高效地进行交通网络建模、仿真和分析。以下是 Aimsun Next 用户界面的主要组成部分及其功能&#xff1a;主菜单&am…

作者头像 李华
网站建设 2026/6/21 9:45:59

解锁 Flutter 动画魔法:从基础到实战打造丝滑交互的卡片翻转动效

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net)&#xff0c;一起共建开源鸿蒙跨平台生态。Flutter 的动画系统是其打造极致用户体验的核心武器之一&#xff0c;但很多开发者在实际开发中&#xff0c;要么只会用简单的AnimatedContainer&a…

作者头像 李华
网站建设 2026/6/19 15:04:36

第十一章中的函数解读(1)

第一个函数create or replace function ST_P2PDistance(x1 float, y1 float, x2 float, y2 float) returns float as $$ begin return sqrt((x2 - x1) * (x2 - x1) (y2 - y1) * (y2 - y1)); end; $$ language plpgsql;第一行&#xff1a;函数定义create or replace funct…

作者头像 李华