ARTICLE DETAIL

资讯详情

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

手搓一个MCP-server:用python-sdk+uv从零实现SSE服务并接入TaoToken

手搓一个MCP-server:用python-sdk+uv从零实现SSE服务并接入TaoToken 1. 从零手搓 MCP-server 的真实场景为什么我要自己写一个你可能已经在 Cherry Studio、Cline、Cursor 里用过别人做好的 MCP 工具点几下就能让模型读文件、查数据库、调接口。但真到自己想加一个「公司内部才有的接口」时就会发现市面上没有现成的 MCP-server 可用。这时候唯一的出路就是自己手搓一个。MCPModel Context Protocol是 Anthropic 开源的一套协议本质上是给大模型和外部工具之间定了一套「普通话」。以前每个 AI 应用要对接一个工具就得写一套私有适配现在只要工具端实现 MCP-server任何支持 MCP-client 的宿主Cherry Studio、Cline、Dify、N8N 等都能直接调用。这就是它最大的价值一次实现多处复用。我这次要做的场景很具体用 python-sdk 加 uv从零写一个支持 SSE 传输的 MCP-server然后把它接到 TaoToken 的统一 Key/API 通道上让模型在对话里能真正调用我自定义的工具。为什么选 SSE 而不是 stdio因为 stdio 只能本机进程通信一旦你想部署到云服务器、让多个客户端共享就必须走 HTTP 长连接SSE 就是最省事的那条路。这篇文章适合三类人一是完全没写过 MCP-server 但想上手的小白二是写过 stdio 版本、想升级到 SSE 部署的开发者三是想把自研工具接进 TaoToken 通道、统一管理 Key 的人。全程可复制命令和代码我都会给全踩过的坑也会标出来。先说清楚整体链路uv 负责 Python 环境和依赖管理python-sdk 提供 FastMCP 这个高层封装我们写的 server 通过 SSE 暴露一个 HTTP 端点MCP-client 连上后能列出工具并调用而模型侧的请求统一走 TaoToken 的 API 通道。下面一步步来。2. TaoToken 前置准备统一 Key 与 API 通道怎么配在写 server 之前先把模型侧的通道准备好。很多人卡在这一步不是因为难而是因为不知道该拿哪个 Key、填哪个地址。TaoToken 的作用是把多家模型的调用收敛到一个 API 入口和一套 Key 体系你不需要为每个模型单独申请、单独记地址。你需要准备两样东西一个 API Key和一个 Base URL。Base URL 固定是https://taotoken.net/api注意这个地址后面不要加多余的斜杠或路径。API Key 到控制台的 API Keys 页面创建创建后只显示一次务必当场复制保存。创建 Key 的入口在这里控制台 API Keyshttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite拿到 Key 之后建议先别急着写 server用一条 curl 验证通道是否通。这一步能帮你排除掉 90% 后面会遇到的 401 问题。curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer 你的API_KEY \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-5, messages: [{role: user, content: 只回复两个字通了}] }如果返回里能看到choices字段和正常内容说明 Key 和通道都没问题。如果返回 401先检查 Key 有没有复制完整、有没有多余空格如果返回模型不存在说明 Model ID 写错了去文档里核对准确的模型名。关于 Model ID 的准确写法可以对照官方文档接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite这里有个关键点要提前说清楚MCP-server 本身不直接调模型它只负责「暴露工具」。真正调模型的是 MCP-client 所在的宿主比如 Cherry Studio。所以 TaoToken 的 Key 是配在宿主里的不是配在 server 里的。但如果你想让 server 内部某个工具去调模型比如做一个「让模型总结文本」的工具那 server 里也需要用到这个 Key 和 Base URL。两种用法我都会在代码里体现。如果你打算长期跑编码类 Agent、频繁调用建议直接看 Coding Plan比按量计费更划算Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite前置准备就这些不复杂。核心记住三件套Base URL 是https://taotoken.net/apiKey 从控制台拿Model ID 从文档核对。下面进入正题开始搭项目。3. 可复制配置uv 初始化 SSE server 代码骨架这一节是全文的技术核心我会把每一步命令和完整代码都给出来你照着敲就能跑起来。先装 uv再初始化项目然后写 server 代码最后配好 SSE 端点。3.1 安装 uv 并初始化项目uv 是用 Rust 写的 Python 包和项目管理工具速度比 pip 快很多而且能顺带管理 Python 版本。Windows 下用 PowerShell 安装powershell -ExecutionPolicy ByPass -c irm https://astral.sh/uv/install.ps1 | iexmacOS 或 Linux 下用curl -LsSf https://astral.sh/uv/install.sh | sh装完检查一下版本能打印出来就说明成功uv --version接着创建项目目录并初始化。我习惯放在F:\work\code\AIcode下你可以换成自己的路径cd F:\work\code\AIcode uv python install 3.13 uv init mcp-server-demo cd mcp-server-demo uv add mcp[cli]uv add mcp[cli]会把 MCP 的 python-sdk 加进依赖同时生成pyproject.toml和uv.lock。这一步做完项目骨架就有了。3.2 写 SSE 版 server 代码把main.py的内容替换成下面这份。我在基础版上加了两处一是把传输方式设成 SSE二是显式指定 host 和 port方便后面部署到服务器。# main.py from mcp.server.fastmcp import FastMCP # 创建 MCP server 实例名字叫 Demo mcp FastMCP(Demo) # 注册一个加法工具 mcp.tool() def add(a: int, b: int) - int: Add two numbers return a b # 注册一个动态问候资源 mcp.resource(greeting://{name}) def get_greeting(name: str) - str: Get a personalized greeting return fHello, {name}! if __name__ __main__: # 关键SSE 模式下指定监听地址和端口 mcp.settings.host 0.0.0.0 mcp.settings.port 8002 mcp.run(transportsse)这里有几个细节值得说。mcp.tool()装饰的函数会被自动注册成工具函数签名和 docstring 会变成工具的描述模型就是靠这个描述决定要不要调用。mcp.resource()注册的是资源用 URI 模板访问和工具是两类东西。transportsse是这次的重点它会让 server 在http://host:port/sse上暴露一个 SSE 端点。3.3 用 JSON 配置把 server 接进客户端如果你只想本机测试用 stdio 方式最省事客户端配置是一段 JSON。以 Cline 或 Cherry Studio 为例配置长这样{ mcpServers: { mcp-server-demo: { command: uv, args: [ --directory, F:\\work\\code\\AIcode\\mcp-server-demo, run, main.py ] } } }注意--directory后面是项目根目录的绝对路径Windows 下反斜杠要写成双反斜杠。这段配置的三件套是命令用uv参数里带--directory和run main.py工作目录指向项目。如果你用 SSE 方式客户端配置就换成 URL 形式填http://127.0.0.1:8002/sse即可。3.4 启动 SSE 服务在项目目录下执行uv run main.py看到类似Uvicorn running on http://0.0.0.0:8002的输出就说明 SSE 服务起来了。此时浏览器访问http://127.0.0.1:8002/sse会保持一个长连接不返回这是正常的SSE 本来就是长连接。到这里一个能跑的 SSE 版 MCP-server 就完成了。下一节我们验证它到底能不能被客户端正确调用。4. 验证请求与成功结果工具列表和调用响应怎么看代码跑起来只是第一步真正要确认的是「客户端能不能列出工具、能不能调用成功」。这一节我用两种方式验证先用 MCP 客户端连再用 curl 直接打 SSE 端点看响应。4.1 用 MCP 客户端验证工具列表打开 Cherry Studio或 Cline添加一个 MCP 服务器类型选 SSE地址填http://127.0.0.1:8002/sse保存。如果配置正确客户端会自动发起一次tools/list请求你会在界面上看到可用工具列表里出现add这个工具描述是Add two numbers。这一步能成功说明三件事都对SSE 端点可达、协议握手正常、工具注册被正确识别。如果列表是空的八成是 server 没起来或者端口被占用。4.2 发起一次真实调用在聊天窗口里勾选这个 MCP 服务器模型随便选一个比如走 TaoToken 通道的 claude-sonnet-4-5然后问55 加 60 等于多少用工具算。模型会识别出需要调用add工具客户端把{a: 55, b: 60}通过 SSE 发给 serverserver 执行后返回115模型再把结果组织成自然语言回复你。整个过程你在 server 的终端里能看到调用日志。4.3 用 curl 直接验证 SSE 端点如果你想脱离客户端、纯手工验证可以用 curl 打 SSE 端点。先建立连接curl -N http://127.0.0.1:8002/sse-N表示禁用缓冲你会看到 server 持续推送事件其中包含一个endpoint事件里面带着用于后续消息发送的 session 地址。拿到这个地址后再发一条 JSON-RPC 请求curl -X POST http://127.0.0.1:8002/messages/?session_id你的session_id \ -H Content-Type: application/json \ -d { jsonrpc: 2.0, id: 1, method: tools/list, params: {} }如果返回里能看到result.tools数组里面有add工具就说明协议层完全通了。这一步是排查问题的利器比在客户端里瞎点高效得多。4.4 成功结果的判断标准一次成功的调用你会看到三个信号同时出现客户端界面显示工具被调用、server 终端打印出请求日志、模型回复里包含正确计算结果。三者缺一就说明链路某处断了。我实测下来最常见的断点是端口没对上和 session_id 过期这两个在下一节详细说。验证通过后你就可以把这个 server 部署到云服务器让 Dify、N8N 这类平台通过 SSE 远程调用了。部署命令和本地一样只是 host 要设成0.0.0.0防火墙要放行对应端口。5. 本篇常见报错排查401、local proxy failed、reading choices 逐个拆这一节是我踩过的坑的合集。MCP-server 的报错分两类一类是 server 本身的连接问题一类是模型通道的问题。分开看会清晰很多。5.1 401 UnauthorizedKey 或 Base URL 错了这个报错几乎都出在模型调用侧。如果你在 server 内部写了调模型的工具或者客户端宿主配了 TaoToken出现 401 就是 Key 无效。排查顺序先确认 Key 有没有复制完整有没有漏字符、有没有多余空格再确认 Base URL 是不是https://taotoken.net/api最后确认请求头是不是Authorization: Bearer 你的KEY。三者都对还报 401就去控制台重新生成一个 Key 试。5.2 local proxy failed本地代理配置冲突这个报错通常出现在客户端连本地 SSE 端点时。原因是系统里配了 HTTP 代理客户端把127.0.0.1的请求也走了代理导致连不上。解决办法是在客户端或环境变量里把127.0.0.1和localhost加入代理白名单或者临时关掉代理再测。注意这里说的是本地回环地址的代理绕过不是让你去搞什么网络工具纯粹是本地配置问题。5.3 reading choices of undefined响应结构不对这个报错说明代码在解析模型响应时拿到的不是预期结构。常见原因有两个一是请求根本没成功返回的是错误对象而不是正常响应代码却直接去读choices二是 Model ID 写错了服务端返回了错误信息。排查方法是先把原始响应打印出来看别急着解析。确认响应里有choices字段再往下走。5.4 OAuth 相关报错认证流程没走完有些 MCP-client 在连接远程 server 时会尝试 OAuth 流程如果 server 没实现对应的认证端点就会报 OAuth 错误。本地测试阶段最简单的办法是先用 SSE 无认证模式跑通确认工具能调用后再考虑加认证。别一上来就搞复杂认证会把问题搅在一起。5.5 工具列表为空注册或传输有问题客户端连上了但工具列表是空的先检查mcp.tool()装饰器有没有加对函数有没有被正确导入。再检查传输方式stdio 和 SSE 的客户端配置完全不同配错了就连不上。最后看 server 启动日志有没有报注册失败。5.6 端口被占用换端口或杀进程Address already in use是部署时最常见的。换个端口比如把 8002 改成 8003或者找到占用进程杀掉。Linux 下用lsof -i:8002查Windows 下用netstat -ano | findstr 8002查。排查的核心思路是先分层再定位。server 层看端口和注册协议层看 SSE 连接和 session模型层看 Key 和 Base URL。分层之后每个报错都能快速归位。6. 语义一致 CTA把自研 MCP-server 接进统一通道走到这里你已经有了一个能跑、能验证、能部署的 SSE 版 MCP-server。接下来最有价值的一步是把它和 TaoToken 的通道真正打通让模型调用和工具调用走同一套 Key 体系省去到处配 Key 的麻烦。如果你还在调试接入阶段先把 API Key 和文档过一遍这是所有后续操作的基础API Keyshttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite如果你想先在网页里验证模型响应、确认 Model ID 和参数都对用模型对话页面最快模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite如果你打算把这个 MCP-server 长期挂在服务器上配合编码 Agent 或自动化工作流反复调用Coding Plan 会比按量计费更省心Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite最后给一个实用建议部署到云服务器后别急着开放公网。先用内网或安全组限制来源 IP确认稳定后再逐步放开。MCP-server 暴露的是你的工具能力安全边界要自己守住。工具写好后把pyproject.toml和main.py一起纳入版本管理下次换机器一条uv sync就能恢复环境比手动装依赖靠谱得多。
返回列表