1. 为什么你的 AI Agent 还是“嘴强王者”
大模型能写诗、能改 bug、能陪你聊到凌晨三点,但你让它帮你读一下本地某个日志文件、把一段配置写进指定路径、或者列一下项目目录结构,它立刻就开始“礼貌地拒绝”——不是它不想干,是它真的够不着。
这就是 MCP(Model 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_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api (这个不加 UTM,直接填进配置里就行)。
先去控制台创建一个 API Key:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,Key 的管理页面在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。创建完先复制出来,后面配置里要用。
本地环境要求不复杂:
# Node.js 版本必须 >= 18,MCP 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.json,Windows 在%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 通道指向 TaoToken,MCP 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_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite ,确认 Key 有效、模型可访问。如果你打算长期跑编码类 Agent 任务,Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 里有更省心的额度方案,适合高频调用场景。
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_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 可以重新生成。接入细节和参数说明看文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有各客户端的完整配置示例。
6. 把工具链路接到你的真实工作流
跑通 demo 只是起点。真正有价值的是把你内部系统的能力封装成 MCP Server——比如查数据库、调内部 API、生成报表。思路是一样的:一个server.tool()对应一个能力,zod 定义好参数,返回结构化结果。
如果你用的是 Claude Code 这类编码 Agent,接入方式略有不同,可以参考 ClaudeCodeAnthropic 的配置说明:https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode-anthropic&utm_campaign=rewrite ,里面有针对编码场景的 MCP 接入细节。
最后给一个实用建议:Tool 的 description 字段别偷懒。模型判断调不调用某个工具,几乎完全依赖这段描述。写清楚“这个工具做什么、什么场景用、参数含义”,比你在 prompt 里反复强调有效得多。我试过把描述从一句话扩成三句话,工具调用准确率肉眼可见地提升。
代码跑起来之后,先拿读文件这种只读工具练手,确认链路稳定了再开放写操作。安全边界永远比功能数量重要。