首页 / 资讯中心 / 文章详情

【珍藏必备】FastMCP实战教程:5步打造生产级MCP服务端与客户端,让LLM调用外部工具如此简单!

【珍藏必备】FastMCP实战教程:5步打造生产级MCP服务端与客户端,让LLM调用外部工具如此简单! ★ FEATURED ARTICLE
1. 从零跑通 FastMCPLLM 调用外部工具到底难在哪如果你正在用 Python 做 LLM 应用大概率遇到过这个场景模型能聊天、能写代码但一旦让它去查数据库、调内部 API、算一段带业务规则的公式它就开始一本正经地胡说。原因不复杂——LLM 本身只会生成 token它没有手也没有脚真正干活得靠外部工具。而把「模型想调用工具」和「工具真的被执行」这两件事接起来就是 MCPModel Context Protocol模型上下文协议要解决的问题。MCP 是 Anthropic 推出的开放标准你可以把它理解成「AI 应用和外部系统之间的 USB-C 接口」以前每个 AI 客户端要对接每个工具得写 N×M 套适配代码有了 MCP工具方按协议暴露能力客户端按协议发现和调用双方解耦。FastMCP 则是这个协议之上的一层 Python 高层框架用装饰器把工具注册、类型校验、传输管理这些样板活全包了你只写业务函数。这篇 FastMCP 实战教程面向的是想跑通「LLM 调用外部工具」完整链路的 Python 开发者。我会用一个计算器服务端 客户端的例子把服务端怎么写、客户端怎么连、本地怎么验证、报错怎么查一步步走完。适合谁写过 Python 函数、知道装饰器大概是什么、想给自己的 Agent 加工具但不想啃 JSON-RPC 规范的人。读完你能得到一个可复制、可扩展、能直接塞进自己项目的 MCP 服务骨架。先说清楚一个容易踩的坑MCP 服务端和普通 Web 服务不一样它默认走 stdio 传输也就是通过标准输入输出和客户端通信。这意味着你的服务端里任何一句print()都可能污染协议消息导致客户端解析失败。所以日志必须走 stderr这是后面代码里反复出现streamsys.stderr的原因。理解了这一点很多「连不上」「解析错误」的报错就都有方向了。2. TaoToken 前置准备给 MCP 客户端接上模型能力MCP 服务端负责「暴露工具」但真正决定「什么时候调用哪个工具」的是 LLM。所以一个完整的链路是LLM负责决策→ MCP 客户端负责转发调用→ MCP 服务端负责执行工具。前面这段里LLM 的接入需要一个稳定的 API 入口。我这边实测下来用 TaoToken 做模型接入比较省事它兼容 OpenAI 风格的接口Python 里换个 base_url 就能用不用改一堆 SDK 调用方式。你需要先拿到两样东西一个 API Key和一个可用的模型 ID。获取路径是登录后在控制台创建密钥地址是 https://taotoken.net/api-keys 创建完复制保存后面配置里要用。模型 ID 可以在模型对话页面确认地址 https://taotoken.net/models 选一个你账号可用的即可。这两个信息是后面所有配置的基础先备好。这里要强调一个概念MCP 客户端本身不绑定某一家模型。你可以把 MCP 客户端理解成一个「工具调度中间层」它对外暴露工具列表给 LLM对内负责把 LLM 的调用请求转成 MCP 协议消息发给服务端。所以模型换不换、用哪家只影响客户端里那几行 API 配置不影响服务端。这也是 MCP 设计的价值——工具和模型解耦。如果你后面要做的是长期跑的编码 Agent 或者多工具编排建议直接看 Coding Plan 方案地址 https://taotoken.net/coding-plan 它更适合持续调用、工具链比较长的场景。而如果只是想先验证「模型能不能正确调用我的工具」用模型对话页面手动测几轮就够了地址 https://taotoken.net/chat 。接入文档在 https://taotoken.net/doc 里面有完整的参数说明配置卡住时对着查。环境准备清单Python 3.10 以上推荐 3.11异步性能更好、pip 或 uv推荐 uvFastMCP 的 CLI 也依赖它、一个能跑命令行的终端。安装 FastMCP 用uv pip install fastmcp没装 uv 就先pip install uv或者直接pip install fastmcp也行。装完验证一下python -c from fastmcp import FastMCP; print(FastMCP installed successfully)能打印出成功字样就说明环境没问题。这一步看着简单但如果你机器上有多个 Python 版本务必确认python指向的是 3.10否则后面async with相关语法会报奇怪的错。3. 可复制配置FastMCP 服务端与客户端完整片段这一节是全文的核心我把服务端和客户端的可复制代码都放出来你直接建文件粘贴就能跑。先建项目目录mkdir fastmcp-calculator cd fastmcp-calculator uv init --python 3.113.1 服务端 calculator_server.py服务端要做三件事注册工具、注册资源、注册提示词。工具是 LLM 能调用的函数资源是只读数据提示词是可复用的指令模板。先看完整代码import logging import sys from typing import Dict from fastmcp import FastMCP # 日志必须走 stderr否则会污染 stdio 协议消息 logging.basicConfig( levellogging.DEBUG, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, streamsys.stderr ) logger logging.getLogger(__name__) mcp FastMCP(nameCalculatorServer) mcp.tool def add(a: float, b: float) - float: 两数相加 Args: a: 第一个数 b: 第二个数 Returns: a 与 b 的和 try: result a b logger.info(fAddition performed: {a} {b} {result}) return result except TypeError as e: logger.error(fType error in add: {e}) raise ValueError(fInvalid input types: {e}) mcp.tool def divide(a: float, b: float) - float: a 除以 b Raises: ValueError: 除零时抛出 try: if b 0: logger.warning(fDivision by zero attempted: {a} / {b}) raise ValueError(Cannot divide by zero) result a / b logger.info(fDivision performed: {a} / {b} {result}) return result except (TypeError, ZeroDivisionError) as e: logger.error(fError in divide: {e}) raise ValueError(fDivision error: {e}) mcp.resource(config://calculator/settings) def get_settings() - Dict: 提供计算器配置与可用操作 return { version: 1.0.0, operations: [add, subtract, multiply, divide], precision: IEEE 754 double precision } mcp.prompt def calculate_expression(expression: str) - str: 生成数学表达式计算指令 return f请分步计算以下数学表达式{expression} 计算步骤 1. 将表达式拆分为单个运算 2. 使用对应计算器工具完成每一步 3. 遵循运算优先级括号 → 乘除 → 加减 4. 展示所有中间步骤 5. 给出最终结果 可用工具add、subtract、multiply、divide.strip() if __name__ __main__: logger.info(Starting Calculator MCP Server...) try: mcp.run(transportstdio) except KeyboardInterrupt: logger.info(Server interrupted by user) sys.exit(0) except Exception as e: logger.error(fFatal error: {e}, exc_infoTrue) sys.exit(1)几个关键点解释一下。mcp.tool装饰的函数参数类型注解和文档字符串会被 FastMCP 自动提取成工具的 schemaLLM 就是靠这个知道「这个工具叫什么、要传什么参数」。所以文档字符串别偷懒写清楚参数含义模型调用准确率会明显提升。mcp.resource的 URI 遵循类型://分类/资源名约定客户端按 URI 读取。mcp.run(transportstdio)是默认传输方式Claude Desktop 这类客户端就是走 stdio。3.2 客户端 calculator_client.py客户端负责连接服务端、发现能力、调用工具。完整代码import asyncio import logging import sys from typing import Any from fastmcp import Client logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, streamsys.stderr ) logger logging.getLogger(__name__) def extract_tool_result(response: Any) - Any: 从工具响应中解析真实结果 try: if hasattr(response, content) and response.content: content response.content[0] if hasattr(content, text) and content.text is not None: import json txt content.text try: parsed json.loads(txt) if isinstance(parsed, dict) and result in parsed: return parsed[result] return parsed except json.JSONDecodeError: try: return float(txt) if . in txt else int(txt) except Exception: return txt return response except Exception as e: logger.warning(f无法解析结果{e}) return response async def main(): from calculator_server import mcp as server logger.info(Initializing Calculator Client...) try: async with Client(server) as client: logger.info(已连接到计算器服务端) tools await client.list_tools() print(f可用工具{len(tools)}) for t in tools: print(f - {t.name}: {t.description}) res await client.call_tool(add, {a: 15, b: 27}) print(f15 27 {extract_tool_result(res)}) try: await client.call_tool(divide, {a: 10, b: 0}) except Exception as e: print(f正确捕获错误{str(e)}) settings await client.read_resource(config://calculator/settings) print(f配置信息{settings[0].text}) prompt_res await client.get_prompt( calculate_expression, {expression: 25 * 4 10 / 2} ) print(f提示词模板\n{prompt_res.messages[0].content.text}) except Exception as e: logger.error(f客户端错误{e}, exc_infoTrue) sys.exit(1) if __name__ __main__: asyncio.run(main())async with Client(server)会自动管理连接的建立和清理你不用手动 close。client.call_tool()传工具名和参数字典返回的是 MCP 响应包装对象真实值藏在content[0].text里所以需要extract_tool_result解一层。这个解析函数看着啰嗦但生产环境里响应格式可能有几种变体多一层兜底能省很多调试时间。3.3 接入模型时的配置片段如果你要把 MCP 客户端接到 LLM 上让模型自动决策调用哪个工具配置大致长这样以 OpenAI 兼容风格为例{ base_url: https://taotoken.net/api, api_key: 你的_API_KEY, model: 你的_MODEL_ID, tools_source: mcp_client, mcp_server_command: python calculator_server.py }这里base_url用 https://taotoken.net/api 注意 API 地址不带多余路径后缀。api_key和model就是第 2 节里备好的那两个。如果你用的是 Claude Code 这类工具配置项名称可能不同但核心三件套不变Base URL、Key、Model ID。这三样对齐了模型侧就能正常发起工具调用请求。4. 验证请求本地启动与连通性检查配置写完接下来是验证。这一步很多人跳过结果后面报错时不知道是服务端问题还是客户端问题。我建议分三步验证每步都有明确的成功标志。第一步单独启动服务端确认它能跑起来python calculator_server.pystdio 模式下服务端启动后不会打印什么显眼的东西它会安静地等客户端连接。如果你看到日志里有Starting Calculator MCP Server...就说明进程起来了。这时候别急着 CtrlC开第二个终端。第二步在第二个终端跑客户端python calculator_client.py成功的话你会看到类似这样的输出可用工具4 - add: 两数相加 - divide: a 除以 b ... 15 27 42.0 正确捕获错误Cannot divide by zero 配置信息{version: 1.0.0, ...}看到工具列表被列出来说明服务端的能力发现正常看到15 27 42.0说明工具调用链路通了看到除零错误被正确捕获说明错误处理生效了。这三条都过本地链路就算跑通了。第三步验证模型侧能不能正确选择工具。这一步用模型对话页面手动测最直观地址 https://taotoken.net/chat 。你把工具描述贴给模型问它「15 加 27 等于多少用哪个工具」看它能不能说出add并给出参数{a: 15, b: 27}。如果模型选错了工具或者参数格式不对多半是工具文档字符串写得不清楚回去补描述。这里有个实测经验工具数量少的时候比如四五个模型选择准确率很高工具一多十几个以上描述就得写得更细最好在描述里点明「什么时候用这个工具」。FastMCP 会把文档字符串原样传给模型所以描述质量直接决定调用质量。如果你在验证时想确认 API 侧是否正常可以单独发一个最小请求测一下curl https://taotoken.net/api/v1/models \ -H Authorization: Bearer 你的_API_KEY能返回模型列表就说明 Key 和网络都没问题。这一步和 MCP 无关但能帮你快速定位问题出在模型接入还是 MCP 链路上。5. 本篇常见错排查401、local proxy failed、reading choices跑不通的时候别慌MCP 链路的报错其实就那么几类对着下面这张表查基本能定位。报错关键词大概率原因处理动作401 UnauthorizedAPI Key 错误或没带上检查 Key 是否复制完整、请求头是否带 Authorizationlocal proxy failed本地网络或端口问题检查服务端进程是否在跑、端口是否被占error reading choices模型返回格式异常检查 model ID 是否正确、base_url 是否带多余路径OAuth / auth.json 相关认证配置缺失补全 Base URL Key Model ID 三件套Connection refused客户端连不上服务端确认服务端已启动、传输方式一致先说 401。这个最常见八成是 Key 没配对或者复制时带了空格。注意 API 地址是 https://taotoken.net/api 别自己加/v1之类的后缀路径不对也会返回认证类错误。如果你用的是 Claude Code 或 Cline 这类工具Key 一般填在 settings 或 auth.json 里填完记得重启工具让配置生效。再说local proxy failed。这个报错字面意思是本地代理失败但实际原因往往是服务端进程没起来或者客户端和服务端用的传输方式不一致。比如服务端跑的是 stdio客户端却想用 HTTP 连自然连不上。排查方法很简单先确认服务端终端里进程还活着再看两边transport参数是否一致。stdio 模式下客户端是通过启动子进程的方式连服务端的所以服务端脚本路径要对。error reading choices这个报错通常出在模型返回侧意思是客户端期待某种响应结构但没拿到。常见原因是 model ID 填错了或者 base_url 多写了路径导致请求打到了错误端点。对照检查base_url 用 https://taotoken.net/api model 用你在模型列表里确认过的 ID。如果用的是 Codex 的 auth.json 配置确认里面 base_url、api_key、model 三个字段都齐了。OAuth 相关报错一般出现在需要认证的工具链里。如果你在配置里看到auth.json或者 OAuth 字样说明这个客户端要求完整的认证三件套。缺任何一个都会报认证失败。把 Base URL、Key、Model ID 补齐重启客户端大部分认证问题就解决了。还有一个隐蔽的坑服务端里不小心写了print()。stdio 模式下stdout 是协议通道你 print 一句调试信息客户端就可能解析失败报出各种莫名其妙的错。所以服务端里所有调试输出都走logger别用 print。这个坑我踩过排查了半天才发现是一行 print 惹的祸。6. 把 MCP 接进你的工作流从验证到长期使用本地链路跑通之后下一步就是把它接进真实工作流。这里分两种场景说。如果你只是偶尔验证模型调用工具的效果用模型对话页面手动测就够了地址 https://taotoken.net/chat 改改工具描述、换换模型快速看效果。这种场景不需要复杂配置重点是验证「模型能不能正确理解你的工具」。如果你要做的是长期跑的编码 Agent或者工具链比较长、调用频率高的场景建议走 Coding Plan地址 https://taotoken.net/coding-plan 。这类场景对稳定性和调用配额要求更高配置一次长期用比每次手动测省事。接入文档在 https://taotoken.net/doc 里面有各客户端的详细配置步骤卡住时对着查最快。长期使用还有几个实践建议。第一工具描述要持续迭代。模型选错工具时别急着换模型先看描述是不是有歧义。第二服务端日志级别别一直开 DEBUG生产环境用 INFO 就够日志太多反而影响性能。第三工具函数里做好输入校验LLM 传参不一定符合预期边界情况要兜住。第四资源 URI 命名保持规范类型://分类/资源名这个约定能让客户端发现逻辑更顺。最后说一个扩展方向FastMCP 支持异步工具如果你的工具要查数据库、调外部 API用async def定义能显著提升并发性能。资源也支持动态参数比如resource://users/{user_id}这种形式可以按需返回不同数据。这些高级用法在基础链路跑通后再加循序渐进别一上来就堆复杂逻辑。把计算器例子换成你自己的业务函数改改装饰器和文档字符串一个生产级的 MCP 服务端就成型了。核心链路就这三段服务端注册能力、客户端发现调用、模型决策触发。跑通一次后面加工具就是复制粘贴改逻辑的事。
阅读完成 · 觉得有帮助?
咨询建站