第 5 篇:用 Spring AI 写 MCP------注解、自动配置、ChatClient 集成
📍 系列导航 :1. 认识 MCP | 2. 本地实战 | 3. 多能力工程 | 4. HTTP URL | 5. Spring AI | 6. 企业治理
5.1 Spring AI 和 MCP SDK 是什么关系
先澄清一个常见误解:
💡 Spring AI 不是 MCP 的替代品,它是 Spring 生态的 AI 应用框架,内部集成了 MCP。
text
Spring AI(应用框架)
└── 集成 MCP
└── 底层:官方 MCP Java SDK
| 维度 | 官方 MCP Java SDK | Spring AI |
|---|---|---|
| 定位 | 协议实现库 | Spring 生态 AI 应用框架 |
| 依赖 | io.modelcontextprotocol.sdk:mcp |
org.springframework.ai:* |
| 写 Server | 手动 McpServer.sync(...)、手动定义 Tool |
注解 + 自动配置 + starter |
| 写 Client | 手动 McpClient.sync(...) |
ChatClient 集成 MCP Client |
| 适合 | 理解协议、轻量 Server、非 Spring 项目 | Spring Boot 项目、Chat/RAG/Tool 一体化 |
⚠️ 版本说明:Spring AI 1.0 前后 artifact 名、注解包路径有调整。以下示例以 Spring AI 1.0.x 为参考,实际以官方文档为准。
5.2 Spring AI 作为 MCP Server:注解式 Tool
5.2.1 pom 依赖
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
artifact 名可能随版本变化,以 Spring AI 官方文档的 MCP Server starter 为准。
5.2.2 application.yml
yaml
server:
port: 8080
servlet:
context-path: /sales
spring:
ai:
mcp:
server:
name: sales-server
version: 1.0.0
# 端点路径由 starter 默认或在此配置,具体键名以版本为准
5.2.3 用 @Tool 声明工具(基础版)
java
package com.example.sales;
import com.example.sales.model.SalesRecord;
import com.example.sales.service.SalesService;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class SalesTools {
private final SalesService service;
public SalesTools(SalesService service) {
this.service = service;
}
@Tool(name = "sales_query", description = "按地区和 ISO 周查询销售数据")
public SalesRecord querySales(
@ToolParam(description = "地区名称,例如 华东、华南、华北") String region,
@ToolParam(description = "ISO 周,格式 YYYY-Www,例如 2026-W20") String week) {
return service.query(region, week);
}
@Tool(name = "sales_top_regions", description = "返回指定周营收最高的地区")
public List<SalesRecord> topRegions(
@ToolParam(description = "ISO 周,格式 YYYY-Www") String week,
@ToolParam(description = "返回数量,默认 3,范围 1-20", required = false) Integer limit) {
return service.topRegions(week, limit == null ? 3 : limit);
}
}
对比第 2 篇:
| 维度 | 官方 SDK(第 2 篇) | Spring AI(本篇) |
|---|---|---|
| 定义 Tool | 手动 McpSchema.Tool + JSON Schema 字符串 |
@Tool + 方法签名 |
| 参数描述 | 手写 JSON Schema | @ToolParam |
| 异常处理 | 手动 try/catch | 可交给全局异常处理 |
| 注册 | 手动 .tools(...) |
自动扫描 @Tool |
| 代码量 | 多 | 少 |
5.2.4 多模态返回:把图片 URL 映射为 ImageContent
Spring AI 相比官方 SDK 的一个优势:对多模态返回有更好的封装。
假设 Tool 需要返回一张图表:
java
@Tool(name = "sales_chart", description = "生成指定周的销售趋势图,返回图表 URL")
public ChartResult generateChart(
@ToolParam(description = "ISO 周,例如 2026-W20") String week) {
String imageUrl = chartService.render(week); // 生成 PNG,上传到对象存储
return new ChartResult(imageUrl, "2026-W20 销售趋势");
}
public record ChartResult(String imageUrl, String caption) {}
Spring AI 在把 Tool 结果序列化回 MCP 时,会根据返回类型和内容自动映射为 MCP 的 ImageContent 或 TextContent。对于包含 imageUrl 的对象,通常有两种方式:
方式一:返回 URL 字符串,让模型通过文本引用
最简单,模型拿到 URL 后可以在回复里引用。适合"让模型描述图片"或"在前端展示"。
方式二:返回 ImageContent,让模型直接看到图片
如果你希望模型直接"看图"做分析,需要返回 MCP 的 ImageContent(Base64 或 URL)。Spring AI 的 MCP 扩展会识别 Media 类型或 org.springframework.ai.content.Media,自动转为 ImageContent:
java
import org.springframework.ai.content.Media;
import org.springframework.core.io.ByteArrayResource;
@Tool(name = "sales_chart", description = "生成指定周的销售趋势图")
public Media generateChart(
@ToolParam(description = "ISO 周,例如 2026-W20") String week) {
byte[] png = chartService.renderBytes(week);
return new Media(
org.springframework.util.MimeTypeUtils.IMAGE_PNG,
new ByteArrayResource(png)
);
}
模型收到 ImageContent 后,可以直接对图表做视觉分析(如"哪个地区下降最快"),而不只是读数字。
💡 选型建议:
- 只需要模型引用图表 → 返回 URL 字符串
- 需要模型"看图"分析 → 返回
Media/ImageContent- Base64 会显著增大上下文,大图优先用 URL
这个能力是官方 SDK 手写 JSON Schema 时比较难优雅实现的,也是 Spring AI 在多模态场景的加分项。
5.2.5 注册 Tool 到 MCP Server
Spring AI 的 MCP Server starter 通常会自动发现 @Tool 方法,或通过 ToolCallbackProvider 显式注册:
java
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 McpToolConfig {
@Bean
public ToolCallbackProvider salesToolCallbackProvider(SalesTools salesTools) {
return MethodToolCallbackProvider.builder()
.toolObjects(salesTools)
.build();
}
}
不同版本可能自动注册,也可能需要显式
ToolCallbackProvider。以官方示例为准。
5.2.6 启动与验证
bash
mvn spring-boot:run
端点(以 starter 默认路径为准,常见为 /sse、/mcp/message 或可配置):
text
http://localhost:8080/sales/sse
用 MCP Inspector 或 Claude Desktop 验证工具列表。
5.3 Spring AI 作为 MCP Client:ChatClient 集成
如果对方是 Spring AI 应用,可以用 ChatClient 直接消费 MCP Server。
5.3.1 pom 依赖
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
5.3.2 application.yml
yaml
spring:
ai:
mcp:
client:
enabled: true
name: my-agent
version: 1.0.0
type: SYNC
sse:
connections:
sales:
url: https://mcp.example.com/sales/mcp
配置键名随版本变化,以 Spring AI 官方文档的 MCP Client 配置为准。
5.3.3 用 ChatClient 调用
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.web.bind.annotation.*;
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder,
ToolCallbackProvider mcpTools) {
this.chatClient = builder
.defaultTools(mcpTools) // 把 MCP Server 的工具注入 ChatClient
.build();
}
@GetMapping("/chat")
public String chat(@RequestParam String q) {
return chatClient.prompt()
.user(q)
.call()
.content();
}
}
用户问"查一下 2026-W20 华东销售",Spring AI 会自动:
- 把 MCP Server 的工具描述注入模型上下文
- 模型决定调用
sales_query - Spring AI 通过 MCP Client 转发给 Server
- 结果回填给模型
- 生成自然语言回复
5.4 什么时候用 Spring AI,什么时候用官方 SDK
| 你的情况 | 建议 |
|---|---|
| 已有 Spring Boot 项目,还要 Chat/RAG | Spring AI |
| 想快速写 Tool,少写胶水 | Spring AI |
| 需要多模态返回(图片、音频) | Spring AI |
| 想理解协议、完全控制 | 官方 SDK |
| 非 Spring 项目、轻量 Server | 官方 SDK |
| 需要 MCP Server + MCP Client 一体化 | Spring AI |
| 需要精细控制 Transport / Schema | 官方 SDK |
两者底层协议一致,可以混用:Server 用官方 SDK,Client 用 Spring AI,或反过来。
5.5 Spring AI 写 MCP 的常见坑
| 坑 | 解决 |
|---|---|
| starter 版本不匹配 | 用 spring-ai-bom 统一版本 |
@Tool 没被扫描 |
确认组件扫描路径,或显式 ToolCallbackProvider |
| 参数描述缺失 | @ToolParam 写清格式和示例 |
| 返回对象无法序列化 | 用 record / POJO,确保 Jackson 可序列化 |
| 异常直接抛出 | 配全局异常处理,转可读错误 |
| MCP Server 端点路径不清楚 | 查 starter 文档,或显式配置 |
| Client 配置键名不对 | 以当前版本文档为准,不要照抄旧博客 |
| 多模态返回 Base64 过大 | 大图优先返回 URL,不让模型直接读 Base64 |
5.6 本篇小结
- Spring AI 是上层框架,底层仍是官方 MCP SDK。
- Server 侧:
@Tool+@ToolParam+ starter 自动配置,代码量大幅减少。 - Client 侧:
ChatClient+ToolCallbackProvider,自动完成工具注入和调用。 - 多模态返回:
Media/ImageContent让模型能"看图",但大图优先用 URL。 - 选型看项目:Spring Boot 项目优先 Spring AI,轻量/非 Spring 用官方 SDK。
下一篇:Spring AI Alibaba 封装 MCP------Nacos 注册、Gateway、企业级治理。