ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零搭建一个 MCP Server:Python 完整示例 + AI 调用机制详解(TaoToken 统一 Key 接入版)

从零搭建一个 MCP Server:Python 完整示例 + AI 调用机制详解(TaoToken 统一 Key 接入版) 1. 为什么我要自己写一个 MCP ServerMCP Server 是什么一句话它是一个用 JSON-RPC 2.0 协议把「工具、资源、提示词」暴露给 AI 客户端的本地进程。能做什么让 Claude Desktop、Cursor、Cline 这类支持 MCP 的客户端在对话里直接调用你写的 Python 函数比如查数据库、读本地文件、调内部接口。适合谁适合想把私有能力接进 AI 工作流、又不想改客户端源码的开发者。我试过把公司内部的日志查询脚本包成 MCP Server结果 AI 在对话里就能直接拉日志、做聚合比每次手动跑脚本省事得多。但第一次写的时候踩了不少坑工具注册了客户端看不到、参数类型对不上导致 AI 传字符串、Windows 下中文 docstring 乱码。这些问题后面会逐个拆。这篇的目标很明确给你一份能直接跑的 Python MCP Server 骨架讲清 AI 到底是怎么「发现」并「调用」你的工具的最后用 TaoToken 的统一 Key 通道做一次端到端验证。TaoToken 在这里的角色是提供兼容 Anthropic / OpenAI 的 API 入口让你不用分别申请多家 Key 就能验证 MCP 工具被模型调用的完整链路。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。先理清一个常见误解MCP Server 本身不「智能」它只是被动响应请求。真正决定调不调、怎么调的是 LLM而 LLM 看到的全部信息来自你函数上的 docstring 和类型注解。所以写 MCP Server 的本质是写一份给 AI 看的 API 文档。这个认知会贯穿全文。环境上你只需要 Python 3.10 和 pip。官方 SDK 装一条命令就够pip install mcp[cli]目录结构保持极简一个文件起步mcp_demo/ ├── server.py # MCP Server 实现 └── README.md下面进入正题先写骨架再拆机制最后验证。2. TaoToken 前置准备统一 Key 与 API 通道在写 Server 之前先把「谁来调用」这件事定下来。MCP Server 只负责暴露能力真正发起对话、决定调用工具的是 LLM 客户端。本地调试时你可以用 TaoToken 的统一 Key 作为模型通道这样验证阶段不用在多个平台之间切换。TaoToken 提供的是兼容主流协议的统一 API 入口Base URL 固定为https://taotoken.net/api。你需要先去控制台创建一个 API Key然后把它写进环境变量避免硬编码进代码。控制台地址带归因参数https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content创建 Key 的入口在 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content拿到 Key 之后在终端里设置环境变量。Linux / macOSexport TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows PowerShell$env:TAOTOKEN_API_KEYsk-你的key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api这里有个关键点要讲清楚MCP Server 和模型 API 是两条独立的链路。Server 通过 stdio 和客户端通信客户端再通过 HTTP 把工具列表塞进模型上下文。TaoToken 负责的是后半段——模型调用。所以你在验证阶段需要同时准备两样东西一个能跑 MCP 的客户端比如 Cline、Claude Code以及一个可用的模型通道TaoToken 统一 Key。如果你用的是 Claude Code 这类工具它的配置里需要同时填 Base URL、API Key 和 Model ID 三件套。缺任何一个都会在启动时报认证或模型不存在的错误。Model ID 按你实际要用的模型填比如claude-sonnet-4-5或gpt-4o具体以控制台可用列表为准。对于长期做编码和 Agent 编排的场景可以考虑 Coding Plan它更适合高频调用https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content准备阶段做完你应该手上有一个 API Key、一个 Base URL、一个确定的 Model ID。这三样在后面的验证环节会直接用到。别急着往下写 Server先把这三样确认能通否则后面报错你分不清是 Server 的问题还是 Key 的问题。3. 可复制配置Python MCP Server 骨架与客户端接入这一节给你两份可直接复制的配置一份是 MCP Server 本体一份是客户端接入配置。先写 Server。# -*- coding: utf-8 -*- 最小可运行 MCP Server 示例 运行方式: python server.py 默认通过 stdio 与 Client 通信 import sys import json from mcp.server.fastmcp import FastMCP # Windows 下强制 UTF-8避免中文 docstring 乱码 sys.stdout.reconfigure(encodingutf-8) mcp FastMCP(demo-server) mcp.tool() def add(a: float, b: float) - str: 两个数字相加返回它们的和。 Args: a: 第一个数字 b: 第二个数字 return f{a} {b} {a b} mcp.tool() def get_weather(city: str) - str: 查询某个城市的当前天气模拟数据。 Args: city: 城市名称如 北京 mock { 北京: 晴18℃, 上海: 多云22℃, 深圳: 雷阵雨28℃, } return mock.get(city, f未收录 {city} 的天气默认晴 20℃) mcp.resource(config://app) def get_config() - str: 暴露一份配置文件作为资源 return json.dumps({version: 1.0.0, env: prod}, ensure_asciiFalse) mcp.prompt() def code_review(code: str) - str: 代码评审提示词 return f请评审以下代码关注安全性和可读性:\n\n\n{code}\n if __name__ __main__: mcp.run(transportstdio)这份骨架暴露了两类能力tools可被 AI 主动调用的函数和resources只读数据源外加一个prompts模板。装饰器mcp.tool()是注册入口函数名就是工具名docstring 就是工具描述类型注解会被转成 JSON Schema。接下来是客户端接入配置。以 Claude Desktop 为例编辑配置文件macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json写入以下 JSON{ mcpServers: { demo-server: { command: python, args: [D:/path/to/server.py], env: { TAOTOKEN_API_KEY: sk-你的key, TAOTOKEN_BASE_URL: https://taotoken.net/api } } } }注意args里的路径要换成你本机的绝对路径Windows 用正斜杠或双反斜杠都行。env字段把 TaoToken 的 Key 和 Base URL 透传给 Server 进程这样 Server 内部如果要调模型 API 就能直接读环境变量。如果你用的是 Cline 或 Claude Code配置思路一致只是字段名不同。Claude Code 的配置里需要显式写全三件套{ apiKey: sk-你的key, baseURL: https://taotoken.net/api, model: claude-sonnet-4-5 }Cline 的 MCP 配置则是在设置面板里填 Server 启动命令模型通道单独在 API 配置区填 Base URL 和 Key。无论哪种客户端核心都是两件事告诉客户端怎么启动你的 Server告诉客户端用哪个模型通道。配置写完先别急着重启客户端用官方 Inspector 做一次本地自检能省掉大量「重启了但看不到工具」的排查时间npx modelcontextprotocol/inspector python server.pyInspector 会打开一个网页你能在里面看到initialize返回的 capabilities、tools/list返回的工具清单还能手动调一次get_weather看返回。这一步通了再往客户端里接。4. 验证请求AI 调用机制与端到端成功结果这一节是全文核心AI 到底怎么知道你的 MCP 提供了哪些方法。拆成四个阶段看。阶段一协议握手。客户端启动 Server 子进程后立刻发一条initialize请求{ jsonrpc: 2.0, id: 1, method: initialize, params: { protocolVersion: 2024-11-05, capabilities: {roots: {listChanged: true}, sampling: {}}, clientInfo: {name: claude-desktop, version: 1.0.0} } }Server 回{ jsonrpc: 2.0, id: 1, result: { protocolVersion: 2024-11-05, capabilities: {tools: {}, resources: {}, prompts: {}}, serverInfo: {name: demo-server, version: 1.0.0} } }关键在capabilitiesServer 在这里告诉客户端「我有 tools、resources、prompts 这三类能力」但还没列具体方法。就像饭店门口挂「本店有炒菜、面食、汤」的牌子还没给你菜单。阶段二能力发现。握手完成后客户端立刻请求tools/list{jsonrpc: 2.0, id: 2, method: tools/list}Server 返回{ jsonrpc: 2.0, id: 2, result: { tools: [ { name: add, description: 两个数字相加返回它们的和。, inputSchema: { type: object, properties: { a: {type: number, description: 第一个数字}, b: {type: number, description: 第二个数字} }, required: [a, b] } }, { name: get_weather, description: 查询某个城市的当前天气模拟数据。, inputSchema: { type: object, properties: { city: {type: string, description: 城市名称如 \北京\} }, required: [city] } } ] } }三个字段的来源要记牢name来自 Python 函数名description来自 docstringinputSchema来自类型注解加参数 docstring。也就是说AI 看到的工具文档完全由你写函数时的 docstring 和类型注解决定。docstring 不写AI 就不知道这工具干嘛很可能不调或乱调。阶段三注入上下文。客户端拿到tools/list结果后把它转成 LLM 能理解的格式塞进请求的tools字段。以 OpenAI 风格为例{ model: gpt-4o, messages: [{role: user, content: 北京天气怎么样}], tools: [ { type: function, function: { name: get_weather, description: 查询某个城市的当前天气模拟数据。, parameters: { type: object, properties: {city: {type: string}}, required: [city] } } } ] }字段映射关系是固定的MCP 的name→ LLM 的namedescription→descriptioninputSchema→parametersAnthropic 风格里叫input_schema。不同模型的 tools 字段格式略有差异适配工作由客户端负责Server 永远只输出 MCP 标准 schema。阶段四决策与回传。用户问「北京天气怎么样」LLM 看到自己有get_weather工具输出工具调用请求{ role: assistant, tool_calls: [ { id: call_abc123, type: function, function: { name: get_weather, arguments: {\city\: \北京\} } } ] }客户端不直接执行函数而是通过 MCP 协议把请求转给 Server{ jsonrpc: 2.0, id: 4, method: tools/call, params: {name: get_weather, arguments: {city: 北京}} }Server 按名字找到函数执行返回{ jsonrpc: 2.0, id: 4, result: { content: [{type: text, text: 晴18℃}] } }客户端把结果塞回 LLM 上下文{role: tool, tool_call_id: call_abc123, content: 晴18℃}LLM 看到结果生成最终回答「北京现在是晴天气温 18℃。」整条链路走通后你在客户端对话框里应该能看到工具调用图标亮起点开能看到add和get_weather两个工具。如果用的是 TaoToken 通道模型请求会走https://taotoken.net/api你可以在控制台的调用日志里看到对应的请求记录确认工具确实被模型调用了。想单独验证模型通道是否正常可以用模型对话页面发一条测试消息https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content5. 本篇常见错排查401、local proxy failed 与 reading choices这一节按真实报错来。你大概率会碰到下面几个。报错一401 Unauthorized。这个几乎都是 Key 的问题。检查三处环境变量名是否拼错、Key 是否带了多余空格、Base URL 是否写成了https://taotoken.net/api/末尾多斜杠有时会导致路径拼接异常。如果你在客户端配置里同时填了 Base URL、Key、Model ID三件套缺一不可缺 Model ID 会报模型不存在缺 Key 直接 401。报错二local proxy failed或连接被拒绝。这类错误通常出现在客户端启动 Server 子进程时。原因可能是command写的python不在 PATH 里换成绝对路径试试也可能是args里的脚本路径有中文或空格用引号包起来。还有一种情况是 Server 启动后立刻退出用 Inspector 单独跑一次就能看到真实堆栈。报错三reading choices或解析响应失败。这个多半是模型返回格式和客户端预期不一致。检查你填的 Model ID 是否和 Base URL 对应的服务匹配比如把 OpenAI 格式的模型名填到了 Anthropic 通道。另外确认客户端版本支持你用的协议版本老版本客户端可能不认2024-11-05。报错四工具没出现在客户端。先确认mcp.tool()装饰器写对了函数有returndocstring 不为空。然后用 Inspector 看tools/list返回如果 Inspector 能看到而客户端看不到问题在客户端配置如果 Inspector 也看不到问题在 Server 代码。报错五Windows 中文乱码。Server 进程默认编码可能是 GBK导致 docstring 里的中文变乱码AI 看不懂。解决方法是加sys.stdout.reconfigure(encodingutf-8)本文示例已经加了。报错六stdio 通信被污染。Server 里任何print()都会写到 stdout被客户端当成 JSON-RPC 消息解析直接报错。调试日志一律写sys.stderr或用logging模块。报错七异步函数阻塞。FastMCP 默认在事件循环里跑长耗时同步函数会卡住整个 Server。解决方法是改成async def或者把耗时任务丢线程池。报错八OAuth 相关报错。如果你接的是需要 OAuth 的远程 MCP Server本地 stdio 模式不涉及但如果你在客户端里配了远程 Server 又没配认证会报 OAuth 失败。本地调试阶段建议先用 stdio跑通再考虑远程。排查顺序建议固定下来先 Inspector 自检 Server再检查客户端配置三件套最后看模型通道日志。这样能把问题范围快速缩小到某一层。6. 继续往下走接入文档与 Coding PlanServer 跑通只是起点。接下来你大概率会想做三件事把工具粒度调细、加错误处理、接真实数据源。工具粒度上太粗的run_anythingAI 不会用太细的add_one会让上下文爆炸。经验值是每个工具做一件明确的事参数控制在 3 到 5 个以内。错误处理上用 MCP 标准格式返回{isError: true, content: [...]}比直接raise异常更友好AI 能读懂错误并决定是否重试。工具还要幂等因为 AI 可能重复调用多次调用结果要一致。只读数据优先用resource有副作用的才用tool。高频固定用法用prompt固化比如代码评审模板不必让用户每次手敲。接入细节和协议规范可以查官方文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content如果你打算把 MCP 用在长期编码或 Agent 编排上调用频率会明显上升Coding Plan 比按次计费更划算https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content最后留一个实用技巧写完 Server 先用 Inspector 手动调一遍每个工具确认返回格式正确再往客户端里接。这一步花五分钟能省掉后面半小时的「为什么 AI 不调我的工具」的困惑。工具质量直接决定 AI 调用质量把 docstring 当 API 文档写把类型注解当契约写剩下的交给协议。
返回列表