1. 为什么我要用 FastMCP 写一个 stdio 服务端MCP 协议服务端创建这件事说穿了就是让大模型能调用你自己写的函数。你写一个 Python 函数用mcp.tool()装饰一下大模型就能在对话里看到这个工具并在需要时调用它。听起来很玄实际跑一遍你会发现核心代码不到 50 行。MCPModel Context Protocol是给大模型提供上下文和能力的标准协议。一个 MCP 服务端可以暴露三类东西Resources资源类似文件数据供客户端读取、Tools工具LLM 可调用的函数、Prompts提示模板。对初次接触的开发者来说Tools 是最直观的切入点——你写一个查天气的函数模型就能帮你查天气。stdio 通信是 MCP 最基础的传输方式服务端通过标准输入接收 JSON-RPC 消息通过标准输出返回结果。不需要开端口、不需要网络配置客户端启动一个子进程就能通信。这也是为什么我建议从 stdio 入手——它把协议和网络两件事拆开了你只需要关心协议本身。这篇文章面向的是第一次接触 MCP 协议的开发者。我会带你从零搭一个可被大模型调用的 stdio 服务端给出完整可复制的代码、依赖安装命令、本地联调验证步骤最后说明如何通过 TaoToken 统一 Key/API 通道接入模型完成端到端调用测试。你不需要事先了解 JSON-RPC也不需要懂异步编程的细节跟着敲一遍就能跑通。我试过用最朴素的方式手写 JSON-RPC 消息去理解 MCP结果被消息格式和握手流程绕晕了。FastMCP 把这个过程封装成了装饰器你只需要关注业务逻辑。实测下来从装环境到看到工具被调用大概 20 分钟。2. 环境准备与 TaoToken 前置配置2.1 依赖安装Python 3.10 与 uvMCP Python SDK 要求 Python 3.10 或更高版本。先确认版本python3 --version # 期望输出Python 3.10.x 或更高如果版本不够建议用 uv 管理 Python 版本它比手动装省事。安装 uvcurl -LsSf https://astral.sh/uv/install.sh | sh安装完成后重新加载 shell 配置然后验证uv --version # 期望输出uv 0.x.x2.2 创建项目并安装 MCP SDKuv init weather-mcp cd weather-mcp uv venv source .venv/bin/activate uv add mcp[cli] httpx touch weather.pymcp[cli]会装上 MCP SDK 和命令行工具httpx用来发异步 HTTP 请求。装完后可以用uv run mcp version确认 SDK 版本建议 1.2.0 以上。2.3 通过 TaoToken 统一 Key/API 通道接入模型服务端写完后你需要一个能调用 MCP 工具的模型客户端来做端到端测试。这里用 TaoToken 的统一 API 通道好处是一个 Key 能对接多种模型不用为每个模型单独配环境。先在 TaoToken 控制台创建 API Key访问 API Keys 管理页新建一个 Key 并复制保存。然后记下两个地址Base URLhttps://taotoken.net/apiAPI Key你刚创建的那串如果你打算长期做编码类 Agent 开发可以了解下 Coding Plan它针对高频调用场景做了额度优化。只是想先验证模型能不能调通工具用按量计费的 Key 就够了。模型 ID 方面TaoToken 支持多种主流模型具体可用列表在 模型对话页 能看到。选一个支持 function calling 的模型即可比如claude-sonnet-4-20250514或gpt-4o。3. 可复制的 FastMCP 服务端配置与代码3.1 服务端完整代码把下面内容写入weather.py。这段代码定义了两个工具get_alerts查美国某州的天气预警get_forecast查某经纬度的天气预报。from typing import Any import httpx from mcp.server.fastmcp import FastMCP # 初始化 FastMCP 服务端名字叫 weather mcp FastMCP(weather) NWS_API_BASE https://api.weather.gov USER_AGENT weather-app/1.0 async def make_nws_request(url: str) - dict[str, Any] | None: 请求 NWS API带错误处理。 headers { User-Agent: USER_AGENT, Accept: application/geojson, } async with httpx.AsyncClient() as client: try: response await client.get(url, headersheaders, timeout30.0) response.raise_for_status() return response.json() except Exception: return None def format_alert(feature: dict) - str: 把预警数据格式化成可读字符串。 props feature[properties] return f Event: {props.get(event, Unknown)} Area: {props.get(areaDesc, Unknown)} Severity: {props.get(severity, Unknown)} Description: {props.get(description, No description available)} Instructions: {props.get(instruction, No specific instructions provided)} mcp.tool() async def get_alerts(state: str) - str: 获取美国某州的天气预警。 Args: state: 两个字母的州代码例如 CA、NY url f{NWS_API_BASE}/alerts/active/area/{state} data await make_nws_request(url) if not data or features not in data: return 无法获取预警数据。 if not data[features]: return 该州当前没有活跃预警。 alerts [format_alert(feature) for feature in data[features]] return \n---\n.join(alerts) mcp.tool() async def get_forecast(latitude: float, longitude: float) - str: 获取某经纬度的天气预报。 Args: latitude: 纬度 longitude: 经度 points_url f{NWS_API_BASE}/points/{latitude},{longitude} points_data await make_nws_request(points_url) if not points_data: return 无法获取该位置的预报数据。 forecast_url points_data[properties][forecast] forecast_data await make_nws_request(forecast_url) if not forecast_data: return 无法获取详细预报。 periods forecast_data[properties][periods] forecasts [] for period in periods[:5]: forecast f {period[name]}: Temperature: {period[temperature]}°{period[temperatureUnit]} Wind: {period[windSpeed]} {period[windDirection]} Forecast: {period[detailedForecast]} forecasts.append(forecast) return \n---\n.join(forecasts) if __name__ __main__: mcp.run(transportstdio)3.2 关键点解析mcp.tool() 装饰器做了什么mcp.tool()是 FastMCP 的核心。它把普通 Python 函数注册成 MCP 工具并自动从类型提示和 docstring 生成工具的输入 schema。也就是说你写的state: str和那段 docstring会被转成模型能理解的参数说明。装饰器内部逻辑大致是接收函数调用self.add_tool(fn, namename, descriptiondescription)注册然后返回原函数。注意它必须写成mcp.tool()带括号的形式直接写mcp.tool会抛TypeError因为装饰器需要先被调用一次。3.3 客户端配置让模型客户端启动这个服务端stdio 服务端不是常驻进程它由客户端按需启动。以 Claude Desktop 为例配置文件路径macOS~/Library/Application Support/Claude/claude_desktop_config.jsonWindows%APPDATA%\Claude\claude_desktop_config.json写入以下 JSON把路径换成你自己的绝对路径{ mcpServers: { weather: { command: uv, args: [ --directory, /ABSOLUTE/PATH/TO/weather-mcp, run, weather.py ] } } }这段配置告诉客户端有一个叫weather的 MCP 服务端通过uv --directory ... run weather.py启动它。改完配置要重启客户端。如果你用的是支持 OpenAI 兼容接口的客户端比如 Cline、Continue 等配置里需要同时填三件套{ mcpServers: { weather: { command: uv, args: [--directory, /ABSOLUTE/PATH/TO/weather-mcp, run, weather.py] } }, llm: { baseUrl: https://taotoken.net/api, apiKey: 你的 TaoToken Key, modelId: claude-sonnet-4-20250514 } }Base URL、Key、Model ID 三者缺一不可。Base URL 固定为https://taotoken.net/api不要加 UTM 参数。4. 验证请求与成功结果4.1 本地 stdio 联调用 mcp dev 检查工具注册在启动客户端之前先用 SDK 自带的调试工具确认服务端能正常握手uv run mcp dev weather.py这会启动一个本地调试界面列出服务端注册的所有工具。如果看到get_alerts和get_forecast两个工具说明服务端代码没问题。这一步能提前排掉大部分语法和注册错误。4.2 直接跑服务端看 stdio 输出也可以直接运行观察它是否在等待 stdinuv run weather.py正常情况下进程会挂起等待标准输入。此时它已经准备好接收 JSON-RPC 消息了。按 CtrlC 退出。4.3 端到端调用让模型调用工具重启客户端后在对话里输入帮我查一下加州现在的天气预警模型会分析可用工具选择get_alerts传入stateCA。客户端通过 stdio 把调用请求发给服务端服务端请求 NWS API 拿到数据格式化后返回。整个链路是客户端把问题发给模型模型决定调用get_alerts生成参数客户端通过 stdio 把工具调用发给服务端服务端执行函数返回结果客户端把结果回传给模型模型组织自然语言回复如果模型回复里包含具体的预警事件和区域描述说明端到端跑通了。第一次调用时客户端通常会弹窗询问是否授权点允许即可。5. 本篇常见错误排查5.1 401 UnauthorizedKey 或 Base URL 配错报错长这样Error: 401 Unauthorized - invalid api key原因通常是三件套里某一项写错。检查顺序Base URL 必须是https://taotoken.net/api结尾不要多斜杠API Key 复制时别带空格Model ID 要和 TaoToken 支持的模型名完全一致。如果用的是 Claude Desktop 这类不直接暴露 LLM 配置的客户端401 一般来自它自己的账号体系和 MCP 服务端无关。5.2 local proxy failed / connection refusedMCP error: local proxy failed to connect这通常是客户端启动服务端子进程失败。检查 JSON 配置里的--directory路径是不是绝对路径Windows 下要用双反斜杠或正斜杠。另外确认uv在 PATH 里客户端可能读不到你 shell 里的环境变量必要时把command写成uv的绝对路径。5.3 reading choices 报错响应结构不符TypeError: Cannot read properties of undefined (reading choices)这个报错说明客户端拿到的响应不是标准 OpenAI 格式。常见原因是 Base URL 填成了网页地址而不是 API 地址。确认填的是https://taotoken.net/api不是带/chat的页面地址。如果客户端要求填完整 endpoint用https://taotoken.net/api/v1/chat/completions。5.4 OAuth 相关报错OAuth error: invalid_client部分客户端默认走 OAuth 流程但 TaoToken 用的是 API Key 鉴权。在客户端设置里把认证方式从 OAuth 改成 API Key填入你的 Key 即可。如果客户端没有这个选项检查是不是选错了接入模式。5.5 工具注册了但模型不调用服务端日志显示工具已注册但模型就是不调用。先确认模型本身支持 function calling部分轻量模型不支持。其次检查 docstring 是否清晰——模型靠 docstring 判断什么时候用这个工具。把获取美国某州的天气预警。写得更具体比如加上当用户询问某地天气状况或预警时使用命中率会明显提升。6. 继续往下走从 stdio 到更完整的接入跑通 stdio 之后你已经掌握了 MCP 服务端的核心用 FastMCP 注册工具、用 stdio 传输、用客户端联调。接下来可以做的事把服务端换成你自己的业务逻辑比如查数据库、调内部 API、读本地文件。工具函数的写法完全一样只是把make_nws_request换成你的请求逻辑。如果你想让服务端被更多客户端复用可以了解 streamable HTTP 传输方式它适合部署成常驻服务。但 stdio 依然是本地开发调试最快的路径。端到端测试时如果遇到模型侧的问题可以到 模型对话页 直接验证 Key 和模型是否正常排除掉 MCP 服务端的干扰。接入细节和参数说明在 接入文档 里有完整对照表。最后一个实用技巧调试 stdio 服务端时不要用print()输出日志因为 stdout 被 JSON-RPC 占用了任何多余输出都会破坏协议。要打日志就写到 stderr或者用logging模块配置到文件。这个坑我第一次写的时候踩过服务端看起来在跑但客户端一直握手失败排查了半天才发现是一行 print。
阅读完成 · 觉得有帮助?