StackTraces
Spring AI已验证

Spring AI MCP 客户端 404:端点从 /sse 迁到 /mcp

@debug_master12 天前更新阅读 6 min00

MCP 的 HTTP 传输已经从 HTTP+SSE 迁到 Streamable HTTP:旧端点是 /sse,新端点是 /mcp。客户端报 404 Not Found on GET request for ".../sse",说明请求打到了已经不存在的旧路径上。改用 spring.ai.mcp.client.streamable-http.connections.<name> 配置,url 只写协议、主机、端口,路径交给默认值为 /mcp 的 endpoint。版本差异要记住:Spring AI 1.0.0 的 MCP 客户端只有 stdio 和 SSE 两种 transport,Streamable HTTP 客户端到 1.1.0 才有,本文基线是 1.1.8。

Issue & Env(案发现场与环境)

  • 业务场景:Spring Boot 应用通过 spring-ai-starter-mcp-client 接一个远端 MCP Server。服务端进程或容器在跑,端口也通,客户端一连接就 404,工具列表始终是空的。
  • 环境:Spring AI 1.1.8,Spring Boot 3.5.15,JDK 17。客户端配置还写着 spring.ai.mcp.client.sse.*,指向 /sse;服务端已经只开放 Streamable HTTP 的 /mcp。
  • 一个容易误判的点:404 表示“服务活着,但没这个路径”,不是“连不上”。能拿到 HTTP 响应,说明网络、端口、网关都没问题,错的是 transport 类型和端点路径。

StackTrace(堆栈日志)

最先看到的通常是这一句,注意 URL 结尾的 /sse:

org.springframework.web.client.HttpClientErrorException$NotFound: 404 Not Found on GET request for "http://localhost:8080/sse"

完整堆栈从 HTTP 客户端一路冒到业务调用点:

org.springframework.web.client.HttpClientErrorException$NotFound: 404 Not Found on GET request for "http://localhost:8080/sse"
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:123)
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:103)
    at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
    at org.springframework.ai.mcp.client.common.transport.HttpClientSseClientTransport.lambda$start$0(HttpClientSseClientTransport.java:98)
    at com.example.demo.mcp.McpToolService.callTool(McpToolService.java:33)
    at com.example.demo.mcp.McpController.invoke(McpController.java:21)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)

如果打了请求日志,会看到客户端连的是某个已经不存在的旧路径,两种典型形态:

GET http://localhost:8080/sse 404
GET http://localhost:8080/mcp/sse 404

判断技巧用一条二值规则就够:

报错文案 含义 先查什么
Connection refused 根本没连上 服务是否启动、端口是否写对
404 Not Found ... "/sse" 连上了,路径不存在 transport 和端点路径是否与新版 MCP 对齐
404 Not Found ... "/mcp" 连上了,路径不存在 服务端是否真的开在 /mcp,或还没升级到 Streamable HTTP

只要文案里出现 404 并且 URL 结尾是 /sse,先怀疑端点迁移,不要再往网络方向上查。

Reproduce(复现代码)

客户端按旧 SSE 配方写:

spring:
  ai:
    mcp:
      client:
        sse:
          connections:
            remote:
              url: http://localhost:8081/sse

服务端只开放 Streamable HTTP 端点:

POST /mcp
GET  /mcp

这段 YAML 语法没错,url 也指向了真实运行的服务,所以启动、注入、Bean 装配都不会报错。错的地方有两处:transport 选成了已 deprecated 的 SSE,路径又指向服务端从未提供的 /sse。请求打到不存在的路由上,服务端只能回 404。

Root Cause(根因分析)

端点模型变了:/sse → /mcp

HTTP+SSE 的模型是“客户端先 GET /sse 建立一条长连接,拿到一个 messages 回调地址,再往那个地址 POST 消息”。Streamable HTTP 把两步合成一步:同一个端点既接受 POST(发 JSON-RPC 请求),也接受 GET(需要时开服务端推送),路径约定为 /mcp。

