
1. 从一次 add(2,3) 调用说起MCP Server 与 Client 到底在做什么MCP 全称 Model Context Protocol是一套让 AI 应用和外部工具、数据源互相认识的开放协议。你可以把它理解成 AI 世界的 USB-C 接口不管对面接的是文件系统、数据库还是某个内部 API只要双方都按 MCP 说话就能插上就用。这篇要做的是用 TypeScript 写一个最小的 MCP Server 和一个 MCP Client跑通一次add(2, 3)的 Tool 调用然后把请求端点改到 TaoToken 统一 Key 通道上验证连通。适合谁适合已经会一点 TypeScript、想搞清楚 MCP 握手、能力协商、Tool 调用链路但不想一上来就啃完整规范的开发者。很多人第一次接触 MCP 会以为它必须和 LLM 绑在一起其实不是。MCP 本身只是一套 Client/Server 协议LLM 可以用它但 MCP 不依赖 LLM 也能独立运行。我先把这句话放在前面因为后面所有代码都建立在这个认知上Server 负责把能力注册成标准接口Client 负责发现并调用这些能力中间传的是 JSON-RPC 消息Transport 只负责把消息从一端搬到另一端。先看最小模型。Server 做三件事创建 MCP Server、注册addTool、通过 stdio 提供 MCP 服务。Client 也只做三件事连接 Server、发现 Tool、调用add。整个过程没有 LLM没有 Agent就是两个进程之间的一次标准请求响应。把这条链路跑通再去理解 Host、Client、Server 三角关系以及 MCP 和 Function Calling、Agent 的分层思路会清楚很多。MCP Client MCP Server │ │ │ 发现可用能力 (tools/list) │ ├───────────────────────────────────►│ │ │ │ add(a:number,b:number) │ │◄───────────────────────────────────┤ │ │ │ add(2,3) (tools/call) │ ├───────────────────────────────────►│ │ │ 执行 ab │ 5 │ │◄───────────────────────────────────┤这张图里真正决定程序行为的代码并不多。registerTool()把名称、描述、输入 Schema 和 Handler 关联到一起listTools()背后是tools/list请求callTool()背后是tools/call请求。所谓“动态发现能力”并不神秘就是 Server 用统一协议把自己的能力描述交给 Client。Schema 之所以重要是因为 Client 连接一个陌生 Server 时不应该提前写死“这个 Server 一定有 add、add 一定接收两个 number”这些信息必须由 Server 自己描述Client 才能机器可读地理解。理解了这一层再看 MCP 的整体架构就顺了。Host 是承载用户体验和 AI 工作流的应用负责调用模型、组织上下文、管理 MCP Client、决定权限边界Client 位于 Host 一侧负责和 Server 通信、发现能力、调用 ToolServer 把某个外部系统的能力转换成 MCP 能理解的形式工程上可以把它看成 Adapter。MCP Server 不只有 Tools还有 Resources提供上下文数据和 Prompts提供可复用交互模板但理解 MCP 的第一步仍然是先掌握 Tools。2. TaoToken 统一 Key 通道前置准备Base URL、API Key 与 Model ID 三件套在把请求端点改到 TaoToken 之前先把三件套准备好Base URL、API Key、Model ID。这三样东西是后面所有配置的基础缺一个都跑不通。TaoToken 的 API 地址是https://taotoken.net/api注意这里不带任何查询参数直接作为 Base URL 使用。API Key 需要到控制台创建模型对话入口可以用来验证模型是否可用接入文档里有完整的端点说明。先明确一点TaoToken 在这里扮演的是统一 Key 通道的角色它让你用一套 Key 和统一的端点去访问不同的模型能力而不是每个模型都单独配一套凭证。对于 MCP 这种需要频繁切换后端能力的场景统一通道能省掉很多重复配置。你可以在控制台里创建 API Key然后在接入文档里对照端点格式确认自己的请求路径拼对了。具体操作上打开控制台创建 Key复制出来先存好。然后打开接入文档找到对话补全的端点路径。TaoToken 的 API 根地址是https://taotoken.net/api对话补全通常拼成https://taotoken.net/api/v1/chat/completions这样的形式。Model ID 则根据你要用的模型填写比如gpt-4o、claude-3-5-sonnet这类标识具体以接入文档里列出的为准。这三样凑齐后面无论是写配置文件还是直接发请求都有据可依。如果你用的是 Claude Code 这类工具配置方式会略有不同但核心还是这三件套。Claude Code 的配置里需要填 Base URL、API Key 和 Model IDBase URL 同样指向 TaoToken 的 API 地址。Cline 的 MCP 配置也是类似逻辑在 settings 里指定端点、Key 和模型。Codex 的auth.json则需要把 Key 和端点写进对应字段。不管哪种工具只要记住“端点 Key 模型”这个组合就不会迷路。这里要提醒一句不要把生产库直连到 MCP Server 上做实验。MCP Server 适合接开发环境、测试数据或者只读接口生产库的写操作应该走独立的权限控制和审计流程。TaoToken 的统一通道解决的是访问凭证和端点统一的问题不解决权限隔离的问题这两件事要分开看。准备好三件套之后可以先不写 MCP 代码直接用 curl 验证一下通道是否通。这一步能帮你排除掉 Key 错误、端点拼错、模型名不对这类基础问题避免后面调试 MCP 时把网络问题和代码问题混在一起。curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o, messages: [{role: user, content: ping}] }如果返回里有正常的choices字段说明通道是通的。如果返回 401先检查 Key 有没有复制完整、有没有多余空格如果返回 404检查端点路径是不是拼错了如果返回模型不存在的错误检查 Model ID 是否和接入文档里的一致。这一步过了再进入 MCP 的代码环节。3. 可复制配置三十行 TypeScript 实现 MCP Server 与 Client现在进入代码环节。先装依赖官方 TypeScript SDK 的包名以你实际安装的版本为准这里用modelcontextprotocol/server和modelcontextprotocol/client作为示例配合zod做 Schema 校验tsx用来直接跑 TypeScript。npm init -y npm install modelcontextprotocol/server modelcontextprotocol/client zod npm install -D tsx typescriptServer 端代码。核心是创建一个McpServer实例注册一个addTool然后通过 stdio 提供服务。registerTool接收三个参数Tool 名称、包含描述和输入 Schema 的配置对象、以及异步 Handler。Handler 返回的内容必须是content数组里面放type: text的文本结果。// server.ts import { McpServer } from modelcontextprotocol/server; import { serveStdio } from modelcontextprotocol/server/stdio; import * as z from zod/v4; function createServer() { const server new McpServer({ name: demo-server, version: 1.0.0, }); server.registerTool( add, { description: Add two numbers, inputSchema: z.object({ a: z.number(), b: z.number(), }), }, async ({ a, b }) ({ content: [ { type: text, text: String(a b), }, ], }) ); return server; } serveStdio(createServer, { legacy: reject });Client 端代码。创建一个Client实例指定版本协商策略然后用StdioClientTransport启动 Server 子进程并连接。连接成功后先listTools()看看 Server 暴露了哪些能力再callTool()调用add最后关闭连接。// client.ts import { Client } from modelcontextprotocol/client; import { StdioClientTransport } from modelcontextprotocol/client/stdio; const client new Client( { name: demo-client, version: 1.0.0 }, { versionNegotiation: { mode: { pin: 2026-07-28 }, }, } ); await client.connect( new StdioClientTransport({ command: npx, args: [tsx, server.ts], }) ); console.log(await client.listTools()); const result await client.callTool({ name: add, arguments: { a: 2, b: 3 }, }); console.log(result); await client.close();跑起来npx tsx client.ts如果一切正常你会先看到listTools()返回的 Tool 定义里面有add的名称、描述和 inputSchema然后看到callTool()返回的结果content里是文本5。到这里一次完整的 MCP Tool 调用就跑通了。现在把请求端点改到 TaoToken 统一 Key 通道。MCP 本身走的是 stdio 或 Streamable HTTP和模型 API 是两层。如果你想让 MCP Server 内部去调用模型或者让 Host 通过 TaoToken 访问模型就需要在配置里指定 TaoToken 的端点。以常见的 settings 配置为例把 Base URL 指向https://taotoken.net/apiKey 填你在控制台创建的 KeyModel ID 填接入文档里列出的模型标识。{ mcpServers: { demo-server: { command: npx, args: [tsx, server.ts], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-你的Key, TAOTOKEN_MODEL: gpt-4o } } } }如果你用的是 Cline 的 MCP 配置格式类似把command、args和env填对即可。Claude Code 的配置则是在对应的 settings 文件里指定 Base URL、API Key 和 Model ID。Codex 的auth.json需要把 Key 和端点写进对应字段。不管哪种工具三件套的对应关系不变Base URL 指向 TaoToken API 地址Key 用控制台创建的Model ID 用接入文档里确认过的。这里有个容易踩的坑MCP Server 的 stdio 配置和模型 API 的配置是两套东西。StdioClientTransport里的command和args是启动 MCP Server 子进程用的env里传的是给 Server 用的环境变量。如果你把 TaoToken 的 Key 放在env里Server 代码里要通过process.env.TAOTOKEN_API_KEY读取。不要把 Key 硬编码在代码里也不要把 Key 提交到版本库。4. 验证请求与成功结果从 tools/list 到 tools/call 的完整链路跑通之后我们来拆解验证过程。client.listTools()看起来像普通 TypeScript 方法但 Client 和 Server 是不同进程不可能直接调用彼此的 JavaScript 函数。SDK 最终把这个动作转换成 MCP 协议消息对应tools/list。概念上可以理解成这样一个 JSON-RPC 请求{ jsonrpc: 2.0, id: 1, method: tools/list }Server 收到后返回自己的 Tool 描述核心信息类似{ name: add, description: Add two numbers, inputSchema: { type: object, properties: { a: { type: number }, b: { type: number } }, required: [a, b] } }所以listTools()背后就是Client 发tools/listServer 查询已注册的 Tools返回 Tool definitionsClient 拿到结果。所谓能力发现核心就是 Server 用统一协议把自己的能力描述交给 Client。callTool()对应tools/call概念上的协议消息类似{ jsonrpc: 2.0, id: 2, method: tools/call, params: { name: add, arguments: { a: 2, b: 3 } } }Server 收到后读取 Tool 名称找到add验证 arguments执行 Handler返回结果。整个 Tool 系统可以压缩成tools/call进入 Tool Registry找到add进入 Handler执行a b返回结果。能力由普通代码实现MCP 负责标准化能力的描述、发现、调用和结果返回。Transport 这一层负责“怎么传”。示例用的是 stdioClient 启动 MCP Server 子进程后通过进程的输入输出通道交换 MCP 消息。Transport 负责的是 MCP 协议消息和真正能够传输的字节之间的转换它不决定 Tool 是什么也不负责执行业务逻辑。当前标准 Transport 主要包括 stdio适合本地进程型 Server和 Streamable HTTP适合独立部署的远程 Server。把 LLM 放回来完整链路会多一层。用户说“92837 加 18273 等于多少”Host 把从 MCP Server 获得的 Tool Schema 提供给模型模型判断应该调用add({ a: 92837, b: 18273 })Host 把这个调用意图转换成真正的 MCP 调用。这里要区分三个角色LLM 负责判断“想调用什么”Host 负责组织和协调流程MCP Client 负责真正按照 MCP 与 Server 通信。模型不等于 MCP Client。Function Calling 和 MCP 解决的问题不同。Function Calling 主要解决模型如何结构化表达一个调用意图MCP 主要解决 Host 有哪些外部能力、怎么描述、怎么发现、怎么调用。常见链路是用户输入进入 LLMLLM 输出 Function/Tool Call IntentHost 通过 MCP Client 调用 MCP ServerMCP Server 访问外部系统。两者可以组合但不是同一种机制。Agent 位于更高的抽象层。一个典型 Agent 会执行目标、判断、选择行动、调用工具、观察结果、继续判断的循环。MCP 可以提供其中的“外部能力调用”部分但不负责实现完整的 Agent Loop。Agent 可以使用 MCP但 MCP 本身不是 Agent。5. 本篇常见错排查401、local proxy failed、reading choices 与 OAuth调试 MCP 和 TaoToken 通道时有几类报错特别常见。下面按真实报错对照排查。401 Unauthorized。这个最直接Key 不对或者没带上。检查三件事Key 有没有复制完整有没有多余空格或换行请求头里Authorization字段格式是不是Bearer sk-xxxKey 是不是已经过期或被禁用。如果用的是环境变量确认process.env.TAOTOKEN_API_KEY真的读到了值可以在代码里临时打印一下长度确认。local proxy failed。这个报错通常出现在网络层说明请求没能到达目标端点。检查 Base URL 是不是拼成了https://taotoken.net/api有没有多写或少写路径段。如果你在本地配了其他网络工具先确认它们没有拦截请求。MCP 的 stdio 模式下Server 子进程的网络请求走的是宿主机的网络环境确认子进程能正常访问外网。reading choices 报错。这个通常出现在解析模型响应时说明返回结构里没有预期的choices字段。可能原因端点路径不对请求打到了非对话补全的路径Model ID 写错服务端返回了错误结构请求体格式不对比如messages字段拼错。先用 curl 单独验证一次确认返回结构里有choices再回到代码里对照。OAuth 相关报错。如果你用的是 Claude Code 或类似工具可能会遇到 OAuth 流程的问题。这类工具在配置 TaoToken 时需要确认 Base URL、API Key 和 Model ID 三件套都填对。OAuth 报错往往是因为端点配置和工具预期的格式不匹配对照接入文档里的说明逐项检查。Claude Code 的配置里Base URL 要指向 TaoToken 的 API 地址不要填成其他路径。Tool 调用返回空或格式错误。检查 Handler 返回的结构是不是符合 MCP 要求必须是{ content: [{ type: text, text: ... }] }这样的形式。如果返回的是裸字符串或对象Client 解析会出问题。另外确认inputSchema里的类型和实际传入的 arguments 匹配z.number()对应数字传字符串会校验失败。版本协商失败。Client 里配置了versionNegotiation如果 Server 不支持指定的协议版本连接会失败。确认 Server 和 Client 使用的 SDK 版本兼容协议版本字符串和 SDK 文档里的一致。如果只是本地实验可以先不 pin 版本让 SDK 自动协商。排查顺序建议先用 curl 验证 TaoToken 通道确认 Key、端点、模型三件套没问题再单独跑 Server确认serveStdio能正常启动最后跑 Client确认 stdio 连接和 Tool 调用。把网络问题和代码问题分开能省很多时间。6. 把 MCP 接到 TaoToken 统一通道长期编码与 Agent 场景的接入建议跑通最小示例之后下一步通常是把 MCP 用到实际场景里。如果你在做长期编码或者 Agent 类应用建议把模型访问统一走 TaoToken 的 Coding Plan 通道这样切换模型、管理 Key、控制成本都会方便很多。Coding Plan 适合需要持续调用模型能力的场景配合 MCP 的能力发现机制可以让 Agent 动态接入不同的工具后端。接入时记住三件套的对应关系Base URL 指向https://taotoken.net/apiAPI Key 用控制台创建的Model ID 用接入文档里确认的。MCP Server 的配置里env字段传这三样给子进程Host 侧的模型调用配置里同样填这三样。两边保持一致避免出现 Server 能跑但模型调不通或者模型能调但 Tool 发现失败的情况。对于需要验证模型能力的场景可以先用模型对话入口快速测试确认 Key 和模型可用再接入 MCP。对于需要管理多个 Key 或查看用量的场景控制台里可以创建和管理 API Key。接入文档里有完整的端点说明和参数格式遇到不确定的路径或字段先查文档再改代码。MCP 的工程价值在于它把“AI 应用连接外部能力”这件事标准化了。一边是 MCP中间是 MCP Server/Adapter另一边是现实世界的 API、数据库、文件系统和服务。理解这一点之后再去看 Filesystem MCP、数据库 MCP、GitHub MCP或者自己实现一个 MCP Server思路都会简单很多。先把add(2, 3)跑通再逐步替换成真实能力这条路最稳。