1. 从零写一个 MCP Server为什么先要解决 Key 管理MCPModel Context Protocol这两年被 Claude Desktop、Cline、Continue 这类工具带火之后很多开发者第一次接触它都会卡在同一个地方Server 写出来了Tool 也注册了但一到真正调用模型就发现 Key 到处散落。Claude 用一份、Cline 用一份、自己写的 Agent 又用一份改一次配置要翻五六个文件。MCP Server 本质上是一个跑在本地或远程的进程它通过标准输入输出stdio或 SSE 跟宿主应用通信对外暴露若干 Tool。宿主应用比如 Claude Desktop负责把用户的自然语言转成 Tool 调用请求Server 负责执行并返回结果。问题在于很多 Tool 内部需要调用大模型 API——比如做一个「代码解释器」Tool或者「文档摘要」Tool这些 Tool 自己就要发请求给模型。如果每个 Tool 都硬编码一个 Key维护成本会迅速失控。这篇指南面向的是需要为 Claude、Cline 等工具扩展自定义能力的开发者。我会带你从零写一个可运行的 MCP Server 骨架把模型调用统一收敛到 TaoToken 的 API 通道上只维护一份 Key然后本地启动、注册到宿主、验证 Tool 调用全流程跑通。TaoToken 在这里扮演的角色是统一入口官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 兼容 OpenAI 风格的请求格式所以你在 Tool 里用现成的 SDK 就能接。适合谁读写过一点 Python 或 Node、用过 Claude Desktop 或 Cline、想给自己加个私有 Tool 的人。不需要你懂 MCP 协议的全部细节跟着做就能跑出第一个 Tool。2. 前置准备TaoToken Key 与 MCP 运行环境在写代码之前先把两件事准备好一个能用的 API Key以及一个干净的 Python 环境。MCP 官方提供了 Python SDKmcp包和 Node SDK我这里用 Python 演示因为它的 stdio Server 写法最直观。2.1 拿到统一 Key登录 TaoToken 控制台在 API Keys 页面创建一个新 Key。建议按用途命名比如mcp-local-dev方便以后区分。创建后立刻复制保存页面刷新后就看不到完整值了。拿到 Key 之后先别急着写 Server用一条 curl 确认通道是通的curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的Key \ -d { model: gpt-4o-mini, messages: [{role: user, content: ping}], max_tokens: 16 }如果返回里带choices字段说明 Key 和通道都没问题。这一步很重要因为后面 MCP Server 报错时你要能快速判断是 Server 逻辑问题还是 Key 问题。2.2 环境与依赖Python 3.10 以上然后装两个包python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install mcp[cli] openai httpxmcp[cli]提供 Server 骨架和调试工具openai用来发模型请求TaoToken 兼容 OpenAI 格式直接复用这个 SDK 最省事httpx是它的底层依赖显式装上避免版本问题。Key 不要写进代码。用环境变量export TAOTOKEN_API_KEYsk-你的Key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows PowerShell 用$env:TAOTOKEN_API_KEYsk-...。这样 Server 代码里只读环境变量换机器、换 Key 都不用改代码。3. 可复制的 MCP Server 骨架与 Tool 实现下面是一个完整的、可以直接跑的 MCP Server。它暴露两个 Toolsummarize_text调模型做摘要和count_words纯本地计算用来验证 Tool 注册是否正常。前者演示如何通过 TaoToken 统一通道调模型后者演示不依赖网络的 Tool 长什么样。3.1 Server 主文件新建server.pyimport os import asyncio from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent from openai import OpenAI app Server(taotoken-demo-server) client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ.get(TAOTOKEN_BASE_URL, https://taotoken.net/api), ) app.list_tools() async def list_tools() - list[Tool]: return [ Tool( namesummarize_text, description对输入文本做摘要适合长文档快速提炼要点, inputSchema{ type: object, properties: { text: {type: string, description: 待摘要的文本}, max_words: {type: integer, default: 120}, }, required: [text], }, ), Tool( namecount_words, description统计文本的字符数与词数纯本地计算, inputSchema{ type: object, properties: {text: {type: string}}, required: [text], }, ), ] app.call_tool() async def call_tool(name: str, arguments: dict) - list[TextContent]: if name summarize_text: text arguments[text] max_words arguments.get(max_words, 120) resp client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: f用不超过{max_words}字做摘要只输出摘要正文。}, {role: user, content: text}, ], temperature0.3, ) summary resp.choices[0].message.content return [TextContent(typetext, textsummary)] if name count_words: text arguments[text] return [TextContent( typetext, textf字符数: {len(text)}, 词数: {len(text.split())}, )] raise ValueError(f未知工具: {name}) async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options()) if __name__ __main__: asyncio.run(main())几个关键点值得说明。app.list_tools()返回的inputSchema是 JSON Schema宿主应用靠它把用户意图映射成参数写清楚description能显著提升模型选对 Tool 的概率。app.call_tool()里做参数校验和分发注意arguments是宿主传进来的字典缺字段要自己兜底。模型调用统一走client这个 client 的base_url指向 TaoToken所以整个 Server 只认一个 Key。3.2 宿主配置片段Claude Desktop 的配置文件在~/Library/Application Support/Claude/claude_desktop_config.jsonmacOS或%APPDATA%\Claude\claude_desktop_config.jsonWindows。Cline 在 VS Code 设置里的 MCP Servers 部分。两者格式一致{ mcpServers: { taotoken-demo: { command: /绝对路径/.venv/bin/python, args: [/绝对路径/server.py], env: { TAOTOKEN_API_KEY: sk-你的Key, TAOTOKEN_BASE_URL: https://taotoken.net/api } } } }注意command一定要用虚拟环境里的 python 绝对路径否则宿主找不到mcp包。env里把 Key 传进去这样 Server 进程能读到。改完配置重启宿主应用。4. 本地启动与 Tool 调用验证配置写好后先别急着开宿主用官方 CLI 单独测一遍 Server能省掉大量「到底是 Server 挂了还是宿主没加载」的排查时间。4.1 用 MCP Inspector 调试mcp dev server.py它会启动一个本地调试界面列出你注册的所有 Tool。点summarize_text在参数框里填一段文本执行。如果返回摘要说明 Server 逻辑和 TaoToken 通道都通了。这一步失败的话看终端报错KeyError: TAOTOKEN_API_KEY是环境变量没传AuthenticationError是 Key 或 base_url 写错。4.2 在宿主里验证重启 Claude Desktop 后对话框右下角会出现工具图标点开能看到taotoken-demo下的两个 Tool。直接说「帮我统计一下这段话有多少字今天天气不错」模型应该会调用count_words并返回结果。再说「把下面这段技术文档摘要成 80 字以内……」会触发summarize_text这次请求就真正打到了 TaoToken 的通道上。实测下来最容易出问题的是宿主缓存。如果 Tool 列表没更新完全退出应用不是关窗口再开。Cline 的话在 MCP 面板点刷新按钮。4.3 验证请求确实走了统一通道想确认模型调用真的经过 TaoToken可以在 Server 里临时加一行日志print(f[debug] calling {client.base_url}, filesys.stderr)stdio 模式下 stdout 被协议占用日志必须走 stderr否则会破坏通信。宿主一般会把 stderr 输出到日志文件Claude Desktop 的在~/Library/Logs/Claude/mcp-server-taotoken-demo.log。看到 base_url 是https://taotoken.net/api就对了。5. 本篇常见错误排查跑不通的时候按下面这个顺序查基本能覆盖九成问题。Server 启动即退出多半是command路径不对或者虚拟环境没装mcp。手动在终端跑一遍python server.py如果报ModuleNotFoundError就是环境问题。Tool 列表为空宿主没读到配置。检查 JSON 有没有多余逗号路径是不是绝对路径。macOS 上~不会被展开必须写全。调用 Tool 报 401Key 无效或没传进 env。用第 2.1 节的 curl 再验一次 Key确认env块里的 Key 没有多余空格。模型返回超时summarize_text里没设超时长文本可能卡住。给 client 加timeout30并在 Tool 里对超长输入做截断。中文乱码stdio 默认编码问题在main()开头加sys.stdout.reconfigure(encodingutf-8)。改了代码不生效宿主不会热重载 Server每次改完都要重启宿主应用。一个我踩过的坑inputSchema里required写错字段名模型传参时缺字段call_tool直接 KeyError。建议在call_tool开头统一做一次参数校验缺什么补什么别让异常冒到协议层。6. 把 Key 收敛到一处继续扩展你的工具链到这里你已经有了一个能跑通的 MCP Server两个 Tool一份统一 Key。接下来扩展的方向很明确每加一个需要调模型的 Tool都复用同一个client不再新增 Key。需要看更多接入细节可以翻接入文档想直接在网页里试模型效果用模型对话如果你打算长期跑编码类 Agent、频繁调用Coding Plan 会更划算。API Keys 管理https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteCoding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite最后给个实用建议把 Server 里的模型名、超时、max_tokens 都抽成环境变量这样同一份代码在本地和 CI 里能跑出不同行为而 Key 始终只有一份。工具链越复杂统一入口的价值越明显。