Spring AI MCP Server接入百炼问题排查

简介: 本文记录Spring AI MCP Server接入阿里云百炼平台时因HTTP连接复用导致的"获取工具异常,请确认MCP是否正常运行"问题。百炼复用连接时,服务端已关闭通道,导致请求体丢失。通过分析日志发现Netty过早关闭连接,最终以添加`Connection: close`响应头强制断开,解决兼容性问题。

Spring AI MCP Server接入百炼问题排查

问题背景

在将基于Spring AI的MCP(Model Context Protocol)服务接入阿里云百炼平台时,遇到一个奇怪的问题:MCP服务在trae客户端上可以正常工作,但在百炼平台上却无法正常使用。具体表现为百炼平台发送第二个请求时,服务端接收不到请求体,导致请求失败。

公司有个AI对话智能体的需求,作为负责这个需求的后端开发,开发阶段被这个问题困扰了好久,不过最终还是然我找到原因了。找遍全网也没有人完整的分享过类似的问题,所以写一篇博客分享一下,避免大家后续使用Spring MCP Server 接入百炼时踩坑!

问题现象

正常情况(trae客户端)

trae客户端每个请求都创建新的连接:

  1. 第一个连接发送initialize请求 → 响应后关闭连接
  2. 第二个连接发送notifications/initialized请求 → 响应后关闭连接
  3. 第三个连接发送tools/list请求 → 正常处理

异常情况(百炼平台)

百炼一直会提示:“获取工具异常,请确认MCP是否正常运行”

”获取工具异常,请确认MCP是否正常运行
错误码:11200001
RequestId: dce6eab1-b35c-4471-bc97-d764639dff73

分析日志后发现是由于百炼平台会尝试复用连接导致的:

  1. 第一个连接发送initialize请求 → 响应后服务器关闭连接
  2. 在同一个连接上尝试发送第二个请求 → 但是此时MCP服务端Netty已经关闭链接了,这时候即使收到请求头,但是Netty的channel可能已经半关闭了,无法再继续接收请求内容,也无法正常响应内容。百炼那边等待超时就抛出异常了!

日志分析

从服务端日志可以看到关键信息:

# 第一个请求正常处理
2025-12-13T11:43:58.745+08:00 INFO  REQUEST_BODY_DEBUG : MCP Request Body (207 bytes): {"jsonrpc":"2.0","method":"initialize","id":"3fe559ae-0",...}

# 服务器响应后关闭连接
2025-12-13T11:43:58.750+08:00 DEBUG r.n.http.server.HttpServerOperations : Last HTTP packet was sent, terminating the channel

# 第二个请求到达,但请求体为空
2025-12-13T11:44:58.805+08:00 INFO  REQUEST_BODY_DEBUG : MCP Request Body (0 bytes): 
2025-12-13T11:44:58.808+08:00 ERROR WebFluxStreamableServerTransportProvider : Failed to deserialize message: No content to map due to end-of-input

问题定位

通过深入分析日志,我们发现问题出现在几个方面:

1. HTTP/1.1连接复用问题

  • 服务器在发送完第一个响应后立即关闭了连接(terminating the channel
  • 百炼平台认为连接仍然可用,尝试复用连接发送第二个请求
  • 由于连接已关闭,第二个请求的请求体无法被正确传输

2. 客户端-服务器行为不一致

  • trae:为每个请求创建新连接,符合服务器预期
  • 百炼:尝试复用连接以提升性能,但与服务器行为冲突

3. 协议处理差异

  • trae使用协议版本:2025-06-18
  • 百炼使用协议版本:2025-03-26
  • trae的capabilities为空对象
  • 百炼的capabilities包含roots.listChanged=true

排查过程

第一阶段:基础排查

  1. 检查协议兼容性:确认两个协议版本都支持
  2. 检查日志级别:启用DEBUG级别日志
  3. 对比请求差异:分析trae和百炼的请求头、请求体差异

第二阶段:深入分析

  1. 添加请求体调试过滤器

    @Component
    @Order(1)
    public class RequestBodyDebugFilter implements WebFilter {
         
     // 记录请求头和请求体内容
    }
    
  2. 发现关键证据

  • 第二个请求的Content-Length: 54,但实际接收到的body是0字节
  • 请求在开始处理后等待60秒超时
  1. 排除可能性
  • 排除100-continue问题(百炼没有发送Expect头)
  • 排除缓冲区大小问题
  • 排除JSON解析问题

第三阶段:问题根源确认

通过对比分析和日志追踪,确认问题是:
服务器在HTTP/1.1连接上过早关闭连接,但客户端仍尝试复用该连接

解决方案

最终解决方案:响应头中告诉百炼,链接已经关闭

@Component
@Order(Integer.MIN_VALUE)
public class ForceConnectionCloseFilter implements WebFilter {
   

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
   
        String path = exchange.getRequest().getURI().getPath();

        if (path.equals("/mcp")) {
   
            // 在响应头中强制设置Connection: close
            exchange.getResponse().getHeaders().set("Connection", "close");
            log.info("Forcing connection close for MCP request on connection: {}", 
                     exchange.getRequest().getId());
        }

        return chain.filter(exchange);
    }
}

