1. 为什么你的 AI Agent 还是“嘴强王者”大模型能写诗、能改 bug、能陪你聊到凌晨三点但你让它帮你读一下本地某个日志文件、把一段配置写进指定路径、或者列一下项目目录结构它立刻就开始“礼貌地拒绝”——不是它不想干是它真的够不着。这就是 MCPModel Context Protocol要解决的核心问题。你可以把它理解成 AI 世界的 USB-C 接口以前每个模型厂商都有自己的 Function Calling 格式OpenAI 一套、Anthropic 一套你写一个工具要适配 N 遍MCP 出现之后你只需要写一次 MCP Server所有支持 MCP 的客户端Claude Desktop、Cursor、Cline、Continue、Zed 等都能直接调用。这篇教程聚焦一个非常具体的场景用 TypeScript/Node.js 从零搭一个 MCP 服务端让 AI Agent 通过统一的 Key/API 通道调用外部工具。我会给出可复制的config.toml与settings.json骨架、CC Switch / Cline 的配置片段并附上启动验证与报错排查步骤。目标很明确——一次跑通工具注册与调用链路不绕弯。适合谁看已经会用 Node.js 写点脚本、想让自己的 AI 助手真正“动手干活”的开发者以及正在用 Cline、CC Switch 这类工具想把内部系统封装成 MCP Server 的工程同学。2. 前置准备TaoToken 统一 Key 与 MCP 运行环境在动手写 Server 之前先把两件事搞定模型通道和本地环境。模型通道这块我建议用 TaoToken 做统一入口。原因是 MCP 工具调用会产生多轮往返如果每个客户端都单独配一套 Key管理起来很乱。TaoToken 提供统一的 API 通道Claude Code、Cline、CC Switch 这些客户端都能共用同一个 Key省去反复切换的麻烦。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 这个不加 UTM直接填进配置里就行。先去控制台创建一个 API Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 的管理页面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建完先复制出来后面配置里要用。本地环境要求不复杂# Node.js 版本必须 18MCP SDK 依赖较新的运行时特性 node --version # 初始化项目并安装依赖 mkdir my-mcp-server cd my-mcp-server npm init -y npm install modelcontextprotocol/sdk zod # TypeScript 支持可选但强烈推荐 npm install -D typescript types/node tsx项目结构建议保持干净my-mcp-server/ ├── src/ │ └── index.ts ├── package.json └── tsconfig.jsontsconfig.json给一份能直接用的最小配置{ compilerOptions: { target: ES2022, module: Node16, moduleResolution: Node16, outDir: dist, rootDir: src, strict: true, esModuleInterop: true, skipLibCheck: true }, include: [src/**/*] }用 TypeScript 的好处是 Tool 的参数 schema 有类型约束写错了编辑器当场就报红比运行时才发现问题舒服得多。当然你用纯 JS 也完全能跑只是调试成本会高一些。3. 可复制配置从零写一个文件管理 MCP Server这一节是全文的核心我会把 Server 代码、config.toml、settings.json以及 Cline / CC Switch 的配置片段一次性给全。3.1 Server 骨架与工具注册先看完整的src/index.ts。这个 Server 暴露三个工具读文件、写文件、列目录。都是 AI 辅助开发场景里最高频的操作。import { McpServer } from modelcontextprotocol/sdk/server/mcp.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { z } from zod; import fs from fs/promises; import path from path; const server new McpServer({ name: file-manager, version: 1.0.0, }); // 工具一读取文件 server.tool( read_file, 读取指定路径的文件内容, { filePath: z.string().describe(文件的绝对路径) }, async ({ filePath }) { try { const content await fs.readFile(filePath, utf-8); return { content: [{ type: text, text: content }] }; } catch (error) { return { content: [{ type: text, text: 读取失败: ${(error as Error).message} }], isError: true, }; } } ); // 工具二写入文件 server.tool( write_file, 将内容写入指定路径的文件, { filePath: z.string().describe(文件的绝对路径), content: z.string().describe(要写入的内容), }, async ({ filePath, content }) { try { await fs.mkdir(path.dirname(filePath), { recursive: true }); await fs.writeFile(filePath, content, utf-8); return { content: [{ type: text, text: 成功写入 ${filePath} }] }; } catch (error) { return { content: [{ type: text, text: 写入失败: ${(error as Error).message} }], isError: true, }; } } ); // 工具三列出目录 server.tool( list_files, 列出指定目录下的所有文件和子目录, { dirPath: z.string().describe(目录路径) }, async ({ dirPath }) { try { const entries await fs.readdir(dirPath, { withFileTypes: true }); const listing entries .map((e) ${e.isDirectory() ? [DIR] : [FILE]} ${e.name}) .join(\n); return { content: [{ type: text, text: listing || 空目录 }] }; } catch (error) { return { content: [{ type: text, text: 列出失败: ${(error as Error).message} }], isError: true, }; } } ); async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(MCP Server 已启动等待连接...); } main().catch(console.error);几个关键点值得单独说。server.tool()的第一个参数是工具名AI 就是靠这个名字决定调不调用第二个是描述写得越清楚模型判断越准第三个是 zod schema它同时承担参数校验和给模型看的“参数说明”两个职责。返回值必须是{ content: [...] }结构出错时加isError: true模型看到这个标记会知道调用失败了。3.2 config.toml 与 settings.json 骨架如果你用的是支持 TOML 配置的客户端比如某些 CLI 工具链config.toml可以这样写[mcp_servers.file-manager] command npx args [tsx, src/index.ts] cwd /Users/yourname/projects/my-mcp-server env { NODE_ENV production } [api] base_url https://taotoken.net/api api_key sk-你的TaoToken密钥 model claude-sonnet-4-5而 Claude Desktop 这类用 JSON 的客户端settings.json或claude_desktop_config.json骨架如下{ mcpServers: { file-manager: { command: npx, args: [tsx, src/index.ts], cwd: /Users/yourname/projects/my-mcp-server } } }配置文件位置按系统区分macOS 在~/Library/Application Support/Claude/claude_desktop_config.jsonWindows 在%APPDATA%\Claude\claude_desktop_config.json。改完必须完全退出客户端再重启不是关窗口是彻底退出进程。3.3 Cline 与 CC Switch 配置片段Cline 的 MCP 配置在 VS Code 设置里找到 Cline 的 MCP Servers 配置项填入{ mcpServers: { file-manager: { command: npx, args: [tsx, /absolute/path/to/my-mcp-server/src/index.ts], disabled: false, autoApprove: [read_file, list_files] } } }注意autoApprove这个字段读操作可以自动批准写操作建议保留人工确认避免 AI 误改文件。CC Switch 的配置思路类似它更偏向多模型切换场景。在它的配置里把 API 通道指向 TaoTokenMCP Server 部分照上面的 JSON 结构填即可。这样你切换模型时MCP 工具链路不用重新配。4. 验证请求确认工具注册与调用链路跑通配置写完先别急着开客户端用官方 Inspector 单独验证 Server 本身能不能跑。npx modelcontextprotocol/inspector npx tsx src/index.ts这条命令会启动一个本地调试界面浏览器打开后你能看到 Server 暴露的所有工具列表。点进read_file填入一个真实文件路径点执行如果返回文件内容说明 Server 端没问题。如果 Inspector 里正常再回到客户端验证。重启 Claude Desktop 或 Cline在对话里输入帮我读一下 /Users/yourname/projects/my-mcp-server/package.json 的内容正常情况下客户端会弹出工具调用确认框显示read_file和参数你点允许AI 就会把文件内容读出来并总结。这一步成功说明整条链路——客户端 → MCP 协议 → 你的 Server → 文件系统——全部打通。想验证模型通道是否也走通了可以直接在模型对话页面发一条测试请求https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 确认 Key 有效、模型可访问。如果你打算长期跑编码类 Agent 任务Coding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 里有更省心的额度方案适合高频调用场景。5. 本篇常见报错排查这一节是我实际踩过的坑按出现频率排序。报错一Server disconnected或工具列表为空。九成是 transport 用错了。stdio 模式是给本地进程通信用SSE 模式才是给远程服务用。本地 Server 必须用StdioServerTransport如果你在客户端配置里写了 URL 而不是 command就会连不上。报错二工具调用时静默失败没有任何返回。大概率是 zod schema 写错了。MCP 在启动时不会校验 schema 的语义只有调用时才暴露问题。排查方法是在 Server 里手动跑一次schema.parse({...})看是否抛异常。另外注意z.string()和z.string().optional()的区别参数必填但模型没传也会静默失败。报错三Windows 下路径反斜杠导致 JSON 解析错误。JSON 里反斜杠是转义字符C:\Users\...会被解析成乱码。统一用正斜杠/Node.js 在 Windows 上也能正确识别。或者用双反斜杠\\转义。报错四npx tsx找不到命令。检查tsx是否装在了项目本地。如果只在全局装了客户端启动时的工作目录可能找不到。建议写进devDependencies配置里用npx tsx让它从本地node_modules找。报错五改了代码但客户端行为没变。MCP Server 是进程级启动的改完代码必须重启客户端光刷新对话没用。调试阶段建议每次改完都完整退出再开。报错六API Key 无效或 401。检查 Key 是否复制完整、有没有多余空格。TaoToken 的 Key 管理页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 可以重新生成。接入细节和参数说明看文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各客户端的完整配置示例。6. 把工具链路接到你的真实工作流跑通 demo 只是起点。真正有价值的是把你内部系统的能力封装成 MCP Server——比如查数据库、调内部 API、生成报表。思路是一样的一个server.tool()对应一个能力zod 定义好参数返回结构化结果。如果你用的是 Claude Code 这类编码 Agent接入方式略有不同可以参考 ClaudeCodeAnthropic 的配置说明https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 里面有针对编码场景的 MCP 接入细节。最后给一个实用建议Tool 的 description 字段别偷懒。模型判断调不调用某个工具几乎完全依赖这段描述。写清楚“这个工具做什么、什么场景用、参数含义”比你在 prompt 里反复强调有效得多。我试过把描述从一句话扩成三句话工具调用准确率肉眼可见地提升。代码跑起来之后先拿读文件这种只读工具练手确认链路稳定了再开放写操作。安全边界永远比功能数量重要。
