1. 为什么我要自己写一个 MCP Server
MCP Server 这个词最近出现频率很高,但很多人第一次接触时会把它想复杂。简单说,它是让大模型调用外部工具的一套标准协议,你把自己写的脚本、内部 API、数据库查询包装成一个「工具」,AI 就能在对话里按需调用。适合谁?适合手里有一堆零散脚本、想让 AI 帮你串起来用的开发者,也适合想把公司内部服务接进 AI 工作流的人。
我这次的目标不是做一个玩具 demo,而是搭一个真正能在本地工具链里跑起来的 MCP Server,并且用 TaoToken 统一管理 Key 和 API 通道。为什么强调统一 Key?因为当你同时用 Cline、CC Switch、Claude Code 这类工具时,每个工具都配一遍 Key、改一遍 base_url,维护成本很高。TaoToken 提供的是一个兼容 OpenAI 风格的 API 入口,把模型调用收敛到一个 Key 上,MCP Server 里只需要读环境变量,不用关心背后换没换模型。
这篇会交付三样东西:一个可复制的 MCP Server 启动脚本、Cline 和 CC Switch 的配置骨架(settings.json / config.toml)、以及一套连通性验证动作。跟着做,你能在半小时内跑通第一个 MCP Server,并确认请求链路是通的。
2. TaoToken 前置准备:拿到统一 Key 和 API 地址
在写代码之前,先把「通道」准备好。TaoToken 的官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。注意 API 地址后面不加任何 UTM 参数,配置里就写这个干净的地址。
你需要做两件事:
第一,注册并创建一个 API Key。进入控制台后找到 API Keys 页面,新建一个 Key,复制出来先存到本地临时文件里。这个 Key 就是后面所有工具共用的那一个。
第二,确认你要用的模型名。TaoToken 的模型对话页面可以直接测试模型是否可用,建议先在网页里发一条消息,确认 Key 和模型都正常,再去配本地工具。这一步能帮你排除掉一半的「配置写了但请求 401」的问题。
提示:Key 不要硬编码进代码或提交到 Git。统一用环境变量注入,MCP Server 里通过 process.env 读取,这样换 Key 时只改一处。
如果你打算长期跑编码类 Agent,可以顺带了解一下 Coding Plan,它更适合高频调用场景;只是偶尔测试的话,按量用 API 就够了。相关入口:模型对话 https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite ,API Keys https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。
3. 从零写一个 MCP Server:可复制的启动脚本
这里我用 Node.js 写一个最小可用的 MCP Server,功能是「查询本地项目信息」——比如读取当前目录的文件列表、返回指定文件的行数。这个例子足够简单,但覆盖了 MCP Server 的核心结构:声明工具、定义参数 schema、实现处理函数、通过 stdio 传输启动。
先初始化项目:
mkdir mcp-local-tools cd mcp-local-tools npm init -y npm install @modelcontextprotocol/sdk zod npm install -D typescript @types/node创建tsconfig.json:
{ "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "outDir": "./build", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true }, "include": ["src/**/*"], "exclude": ["node_modules"] }修改package.json,加上 type 和 build 脚本:
{ "type": "module", "scripts": { "build": "tsc && chmod 755 build/index.js" } }然后写核心文件src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import { readdir, readFile } from "fs/promises"; import path from "path"; const server = new McpServer({ name: "local-tools", version: "1.0.0", capabilities: { tools: {} }, }); // 工具一:列出目录文件 server.tool( "list_files", { dir: z.string().describe("要列出的目录路径") }, async ({ dir }) => { try { const files = await readdir(dir); return { content: [{ type: "text", text: JSON.stringify(files, null, 2) }], }; } catch (e) { return { content: [{ type: "text", text: `Error: ${(e as Error).message}` }], isError: true, }; } } ); // 工具二:统计文件行数 server.tool( "count_lines", { file: z.string().describe("文件路径") }, async ({ file }) => { try { const content = await readFile(file, "utf-8"); const lines = content.split("\n").length; return { content: [{ type: "text", text: `文件 ${path.basename(file)} 共 ${lines} 行` }], }; } catch (e) { return { content: [{ type: "text", text: `Error: ${(e as Error).message}` }], isError: true, }; } } ); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("MCP Server 已启动,等待 stdio 连接"); } main();构建:
npm run build构建产物在build/index.js。这个 Server 本身不调用大模型,所以暂时不需要 TaoToken 的 Key;但下一步我们要让它具备「调用模型」的能力,Key 就派上用场了。
3.1 让 MCP Server 通过 TaoToken 调用模型
给 Server 加一个工具,用 TaoToken 的 API 做一次模型调用。这样 AI 工具在调用你的 MCP Server 时,Server 内部再走统一通道请求模型,链路就串起来了。
server.tool( "ask_model", { prompt: z.string().describe("要问模型的问题") }, async ({ prompt }) => { const apiKey = process.env.TAOTOKEN_API_KEY; const baseUrl = process.env.TAOTOKEN_BASE_URL || "https://taotoken.net/api"; if (!apiKey) { return { content: [{ type: "text", text: "缺少 TAOTOKEN_API_KEY 环境变量" }], isError: true, }; } const res = await fetch(`${baseUrl}/v1/chat/completions`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}`, }, body: JSON.stringify({ model: process.env.TAOTOKEN_MODEL || "gpt-4o-mini", messages: [{ role: "user", content: prompt }], }), }); const data = await res.json(); const text = data.choices?.[0]?.message?.content ?? JSON.stringify(data); return { content: [{ type: "text", text }] }; } );注意 base_url 写https://taotoken.net/api,路径拼/v1/chat/completions。模型名按你在模型对话页面确认过的填,不要凭记忆写。
4. 接入本地工具:settings.json 与 config.toml 配置骨架
MCP Server 写好了,接下来把它挂到本地 AI 工具上。不同工具的配置文件格式不一样,这里给两个最常见的骨架。
4.1 Cline(VS Code 插件)配置
Cline 的 MCP 配置通常放在 VS Code 的 settings.json 里,或者插件自己的 MCP 配置面板。核心结构是mcpServers:
{ "mcpServers": { "local-tools": { "command": "node", "args": ["/absolute/path/to/mcp-local-tools/build/index.js"], "env": { "TAOTOKEN_API_KEY": "你的Key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_MODEL": "gpt-4o-mini" } } } }args里必须是build/index.js的绝对路径,相对路径在插件启动子进程时经常解析失败,这是踩过的坑之一。
4.2 CC Switch 配置(config.toml)
CC Switch 用 TOML 管理多个通道配置,结构大致如下:
[[servers]] name = "local-tools" command = "node" args = ["/absolute/path/to/mcp-local-tools/build/index.js"] [servers.env] TAOTOKEN_API_KEY = "你的Key" TAOTOKEN_BASE_URL = "https://taotoken.net/api" TAOTOKEN_MODEL = "gpt-4o-mini"如果你在 CC Switch 里同时管理多个模型通道,把 TaoToken 作为一个 provider 配进去,base_url 填https://taotoken.net/api,Key 填同一个,这样切换工具时不用重复填 Key。
注意:配置文件里的 Key 是明文,建议把配置文件加入
.gitignore,或者用系统环境变量引用而不是直接写值。
5. 验证请求链路:三步确认跑通
配置写完不代表通了,必须做连通性验证。我一般分三步。
第一步,单独跑 MCP Server,确认进程能起来:
TAOTOKEN_API_KEY=你的Key node build/index.js如果看到 stderr 输出「MCP Server 已启动」,说明 Server 本身没问题。注意 MCP 用 stdio 通信,正常运行时 stdout 是给协议用的,日志要打到 stderr,否则会污染协议流。
第二步,用官方 Inspector 调试工具连上去,手动调用工具:
npx @modelcontextprotocol/inspector node build/index.js启动后打开它提示的本地地址,在界面里选择ask_model工具,输入一句「你好,回复两个字」,点运行。如果返回了模型输出,说明 MCP Server → TaoToken → 模型这条链路是通的。
第三步,回到 Cline 或 CC Switch,在对话里让 AI 调用这个工具。比如输入「用 local-tools 的 ask_model 工具问一下今天适合写代码吗」。AI 触发工具调用后,你能在工具执行记录里看到返回内容。
三步都过,链路就算跑通了。任何一步失败,问题范围都能缩小:第一步失败是 Server 代码问题,第二步失败是 Key 或网络问题,第三步失败是工具配置问题。
6. 本篇常见错误排查
报错Cannot find module '@modelcontextprotocol/sdk/server/mcp.js'多半是 Node 版本太低或 module 配置不对。确认 Node 18+,package.json里有"type": "module",tsconfig.json的 module 设为 Node16。
Inspector 连不上,界面一直转圈检查启动命令里 node 后面的路径是不是build/index.js,以及有没有先执行npm run build。没构建就没有这个文件。
调用 ask_model 返回 401Key 没读到或写错了。先在终端echo $TAOTOKEN_API_KEY确认环境变量存在,再检查配置文件里 env 字段的 Key 有没有多余空格。
返回 404base_url 拼错了。正确写法是https://taotoken.net/api,请求路径/v1/chat/completions。不要写成https://taotoken.net/api/v1再拼/v1/...,会重复。
模型名报 not found去模型对话页面确认当前 Key 可用的模型列表,配置里用确认过的名字,不要照搬别处的示例。
Cline 里工具列表不显示配置文件 JSON 格式错误,或者 args 用了相对路径。用 JSON 校验工具过一遍,路径改绝对路径。
7. 下一步:把 Key 管理和工具链收敛到一处
跑通第一个 MCP Server 之后,你会发现真正麻烦的不是写 Server,而是工具多了以后 Key 和通道散落各处。我的做法是:所有本地工具统一读同一组环境变量,base_url 固定指向 TaoToken,模型名按需在配置里覆盖。这样换模型、换 Key 只改一个地方。
如果你要接更多工具,接入文档里有完整的参数说明和示例,可以先看文档再动手:接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。需要新建或轮换 Key 时去 API Keys 页面:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。想先验证模型是否可用,直接在模型对话里发一条消息最快:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。长期跑编码 Agent 的话,Coding Plan 会更省心:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。
最后留一个实用建议:MCP Server 的日志一定走 stderr,工具处理函数里所有异常都要 catch 并返回isError: true,否则一个未捕获的异常会让整个 stdio 连接断掉,AI 工具那边只会显示「工具无响应」,排查起来很费时间。