ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从0开发一个 Agent 第七章:用 TaoToken 统一 Key 跑通 MCP Client-Server 骨架

从0开发一个 Agent 第七章:用 TaoToken 统一 Key 跑通 MCP Client-Server 骨架 1. 为什么你的 Agent 需要一个 MCP 骨架如果你跟着前六章一路写下来Tool Calling 那套东西应该已经跑通了在tools.ts里定义几个函数塞给模型模型决定调哪个你执行完把结果回传。单机玩具阶段这样完全够用。但只要你开始接第二个、第三个外部系统问题就会冒出来——每接一个系统你都要在 Agent 核心代码里加一个工具函数参数校验、鉴权、错误处理全写在一起。接十个系统tools.ts就变成一坨谁都不敢动的意大利面。MCPModel Context Protocol想解决的就是这件事。你可以把它理解成 AI 世界的 USB 接口Agent 是电脑各种外部能力是 U 盘、键盘、打印机只要大家都遵守同一套插口规范插上就能用拔掉也不影响主机。MCP 由 Anthropic 提出定义了模型、工具、数据源之间标准化交互的规范核心是 Client-Server 架构——Agent 侧跑一个 MCP Client每个外部能力封装成一个 MCP Server两者通过标准协议通信。这一章的目标很明确不追求功能多而是把 MCP 的 Client-Server 通信骨架从零搭起来跑通一次完整的请求-响应。同时用 TaoToken 统一 Key 和 API 通道来接入模型调用这样你后面接多少个 MCP Server模型侧的配置都不用改。适合已经写过基础 Agent、想往工程化方向走一步的开发者。整条链路我会给出可复制的config.toml、settings.json、Server 启动命令以及一次能亲眼看到结果的验证动作。2. TaoToken 前置统一 Key 与 API 通道在搭 MCP 之前先把模型调用这条线理顺。MCP 负责的是「Agent 怎么发现和调用工具」但工具调用过程中模型本身还是要发请求的如果每个 Server、每个脚本都各自配一套 Key 和 Base URL后面排查问题会很痛苦。TaoToken 在这里的角色就是统一入口一个 Key、一个 API 地址模型对话、编码、Agent 调用都走同一条通道。你需要先拿到两样东西API Key 和 API 地址。Key 在控制台的 API Keys 页面创建地址固定用https://taotoken.net/api注意这个地址不带任何查询参数直接作为 base_url 使用。创建 Key 的入口在这里https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite拿到 Key 之后先别急着写 MCP 代码用一条最简单的请求确认通道是通的。这一步很重要因为后面 MCP 链路出问题时你得能快速判断是模型通道的问题还是 MCP 协议的问题。验证方式可以用 curl也可以用官方提供的模型对话页面直接试https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite如果你更习惯命令行下面这条 curl 可以直接验证 Key 是否有效把$TAOTOKEN_API_KEY换成你自己的 Keycurl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: 回复 ok 两个字}], max_tokens: 16 }返回里能看到正常的choices结构说明 Key 和通道都没问题。这一步过了再往下搭 MCP 才有意义。如果你打算长期跑编码类 Agent可以顺手了解一下 Coding Plan它更适合高频调用的场景https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite3. 可复制配置config.toml 与 settings.json 骨架MCP 生态里不同客户端读的配置文件格式不太一样我这边统一用两份一份config.toml描述 MCP Server 的启动方式和环境变量一份settings.json描述模型通道和 Client 行为。这样职责清晰Server 归 Server模型归模型。先看config.toml。它的作用是告诉 MCP Client有哪些 Server、每个 Server 怎么启动、需要什么环境变量。下面这份是可直接复制的最小骨架包含一个本地 stdio Server 和一个远程 SSE Server# config.toml —— MCP Server 注册表 [client] name agent-mcp-client version 0.1.0 # 单次工具调用超时毫秒 tool_timeout_ms 30000 # 本地 stdio Server适合文件系统、本地脚本类能力 [[servers]] name filesystem transport stdio command npx args [-y, modelcontextprotocol/server-filesystem, ./workspace] env { NODE_ENV production } # 远程 SSE Server适合部署在云端的微服务 [[servers]] name remote-tools transport sse url http://localhost:3001/sse # 远程 Server 的鉴权头走统一通道时这里可以留空 headers { } [model] # 统一走 TaoToken 通道 base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY default_model claude-sonnet-4-20250514几个关键点解释一下。transport目前主流有三种stdio用于本地进程Client 直接拉起 Server 子进程通过标准输入输出通信sse用于远程长连接Server 先跑起来Client 连它的/sse端点还有更新的streamable-http适合无状态部署。骨架阶段先用 stdio 和 sse 两种就够覆盖大部分场景。再看settings.json这份是给 Agent 运行时读的管的是模型通道和 Client 的全局行为{ mcp: { configPath: ./config.toml, autoDiscover: true, reconnect: { enabled: true, maxRetries: 3, backoffMs: 1000 } }, model: { provider: openai-compatible, baseURL: https://taotoken.net/api, apiKeyEnv: TAOTOKEN_API_KEY, defaultModel: claude-sonnet-4-20250514, maxSteps: 5 }, logging: { level: debug, mcpProtocol: true } }autoDiscover: true是 MCP 的精髓所在——Client 连上 Server 后会自动拉取对方暴露的 Tools、Resources、Prompts不需要你手写注册。mcpProtocol: true打开协议层日志调试阶段非常有用能看到每一次tools/list、tools/call的原始报文。baseURL和apiKeyEnv指向 TaoToken这样模型调用和 MCP 工具调用共用一套凭证不用在多个地方维护 Key。把这两份文件放在项目根目录环境变量里导出你的 Keyexport TAOTOKEN_API_KEY你的Key4. 启动 MCP Server 并跑通一次请求-响应配置就位后先单独把 Server 跑起来确认它能独立工作再让 Client 去连。这一步是排障的关键分界线Server 自己能跑问题就在 ClientServer 自己都起不来就别往下查了。先启动远程 SSE Server。假设你用的是官方示例或自己写的一个简单 Server启动命令类似npx -y modelcontextprotocol/server-everything --transport sse --port 3001看到日志里输出SSE endpoint: http://localhost:3001/sse就说明 Server 起来了。这个server-everything是官方提供的测试用 Server会暴露一批示例工具非常适合验证链路。接着写一个最小的 Client 脚本把「连接 → 发现工具 → 调用工具 → 拿到结果」这条链路走完。下面这段是可直接运行的 Node 脚本// mcp-smoke-test.mjs import { Client } from modelcontextprotocol/sdk/client/index.js; import { SSEClientTransport } from modelcontextprotocol/sdk/client/sse.js; const client new Client({ name: agent-mcp-client, version: 0.1.0 }); const transport new SSEClientTransport(new URL(http://localhost:3001/sse)); await client.connect(transport); console.log([1] 已连接 MCP Server); const { tools } await client.listTools(); console.log([2] 发现工具:, tools.map(t t.name).join(, )); const target tools[0]; const result await client.callTool({ name: target.name, arguments: {} }); console.log([3] 调用结果:, JSON.stringify(result.content, null, 2)); await client.close(); console.log([4] 连接已关闭);运行node mcp-smoke-test.mjs你应该能看到四行输出连接成功、工具列表、调用结果、连接关闭。这就是一次完整的 MCP 请求-响应。到这里Client-Server 骨架就算跑通了。接下来把模型接进来让模型来决定调哪个工具。这一步走 TaoToken 通道用 OpenAI 兼容格式发请求把 MCP 发现的工具转成模型能理解的 schema// agent-with-mcp.mjs import { Client } from modelcontextprotocol/sdk/client/index.js; import { SSEClientTransport } from modelcontextprotocol/sdk/client/sse.js; const client new Client({ name: agent-mcp-client, version: 0.1.0 }); await client.connect(new SSEClientTransport(new URL(http://localhost:3001/sse))); const { tools } await client.listTools(); // 把 MCP 工具转成 OpenAI 兼容的 tools 格式 const modelTools tools.map(t ({ type: function, function: { name: t.name, description: t.description, parameters: t.inputSchema } })); const resp await fetch(https://taotoken.net/api/v1/chat/completions, { method: POST, headers: { Authorization: Bearer ${process.env.TAOTOKEN_API_KEY}, Content-Type: application/json }, body: JSON.stringify({ model: claude-sonnet-4-20250514, messages: [{ role: user, content: 帮我看看有哪些工具可用然后随便调一个 }], tools: modelTools, max_tokens: 512 }) }); const data await resp.json(); const call data.choices[0].message.tool_calls?.[0]; console.log(模型决定调用:, call?.function.name); if (call) { const result await client.callTool({ name: call.function.name, arguments: JSON.parse(call.function.arguments || {}) }); console.log(MCP 执行结果:, JSON.stringify(result.content)); } await client.close();跑通后你会看到「模型决定调用 xxx」和「MCP 执行结果 xxx」两行。到这一步模型决策、MCP 协议执行、TaoToken 通道三者就串起来了。整个链路里模型侧只认https://taotoken.net/api一个地址MCP 侧只认config.toml里的 Server 注册表两边解耦后面加 Server 不用动模型代码。5. 本篇常见错排查链路跑不通时按下面几个高频问题对号入座基本能覆盖八成情况。连接被拒ECONNREFUSED 127.0.0.1:3001。说明 SSE Server 没起来或者端口不对。先单独跑 Server 启动命令确认日志里有SSE endpoint输出。如果 Server 起来了还连不上检查config.toml里的url和实际端口是否一致别一个写 3001 一个写 3000。工具列表为空。Client 连上了但listTools()返回空数组通常是 Server 没正确注册工具或者autoDiscover被关掉了。打开settings.json里的mcpProtocol: true看日志里tools/list的响应体如果 Server 返回的就是空那问题在 Server 侧如果 Server 返回了工具但 Client 没拿到检查 SDK 版本是否匹配。模型不调用工具直接闲聊。这多半是工具 schema 转换出了问题。MCP 返回的是 JSON SchemaOpenAI 兼容格式要求parameters是合法的 JSON Schema 对象。如果某个工具的inputSchema里带了$schema字段或嵌套$ref部分模型会解析失败。排查方法把modelTools打印出来逐个检查parameters结构必要时做一层清洗去掉$schema、把$ref展开。调用工具时报参数校验失败。模型生成的arguments和 Server 期望的 schema 对不上。常见原因是 schema 里字段是必填但模型没给或者类型不匹配比如期望 number 给了 string。调试时把call.function.arguments原样打印出来和t.inputSchema对比。生产环境建议在 Client 侧加一层参数校验和默认值填充别完全信任模型输出。每次请求都新建连接延迟高。这是骨架阶段最容易埋的坑。上面示例里每次脚本运行都new Client()再connect()握手开销不小。生产环境要把 Client 做成单例进程启动时连一次后续复用长连接。SSE 和 streamable-http 都支持长连接复用stdio 则要保持子进程常驻。如果你发现工具调用有明显卡顿先查是不是在请求处理函数里反复建连。TaoToken 通道返回 401。检查TAOTOKEN_API_KEY环境变量是否导出成功echo $TAOTOKEN_API_KEY看有没有值。另外确认请求头是Authorization: Bearer xxx格式base_url 用的是https://taotoken.net/api而不是带路径的地址。如果 Key 刚创建稍等几秒再试有时候有短暂的生效延迟。6. 下一步把骨架变成可扩展的 Agent骨架跑通之后你会发现 MCP 真正的价值不在「能调工具」而在「加工具不用改 Agent 代码」。今天你注册一个 filesystem Server明天加一个数据库 ServerAgent 侧只是config.toml多一段配置模型侧完全无感。这种解耦是传统 Tool Calling 给不了的。接下来可以往两个方向走。一是把 Client 做成常驻服务配合连接池管理多个 Server 的长连接这是工程化的必经之路。二是把 MCP 工具和本地工具混合调度让模型在同一个对话里既能调本地函数又能调远程 MCP 工具这需要你在工具注册层做一次合并。如果你打算长期跑这类 Agent建议把模型通道固定到 TaoToken 的 Coding Plan省得每次调模型都操心 Key 和额度https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite接入文档里有更完整的 SDK 用法和协议细节遇到协议层问题时可以对照查https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite我自己的习惯是每加一个新 Server先单独用 smoke test 脚本验证它能连、能列工具、能调用确认没问题再注册进config.toml。这样出问题时排查范围永远只有一小块不会在整条链路上瞎找。骨架阶段多花十分钟做隔离验证后面能省下几小时的抓瞎时间。
返回列表