方案原理

  1. 显式通信:通过Connection: close头明确告诉客户端不要复用连接
  2. 客户端适配:百炼平台看到这个头后会为每个请求创建新连接
  3. 消除歧义:避免客户端和服务器对连接状态的理解不一致

其他尝试方案(未成功)

  1. 调整Netty配置尝试保持连接
  2. 修改HTTP解码器参数
  3. 添加Expect: 100-continue处理
  4. 调整连接超时时间

技术要点总结

1. HTTP/1.1连接管理

  • 默认情况下,HTTP/1.1支持连接复用(keep-alive)
  • 服务器可以通过Connection: close头显式关闭连接
  • Spring WebFlux/Netty在特定情况下可能过早关闭连接

2. Spring WebFlux行为

  • Reactive编程模型对连接管理有特殊处理
  • Netty默认配置可能不适合所有客户端
  • 需要显式配置来处理连接复用

3. 客户端差异

  • 不同HTTP客户端对连接复用的实现策略不同
  • 有些客户端更激进地尝试复用连接
  • 服务器需要能够处理各种客户端行为

经验教训

  1. 不要假设客户端行为一致:不同的HTTP客户端实现可能有显著差异
  2. 连接管理要显式:模糊的连接管理容易导致问题
  3. 日志是最好的调试工具:详细的日志可以快速定位问题
  4. 简单方案往往最有效:复杂的配置调整不如简单的Connection: close
  5. 理解底层协议:对HTTP协议的理解有助于解决这类问题

结论

通过添加ForceConnectionCloseFilter,我们成功地解决了Spring AI MCP Server接入百炼平台的问题。这个问题的核心在于客户端和服务器对HTTP/1.1连接复用的行为不一致。解决方案虽然简单,但背后涉及对HTTP协议、Spring WebFlux连接管理和客户端兼容性的深入理解。

目录
相关文章
|
人工智能 Java Serverless
【MCP教程系列】搭建基于 Spring AI 的 SSE 模式 MCP 服务并自定义部署至阿里云百炼
本文详细介绍了如何基于Spring AI搭建支持SSE模式的MCP服务,并成功集成至阿里云百炼大模型平台。通过四个步骤实现从零到Agent的构建,包括项目创建、工具开发、服务测试与部署。文章还提供了具体代码示例和操作截图,帮助读者快速上手。最终,将自定义SSE MCP服务集成到百炼平台,完成智能体应用的创建与测试。适合希望了解SSE实时交互及大模型集成的开发者参考。
15190 60
|
18天前
|
存储 安全 Java
首个 Java Harness Framework 来了 -- AgentScope 1.1 HarnessAgent 详解
AgentScope Java 1.1.0正式发布,完整实现Harness Framework:支持工作区驱动、可插拔抽象文件系统、开箱即用上下文管理与子Agent编排,兼顾个人提效与企业级安全、隔离、分布式部署需求。
|
人工智能 网络协议 Linux
MCP 协议: Streamable HTTP 是最佳选择
随着AI应用变得越来越复杂并被广泛部署,原有的通信机制面临着一系列挑战。近期MCP仓库的PR #206引入了一个全新的Streamable HTTP传输层替代原有的HTTP+SSE传输层。本文将详细分析该协议的技术细节和实际优势。
7643 102
|
人工智能 Java API
MCP客户端调用看这一篇就够了(Java版)
本文详细介绍了MCP(Model Context Protocol)客户端的开发方法,包括在没有MCP时的痛点、MCP的作用以及如何通过Spring-AI框架和原生SDK调用MCP服务。文章首先分析了MCP协议的必要性,接着分别讲解了Spring-AI框架和自研SDK的使用方式,涵盖配置LLM接口、工具注入、动态封装工具等步骤,并提供了代码示例。此外,还记录了开发过程中遇到的问题及解决办法,如版本冲突、服务连接超时等。最后,文章探讨了框架与原生SDK的选择,认为框架适合快速构建应用,而原生SDK更适合平台级开发,强调了两者结合使用的价值。
14310 33
MCP客户端调用看这一篇就够了(Java版)
|
9月前
|
人工智能 Java 数据库
Spring AI
Spring AI 为 Java 生态注入智能,提供统一抽象接口,简化大模型集成,助力开发者高效构建 AI 应用,推动企业智能化转型。
1385 147
|
6月前
|
人工智能 自然语言处理 前端开发
SpringAI+DeepSeek大模型应用开发
SpringAI整合主流大模型,支持对话、函数调用与RAG,提供统一API,简化开发。涵盖多模态、流式传输、会话记忆等功能,助力快速构建AI应用。

热门文章

最新文章