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连接管理和客户端兼容性的深入理解。

目录
相关文章
|
1月前
|
SQL 自然语言处理 调度
Agent Skills 的一次工程实践
**本文采用 Agent Skills 实现整体智能体**,开发框架采用 AgentScope,模型使用 **qwen3-max**。Agent Skills 是 Anthropic 新推出的一种有别于mcp server的一种开发方式,用于为 AI **引入可共享的专业技能**。经验封装到**可发现、可复用的能力单元**中,每个技能以文件夹形式存在,包含特定任务的指导性说明(SKILL.md 文件)、脚本代码和资源等 。大模型可以根据需要动态加载这些技能,从而扩展自身的功能。目前不少国内外的一些框架也开始支持此种的开发方式,详细介绍如下。
2425 8
|
3月前
|
人工智能
实训Agent创客:一键生成电商场景Agent
在阿里云百炼一键生成电商场景Agent,轻松帮您搞定商品展示图片、视频。快来参与活动任务吧!
482 2
|
1月前
|
存储 人工智能 安全
AICoding实践:从Prd到代码生成
本文探讨了在AI技术推动软件工程范式变革的新阶段,如何通过构建增强型AI编程系统(codefuse)实现从需求到代码的端到端自动生成。
695 21
AICoding实践:从Prd到代码生成
|
1月前
|
弹性计算 人工智能 应用服务中间件
阿里云服务器最新收费价格表:含年付、月付及小时计费标准
今年阿里云服务器推出多重优惠活动,覆盖 ECS 云服务器、轻量应用服务器、GPU 服务器等主流机型,支持北京、上海、中国香港、新加坡、日本、美国等多地域部署。爆款配置性价比突出,轻量应用服务器低至 38 元 / 年,ECS 经济型服务器 99 元 / 年起,GPU 服务器小时计费 1.2 元起,满足个人开发者、企业及 AI 训练等不同场景需求。
|
人工智能 Java Serverless
【MCP教程系列】搭建基于 Spring AI 的 SSE 模式 MCP 服务并自定义部署至阿里云百炼
本文详细介绍了如何基于Spring AI搭建支持SSE模式的MCP服务,并成功集成至阿里云百炼大模型平台。通过四个步骤实现从零到Agent的构建,包括项目创建、工具开发、服务测试与部署。文章还提供了具体代码示例和操作截图,帮助读者快速上手。最终,将自定义SSE MCP服务集成到百炼平台,完成智能体应用的创建与测试。适合希望了解SSE实时交互及大模型集成的开发者参考。
13320 60
|
8月前
|
人工智能 Java API
MCP客户端调用看这一篇就够了(Java版)
本文详细介绍了MCP(Model Context Protocol)客户端的开发方法,包括在没有MCP时的痛点、MCP的作用以及如何通过Spring-AI框架和原生SDK调用MCP服务。文章首先分析了MCP协议的必要性,接着分别讲解了Spring-AI框架和自研SDK的使用方式,涵盖配置LLM接口、工具注入、动态封装工具等步骤,并提供了代码示例。此外,还记录了开发过程中遇到的问题及解决办法,如版本冲突、服务连接超时等。最后,文章探讨了框架与原生SDK的选择,认为框架适合快速构建应用,而原生SDK更适合平台级开发,强调了两者结合使用的价值。
11663 33
MCP客户端调用看这一篇就够了(Java版)
|
JSON JavaScript Linux
【MCP教程系列】Node.js+TypeScript搭建NPX MCP服务并自定义部署至阿里云百炼
本文介绍如何将阿里云百炼的工作流封装成MCP服务并部署,随后引入到智能体中使用。主要步骤包括:1) 封装MCP服务;2) 发布到npm官方平台;3) 在阿里云百炼平台创建自定义MCP服务;4) 在智能体中添加自定义MCP服务。通过这些步骤,用户可以轻松将工作流转化为MCP服务,并在智能体中调用。
3222 0