ARTICLE DETAIL

资讯详情

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

Agent 开发实战:用 TaoToken 统一 Key 打通 MCP 客户端与服务端配置

Agent 开发实战:用 TaoToken 统一 Key 打通 MCP 客户端与服务端配置 1. 从一次 MCP 联调翻车说起如果你正在用 Spring AI 做 Agent大概率会遇到这样一个场景客户端要接高德地图、文件系统、图片搜索三个 MCP 服务服务端又要声明自己的工具给别的 Agent 用。每个 MCP 服务背后都挂着一个模型调用每个模型调用都要配一份 Key。于是你的settings.json里躺着三份不同的 API Keyconfig.toml里又抄了一遍改一个环境变量要翻五个文件。MCPModel Context Protocol模型上下文协议本身解决的是AI 怎么标准化调用外部工具的问题它把工具、资源、提示词统一成一套 JSON-RPC 规范客户端和服务端只要遵守协议就能互通。但协议标准化了通信没标准化凭证管理。多工具 Key 分散、模型上下文协议接入配置繁琐是 Spring AI 下 MCP 客户端与服务端联调时最容易被低估的坑。这篇就聚焦这个联调场景用 TaoToken 作为统一的 Key 与 API 通道把 MCP 客户端注册和服务端声明串起来。你会拿到可直接复制的settings.json与config.toml骨架走完一次请求验证动作确认链路连通最后附上我踩过的几个典型报错。适合已经写过 Spring AI 基础对话、准备把 MCP 接进生产链路的开发者。TaoToken 在这里的角色是统一入口官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 通道 https://taotoken.net/api 。所有 MCP 客户端和服务端共享同一个 Key模型调用走同一条通道配置从每个服务一份变成全局一份。2. TaoToken 前置把 Key 和通道先备好在动 MCP 配置之前先把统一凭证准备好。这一步不做后面所有配置文件都得反复改 Key。2.1 创建统一 API Key进入控制台创建 Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。建议按项目维度建 Key比如spring-ai-mcp-dev方便后续按环境隔离和用量追踪。创建后立刻复制保存页面刷新后不再完整显示。Key 的形态是一串以sk-开头的字符串。它同时用于两处MCP 客户端调用模型时的鉴权以及 MCP 服务端声明工具后回传模型时的鉴权。这就是统一 Key的核心——客户端和服务端不再各配各的。2.2 确认 API 通道地址TaoToken 的 API 基址是 https://taotoken.net/api 兼容 OpenAI 风格的/v1/chat/completions路径。Spring AI 的 OpenAI starter 可以直接把base-url指向它MCP 客户端在 stdio 模式下通过环境变量把 Key 传给子进程服务端在 SSE 模式下通过请求头携带。这里有个容易混淆的点MCP 协议本身不负责模型调用它只负责工具发现和调用。真正调模型的是 MCP 客户端宿主比如你的 Spring AI 应用。所以统一 Key统一的是宿主调模型这一层MCP 服务端如果自己也要调模型比如做采样 Sampling同样复用这个 Key。2.3 依赖版本对齐Spring AI 的 MCP 支持在 1.0.0-M6 之后趋于稳定建议锁定版本避免包路径漂移。核心依赖两块dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-client-spring-boot-starter/artifactId version1.0.0-M6/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-webmvc-spring-boot-starter/artifactId version1.0.0-M6/version /dependency客户端用 client starter服务端用 server-webmvc starter要 SSE 传输就选它纯 stdio 可以选不带 webmvc 的。两个 starter 共享同一份模型配置这是统一 Key 能生效的前提。3. 可复制配置settings.json 与 config.toml 骨架这一节是全文的操作核心。MCP 客户端注册和服务端声明分别对应两份配置我按客户端宿主配置和MCP 服务端声明拆开讲。3.1 客户端注册settings.json 骨架MCP 客户端注册 MCP 服务用的是 Claude Desktop 风格的mcp-servers.json。Spring AI 通过spring.ai.mcp.client.stdio.servers-configuration指向它。骨架如下{ mcpServers: { image-search: { command: java, args: [ -Dspring.ai.mcp.server.stdiotrue, -Dspring.main.web-application-typenone, -Dlogging.pattern.console, -jar, yu-image-search-mcp-server/target/yu-image-search-mcp-server-0.0.1-SNAPSHOT.jar ], env: { TAOTOKEN_API_KEY: sk-你的统一Key, TAOTOKEN_BASE_URL: https://taotoken.net/api } }, filesystem: { command: npx, args: [ -y, modelcontextprotocol/server-filesystem, /Users/you/workspace ], env: { TAOTOKEN_API_KEY: sk-你的统一Key } } } }关键点env里注入的TAOTOKEN_API_KEY会被 MCP 客户端在建立 stdio 连接时设置到子进程环境变量中。服务端用System.getenv(TAOTOKEN_API_KEY)就能拿到不需要在服务端代码里硬编码。这样多个 MCP 服务共享同一个 Key改 Key 只改这一处。Windows 环境下command要加.cmd后缀比如npx.cmd否则会报找不到命令。这是 stdio 模式跨平台最常见的坑。3.2 服务端声明config.toml 骨架如果你的 MCP 服务端用 SSE 传输或者宿主是支持 TOML 配置的客户端部分 IDE 和 Agent 框架用config.toml管理 MCP 服务骨架如下[mcp] enabled true name yu-image-search-mcp-server version 0.0.1 type SYNC stdio false sse-endpoint /sse sse-message-endpoint /mcp/message [mcp.model] base-url https://taotoken.net/api api-key sk-你的统一Key model claude-3-5-sonnet [mcp.tools.image-search] description search image from web enabled true[mcp.model]这一段就是统一通道的落点服务端如果要做采样Sampling即服务端反向请求模型生成内容直接读这段配置不用再单独维护一份 Key。[mcp.tools.*]声明服务端对外暴露的工具客户端通过tools/list发现它们。3.3 Spring 侧配置对齐客户端宿主的application.yml要指向mcp-servers.json同时把模型通道指向 TaoTokenspring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-3-5-sonnet mcp: client: enabled: true name: my-mcp-client version: 1.0.0 request-timeout: 30s type: SYNC stdio: servers-configuration: classpath:mcp-servers.json服务端如果是 SSE 模式application-sse.yml里把stdio关掉、sse-endpoint打开spring: ai: mcp: server: name: yu-image-search-mcp-server version: 0.0.1 type: SYNC stdio: false sse-endpoint: /sse sse-message-endpoint: /mcp/message server: port: 8127到这里客户端注册mcp-servers.json和服务端声明config.tomlapplication-sse.yml都指向了同一个 Key 和同一条 API 通道。配置层面已经统一。4. 验证请求一次调用确认链路连通配置写完不算完得跑一次真实请求确认客户端能发现工具、服务端能执行工具、模型能拿到结果。4.1 服务端工具声明服务端用Tool注解声明工具方法体里读环境变量拿 KeyService public class ImageSearchTool { Tool(description search image from web) public String searchImage( ToolParam(description Search query keyword) String query) { String apiKey System.getenv(TAOTOKEN_API_KEY); if (apiKey null || apiKey.isBlank()) { return Error: TAOTOKEN_API_KEY not set; } // 调用图片搜索 API此处省略具体 HTTP 细节 return https://example.com/img/ query .jpg; } }注意不要用System.out.println输出调试信息。stdio 模式下标准输出流被 JSON-RPC 通信占用自己打印会干扰协议解析导致客户端报invalid JSON。4.2 客户端绑定工具客户端通过ToolCallbackProvider拿到 MCP 服务暴露的所有工具绑给ChatClientResource private ToolCallbackProvider toolCallbackProvider; public String chatWithMcp(String message, String chatId) { ChatResponse response chatClient .prompt() .user(message) .advisors(spec - spec .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId) .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)) .tools(toolCallbackProvider) .call() .chatResponse(); return response.getResult().getOutput().getText(); }4.3 发起验证请求写个单元测试跑一次Test void doChatWithMcp() { String chatId UUID.randomUUID().toString(); String message 帮我搜索一些适合程序员的桌面壁纸图片; String answer loveApp.chatWithMcp(message, chatId); Assertions.assertNotNull(answer); System.out.println(answer); }预期结果分三步第一启动日志里能看到 MCP 客户端加载了image-search服务提供的工具functionCallbacks列表非空第二模型判断需要调用searchImage客户端把调用请求通过 stdio 发给服务端子进程第三服务端执行后返回图片地址模型总结成自然语言回复。如果三步都走通说明统一 Key 在客户端注册和服务端声明两侧都生效了。此时你可以去控制台看用量https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 确认这次请求确实走了统一通道。4.4 SSE 模式验证SSE 模式下服务端独立启动客户端配置改成spring: ai: mcp: client: sse: connections: image-search: url: http://localhost:8127启动服务端profiles.activesse再跑同一个测试。SSE 模式的好处是服务端可以独立调试日志和断点都更直观适合中大型项目多客户端共享。5. 本篇常见错排查联调阶段报错集中在几类我按出现频率排。5.1 找不到命令 / command not foundWindows 下 stdio 模式最常见。npx要写成npx.cmdjava一般没问题但要确认在 PATH 里。如果服务端是 jar 包-jar后面的路径要用绝对路径或相对于工作目录的正确相对路径路径里有空格要加引号。5.2 invalid JSON / 协议解析失败服务端代码里有System.out.println或日志框架往标准输出打日志。stdio 模式下标准输出是协议通道任何非 JSON-RPC 内容都会破坏解析。解决办法日志重定向到标准错误或者启动参数加-Dlogging.pattern.console清空控制台日志格式。5.3 401 / 鉴权失败Key 没传到子进程。检查mcp-servers.json的env块里TAOTOKEN_API_KEY是否拼写正确服务端System.getenv的变量名是否一致。SSE 模式下 Key 通过请求头传检查客户端sse.connections配置有没有带上鉴权头。5.4 工具列表为空客户端连上了服务端但tools/list返回空。原因通常是服务端没有注册ToolCallbackProviderBean。检查主类里有没有Bean public ToolCallbackProvider imageSearchTools(ImageSearchTool tool) { return MethodToolCallbackProvider.builder() .toolObjects(tool) .build(); }没有这个 BeanTool注解不会被扫描成 MCP 工具。5.5 请求超时MCP 服务端单次执行时间过长或者客户端request-timeout设太短。默认 30s耗时操作建议服务端改异步客户端适当调大。SSE 模式下还要检查网络连通性和端口占用。5.6 端口冲突SSE 模式服务端默认端口和客户端宿主端口撞了。改server.port同时确认客户端sse.connections.url指向的端口一致。stdio 模式不存在这个问题因为它是子进程通信。6. 后续怎么走按场景分流链路跑通之后下一步取决于你的使用场景。如果你主要在排障和接入阶段反复调 MCP 客户端与服务端的连接建议先把 API Keys 和接入文档过一遍API Keys 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。文档里有 stdio 和 SSE 两种模式的完整参数说明比翻源码快。如果你要验证模型在 MCP 工具调用下的表现比如确认模型能不能正确选择工具、参数填得对不对直接用模型对话页面测https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 。把工具描述贴进去看模型的调用决策比在代码里反复跑单元测试高效。如果你在做长期编码或 Agent 项目MCP 服务会越接越多Key 和通道的统一管理就更重要。Coding Plan 适合这种持续开发场景https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。它把模型调用和 MCP 工具链的凭证收敛到一处减少配置文件漂移。最后说个实际经验MCP 服务端和客户端不要放在同一个 Maven 模块里开发。我试过把图片搜索 MCP 服务端作为子模块塞进主项目结果 stdio 模式下 jar 路径解析和类加载冲突折腾了半天。单独开一个 module、单独打包、客户端通过 jar 路径引用路径清晰调试也方便。SSE 模式虽然可以同项目但独立部署更符合 MCP职责单一的设计初衷。
返回列表