FastMCP源码深度解析:一个@tool()装饰器背后到底发生了什么?
关键词FastMCP、MCP、Python、tool、装饰器、JSON Schema、AI Agent、Model Context Protocol、源码解析在前面的文章中我们已经学会了使用FastMCP开发自己的MCP Server。例如只需要几行代码from mcp.server.fastmcp import FastMCP app FastMCP(Demo) app.tool() def weather(city: str): 查询天气 return f{city}晴天28℃ app.run()Claude、Cursor就能够自动发现这个工具。很多开发者都会产生疑问为什么加了一个tool()AI就知道这个函数可以调用了今天我们不再停留在会用的层面而是深入FastMCP的实现原理从源码设计角度理解tool()背后发生的一切。一、先理解Python装饰器很多人第一次接触app.tool()觉得十分神奇。其实它本质上还是Python装饰器。例如def logger(func): def wrapper(*args, **kwargs): print(开始执行) result func(*args, **kwargs) print(执行结束) return result return wrapper然后logger def hello(): print(Hello)真正执行的是hello logger(hello)FastMCP采用的是同样的思想。二、tool()并不是执行工具而是注册工具很多人误以为app.tool()会立即执行函数。实际上不会。它真正做的是把这个函数注册到MCP Server。可以理解成weather() ↓ 注册 ↓ Tool Registry工具注册表例如FastMCP内部通常会维护一个工具集合不同版本实现细节可能略有不同self.tools {}每注册一个Toolapp.tool() def weather():最终都会进入Tool Registry ↓ weather calculator database github以后。客户端获取工具列表。实际上读取的就是Tool Registry三、源码第一步接收Python函数装饰器拿到的是整个Python函数对象。例如app.tool() def weather(city: str): return Sunny实际上weather本身就是Python对象。因此。FastMCP可以直接获得function.__name__得到weather再获得function.__doc__得到查询天气所以根本不用开发者重复写配置。四、第二步解析函数签名接下来。FastMCP会读取Python类型提示。例如def weather( city: str, days: int ):利用inspect.signature()可以得到city ↓ str days ↓ int最终得到{ city:string, days:integer }所以MCP协议里面参数。其实来自Python类型。而不是开发者手写JSON。五、第三步自动生成JSON SchemaMCP协议要求每个Tool。必须提供{ inputSchema: {} }FastMCP根据函数签名自动构建Schema。例如def add( a:int, b:int )生成{ type:object, properties:{ a:{ type:integer }, b:{ type:integer } }, required:[ a, b ] }这一步非常关键。因为LLM真正理解工具。依赖的是Schema。不是Python代码。六、第四步封装成Tool对象接下来。FastMCP会把所有信息封装。例如Tool ├── name ├── description ├── schema ├── function └── metadata以后。真正执行的时候。实际上调用的是tool.function(...)所以Tool。只是函数。加上一层描述信息。七、第五步注册到Server然后加入FastMCP ↓ tools ↓ Dictionary例如{ weather:Tool(), query_db:Tool(), email:Tool() }以后客户端请求tools/listFastMCP直接遍历。整个Dictionary。返回即可。八、AI为什么能够看到这些Tool很多人认为AI会扫描Python代码。实际上不会。真正流程Cursor ↓ 连接MCP ↓ tools/list ↓ 返回JSON ↓ LLM阅读JSON ↓ 决定调用哪个Tool例如客户端收到[ { name:weather, description:查询天气 }, { name:mysql } ]LLM看到weather就知道可以查询天气。所以真正看的一直都是JSON。九、真正调用时发生了什么例如用户北京今天多少度LLM输出{ tool:weather, arguments:{ city:北京 } }然后FastMCP收到weather ↓ 找到Tool对象 ↓ 找到function ↓ 执行 ↓ weather(北京)得到晴天返回LLM。整个调用流程如下用户输入 ↓ LLM分析 ↓ 生成Tool Call ↓ FastMCP路由 ↓ Python函数执行 ↓ 返回结果 ↓ LLM组织语言整个过程中。LLM从来没有直接执行Python。十、为什么FastMCP开发效率这么高因为大量重复工作SDK都帮我们完成了。例如以前。开发Tool。需要写JSON ↓ 写Schema ↓ 写RPC ↓ 注册Tool ↓ 处理协议 ↓ 解析参数现在开发者只写app.tool() def weather(): ...剩下FastMCP全部自动完成。包括Tool注册参数解析Schema生成协议封装请求分发返回结果格式化开发者可以把精力集中在业务逻辑而不是协议细节上。十一、企业项目中的最佳实践在企业开发中tool()不仅仅是把函数暴露给AI这么简单还应遵循一些设计原则。1. 一个Tool只做一件事推荐app.tool() def query_order(order_id: str): ...不推荐app.tool() def system_manager(action: str): ...职责越单一模型越容易正确选择。2. 提供清晰的Docstring例如app.tool() def query_inventory(product_id: str): 查询指定商品的库存数量比查询效果更好。3. 做好参数校验不要假设模型一定会传入正确参数。建议检查参数是否为空校验数据类型验证业务规则返回规范错误信息这样能提高Tool的稳定性。4. 不要在Tool中混杂复杂业务推荐app.tool() def query_customer(customer_id: str): return customer_service.query(customer_id)把真正的业务逻辑放到Service层Tool仅作为AI调用入口。总结一个简单的app.tool()背后其实完成了一整套自动化流程Python函数 ↓ 装饰器接管 ↓ 读取函数信息 ↓ 解析参数类型 ↓ 生成JSON Schema ↓ 创建Tool对象 ↓ 注册到Tool Registry ↓ 响应tools/list ↓ AI发现工具 ↓ LLM生成Tool Call ↓ Python函数执行 ↓ 返回结果这也是FastMCP最吸引人的地方它把MCP协议中繁琐的注册、Schema生成、协议封装和请求分发全部隐藏起来让开发者可以像写普通Python函数一样开发AI工具。理解了tool()背后的工作机制后你会发现FastMCP并没有魔法它只是巧妙地利用了Python装饰器、类型注解、反射机制和MCP协议将普通函数转换成了AI可以理解和调用的标准化工具。本文知识点✅ Python装饰器原理✅ FastMCP Tool注册机制✅inspect.signature()参数解析✅ JSON Schema自动生成✅ Tool Registry设计思想✅ MCP工具发现流程✅ 企业级Tool设计最佳实践