摘要: 在使用 Spring AI 集成 Model Context Protocol (MCP) 服务时,开发者常遇到 404 Not Found 错误。本文通过分析 MCP 协议从 2024 版本(SSE)到 2025 版本(Streamable HTTP)的演进,结合 LangChain4j 的源码差异,详细解释了为何直接升级传输层会导致报错,并给出了适用于 Spring AI 框架的最佳实践方案。
1. 引言:背景与问题
随着 AI 应用的复杂化,MCP(Model Context Protocol)成为了连接 LLM 和外部工具的标准。在 Java 生态中,我们通常使用 LangChain4j 或 Spring AI 来构建客户端。
最近在尝试对接一个 MCP 服务端时,我遇到了一个典型的报错:
java
java.lang.RuntimeException: Unexpected status code: 404
at dev.langchain4j.mcp.client.transport.http.StreamableHttpMcpTransport.lambda$execute$0(StreamableHttpMcpTransport.java:209)
明明旧的代码能跑通,为什么换成新的 StreamableHttpMcpTransport 就报 404?这背后其实是 MCP 协议版本迭代带来的"代沟"。
2. 协议演进:从 SSE 到 Streamable HTTP
为了理解这个问题,我查阅了 MCP 官方文档的历史版本,对比了 2024-11-05 (旧版)和 2025-03-26(新版)的核心差异。
2.1 传输机制的根本变革
| 特性 | 2024-11-05 (旧版) | 2025-03-26 (新版) |
|---|---|---|
| 核心协议 | HTTP + SSE (Server-Sent Events) | Streamable HTTP |
| 通信模式 | 双工模式:GET 建立 SSE 长连接 + POST 发送指令 |
单一端点:统一通过 POST 交互,可选 GET 流式响应 |
| 端点数量 | 通常需要两个端点(如 /sse 和 /post) |
仅需一个 MCP Endpoint (如 /mcp) |
| Spring AI 支持 | ✅ 原生支持 | ❌ 暂不支持(截至 2026 年初) |
深度解析:
- 旧版 (SSE) :客户端发送
GET /sse建立一个"听筒",服务端通过这个连接推送消息;客户端发送POST到另一个地址(如/message)来发送指令。 - 新版 (Streamable) :废弃了 SSE。客户端直接向
POST /mcp发送所有请求。如果需要流式响应,服务端会返回Content-Type: text/event-stream。
2.2 为什么会出现 404?
当你在代码中使用:
java
McpTransport transport = StreamableHttpMcpTransport.builder()
.url("http://127.0.0.1:3000/sse") // 错误!
.build();
你的意图是使用 新版客户端 去连接 旧版服务端。
- 新版客户端 会向
http://127.0.0.1:3000/sse发送一个POST请求。 - 旧版服务端 的
/sse路径只接受GET请求(用来建立长连接),根本不认识POST。 - 结果:服务端懵了,返回 404 Not Found。
3. Spring AI 与 LangChain4j 的现状分析
3.1 Spring AI 的兼容性
截至目前,Spring AI 框架主要基于旧版的 LangChain4j 实现 ,其底层逻辑完全遵循 2024-11-05 (SSE) 规范。
这意味着,如果你在 Spring Boot 项目中配置 MCP:
yaml
spring:
ai:
mcp:
clients:
my-client:
transport: http
http:
sse-url: http://127.0.0.1:3000/sse # 必须指向 SSE 端点
你必须确保后端服务支持旧版的 SSE 协议。强行在配置中填入 Streamable 的参数是无效的,因为框架底层并没有实现新版的握手逻辑。
3.2 LangChain4j 的过渡期
LangChain4j 1.0 版本为了兼容未来,引入了 StreamableHttpMcpTransport。但这并不意味着它能自动适配所有服务端。这是一个"非此即彼"的选择:
- 服务端是旧版 →\rightarrow→ 客户端必须用
HttpMcpTransport(SSE模式)。 - 服务端是新版 →\rightarrow→ 客户端必须用
StreamableHttpMcpTransport。
4. 解决方案与最佳实践
针对目前的开发环境,我建议采取以下策略:
4.1 方案一:拥抱现状(推荐)
既然 Spring AI 和大部分现有工具链都还在使用旧版协议,我们就不要强行"升级"传输层。
保持旧代码:
java
McpTransport transport = new HttpMcpTransport.Builder()
.sseUrl("http://127.0.0.1:3000/sse") // 指向服务端的 SSE 入口
.build();
服务端要求: 确保你的 MCP 服务端(如 Python 的 FastMCP)开启了 transport="sse" 或兼容模式。
4.2 方案二:纯 Java 项目升级指南
如果你不使用 Spring AI,而是纯 LangChain4j 项目,并且你确定服务端已经升级到了 2025-03-26 协议:
-
服务端 :必须监听单一的 POST 端点(通常是
/mcp),不再区分 SSE。 -
客户端 :修改 URL 为
/mcp。javaMcpTransport transport = StreamableHttpMcpTransport.builder() .url("http://127.0.0.1:3000/mcp") // 注意路径变了 .build();
5. 总结
MCP 协议的演进是为了简化流式传输,但在过渡期给开发者带来了不小的困扰。
核心结论:
- 不要混用:新版客户端不能连旧版服务端,反之亦然。
- Spring AI 用户 :请继续使用
.sseUrl()配置,目前的 Spring AI 版本尚未适配 Streamable HTTP。 - 404 报错的本质:是 HTTP Method 不匹配(客户端发 POST,服务端只听 GET)。
希望这篇文章能帮你少走弯路!如果你也在研究 MCP,欢迎在评论区交流。