1. 为什么要在 Spring AI 里接 MCPMCP 全称 Model Context Protocol是一套让大模型和外部系统对话的协议标准。你可以把它理解成「AI 世界的 USB-C 接口」模型本身只会生成文本但通过 MCP它能标准化地调用数据库、文件系统、内部 API、第三方服务把「只会聊天」变成「能干活」。Spring AI 是 Spring 生态里的 AI 集成框架对 MCP 有原生支持所以 Java 后端团队想给自己的系统加 AI 能力Spring AI MCP 是一条很顺的路。但真正落地时很多人卡在同一个地方模型通道和工具通道要分别配 Key、分别管额度、分别看日志。一个项目里既有 OpenAI 兼容的对话模型又有 MCP 工具服务配置散落在好几个文件换环境就崩。这篇就聚焦这个场景——用 TaoToken 统一 Key 和 API 通道把 Spring AI 的模型调用和 MCP 工具链收敛到一套配置里给出 application.yml 骨架、MCP 客户端配置、工具注册、调用演示和日志验证目标是一次跑通最小可复制示例。适合谁看写过 Spring Boot、想给现有系统加 AI 工具调用的后端同学正在评估 MCP 落地方式、被多套 Key 管理烦到的团队。下面所有配置我都按「能直接抄」的标准写你替换自己的 Key 就能跑。2. TaoToken 前置准备统一 Key 与通道TaoToken 在这里扮演的角色是「统一入口」模型对话、编码类请求、MCP 工具链背后的模型调用都走同一个 API 通道和同一把 Key。这样 Spring AI 里只需要维护一份凭证不用为每个模型供应商单独配。第一步去官网注册并拿到 API Key。地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后在控制台创建 Key。控制台入口https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。Key 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 建议给项目单独建一把方便按项目看用量和吊销。第二步确认 API 基地址。TaoToken 的 API 端点是 https://taotoken.net/api 注意这个地址不带任何查询参数直接作为 base-url 用。Spring AI 的 OpenAI 兼容客户端、以及 MCP 里需要走模型的地方都指向它。第三步把 Key 放进环境变量别硬编码进代码。Linux/macOS 下export TAOTOKEN_API_KEYsk-你的KeyWindows PowerShell$env:TAOTOKEN_API_KEYsk-你的Key注意Key 只放环境变量或配置中心别提交到 Git。application.yml 里用${TAOTOKEN_API_KEY}占位引用。如果你还想先验证模型通道是否通可以直接用模型对话页面测一下https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 。确认能正常返回再往下配 Spring AI能省掉一半排障时间。3. Spring AI MCP 可复制配置3.1 pom.xml 依赖Spring AI 的版本迭代较快MCP 相关模块在 1.0 之后有独立 starter。下面给一份可用的依赖骨架重点是引入 OpenAI 兼容 starter走 TaoToken 通道和 MCP 客户端 starterproperties spring-ai.version1.0.0/spring-ai.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring AI OpenAI 兼容客户端指向 TaoToken -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency !-- MCP 客户端支持 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-client-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency /dependencies如果 Maven 拉不到检查是否加了 Spring Milestones 仓库Spring AI 的正式版和里程碑版仓库地址不同按你用的版本补上即可。3.2 application.yml 骨架这是整篇的核心。模型通道和 MCP 工具通道都收敛到 TaoToken 的 base-url 和同一把 Keyspring: ai: openai: # TaoToken 统一 API 通道 base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.7 mcp: client: enabled: true name: spring-ai-mcp-client version: 1.0.0 # 请求超时工具调用可能较慢给足时间 request-timeout: 60s type: SYNC # 工具执行结果是否回传给模型继续推理 toolcallback: enabled: true关键点说明base-url指向 TaoTokenapi-key引用环境变量MCP 客户端开启toolcallback这样模型在对话中能自动触发工具调用并把结果带回上下文。request-timeout别设太短工具执行比如查库本身有耗时。3.3 MCP 客户端与工具注册Spring AI 里注册工具最直接的方式是用Tool注解配合ToolCallbackProvider暴露给模型。先写一个查询工具package com.example.mcp.tools; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; Component public class UserQueryTool { private final JdbcTemplate jdbcTemplate; public UserQueryTool(JdbcTemplate jdbcTemplate) { this.jdbcTemplate jdbcTemplate; } Tool(name query_user_info, description 根据用户ID查询用户基本信息) public String queryUserInfo( ToolParam(description 用户ID) Long userId) { String sql SELECT id, name, email FROM users WHERE id ?; ListMapString, Object rows jdbcTemplate.queryForList(sql, userId); if (rows.isEmpty()) { return 未找到用户信息; } MapString, Object user rows.get(0); return String.format(ID%s, 姓名%s, 邮箱%s, user.get(id), user.get(name), user.get(email)); } }然后把工具注册进 ChatClient让模型知道有哪些工具可用package com.example.mcp.config; import com.example.mcp.tools.UserQueryTool; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.method.MethodToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class McpConfig { Bean public ToolCallbackProvider userToolCallbackProvider(UserQueryTool userQueryTool) { return MethodToolCallbackProvider.builder() .toolObjects(userQueryTool) .build(); } Bean public ChatClient chatClient(ChatClient.Builder builder, ToolCallbackProvider toolCallbackProvider) { return builder .defaultToolCallbacks(toolCallbackProvider) .build(); } }MethodToolCallbackProvider会扫描Tool注解方法生成模型可调用的工具描述。defaultToolCallbacks把它挂到 ChatClient 上之后每次对话模型都能自主决定是否调用。3.4 控制器暴露调用入口package com.example.mcp.controller; import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/api/ai) public class AiController { private final ChatClient chatClient; public AiController(ChatClient chatClient) { this.chatClient chatClient; } GetMapping(/ask) public String ask(RequestParam String q) { return chatClient.prompt() .user(q) .call() .content(); } }到这里模型通道TaoToken、MCP 工具注册、调用入口就串起来了。4. 验证请求与成功结果4.1 启动与日志观察启动应用后日志里应该能看到 MCP 客户端初始化和工具注册的信息类似MCP client initialized: spring-ai-mcp-client v1.0.0 Registered tool: query_user_info如果没看到工具注册日志八成是ToolCallbackProvider没被扫描到检查包路径是否在启动类同级或子包下。4.2 发起一次带工具调用的请求假设 users 表里有 id1 的记录请求curl http://localhost:8080/api/ai/ask?q帮我查一下用户1的信息模型收到问题后会判断需要调用query_user_info执行后把结果组织成自然语言返回类似用户1的信息如下ID1姓名张三邮箱zhangsanexample.com4.3 确认工具真的被调用光看返回不够要确认工具执行链路。在UserQueryTool里加一行日志System.out.println([MCP-TOOL] queryUserInfo called, userId userId);再次请求控制台出现[MCP-TOOL]就说明模型确实触发了工具而不是自己编的答案。这一步是验证 MCP 是否真正打通的关键很多人以为返回对了就行其实模型可能在「幻觉」。4.4 用模型对话页交叉验证如果本地日志正常但返回内容奇怪可以去 https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 用同样的 prompt 测一下纯模型表现对比就能判断问题出在模型通道还是工具链路。5. 本篇常见错误排查5.1 401 / 403Key 或 base-url 不对最常见。检查三点base-url是不是https://taotoken.net/api别多加斜杠或路径api-key环境变量有没有真正注入echo $TAOTOKEN_API_KEY验证Key 是否被吊销。Spring AI 的 OpenAI starter 默认会拼/v1/chat/completions如果 base-url 写错层级就会 404 或 401。5.2 工具不触发模型没「看到」工具返回正常但日志没有[MCP-TOOL]说明工具没注册成功。排查顺序Tool注解的包导入对不对是org.springframework.ai.tool.annotation.Tool不是旧版spec.ToolToolCallbackProviderBean 是否被 Spring 管理ChatClient是否真的挂了defaultToolCallbacks。三者缺一模型就不知道有工具。5.3 超时工具执行太久查库或调外部 API 慢时会报超时。把spring.ai.mcp.client.request-timeout调大同时给工具方法本身加超时和降级逻辑。别把慢查询直接暴露给模型模型会一直等。5.4 版本不匹配注解和类找不到Spring AI 各版本 API 变动大Tool、ToolCallbackProvider、MethodToolCallbackProvider在不同版本里包名和签名可能不同。锁定一个版本后对照该版本的官方文档写别混用网上不同时期的示例。踩过的坑就是抄了 0.8 的示例配 1.0 的依赖编译一堆红。5.5 MCP 服务端连不上如果你用的是独立 MCP serverstdio 或 http 传输要确认进程能启动、端口能通。stdio 模式下命令路径写错是最常见的日志里会有Cannot run program之类提示。先用命令行手动跑一遍 MCP server确认它能独立工作再交给 Spring AI 托管。6. 下一步把通道和工具链固定下来跑通最小示例后建议做两件事。一是把 Key 和 base-url 抽到配置中心或环境变量模板团队多人协作时不再各自维护二是把工具按业务域拆分每个域一个ToolCallbackProvider方便按需挂载。TaoToken 的统一 Key 在这里的价值会越来越明显——模型通道和工具链共用一套凭证换环境只改一个变量。如果你准备长期做编码类或 Agent 类项目可以看下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合高频调用场景。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到参数细节可以直接查。Claude Code 相关的接入说明在 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 需要的话对照配置。最后留一个实用技巧在application.yml里给 MCP 客户端单独开一个 profile本地用 stdio 调试线上用 http 传输切换时只改 profile 不改代码。这样从开发到上线工具链的配置差异被隔离在一处排障时也更容易定位是通道问题还是工具问题。
