今天来聊两个今年在Java社区讨论度很高的方向------MCP(模型上下文协议) 和 Session API。
这两个都是Spring AI在1.1版本之后重点推进的特性。MCP解决的是"AI怎么跟外部系统打交道"的标准问题,Session API解决的是"长对话记忆怎么管理"的工程问题。一个打通了AI和业务系统之间的连接,一个解决了多轮对话的稳定性。
第一部分:MCP ------ 从「Function Call乱战」到「AI时代的USB接口」
先说说MCP。
一、MCP是什么?为什么需要它?
MCP(Model Context Protocol,模型上下文协议)由Anthropic于2024年11月开源发布,是LLM调用外部工具的一个标准化协议。
在MCP出现之前,各家AI模型的Function Calling走的是各自为政的路线。OpenAI有OpenAI的一套,文心有文心的一套,你写代码的时候,每家的JSON Schema定义、调用方式、参数格式都长得不一样。代码里一堆条件判断,if用的是GPT走方案A,else if用的是Claude走方案B,else if用的是Gemini走方案C------维护成本高得离谱。
MCP相当于AI领域的JDBC标准。它定义了统一的接口规范,你用Spring AI开发好一个MCP Server,可以同时被Claude、Cursor、各种AI应用调用,完全解耦。
二、环境搭建与依赖配置
MCP Server的核心依赖只有一个------spring-ai-starter-mcp-server。
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server</artifactId>
<version>1.1.2</version>
</dependency>
如果想用HTTP/SSE/Streamable HTTP模式,需要额外引入对应的starter。Spring AI官方提供了三种传输方式的Starter:
| Starter | 传输方式 | 适用场景 |
|---|---|---|
spring-ai-starter-mcp-server |
Stdio | 本地调用,Claude Desktop / Cursor 直接拉起,最常见 |
spring-ai-starter-mcp-server-webmvc |
Streamable HTTP | 远程工具,团队共享,适合微服务架构 |
spring-ai-starter-mcp-server-webflux |
Streamable HTTP | 响应式编程,高并发场景 |
如果选择Stdio模式,配置文件里需要显式关闭Banner和Web容器:
yaml
spring:
main:
banner-mode: off
web-application-type: none
ai:
mcp:
server:
stdio: true
为什么需要关Banner和Web容器? MCP通过Stdio协议通信,任何额外输出到控制台的文本都会破坏协议通信。Log日志也会污染通信流,这一点稍后会说。
三、MCP Server开发实战
假设我要构建一个企业内部的知识助手,MCP Server需要暴露两个业务能力:
- 根据员工ID查询剩余年假天数(调用内部HR系统)
- 根据订单号查询物流状态(调用物流系统)
这两个能力分别对应两个工具。用Spring AI开发MCP Server,核心工具类定义如下:
java
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
@Component
public class EnterpriseTools {
@Tool(description = "查询指定员工的年假余额")
public String getUserAnnualLeave(@ToolParam(description = "员工ID") String userId) {
// 实际业务:调用内部HR系统接口
// 伪代码:String result = hrApi.getLeaveBalance(userId);
return "员工 " + userId + " 的年假余额为 12 天";
}
@Tool(description = "查询订单的物流状态")
public String getOrderStatus(@ToolParam(description = "订单号") String orderId) {
// 实际业务:调用物流系统查询接口
// 伪代码:String status = logisticsApi.track(orderId);
return "订单 " + orderId + " 当前状态为:已发货,预计3天内送达";
}
}
@Tool 注解的作用很关键------它会自动把Java方法暴露成符合MCP规范的Tool,框架在启动时会扫描所有被标记的Bean,自动生成Tool定义。
整个服务端就这几行核心代码。打包成可执行JAR后,任何支持MCP协议的AI客户端(Claude Desktop、Cursor等)都可以直接加载调用。
版本提示 :Spring AI 1.x 版本使用
@Tool注解,2.0 版本已升级为@McpTool,写法上有细微差异,使用时请核对版本号。
四、MCP Client开发:把多个Server的能力集成到一起
MCP Server开发好之后,自然需要MCP Client来连接和使用这些能力。
Spring AI提供了McpSyncClient作为统一的客户端接口。在集成工具时,代码可以写成这样:
java
@Configuration
public class AiConfig {
@Bean
public ChatClient chatClient(ChatClient.Builder builder, List<McpSyncClient> mcpSyncClients) {
return builder
.defaultSystem("你是一个企业AI助手,可以调用系统中的工具回答员工问题。")
.defaultToolCallbacks(mcpSyncClients)
.build();
}
}
实际项目中的场景比单个Server更复杂。比如企业有HR系统、订单系统、知识库系统,分别被封装成不同的MCP Server,分布在不同的端口上。MCP Client需要连接多个MCP Server,统一协调工具列表。
五、集成测试与调试
MCP Server发布后,用Baeldung提供的测试策略来验证工具是否正常注册和调用:
java
@SpringBootTest
@AutoConfigureMockMvc
public class McpToolIntegrationTest {
@Autowired
private ApplicationContext context;
@Test
void testToolIsRegistered() {
// 验证MCP Server是否正确加载并注册了所有工具
McpServer mcpServer = context.getBean(McpServer.class);
assertThat(mcpServer).isNotNull();
}
}
集成测试的核心是验证三件事:工具是否被正确扫描注册、参数是否能正确解析、返回值格式是否符合预期。MCP Tool和LLM响应不同,它是确定性代码,完全支持自动化单元测试。
调试方面,Spring AI官方推荐使用 MCP Inspector 工具。这是一个可视化的调试工具,可以连接到MCP Server,手动测试tools/list和tools/call,实时查看工具列表和调用结果。
六、生产环境避坑指南
MCP Server在企业落地过程中,有几个坑值得提前知道。
坑一:Stdio模式下,Log会破坏通信。 如果选择了Stdio协议,绝对不能往控制台打印Log。Stdio协议走的是控制台输入输出流,任何额外的输出都会让协议解析失败。解决方法:把日志重定向到文件,或者用logback配置将控制台日志输出关掉。
坑二:Client连接Server时,路径要严格对齐。 MCP Server的端点暴露路径必须和Client配置的url和endpoint完全一致,否则Client扫描不到任何工具,AI模型会提示"no tools available"。
坑三:JDK版本兼容性问题。 MCP功能依赖Java 17以上版本,如果还在用Java 8或者11,需要先升级,否则会遇到莫名其妙的类加载错误。
第二部分:Session API ------ 从「ChatMemory」到「事件源记忆」
聊完MCP,再说Session API。
一、为什么需要Session API?
传统的ChatMemory存储对话历史的方式有一个致命问题:它把对话当成一个扁平的Message列表来存,窗口满了就从最老的消息开始删。
这在简单场景下勉强能用,但在工具调用场景中会出大问题------想象一下,模型调用了一个工具,返回结果还没来得及处理,工具调用的记录就被窗口截断了。模型拿到一个孤立的结果,不知道是从哪来的。结果就是整个对话上下文全乱掉。
Session API的思路完全不同。它用事件溯源 的方式记录对话:每个UserMessage、AssistantMessage、ToolCall、ToolResult都是独立的事件,每条事件都带UUID、sessionId、timestamp。窗口满了不再机械地按消息条数截断,而是在Turn(一次对话轮次)的边界上做压缩,确保截断后模型不会看到孤立的消息。
二、核心概念:Session和Turn
在Session API中,最核心的两个概念是Session和Turn。
Session 代表一个完整的对话会话,存储sessionId、userId、TTL等信息。Turn是原子对话单元------从用户发出一条UserMessage开始,到模型回复、工具调用、工具结果,直到下一个UserMessage出现前结束。
所有压缩策略都在Turn边界上执行,不会把ToolCall和ToolResult拆散。
三、从ChatMemory迁移到Session API
用Session API创建一个会话:
java
SessionService service = new DefaultSessionService(
InMemorySessionRepository.builder().build()
);
Session session = service.create(
CreateSessionRequest.builder()
.userId("alice")
.build()
);
service.appendMessage(session.id(), new UserMessage("帮我查一下订单"));
service.appendMessage(session.id(), new AssistantMessage("好的,请提供订单号"));
获取完整对话历史:
java
List<Message> history = service.getMessages(session.id());
四、实战演练:构建多会话的客服助手
假设一个客服系统需要同时处理多个用户的对话。客服Agent要根据历史记录上下文来回答问题,而且客服Agent内部可能还使用了MCP Server提供的工具。
完整代码结构如下:
java
@Service
public class CustomerServiceSessionManager {
private final SessionService sessionService;
private final ChatClient chatClient;
public CustomerServiceSessionManager(ChatClient.Builder builder) {
this.sessionService = new DefaultSessionService(
JdbcSessionRepository.builder()
.dataSource(dataSource)
.build()
);
this.chatClient = builder.build();
}
// 获取或创建用户会话
public Session getOrCreateSession(String userId) {
Optional<Session> existing = sessionService.findByUserId(userId);
return existing.orElseGet(() ->
sessionService.create(
CreateSessionRequest.builder()
.userId(userId)
.ttl(Duration.ofDays(7))
.build()
)
);
}
// 处理用户消息(同时集成MCP工具调用)
public String processMessage(String userId, String userMessage) {
Session session = getOrCreateSession(userId);
// 记录用户消息
sessionService.appendMessage(session.id(), new UserMessage(userMessage));
// 获取完整历史记录(包含之前的工具调用记录)
List<Message> history = sessionService.getMessages(session.id());
// 调用AI模型
String response = chatClient.prompt()
.messages(history)
.call()
.content();
// 记录AI回复
sessionService.appendMessage(session.id(), new AssistantMessage(response));
return response;
}
}
代码逻辑很简单:sessionId绑定userId,系统根据userId自动找回历史上下文。如果客服Agent调用MCP工具获取了订单信息,getMessages()会返回包含ToolCall和ToolResult的完整事件流。
五、避坑指南
Session API当前在spring-ai-community仓库中孵化,目标是Spring AI 2.1版本(预计2026年11月)正式GA ,届时ChatMemory会被标记为废弃。目前不建议在生产环境使用,但可以在测试项目中提前熟悉API设计。
写在最后
MCP和Session API代表了Spring AI最近迭代的两个方向。
MCP用标准化的协议连接外部工具,解决了AI应用的核心痛点------如何让LLM安全、可控地调用真实系统能力。它让Java开发者可以用自己熟悉的方式暴露业务能力,同时这些能力可以被不同AI客户端复用。Session API用事件溯源的方式管理对话历史,解决了长对话场景下的状态管理难题,多Agent分支隔离、按Turn压缩、可搜索召回存储这些能力,都是传统ChatMemory无法实现的。
MCP打通的是"AI能做什么",Session API解决的是"AI怎么做才稳"。
一个是向外扩展能力边界,一个是向内优化对话稳定。两者同时使用,恰好覆盖了生产级AI应用的两个关键维度。
下一篇文章准备聊聊Spring AI Agentic Patterns的其他内容,比如AutoMemoryTools和Task Subagents,这些都是值得探索的方向。如果大家在生产环境用Spring AI遇到了什么新问题或者有什么好玩的实践,欢迎留言交流。