1. 为什么要在 Spring AI 里接 MCP
MCP 全称 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_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册后在控制台创建 Key。控制台入口:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。Key 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,建议给项目单独建一把,方便按项目看用量和吊销。
第二步,确认 API 基地址。TaoToken 的 API 端点是 https://taotoken.net/api ,注意这个地址不带任何查询参数,直接作为 base-url 用。Spring AI 的 OpenAI 兼容客户端、以及 MCP 里需要走模型的地方,都指向它。
第三步,把 Key 放进环境变量,别硬编码进代码。Linux/macOS 下:
export TAOTOKEN_API_KEY="sk-你的Key"Windows PowerShell:
$env:TAOTOKEN_API_KEY="sk-你的Key"注意:Key 只放环境变量或配置中心,别提交到 Git。application.yml 里用
${TAOTOKEN_API_KEY}占位引用。
如果你还想先验证模型通道是否通,可以直接用模型对话页面测一下:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 。确认能正常返回,再往下配 Spring AI,能省掉一半排障时间。
3. Spring AI + MCP 可复制配置
3.1 pom.xml 依赖
Spring AI 的版本迭代较快,MCP 相关模块在 1.0 之后有独立 starter。下面给一份可用的依赖骨架,重点是引入 OpenAI 兼容 starter(走 TaoToken 通道)和 MCP 客户端 starter:
<properties> <spring-ai.version>1.0.0</spring-ai.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring AI OpenAI 兼容客户端,指向 TaoToken --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- MCP 客户端支持 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> </dependencies>如果 Maven 拉不到,检查是否加了 Spring Milestones 仓库,Spring AI 的正式版和里程碑版仓库地址不同,按你用的版本补上即可。
3.2 application.yml 骨架
这是整篇的核心。模型通道和 MCP 工具通道都收敛到 TaoToken 的 base-url 和同一把 Key:
spring: 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指向 TaoToken,api-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 = ?"; List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, userId); if (rows.isEmpty()) { return "未找到用户信息"; } Map<String, 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 表里有 id=1 的记录,请求:
curl "http://localhost:8080/api/ai/ask?q=帮我查一下用户1的信息"模型收到问题后,会判断需要调用query_user_info,执行后把结果组织成自然语言返回,类似:
用户1的信息如下:ID=1,姓名=张三,邮箱=zhangsan@example.com4.3 确认工具真的被调用
光看返回不够,要确认工具执行链路。在UserQueryTool里加一行日志:
System.out.println("[MCP-TOOL] queryUserInfo called, userId=" + userId);再次请求,控制台出现[MCP-TOOL]就说明模型确实触发了工具,而不是自己编的答案。这一步是验证 MCP 是否真正打通的关键,很多人以为返回对了就行,其实模型可能在「幻觉」。
4.4 用模型对话页交叉验证
如果本地日志正常但返回内容奇怪,可以去 https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 用同样的 prompt 测一下纯模型表现,对比就能判断问题出在模型通道还是工具链路。
5. 本篇常见错误排查
5.1 401 / 403:Key 或 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.Tool);ToolCallbackProviderBean 是否被 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 server(stdio 或 http 传输),要确认进程能启动、端口能通。stdio 模式下命令路径写错是最常见的,日志里会有Cannot run program之类提示。先用命令行手动跑一遍 MCP server,确认它能独立工作,再交给 Spring AI 托管。
6. 下一步:把通道和工具链固定下来
跑通最小示例后,建议做两件事。一是把 Key 和 base-url 抽到配置中心或环境变量模板,团队多人协作时不再各自维护;二是把工具按业务域拆分,每个域一个ToolCallbackProvider,方便按需挂载。TaoToken 的统一 Key 在这里的价值会越来越明显——模型通道和工具链共用一套凭证,换环境只改一个变量。
如果你准备长期做编码类或 Agent 类项目,可以看下 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它更适合高频调用场景。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到参数细节可以直接查。Claude Code 相关的接入说明在 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode-anthropic&utm_campaign=rewrite ,需要的话对照配置。
最后留一个实用技巧:在application.yml里给 MCP 客户端单独开一个 profile,本地用 stdio 调试,线上用 http 传输,切换时只改 profile 不改代码。这样从开发到上线,工具链的配置差异被隔离在一处,排障时也更容易定位是通道问题还是工具问题。