1. 从 Skill 到 MCP Server:为什么我要做这次迁移
如果你手里已经攒了一堆 Skill,比如 OCR 识别、文档解析、数据清洗,每个都跑在某个特定平台里,那你大概也遇到过这个场景:换一个客户端,所有 Key 要重新配一遍;换一个工具,Skill 逻辑要重新接一次。Skill 本身没问题,问题是它被绑死在某个宿主环境里。
MCP Server 解决的正是这件事。MCP 是 Model Context Protocol 的缩写,你可以把它理解成 AI 工具之间的“USB-C 接口”——只要你的能力按这个协议暴露出来,Claude Desktop、OpenClaw、各类支持 MCP 的客户端都能直接调用,不需要为每个平台写适配层。而 Skill 更像是某个软件内部的宏,换个软件就失效。
这次迁移的目标很明确:把已有的 Python Skill 逻辑封装成标准 MCP Server,同时用 TaoToken 统一管理 API Key 和调用通道,让一套能力集在多端稳定运行。适合谁看?已经写过 Skill、想升级成跨平台服务的开发者;或者刚开始接触 MCP、想找一个能跑通的完整示例的人。
我试过把三个 Skill 分别迁移,踩过的坑主要集中在配置格式和传输模式上,下面会把可复制的骨架都给你。
2. TaoToken 前置:统一 Key 与 API 通道
迁移之前先把“钥匙”这件事理顺。原来每个 Skill 各自读环境变量、各自配 Key,迁移到 MCP Server 后如果还这么干,只是把碎片化从 Skill 层搬到了 Server 层。我的做法是让所有 MCP Server 统一走 TaoToken 的 API 通道。
TaoToken 在这里的角色是统一入口:你只需要在 TaoToken 控制台创建一个 API Key,所有 MCP Server 都引用同一个 Key,模型调用、额度管理、通道切换都在一处完成。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api 。
操作路径不复杂:进控制台创建 Key,然后把它写进 MCP Server 的配置里。如果你还没建 Key,直接去 API Keys 页面:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。建好之后先别急着写代码,把 Key 存到环境变量里,后面 config.toml 和 settings.json 都会引用它。
注意:不要把 Key 硬编码进源码提交到仓库。用环境变量或本地配置文件,配置文件加进 .gitignore。
3. 可复制配置:config.toml 与 settings.json 骨架
MCP Server 的配置分两层:一层是 Server 自身的运行参数(config.toml),一层是客户端如何找到并启动这个 Server(settings.json)。很多人卡住是因为把这两层混在一起写。
先看 config.toml,放在项目根目录:
[server] name = "unified-skill-server" version = "0.1.0" transport = "stdio" [taotoken] base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" default_model = "claude-sonnet-4-20250514" timeout = 60 [skills] enabled = ["ocr", "doc_parse", "data_clean"]这里的关键是 api_key_env 指向环境变量名,而不是 Key 本身。default_model 按你实际可用的模型填,timeout 给足,OCR 这类任务容易超时。
再看 settings.json,这是给客户端(比如 Claude Desktop)读的:
{ "mcpServers": { "unified-skill-server": { "command": "python", "args": ["-m", "server.main"], "env": { "TAOTOKEN_API_KEY": "你的Key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api" } } } }两个文件的职责要分清:config.toml 管 Server 内部行为,settings.json 管客户端怎么拉起 Server。改传输模式、改模型,动 config.toml;换 Python 路径、加环境变量,动 settings.json。
4. Python 实现:把 Skill 逻辑封装为 MCP Tool
配置就绪后开始写代码。核心思路是三层分离:业务逻辑层保留你原来的 Skill 代码不动,接口层用 mcp 库把函数映射成 Tool,传输层决定用 stdio 还是 HTTP。
先装依赖:
pip install mcp httpx pydantic然后写 Server 主体。下面这个骨架把原来的 OCR Skill 包了进来,同时通过 TaoToken 通道调用模型:
import os import httpx from mcp.server.fastmcp import FastMCP from pydantic import BaseModel mcp = FastMCP("unified-skill-server") TAOTOKEN_BASE = os.environ.get("TAOTOKEN_BASE_URL", "https://taotoken.net/api") TAOTOKEN_KEY = os.environ["TAOTOKEN_API_KEY"] class OcrResult(BaseModel): company_name: str reg_no: str raw_text: str @mcp.tool() async def recognize_business_license(image_url: str) -> str: """通过 URL 识别营业执照,返回结构化 JSON 字符串。""" async with httpx.AsyncClient(timeout=60) as client: resp = await client.post( f"{TAOTOKEN_BASE}/v1/chat/completions", headers={"Authorization": f"Bearer {TAOTOKEN_KEY}"}, json={ "model": "claude-sonnet-4-20250514", "messages": [ {"role": "user", "content": f"识别这张营业执照并返回JSON:{image_url}"} ], }, ) resp.raise_for_status() data = resp.json() return data["choices"][0]["message"]["content"] @mcp.resource("info://server/status") def get_status() -> str: """返回服务运行状态。""" return "unified-skill-server: online, transport: stdio" if __name__ == "__main__": mcp.run(transport="stdio")几个要点。第一,@mcp.tool() 装饰的函数就是暴露给 AI 的能力,docstring 会被当作工具描述,写清楚输入输出。第二,@mcp.resource() 暴露的是只读资源,适合状态查询、配置读取这类场景。第三,所有模型调用都走 TAOTOKEN_BASE,换模型只改 config.toml 里的 default_model,不用动业务代码。
如果你原来的 Skill 是同步函数,直接改成 async 或者用 asyncio.to_thread 包一层即可,逻辑本身不用重写。
5. 验证请求:一次调用跑通全链路
写完代码先别急着接客户端,用 mcp-inspector 本地验证最快:
npx @modelcontextprotocol/inspector python -m server.main启动后浏览器会打开调试界面,你能看到所有注册的 Tool 和 Resource,点进去手动传参调用。如果 recognize_business_license 返回了结构化 JSON,说明 Server 本身没问题。
接着验证 TaoToken 通道是否通。单独发一个请求:
curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"claude-sonnet-4-20250514","messages":[{"role":"user","content":"ping"}]}'返回里有 choices 字段就说明 Key 和通道都正常。最后把 settings.json 配到客户端,重启后 AI 应该能自动识别到 Tool 并调用。成功的结果是:你在对话里说“识别这张营业执照”,AI 自动触发 recognize_business_license,返回结构化结果,全程不需要你手动指定用哪个 Skill。
想直接在网页里验证模型对话是否正常,可以用模型对话页面:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。
6. 本篇常见错排查
报错一:ModuleNotFoundError: No module named 'mcp'多半是 Python 环境不对。settings.json 里的 command 写的是 python,但你的 mcp 装在虚拟环境里。改成虚拟环境的绝对路径,比如 /Users/you/venv/bin/python。
报错二:401 UnauthorizedKey 没传进去。检查 settings.json 的 env 字段里 TAOTOKEN_API_KEY 是否填了真实值,或者环境变量名和代码里读的是否一致。注意 config.toml 里写的是变量名,不是值。
报错三:Tool 不显示 / AI 不调用docstring 太模糊。AI 靠描述判断什么时候调用,把“识别营业执照”改成“通过图片 URL 识别营业执照并返回企业名称、注册号等结构化信息”,命中率会明显提升。
报错四:stdio 模式下日志污染如果你在代码里 print 了调试信息,stdio 传输会被污染导致客户端解析失败。所有日志走 stderr,或者用 logging 模块写文件。
报错五:超时OCR 和长文档任务容易超 60 秒。config.toml 里把 timeout 调到 120,httpx 客户端的 timeout 也要同步调。
7. 长期编码与 Agent 场景的接入建议
如果你不只是做一次性验证,而是要把这套 MCP Server 长期跑在编码助手或 Agent 工作流里,建议把 Key 管理和额度规划放到 Coding Plan 层面统一处理:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。这样多个 Server、多个客户端共享同一套通道,不会出现某个端额度用完另一个端还不知道的情况。
接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的 API 参数说明和错误码对照。Claude Code 相关的接入细节可以看 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite 。
迁移完成后你会发现,Skill 不再是某个平台的附属品,而是一个可以被任何支持 MCP 的客户端调用的标准节点。一套代码,多端运行,Key 只配一次。