端点名字变了,跟着一起变的还有三件事:

维度 HTTP+SSE(deprecated) Streamable HTTP
客户端配置前缀 spring.ai.mcp.client.sse spring.ai.mcp.client.streamable-http
端点路径 /sse(服务端自定,常见还有 /mcp/sse) /mcp(endpoint 的默认值)
可用版本 Spring AI 1.0.0 起 Spring AI 1.1.0 起

规范已把 HTTP+SSE 标为 deprecated

MCP 官方规范把 HTTP+SSE transport 明确标记为 deprecated,并给出迁移目标是 Streamable HTTP。这不表示旧方案立刻失效,而是说新实现不再围绕它设计:新建服务端默认只挂 /mcp,老的 /sse 路由干脆不注册。404 就是这么来的。

版本差异:客户端支持两种,但不是同一时间点

Spring AI 1.0.0 的 MCP 客户端只有 stdio 和 SSE 两种 transport,Streamable HTTP 客户端是 1.1.0 才加进来的。所以排查前先确认版本:

  • 项目跑在 1.1.8:streamable-http 配置可用,直接切过去。
  • 项目还停在 1.0.0:没有 streamable-http 配置项,只能升到 1.1 线,或者让服务端继续保留 /sse 兼容路由。

把 SSE 的配置键搬到只开 /mcp 的服务端,无论 URL 怎么写,结果都是 404。

The Fix(解决方案)

1. 客户端改成 Streamable HTTP 配置

spring:
  ai:
    mcp:
      client:
        streamable-http:
          connections:
            remote:
              url: http://localhost:8081
              endpoint: /mcp

两条规则:

  • url 只写协议、主机和端口,不要带路径。
  • endpoint 的默认值就是 /mcp,只有服务端挂在别的路径时才需要显式写这一行。

2. 用 curl 确认端点真实可用

curl -i -X POST http://localhost:8081/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Accept 头必须同时带上 application/json 和 text/event-stream 两种类型,否则服务端可能直接拒掉请求,让你误判成端点不存在。

结果二值化判定:

  • 返回 200 且响应体里出现 result:端点可用,问题只在客户端配置,回到第 1 步。
  • 返回 404:路径不对。先确认服务端到底开在 /mcp 还是旧的 /sse,再回头改配置。

3. 对齐版本依赖

  • 客户端要用 streamable-http,Spring AI 必须在 1.1.0 及以上(本文基线 1.1.8),starter 用 spring-ai-starter-mcp-client,版本交给 BOM 统一管理。
  • 服务端一时只有 /sse:要么把服务端升到支持 Streamable HTTP,要么客户端在这段时间里继续用 spring.ai.mcp.client.sse 并指向服务端真实路径,两边 transport 必须一致。
  • 换了 transport 之后顺手验证一次工具列表能不能拉全。只看到连接成功、工具为空,说明协议通了但服务端工具没注册,那是另一类问题。

小结

  • 404 是“服务活着但没这个路径”,不是“连不上”;先看报错 URL 的结尾是 /sse 还是 /mcp。
  • MCP 端点已从 /sse 迁到 /mcp,HTTP+SSE 在规范里标为 deprecated;Spring AI 用 streamable-http 配置对齐。
  • 客户端侧只需 url(协议+主机+端口)加 endpoint(默认 /mcp)两项,url 里不要带路径。
  • 判断端点可用性用带双 Accept 头的 curl:200 加 result 即可用,404 说明路径错。
  • 版本差异要盯住:Streamable HTTP 客户端从 Spring AI 1.1.0 才有,1.0.0 只有 stdio 和 SSE。

References(参考来源)

最后更新于 2026-09-29

这篇帮到你了吗?

刚解决了一个棘手的报错?花两分钟记录下来,帮助下一个遇到同样问题的开发者。

贡献一条解法