2026/10/2 12:24:31

FastMCP详解:用装饰器把Python函数变成MCP工具,JSON-RPC调用一次跑通

FastMCP详解:用装饰器把Python函数变成MCP工具,JSON-RPC调用一次跑通 1. 从一次“工具发现失败”说起FastMCP 到底解决了什么问题如果你正在用 Python 写 Agent大概率遇到过这种场景本地写了一个查天气的函数想让大模型调用它结果要么是手写 JSON Schema 写到吐要么是客户端tools/list返回空列表要么是tools/call报Method not found。这些问题的根源往往不是模型不行而是工具暴露层没打通。FastMCP 就是干这件事的它用装饰器把普通 Python 函数变成符合 MCP 协议的 JSON-RPC 工具服务端自动生成inputSchema客户端通过tools/list发现、tools/call调用整条链路一次跑通。适合谁适合想快速把已有 Python 函数暴露成 MCP 工具、又不想手写协议解析的开发者也适合已经在用 Claude Code、Cline 这类客户端想把本地能力接进去的人。我试过最省事的路径是本地起一个 FastMCP 服务端用fastmcp dev打开 Inspector 验证工具发现再用一个最小 Python 客户端发tools/call确认返回结构。等本地通了再把 endpoint 换到统一 Key/API 通道做联调避免每个工具都去配一遍鉴权。下面按“原问题 → 前置 → 配置 → 验证 → 排障 → 收尾”的顺序走一遍每一步都能复制。核心检索词先记住三个FastMCP 装饰器写法、MCP JSON-RPC 调用链路、Python 暴露 MCP 工具。这三个词贯穿全文后面每个章节都会落到具体代码上。2. TaoToken 前置统一 Key 与 API 通道怎么准备在把 endpoint 切到统一通道之前先把本地环境跑通。FastMCP 的安装推荐用 uv比 pip 快且依赖隔离干净uv add fastmcp python -c import fastmcp; print(fastmcp.__version__)如果输出类似2.x.x就说明装好了。接着准备统一通道的凭证。访问 TaoToken 官网 注册后在控制台创建 API Key路径是 Console。Key 创建完去 API Keys 页面 复制注意只显示一次。这里要区分两个地址官网带 UTM 用于归因API 基址是https://taotoken.net/api不带任何参数。客户端配置里填的是后者。如果你用的是 Claude Code 这类工具接入文档在 doc里面有 Base URL、Key、Model ID 三件套的完整说明。为什么要先做这一步因为 FastMCP 服务端本身不负责模型鉴权它只暴露工具。真正需要统一 Key 的是调用模型的客户端。所以联调时的顺序是FastMCP 服务端本地跑通 → 客户端能发现工具 → 客户端把模型请求指向统一通道 → 端到端验证。这样出问题时能快速定位是工具层还是模型层。一个容易踩的坑有人把 API Key 直接写进 FastMCP 服务端代码里这是错的。服务端只管工具注册Key 属于客户端配置。分开之后换 Key 不用动服务端代码。3. 可复制配置装饰器注册 客户端 settings 片段先写服务端。新建server.py用装饰器注册三个工具覆盖无参、必填参、可选参三种情况from fastmcp import FastMCP mcp FastMCP( namedemo-tools, version1.0.0, instructions演示 FastMCP 装饰器注册与 JSON-RPC 调用 ) mcp.tool() def add(a: float, b: float) - float: 两数相加 return a b mcp.tool() def greet(name: str, excited: bool False) - str: 打招呼excited 为 True 时加感叹号 suffix ! if excited else . return fHello, {name}{suffix} mcp.tool() def list_items(limit: int 5) - list: 返回示例列表 return [{id: i, name: fitem-{i}} for i in range(limit)]装饰器mcp.tool()会自动读取函数签名和 docstring生成 JSON Schema。a: float变成{type: number}excited: bool False变成可选布尔字段。这就是 FastMCP 相比手写 SDK 最省事的地方。启动服务端用 SSE 传输方便客户端连fastmcp run server.py --transport sse --host 127.0.0.1 --port 8000客户端配置。如果你用 Cline 或类似支持 MCP 的编辑器settings 片段长这样{ mcpServers: { demo-tools: { url: http://127.0.0.1:8000/sse, transport: sse } } }如果客户端需要走统一通道调模型模型侧的配置单独放。以 Claude Code 为例~/.claude/settings.json里加{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: 你的Key, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }三件套齐了Base URL 指向https://taotoken.net/apiKey 用控制台创建的Model ID 按文档填。注意 Base URL 不带 UTMUTM 只用于官网跳转归因。Codex 用户如果用的是auth.json结构类似{ base_url: https://taotoken.net/api, api_key: 你的Key, model: gpt-4o }这里的关键是MCP 服务端配置和模型通道配置是两套东西不要混在一个文件里。混了之后排障会很痛苦因为分不清是工具没注册还是模型没连上。4. 验证请求tools/list 与 tools/call 一次跑通服务端起好后先别急着接客户端用 Python 写个最小客户端验证 JSON-RPC 链路。这样能排除编辑器配置的干扰import asyncio from fastmcp import Client async def main(): async with Client(http://127.0.0.1:8000/sse) as client: tools await client.list_tools() print(发现工具:, [t.name for t in tools]) result await client.call_tool(add, {a: 3, b: 4}) print(add 结果:, result) result await client.call_tool(greet, {name: MCP, excited: True}) print(greet 结果:, result) asyncio.run(main())预期输出发现工具: [add, greet, list_items] add 结果: 7.0 greet 结果: Hello, MCP!如果list_tools返回空说明装饰器没生效或服务端没重启。如果call_tool报Tool not found检查工具名是否和注册时一致。FastMCP 默认用函数名作为工具名除非你在装饰器里传了name。想直接看 JSON-RPC 原始报文可以用 curl 发一个tools/listcurl -X POST http://127.0.0.1:8000/messages \ -H Content-Type: application/json \ -d {jsonrpc:2.0,id:1,method:tools/list,params:{}}返回结构里result.tools是数组每个元素有name、description、inputSchema。inputSchema就是 FastMCP 从函数签名自动生成的你可以对照add的 schema 看a和b是不是number类型。验证通过后把客户端里的模型请求指向统一通道再问一句“帮我算 3 加 4”如果模型能正确调用add工具并返回 7说明工具发现、调用、模型通道三层全通了。这一步是整个链路的关键验证点过了就说明配置没问题。5. 本篇常见错排查401、local proxy failed、reading choices排障按报错信息对号入座下面几个是我实际遇到过的。401 Unauthorized出现在客户端调模型时。原因通常是 Key 没填、填错或者 Base URL 写成了带 UTM 的官网地址。检查ANTHROPIC_BASE_URL或base_url是不是https://taotoken.net/apiKey 是不是从 API Keys 页面 复制的完整字符串。注意 Key 只显示一次复制时别带空格。local proxy failed这个报错通常出现在客户端尝试连接 MCP 服务端时。检查fastmcp run的 host 和 port 是否和客户端配置一致。如果服务端跑在127.0.0.1:8000客户端却写localhost:8000某些环境下会解析失败。统一用127.0.0.1更稳。另外确认 SSE 路径是/sse不是根路径。reading choices 报错这个一般出现在模型返回结构解析阶段常见于客户端把非标准响应当成 OpenAI 格式解析。检查 Model ID 是否填对以及 Base URL 是否指向了正确的 API 路径。如果用的是 Claude Code确认ANTHROPIC_MODEL是文档里列出的可用模型。OAuth 相关报错如果客户端提示 OAuth 失败说明它尝试走 OAuth 流程但配置里没提供。MCP 服务端本地调试不需要 OAuth把客户端里相关的 auth 字段去掉只保留 url 和 transport。tools/call 返回 Method not found检查方法名拼写。MCP 标准方法是tools/list、tools/call、resources/list、prompts/list。大小写敏感Tools/List会失败。装饰器没生效确认mcp.tool()写在函数定义正上方中间没有空行或其他装饰器干扰。如果函数是 async 的FastMCP 也支持但调用时要确保客户端用 await。排障时建议开 DEBUG 日志fastmcp run server.py --transport sse --log-level DEBUG日志里能看到每个 JSON-RPC 请求的 method 和 params对照客户端发的报文很快能定位是哪一层的问题。6. 收尾把工具接进长期编码流本地跑通之后下一步是把这套东西接进日常编码流。如果你只是偶尔验证模型能力用 模型对话 手动测一下工具调用就行。如果是要长期跑 Agent、做自动化编码建议走 Coding Plan把统一 Key 和 MCP 工具一起管起来省得每次换项目都重新配。一个实用技巧把 FastMCP 服务端写成可复用的模块工具函数按业务拆文件用mcp.mount()挂载子服务。这样新增工具不用改主文件也不会因为一个工具报错影响其他工具。另外工具函数的 docstring 一定要写清楚FastMCP 会把它作为description传给模型描述越准确模型选对工具的概率越高。最后提醒一句MCP 服务端不要直连生产数据库。本地调试用 mock 数据或只读副本等工具逻辑稳定了再考虑接真实数据源并且加好权限校验。工具暴露出去之后模型能调用的能力就是真实能力安全边界要在服务端守住。