
1. SpringAI 接入 MCP 的真实痛点工具链散落各处如果你正在做 SpringAI 项目大概率会遇到这样一个场景模型本身跑得挺顺但一旦要让它调用外部工具比如查数据库、读文件、调第三方接口整个链路就开始散架。每个工具一套鉴权、一套地址、一套超时配置代码里到处是硬编码的 API Key换个环境就得改一遍。MCPModel Context Protocol本来是为了解决这个问题的。它把工具能力抽象成标准协议SpringAI 通过spring-ai-starter-mcp-client-webflux这个 starter 就能把远端 MCP Server 的工具自动装配成ToolCallbackProvider业务层直接注入使用。听起来很美好但落地时你会发现两个卡点第一MCP Client 的配置项和模型侧的配置项是分开的Key 管理容易乱第二工具调用链一旦出问题排查起来没有统一的入口。我这次的做法是用 TaoToken 作为统一的 Key 和 API 通道把模型调用和 MCP 工具调用都收敛到同一个出口。TaoToken 是一个面向开发者的 API 聚合通道提供兼容 OpenAI 风格的接口同时支持模型对话、Coding Plan 等能力。它的价值在于你不需要在 SpringAI 里维护多套 Key模型侧和工具侧可以共用一套鉴权体系配置集中、排查集中。这篇文章面向的是已经有一定 SpringAI 基础、正在尝试接入 MCP 的 Java 开发者。我会从 Maven 依赖开始一步步给出可复制的配置片段然后跑通一次完整的工具调用验证。目标很明确让你从零配置到看到工具被模型成功调用形成一个最小闭环。先说清楚整体架构。你的 Spring Boot 应用作为 MCP Client通过 SSE 或 HTTP 连接外部的 MCP Server。SpringAI 的 MCP Client Starter 会自动读取配置、建立连接、生成ToolCallbackProvider。业务层的ChatService注入这个 Provider把工具挂到ReactAgent上。当模型决定调用某个工具时ToolCallback被触发SpringAI 通过 MCP Client 转发请求到远端 Server拿到结果后再回传给模型继续生成。这个链路里模型侧的ChatModel需要 API KeyMCP Client 侧如果需要鉴权也需要 Key。如果两边分别配置环境变量会越来越多。用 TaoToken 统一之后你只需要维护一个 Key模型和工具走同一个通道配置文件和代码都干净很多。接下来我会先讲 TaoToken 的前置准备然后给出完整的 Maven 和 YAML 配置再写业务层的注入和调用代码最后跑一次验证并列出常见报错。每一步都有可复制的片段你可以直接跟着做。2. TaoToken 前置准备统一 Key 与 API 通道在开始写 SpringAI 代码之前先把 TaoToken 这边的准备工作做完。这一步不复杂但顺序不能乱否则后面配置会找不到对应的值。首先你需要一个 TaoToken 账号。访问官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 完成注册然后进入控制台。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 登录后你能看到 API Key 管理页面。在 API Keys 页面创建一个新的 Key。建议按项目命名比如springai-mcp-demo这样后面如果有多个项目排查时能快速定位。创建完成后把 Key 复制出来注意这个 Key 只显示一次丢了就得重新生成。TaoToken 的 API 基础地址是 https://taotoken.net/api 这个地址在 SpringAI 配置里会用到。它兼容 OpenAI 风格的接口路径所以 SpringAI 的 OpenAI starter 可以直接对接。如果你用的是 DashScope 或者其他模型也可以通过 TaoToken 的通道转发具体取决于你选的模型。这里要说明一下 Key 的使用方式。TaoToken 的 Key 是统一鉴权凭证模型对话和工具调用都走这个 Key。在 SpringAI 里你不需要为 MCP Client 单独配一套鉴权只要模型侧的ChatModel配置好了工具调用链会复用同一个通道。这也是我用 TaoToken 的核心原因减少配置项降低出错概率。如果你打算长期做编码类任务或者 Agent 开发可以了解一下 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。它针对的是持续性的编码场景和单次模型调用是互补的。不过这篇文章的重点是 MCP 工具调用Coding Plan 不是必须的你可以先跑通基础链路再考虑。模型对话的调试入口在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 你可以在那里先验证 Key 是否可用确认模型能正常返回再去配 SpringAI。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有接口路径和参数说明配置时对照着看。准备工作做完后你手里应该有三样东西一个可用的 API Key、基础地址https://taotoken.net/api、以及确认可用的模型名称。接下来进入 SpringAI 项目配置。3. 可复制配置Maven 依赖与 settings 片段这一节是核心我会给出完整的 Maven 依赖和配置文件片段。你可以直接复制到项目里只需要替换 Key 和模型名称。先看 Maven 的pom.xml。SpringAI 的版本管理用 BOM 方式引入MCP Client 用 webflux 版本的 starter。以下是关键片段properties maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding spring-ai.version1.1.0/spring-ai.version spring-ai-alibaba.version1.1.0.0-RC2/spring-ai-alibaba.version spring-ai-alibaba-extensions.version1.1.0.0-RC2/spring-ai-alibaba-extensions.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-extensions-bom/artifactId version${spring-ai-alibaba-extensions.version}/version typepom/type scopeimport/scope /dependency dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-bom/artifactId version${spring-ai-alibaba.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-mcp-client-webflux/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId /dependency /dependencies这里我用了spring-ai-openai-spring-boot-starter来对接 TaoToken 的 OpenAI 兼容接口。如果你用的是 DashScope可以换成对应的 starter但配置项会不同。为了统一 Key我建议用 OpenAI 兼容方式因为 TaoToken 的接口路径就是按这个风格设计的。接下来是application.yml的配置。这是整个链路的关键模型侧和 MCP 侧都在这里声明spring: ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api chat: options: model: gpt-4o-mini temperature: 0.7 timeout: 180000 retry: max-attempts: 3 backoff: initial-interval: 2000 multiplier: 2 max-interval: 10000 mcp: client: enabled: true name: springai-mcp-client version: 1.0.0 type: SYNC request-timeout: 30s sse: connections: server1: url: http://localhost:8081 sse-endpoint: /sse注意几个点。api-key用环境变量TAOTOKEN_API_KEY注入不要硬编码在文件里。base-url指向https://taotoken.net/api这是 TaoToken 的统一入口。model填你在 TaoToken 控制台确认可用的模型名称比如gpt-4o-mini或者你选的其他模型。MCP 部分的sse.connections.server1.url是你远端 MCP Server 的地址。我这里写的是本地localhost:8081实际使用时替换成你的 Server 地址。sse-endpoint是 SSE 的连接路径通常是/sse具体看你的 MCP Server 实现。如果你需要更细粒度的控制比如多个 MCP Server可以在connections下面加server2、server3每个都有自己的 URL 和 endpoint。SpringAI 会自动为每个连接生成对应的ToolCallback。还有一个可选配置是spring.ai.mcp.client.toolcallback.enabled默认是true。如果你只想手动管理工具可以设为false但大多数场景保持默认即可。配置写完后启动应用Spring Boot 会自动装配 MCP Client读取spring.ai.mcp.client.*的配置建立 SSE 连接然后生成ToolCallbackProvider。你可以在启动日志里看到类似MCP Client initialized的输出说明连接建立成功。这里要提醒一点MCP Server 必须先启动否则 SpringAI 启动时会因为连不上而报错。如果你暂时没有 MCP Server可以先注释掉mcp.client部分等 Server 就绪后再打开。4. 验证请求从 ToolCallbackProvider 到一次工具调用配置完成后下一步是写业务代码把ToolCallbackProvider注入进来挂到ReactAgent上然后发一次请求验证工具是否被调用。先看ToolCallbackProvider的源码定义理解它是什么package org.springframework.ai.tool; import java.util.List; public interface ToolCallbackProvider { ToolCallback[] getToolCallbacks(); static ToolCallbackProvider from(List? extends ToolCallback toolCallbacks) { return new StaticToolCallbackProvider(toolCallbacks); } static ToolCallbackProvider from(ToolCallback... toolCallbacks) { return new StaticToolCallbackProvider(toolCallbacks); } }这个接口很简单核心方法就是getToolCallbacks()返回一个ToolCallback数组。SpringAI 的 MCP Client Starter 会自动生成一个实现类把远端 MCP Server 提供的工具都包装成ToolCallback。在ChatService里注入Service public class ChatService { private static final Logger logger LoggerFactory.getLogger(ChatService.class); Autowired private ToolCallbackProvider tools; Autowired private ChatModel chatModel; public String chat(String message) { ToolCallback[] toolCallbacks tools.getToolCallbacks(); logger.info(可用工具列表:); for (ToolCallback toolCallback : toolCallbacks) { logger.info( {}, toolCallback.getToolDefinition().name()); } ReactAgent agent ReactAgent.builder() .chatModel(chatModel) .tools(toolCallbacks) .build(); return agent.call(message); } }如果你还有本地用Tool注解定义的工具可以用methodTools(...)单独挂和 MCP 的.tools(...)分开ReactAgent agent ReactAgent.builder() .chatModel(chatModel) .methodTools(localTools) .tools(mcpToolCallbacks) .build();ChatController里暴露一个接口RestController RequestMapping(/api) public class ChatController { Autowired private ChatService chatService; PostMapping(/chat) public String chat(RequestBody String message) { return chatService.chat(message); } }启动应用后先看日志里有没有打印出可用工具列表。如果有说明 MCP Client 连接成功工具已经注册进来了。然后发一个请求curl -X POST http://localhost:8080/api/chat \ -H Content-Type: text/plain \ -d 帮我查一下当前目录下有哪些文件如果模型决定调用工具你会在日志里看到ToolCallback被触发的记录然后 MCP Client 通过 SSE 把请求转发到远端 Server拿到结果后回传给模型模型继续生成最终答案。整个调用时序是这样的前端请求到ChatControllerChatService创建ReactAgent挂上 MCP 工具调用agent.call()。模型判断需要调用工具触发ToolCallbackSpringAI MCP Client 通过 SSE 调外部 MCP Server工具结果返回给模型模型生成最终答案。验证成功的标志有三个日志里打印出工具列表、请求返回了包含工具调用结果的答案、MCP Server 侧能看到请求记录。如果只满足第一个说明连接成功但模型没触发工具可能是提示词不够明确或者工具描述和用户意图不匹配。5. 常见报错排查401、local proxy failed、reading choices这一节列出我在接入过程中实际遇到的报错以及对应的排查思路。你如果卡在某一步可以先对照这里。401 Unauthorized这是最常见的鉴权问题。报错信息通常是401 Unauthorized或者Invalid API key。原因一般是TAOTOKEN_API_KEY环境变量没设置或者 Key 复制时多了空格。排查步骤先在终端echo $TAOTOKEN_API_KEY确认变量有值然后检查application.yml里api-key的引用是否正确。如果用的是 IDE 启动注意 IDE 的环境变量配置和终端可能不一致。还有一种情况是base-url写错了。TaoToken 的基础地址是https://taotoken.net/api不要漏掉/api路径也不要多加斜杠。如果写成https://taotoken.net就会 404 或者 401。local proxy failed这个报错通常出现在 MCP Client 连接远端 Server 时。信息类似local proxy failed to connect或者Connection refused。原因是 MCP Server 没启动或者 URL 写错了。排查步骤先用curl http://localhost:8081/sse确认 Server 是否可达。如果 Server 在另一台机器上检查网络和防火墙。另外注意sse-endpoint的路径有些 MCP Server 用的是/mcp/sse而不是/sse具体看 Server 的文档。reading choices 相关报错这个报错一般出现在模型返回解析阶段信息类似Error reading choices或者Cannot deserialize value。原因是模型返回的 JSON 结构和 SpringAI 期望的不一致。排查步骤先确认base-url和model是否匹配。TaoToken 的 OpenAI 兼容接口返回的是标准格式但如果你选的模型名称不对可能返回错误结构。另外检查spring.ai.openai.chat.options.model是否填了正确的模型 ID不要填显示名称。OAuth 相关报错如果你在 MCP Client 配置里启用了 OAuth但 Server 侧没配好会报OAuth authentication failed。排查步骤先确认 Server 是否需要 OAuth如果不需要把spring.ai.mcp.client.oauth相关配置去掉。如果需要检查 client-id、client-secret、token-uri 是否正确。TaoToken 的 Key 是 API Key 方式不涉及 OAuth所以 MCP Client 侧不需要额外配 OAuth只要模型侧鉴权通过即可。工具列表为空日志里没有打印工具列表或者getToolCallbacks()返回空数组。原因是 MCP Client 没连接成功或者 Server 没有注册工具。排查步骤检查spring.ai.mcp.client.enabled是否为truesse.connections下的 URL 是否正确。如果 Server 启动了但工具为空检查 Server 侧的工具注册逻辑。超时问题如果请求长时间不返回可能是request-timeout设置太短或者模型侧timeout不够。MCP 的request-timeout默认是 30 秒模型侧我设的是 180 秒。如果工具执行时间较长适当调大这两个值。排查时建议打开 DEBUG 日志logging: level: org.springframework.ai: DEBUG org.springframework.ai.mcp: DEBUG这样能看到 MCP Client 的连接过程和工具注册细节定位问题会快很多。6. 统一 Key 打通工具链的后续动作跑通最小闭环之后你可以做几件事来巩固这套配置。第一把 Key 管理规范化。不要在每个项目的application.yml里硬编码统一用环境变量或者配置中心。TaoToken 的 Key 可以在多个项目间复用但建议按项目创建独立的 Key方便审计和吊销。第二扩展 MCP Server 的数量。你可以在spring.ai.mcp.client.sse.connections下面加多个 Server每个 Server 提供不同的工具集。SpringAI 会自动合并所有ToolCallback模型可以根据任务选择调用哪个。第三区分本地工具和远端工具。本地用Tool注解定义的工具通过methodTools(...)挂载远端 MCP 工具通过.tools(...)挂载。两者可以共存模型会根据工具描述自动选择。第四如果你要做流式输出把agent.call()换成agent.stream()配合 SSE 返回给前端。MCP 工具调用的逻辑不变只是返回方式不同。第五长期编码类任务可以考虑 Coding Plan它和单次模型调用是互补的。如果你的 Agent 需要持续运行、频繁调用工具Coding Plan 的配额模式可能更合适。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有接口细节和参数说明。API Keys 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 需要新增或吊销 Key 时去那里操作。模型对话调试在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 配好之后可以先在那里验证模型是否正常返回。最后说一个实际经验MCP 工具的描述文本很重要。模型是根据工具名称和描述来决定是否调用的如果描述太模糊模型可能不触发。建议在 MCP Server 侧把工具描述写清楚包括参数含义和适用场景。这样模型调用的准确率会高很多。