借助智能体编写高效的智能体工具:用 Claude Code 与 MCP 打造可复用工具链的 TaoToken 实践
1. 当 Claude Code 开始给自己造工具一个真实项目的起点你可能已经用 Claude Code 写过业务代码但有没有想过让它给自己造工具我最近在做一个内部数据查询系统时遇到了一个典型场景团队需要频繁查询订单状态、用户信息和日志摘要每次都要手动写 SQL 或者翻日志文件。最初的做法是给 Claude Code 一个长长的系统提示把所有查询逻辑都塞进去结果上下文很快就被撑爆而且每次调用都要重新解释一遍表结构。后来我换了个思路让 Claude Code 自己生成 MCP 工具把这些查询逻辑封装成独立的工具函数注册到 MCP 服务器上然后 Claude Code 通过工具调用来完成任务。这样上下文里只需要保留工具的描述和参数定义具体的查询逻辑都在工具内部执行Token 消耗直接降了三分之二。这个思路的核心就是「用智能体写智能体工具」——Claude Code 作为编码智能体MCP 作为工具协议两者结合形成一个可复用的工具链。你不需要手动写每一个工具的实现而是让 Claude Code 根据你的需求生成工具代码、调试、注册最后沉淀成一套可以反复使用的工具集。这篇文章会带你走完整个流程从环境准备到工具生成从 MCP 配置到端到端验证。每一步都有可复制的配置片段和命令你可以直接跟着操作。适合已经用过 Claude Code 或者 Cline 这类编码智能体、想进一步把工具链落到真实项目的开发者。如果你还没接触过 MCP也不用担心我会从最基础的概念讲起。2. 前置准备TaoToken 接入与 Claude Code 环境配置在开始写工具之前你需要先确保 Claude Code 能正常调用模型。这里我用 TaoToken 作为 API 接入层它兼容 Anthropic 的接口格式配置起来比较直接。如果你已经有其他接入方式也可以跳过这部分只要保证 Claude Code 能正常发请求就行。首先去 TaoToken 官网注册账号然后在控制台创建一个 API Key。地址是 https://taotoken.net/api-keys创建的时候注意选择对应的权限范围一般选默认的读写权限就够了。创建完成后把 Key 复制下来后面配置会用到。接下来配置 Claude Code 的环境变量。Claude Code 默认会读取ANTHROPIC_API_KEY和ANTHROPIC_BASE_URL这两个变量。你可以在终端里直接 export也可以写进 shell 配置文件里持久化。我习惯用.env文件管理这样切换环境方便。# 在项目根目录创建 .env 文件 cat .env EOF ANTHROPIC_API_KEYsk-your-taotoken-key-here ANTHROPIC_BASE_URLhttps://taotoken.net/api EOF # 加载环境变量 source .env如果你用的是 Claude Code 的 CLI 版本还需要确认一下版本号。我实测下来 0.8.x 以上的版本对 MCP 的支持比较完整。可以用claude --version查看如果版本太低就升级一下。# 查看当前版本 claude --version # 如果低于 0.8.0用 npm 升级 npm install -g anthropic-ai/claude-codelatest配置完成后先跑一个简单的请求验证一下连通性。不需要写代码直接在终端里用 curl 测试curl -X POST https://taotoken.net/api/v1/messages \ -H x-api-key: $ANTHROPIC_API_KEY \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-sonnet-4-20250514, max_tokens: 100, messages: [{role: user, content: 回复 OK 两个字母}] }如果返回的 JSON 里有content字段且包含 OK说明接入正常。这一步很重要因为后面 MCP 工具调用会频繁发请求如果基础接入有问题排查起来会很麻烦。关于模型选择Claude Code 默认用的是 Sonnet 系列你也可以在配置里指定其他模型。TaoToken 支持多个模型 ID具体可以在模型对话页面查看https://taotoken.net/models。我一般用claude-sonnet-4-20250514做工具生成速度快且代码质量稳定。还有一个容易忽略的点Claude Code 需要访问文件系统来读写工具代码。确保你的工作目录有足够的权限并且不要在只读文件系统里运行。如果你在容器里开发记得把工作目录挂载进去。3. 可复制的 MCP 工具配置从零生成一个订单查询工具现在进入核心部分让 Claude Code 生成一个 MCP 工具。我会用一个订单查询的场景来演示工具的功能是「根据订单号查询订单状态和金额」。这个工具会封装数据库查询逻辑Claude Code 只需要传入订单号就能拿到结果。首先创建项目结构。我习惯把 MCP 服务器和工具代码分开存放这样后续扩展方便mkdir -p mcp-order-tools/src/tools cd mcp-order-tools npm init -y npm install modelcontextprotocol/sdk zod npm install -D typescript types/node tsx然后创建 TypeScript 配置{ compilerOptions: { target: ES2022, module: Node16, moduleResolution: Node16, outDir: ./dist, rootDir: ./src, strict: true, esModuleInterop: true, skipLibCheck: true }, include: [src/**/*] }接下来是关键步骤让 Claude Code 生成工具代码。你可以直接在 Claude Code 的对话里描述需求比如帮我写一个 MCP 工具名字叫 query_order接收一个 order_id 参数返回订单的状态、金额和创建时间。用 zod 做参数校验返回格式用 JSON。Claude Code 会生成类似下面的代码。我把它保存到src/tools/query-order.tsimport { z } from zod; import { McpServer } from modelcontextprotocol/sdk/server/mcp.js; // 模拟数据库查询实际项目中替换为真实查询 async function fetchOrderFromDB(orderId: string) { // 这里用模拟数据演示真实场景接数据库 const mockOrders: Recordstring, any { ORD-2024-001: { order_id: ORD-2024-001, status: shipped, amount: 299.00, currency: CNY, created_at: 2024-11-01T10:23:00Z, }, ORD-2024-002: { order_id: ORD-2024-002, status: pending, amount: 158.50, currency: CNY, created_at: 2024-11-03T14:05:00Z, }, }; return mockOrders[orderId] || null; } export function registerQueryOrderTool(server: McpServer) { server.tool( query_order, 根据订单号查询订单的详细状态包括状态、金额和创建时间, { order_id: z.string().describe(订单号格式如 ORD-2024-001), }, async ({ order_id }) { const order await fetchOrderFromDB(order_id); if (!order) { return { content: [ { type: text, text: JSON.stringify({ error: ORDER_NOT_FOUND, message: 未找到订单 ${order_id}请检查订单号是否正确, }), }, ], }; } return { content: [ { type: text, text: JSON.stringify(order), }, ], }; } ); }这段代码有几个设计点值得注意。第一工具描述写得很具体明确说了「根据订单号查询订单的详细状态」这样 Claude Code 在决定是否调用这个工具时能准确判断。第二参数用 zod 做了类型校验并且describe里给了格式示例减少传错参数的概率。第三错误返回不是简单的「not found」而是带了错误码和可操作的提示信息这样 Claude Code 看到后能自己调整策略。然后创建 MCP 服务器的入口文件src/server.tsimport { McpServer } from modelcontextprotocol/sdk/server/mcp.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { registerQueryOrderTool } from ./tools/query-order.js; const server new McpServer({ name: order-tools, version: 1.0.0, }); // 注册所有工具 registerQueryOrderTool(server); // 启动服务器 const transport new StdioServerTransport(); await server.connect(transport); console.error(Order MCP server running on stdio);注意这里用的是console.error而不是console.log因为 stdio 传输模式下 stdout 会被 MCP 协议占用日志必须走 stderr否则会干扰协议通信。这个坑我踩过当时调试了半天才发现是日志输出位置的问题。接下来配置 Claude Code 连接这个 MCP 服务器。在项目根目录创建.mcp.json{ mcpServers: { order-tools: { command: npx, args: [tsx, src/server.ts], env: { NODE_ENV: development } } } }如果你想让 Claude Code 全局都能用这个工具可以把配置写到~/.claude/mcp.json里。项目级的配置只在当前目录生效适合团队协作时共享。配置完成后用 Claude Code 的 CLI 命令验证一下 MCP 服务器是否能正常启动claude mcp list如果看到order-tools在列表里且状态是 connected说明配置成功。如果显示 failed可以用claude mcp logs order-tools查看具体错误。4. 端到端验证生成工具→注册→调用→回读结果配置写好了但工具到底能不能用这一节我们走一遍完整的验证流程确保每个环节都通。第一步确认 MCP 服务器能独立启动。在终端里直接运行npx tsx src/server.ts如果看到 stderr 输出Order MCP server running on stdio说明服务器启动正常。按 CtrlC 退出然后进入下一步。第二步在 Claude Code 里发起一个需要调用工具的请求。打开 Claude Code 的交互界面输入帮我查一下订单 ORD-2024-001 的状态Claude Code 应该会自动识别出需要调用query_order工具并传入order_id: ORD-2024-001。你会在界面上看到工具调用的过程包括请求参数和返回结果。如果一切正常返回结果应该是{ order_id: ORD-2024-001, status: shipped, amount: 299.00, currency: CNY, created_at: 2024-11-01T10:23:00Z }第三步测试错误场景。输入一个不存在的订单号查一下订单 ORD-9999-999Claude Code 调用工具后会收到错误响应然后它应该能根据错误信息告诉你「未找到该订单请检查订单号」。这说明工具的错误处理逻辑生效了而且 Claude Code 能正确解读错误响应。第四步回读结果并验证。让 Claude Code 把查询结果整理成表格把刚才两个订单的查询结果整理成表格包含订单号、状态、金额三列Claude Code 会基于之前的工具调用结果生成表格。这一步验证的是「工具返回的上下文是否足够支撑后续推理」。如果工具返回的信息太简略Claude Code 就没法完成这个任务如果返回了太多无关字段又会浪费上下文。整个流程跑通后你可以把工具代码提交到 Git 仓库团队成员拉取后只需要配置自己的 API Key 就能使用同一套工具。这就是「可复用工具链」的价值——工具逻辑只写一次所有人共享。如果你想让工具更健壮可以加一个简单的评估脚本。比如写一个eval.ts批量测试多个订单号的查询结果是否符合预期import { fetchOrderFromDB } from ./tools/query-order.js; const testCases [ { input: ORD-2024-001, expectedStatus: shipped }, { input: ORD-2024-002, expectedStatus: pending }, { input: ORD-9999-999, expectedStatus: null }, ]; async function runEval() { let passed 0; for (const tc of testCases) { const result await fetchOrderFromDB(tc.input); const actualStatus result ? result.status : null; if (actualStatus tc.expectedStatus) { passed; console.log(PASS: ${tc.input}); } else { console.log(FAIL: ${tc.input}, expected ${tc.expectedStatus}, got ${actualStatus}); } } console.log(\n${passed}/${testCases.length} passed); } runEval();这个评估脚本虽然简单但能帮你在修改工具逻辑后快速回归测试。Claude Code 也可以帮你扩展这个脚本比如加入更多边界用例、自动生成测试数据等。5. 常见报错排查401、local proxy failed 与 reading choices即使配置看起来没问题实际运行时还是可能遇到各种报错。这一节整理几个我实际遇到过的错误和排查方法。401 Unauthorized这是最常见的错误通常是 API Key 配置有问题。先检查.env文件里的ANTHROPIC_API_KEY是否以sk-开头有没有多余的空格或换行。然后确认ANTHROPIC_BASE_URL设置的是https://taotoken.net/api不要在后面加/v1或者斜杠。如果 Key 确认没问题用 curl 单独测试一下curl -X POST https://taotoken.net/api/v1/messages \ -H x-api-key: $ANTHROPIC_API_KEY \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d {model:claude-sonnet-4-20250514,max_tokens:10,messages:[{role:user,content:hi}]}如果 curl 也返回 401说明 Key 本身有问题去 TaoToken 控制台重新生成一个。如果 curl 正常但 Claude Code 报 401检查 Claude Code 是否读取了正确的环境变量——有时候 shell 里 export 了但 Claude Code 进程没继承到。local proxy failed / connection refused这个错误通常出现在 MCP 服务器启动失败时。Claude Code 尝试连接本地 MCP 进程但连不上。排查步骤先手动运行 MCP 服务器命令看是否有报错npx tsx src/server.ts如果提示模块找不到检查package.json里的依赖是否安装完整运行npm install重新安装。如果是 TypeScript 编译错误用npx tsc --noEmit单独检查。另一个常见原因是路径问题。.mcp.json里的args路径是相对于项目根目录的如果你在子目录里启动 Claude Code路径就会不对。建议用绝对路径或者确认工作目录正确。reading choices 相关错误这个错误一般出现在模型返回格式不符合预期时。比如工具返回的内容不是合法的 JSON或者返回了空内容。检查工具代码里的返回逻辑确保content数组里至少有一个type: text的元素且text字段是字符串。如果你在工具里做了异步操作但忘记 await也可能导致返回 undefined。用 TypeScript 的严格模式能提前发现这类问题。OAuth 相关报错如果你看到 OAuth 相关的错误通常是因为 Claude Code 尝试用 OAuth 流程认证但配置不支持。在.env里显式设置ANTHROPIC_API_KEY后Claude Code 会优先用 Key 认证不会再走 OAuth。如果还是报错检查是否有其他环境变量干扰比如CLAUDE_CODE_USE_OAUTH之类的设置。工具调用成功但结果不对这种情况一般是工具逻辑本身的问题。建议在工具函数里加日志输出实际查询的参数和返回的数据。因为 stdio 模式下 stdout 被占用日志要写到 stderr 或者文件里import { appendFileSync } from fs; function logDebug(msg: string) { appendFileSync(/tmp/mcp-debug.log, ${new Date().toISOString()} ${msg}\n); }然后在工具函数的关键节点调用logDebug运行后查看/tmp/mcp-debug.log就能定位问题。排查完这些常见错误后你的工具链应该能稳定运行了。如果遇到其他报错可以去 TaoToken 的接入文档页面看看有没有相关说明https://taotoken.net/doc。6. 把工具链沉淀下来从单次使用到长期复用工具跑通之后下一步是让它真正成为团队的基础设施。我自己的做法是建一个独立的 Git 仓库专门存放 MCP 工具每个工具一个目录配好 README 和评估脚本。新项目需要什么工具直接从仓库里引入对应的 MCP 服务器配置就行。具体来说我会在仓库根目录放一个tools-registry.json记录所有可用工具的元信息{ tools: [ { name: order-tools, description: 订单查询相关工具, path: ./mcp-order-tools, command: npx tsx src/server.ts, tools: [query_order] }, { name: log-tools, description: 日志检索工具, path: ./mcp-log-tools, command: npx tsx src/server.ts, tools: [search_logs, get_log_context] } ] }然后在项目里写一个脚本根据这个 registry 自动生成.mcp.json。这样新增工具时只需要更新 registry不用手动改每个项目的配置。对于长期编码和 Agent 场景可以考虑用 Coding Plan 来管理工具链的调用配额和权限。地址是 https://taotoken.net/coding-plan适合需要频繁调用工具、对稳定性要求高的团队。另外Claude Code 本身也支持通过claude mcp add命令动态添加 MCP 服务器。如果你不想手动编辑 JSON 文件可以用命令行claude mcp add order-tools npx tsx /path/to/mcp-order-tools/src/server.ts这个命令会把配置写到全局的~/.claude/mcp.json里所有项目都能用。删除的话用claude mcp remove order-tools。最后分享一个实用技巧把常用的工具调用组合成「工作流」。比如「查订单→查日志→生成报告」这个流程可以在 Claude Code 里用一条指令触发它会自动按顺序调用多个工具。你只需要在系统提示里描述清楚工作流的步骤Claude Code 就能自己编排工具调用顺序。这样即使工具数量增长到几十个你也不用记住每个工具的用法只需要描述目标让 Claude Code 自己决定调哪些工具。工具链的价值不在于工具本身有多复杂而在于它能不能让智能体更高效地完成任务。我试过把同一个查询逻辑分别做成「一个大工具」和「三个小工具」结果发现小工具的组合方式更灵活Claude Code 在不同场景下能选择不同的调用路径整体成功率反而更高。所以设计工具时不用追求「一步到位」先做最小可用的版本然后在实际使用中根据评估结果迭代这样沉淀下来的工具链才真正好用。