1. 为什么 Spring AI 接阿里 MCP 总在鉴权这关卡住
如果你正在用 Spring AI 搭一个能调用外部工具的智能体,多半会遇到阿里 MCP 协议这条链路。MCP 全称 Model Context Protocol,你可以把它理解成「模型和工具之间的 USB 接口」——模型不直接碰数据库、不直接调内部服务,而是通过 MCP Server 暴露出来的工具清单,按协议发起调用。阿里这边把 MCP 能力做进了它的模型计算平台体系里,Spring AI 则负责在 Java 侧把对话、工具注册、函数回调串起来。
问题往往不在业务代码,而在两件事:一是鉴权信息散落在各个 SDK 配置里,阿里云 AccessKey、MCP 服务地址、模型 Key 各管各的,本地联调时改一处漏一处;二是通道配置对不上,Spring AI 的ToolCallback注册完了,请求发出去却拿不到工具返回,日志里只有一句干巴巴的 401 或超时。
这篇就聚焦本地开发联调这个场景,给你一套能直接复制的application.yml骨架,用统一的 Key 把 Spring AI 到阿里 MCP 的通道打通,再演示一次真实的 MCP 工具调用验证动作。目标很明确:让你在本地跑通 Spring AI 与阿里 MCP 的最小链路,而不是停在「依赖加了但调不通」的状态。适合已经写过 Spring Boot、想快速验证 MCP 工具调用可行性的同学。
2. TaoToken 统一 Key 在链路里的位置
先说清楚统一 Key 解决的是什么。本地联调最烦的是环境变量满天飞:ALIYUN_ACCESS_KEY、MCP_ENDPOINT、MODEL_API_KEY三套东西,团队里每个人机器上还不一样。TaoToken 的做法是提供一个统一的接入入口,把模型对话和工具调用所需的鉴权收敛到一个 Key 上,Spring AI 侧只需要认这一个凭证。
它的 API 入口是https://taotoken.net/api,控制台里可以创建和管理 Key。对 Spring AI 来说,你不需要改业务逻辑,只要把base-url和api-key指向统一入口,MCP 工具调用的请求就会走同一条通道出去。这样做的好处是:本地、测试、预发三套环境只换 Key 不换代码结构,排查问题时也能确定「鉴权这一层是干净的」。
需要提前准备的东西不多:一个可用的 TaoToken Key(在控制台创建),JDK 17 以上,Spring Boot 3.x 工程,以及阿里 MCP 服务那边暴露出来的工具地址。Key 的创建入口在控制台的 API Keys 页面,拿到后先别急着写进代码,下一步我们放进配置文件。
3. application.yml 可复制配置骨架
下面这份配置是我在本地联调时反复调过的版本,直接改 Key 和地址就能用。核心思路是把 Spring AI 的 OpenAI 兼容客户端指向 TaoToken 的统一入口,同时把 MCP 工具相关的超时、重试参数显式写出来,避免默认值在本地网络下表现诡异。
spring: ai: openai: # 统一入口,模型对话与工具调用共用 base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-3-5-sonnet temperature: 0.2 # 工具调用相关,MCP 走这里注册 embedding: options: model: text-embedding-3-small # MCP 工具通道配置 mcp: client: enabled: true # 阿里 MCP 服务暴露的工具端点 endpoint: ${MCP_ENDPOINT:http://localhost:8081/mcp} connect-timeout: 5000 read-timeout: 30000 # 工具调用失败重试次数,本地联调建议 1 max-retries: 1 # 统一 Key 透传到 MCP 请求头 auth-header: Authorization auth-prefix: "Bearer " logging: level: org.springframework.ai: DEBUG com.example.mcp: DEBUG几个参数值得单独说。base-url结尾不要带/v1,Spring AI 的 OpenAI 客户端会自己拼路径,多写一段就会 404。api-key用环境变量注入,别硬编码进仓库,本地用 IDE 的 Run Configuration 或者.env文件加载都行。read-timeout给到 30 秒是因为 MCP 工具如果涉及外部查询,首次冷启动会慢,设太短会误判成超时。max-retries本地设 1 就够,重试太多反而掩盖真实错误。
对应的pom.xml依赖保持精简,Spring AI 的 starter 加上 Web 就够了:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>1.0.0-M4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>版本号按你工程里实际用的 Spring AI 版本对齐,M 系列和正式版的包名有差异,升级时留意一下。
4. 注册 MCP 工具并验证一次调用
配置写完只是通道通了,真正要验证的是「模型能不能通过 MCP 调到工具」。Spring AI 里注册工具用@Bean暴露ToolCallback,下面这段代码注册一个查询天气的示例工具,模拟阿里 MCP 服务返回结构化数据。
@Configuration public class McpToolConfig { @Bean public ToolCallback weatherTool() { return ToolCallback.builder() .name("get_weather") .description("查询指定城市的天气,输入城市名") .inputType(WeatherRequest.class) .function(req -> { // 实际项目中这里调用阿里 MCP 服务 WeatherRequest r = (WeatherRequest) req; return new WeatherResponse(r.city(), "晴", 26); }) .build(); } public record WeatherRequest(String city) {} public record WeatherResponse(String city, String condition, int temp) {} }然后在 Controller 里发起一次带工具的对话请求,观察模型是否主动触发工具调用:
@RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder, ToolCallback weatherTool) { this.chatClient = builder .defaultTools(weatherTool) .build(); } @GetMapping("/chat") public String chat(@RequestParam String q) { return chatClient.prompt() .user(q) .call() .content(); } }启动应用后,用 curl 打一发:
curl "http://localhost:8080/chat?q=杭州今天天气怎么样"预期结果是模型先返回一段「正在查询杭州天气」的推理,然后调用get_weather,最后把工具返回的「晴,26 度」组织成自然语言答复。日志里你会看到ToolCallback被触发的记录,以及请求经过统一入口的 DEBUG 输出。如果工具没被调用,先看日志里模型是否识别到了工具描述,再检查defaultTools有没有真的注册进去。
5. 本地联调常见报错排查
联调阶段踩的坑基本集中在下面几类,对照日志逐条排。
第一类是 401 Unauthorized。九成是 Key 没注入成功,检查环境变量名和application.yml里的占位符是否一致,${TAOTOKEN_API_KEY}拼错一个字母就会静默变成空字符串。另外确认auth-prefix的Bearer后面有空格,少了空格服务端解析不出凭证。
第二类是工具调用返回空。先看mcp.client.endpoint是否指向了正确的 MCP 服务地址,本地服务没起或者端口写错都会导致连接被拒。如果日志显示连接成功但工具没执行,多半是工具描述写得太模糊,模型没匹配上,把description写具体一点,比如加上「输入必须是城市中文名」。
第三类是超时。本地网络抖动或者 MCP 服务首次加载慢,把read-timeout临时调到 60000 观察一次,如果稳定通过再往回收。别一上来就怪网络,先确认是不是工具内部有阻塞逻辑。
第四类是版本冲突。Spring AI 的 M 版本之间 API 变动较大,ToolCallback.builder()在部分版本里签名不同,报编译错时先对齐官方文档的版本说明,别硬改。
排查顺序建议:先确认 Key 生效(看请求头),再确认通道可达(看连接日志),最后确认工具注册(看模型是否识别)。三层分开验证,比一股脑改配置快得多。
6. 把链路固定下来,后续扩展就顺了
跑通最小链路之后,你会发现真正省事的地方在于配置结构稳定了。统一 Key 让鉴权只维护一处,MCP 工具按ToolCallback逐个注册,新增工具不影响已有通道。本地验证通过后,把application.yml里的环境变量换成对应环境的 Key,代码一行不用动就能推到测试环境。
如果你后面要做更复杂的编码类智能体,或者需要长期跑 Agent 任务,可以了解下 Coding Plan 这类按周期计费的方案,比按次调用更适合高频场景。模型对话的调试入口在模型对话页面,接入文档和参数细节在接入文档里都能查到。先把今天这条最小链路跑稳,再往上叠功能,节奏会舒服很多。