Spring AI + MCP Streamable HTTP:让AI工具调用像HTTP一样简单

摘要:本文深入解析MCP协议及Streamable HTTP传输方式,结合Spring AI实战案例,手把手教你构建高效的AI工具调用系统。从协议原理到代码实现,从避坑指南到进阶技巧,一文掌握MCP核心技术。


引言

在AI应用开发中,如何让大模型安全、高效地调用外部工具,一直是开发者面临的挑战。传统的Function Call方式虽然简单,但在跨服务、跨语言场景下显得力不从心。

MCP(Model Context Protocol) 的出现,为这个问题提供了标准化的解决方案。本文将聚焦MCP的最新传输方式------Streamable HTTP,通过Spring AI框架的实战案例,带你快速掌握这一技术。

灵铠交互深耕AI交互领域,专注智能客服、音视频AI应用定制开发,助力企业快速落地轻量化、高可用的智能AI交互系统。



一、MCP协议:AI工具调用的"USB接口"

1.1 什么是MCP?

MCP(Model Context Protocol)是由Anthropic提出的开放协议,旨在标准化AI模型与外部工具的交互方式。

通俗理解:如果把AI模型比作电脑,那么MCP就是USB接口------无论什么工具(天气查询、数据库访问、文件操作),只要遵循MCP协议,AI都能即插即用。

1.2 MCP的三大传输方式

传输方式 底层协议 适用场景 特点
STDIO 标准输入输出 本地进程通信 简单,仅限同机
SSE HTTP + 服务端推送 服务端推送流式数据 单向流,兼容性好
Streamable HTTP HTTP/1.1 + 分块传输 双向流式通信 双向流、高性能、无状态

重点:Streamable HTTP是MCP协议在2025年引入的新传输方式,也是本文的核心内容。


二、Streamable HTTP:为什么它是最佳选择?

2.1 核心优势

Streamable HTTP的设计目标很明确:让MCP调用像普通HTTP请求一样简单,同时保持流式通信的高性能

四大核心优势

  1. 无状态通信:每个请求独立,Server不需维护会话状态,易于水平扩展

  2. 双向流式:支持请求和响应均为流式传输,实时性更强

  3. 标准兼容:基于HTTP/1.1,无需特殊协议支持,兼容现有基础设施

  4. 高性能:复用TCP连接,减少握手开销

2.2 工作原理

Streamable HTTP的核心是HTTP/1.1的分块传输编码(Chunked Transfer Encoding)

plain 复制代码
客户端请求:
POST /api/mcp HTTP/1.1
Content-Type: application/json
Transfer-Encoding: chunked

{"jsonrpc":"2.0","method":"tools/call","params":{"name":"getWeather"}}

服务端响应:
HTTP/1.1 200 OK
Transfer-Encoding: chunked

{"type":"stream","content":"北京天气"}
{"type":"done","content":"北京:晴,26°C"}

关键点

  • 使用Transfer-Encoding: chunked实现流式传输

  • 每个JSON帧独立完整,可实时解析

  • 通过TCP Keep-Alive复用连接,减少开销



三、实战:用Spring AI构建MCP Server

3.1 依赖配置

MCP Server的依赖配置取决于项目选择的Web框架。Spring AI提供了两种方案:

方案一:WebFlux(推荐用于流式场景)

xml 复制代码
<dependencies>
    <!-- WebFlux 响应式框架 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <!-- MCP Server WebFlux 实现 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
        <version>1.1.0</version>
    </dependency>
</dependencies>

方案二:WebMVC(传统同步场景)

xml 复制代码
<dependencies>
    <!-- 传统WebMVC框架 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- MCP Server 核心实现 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server</artifactId>
        <version>1.1.0</version>
    </dependency>
</dependencies>

选择建议

  • 新项目 → WebMVC(学习成本低,生态成熟)

  • 已有WebMVC项目 → WebMVC(保持一致)

  • 追求高并发流式 → WebFlux(性能优势明显)

注意 :不要同时引入spring-boot-starter-webspring-boot-starter-webflux!混用会导致依赖冲突。

3.2 定义MCP工具

使用@Tool注解定义工具,Spring AI会自动将其暴露为MCP服务:

java 复制代码
@Component
public class WeatherTool {

    @Tool(description = "根据城市名称查询当前实时天气信息")
    public String getWeather(
            @ToolParam(description = "城市名称,如:北京、上海") String city) {
        
        // 调用外部天气API
        String weatherData = callWeatherApi(city);
        
        return String.format("""
            {
                "city": "%s",
                "temperature": "26°C",
                "weather": "晴",
                "humidity": "45%%"
            }""", city);
    }
}

关键点

  • @Tool注解的description属性至关重要,LLM会根据描述判断何时调用该工具

  • @ToolParam用于描述参数,帮助LLM理解如何传参

3.3 注册工具

在启动类中注册ToolCallbackProvider,将工具暴露为MCP服务:

java 复制代码
@SpringBootApplication
public class McpServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(McpServerApplication.class, args);
    }

    @Bean
    public ToolCallbackProvider weatherTools(WeatherTool weatherTool) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(weatherTool)
                .build();
    }
}

原理解析

  1. MethodToolCallbackProvider扫描@Tool注解方法

  2. 解析方法签名和参数,生成JSON Schema

  3. 注册到MCP Server的工具列表

  4. 客户端连接后自动发现这些工具



四、实战:用Spring AI构建MCP Client

4.1 配置文件自动注入

Spring AI提供了强大的自动配置能力,只需在application.yaml中配置MCP Server地址,即可自动创建客户端:

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        enabled: true
        type: SYNC
        streamable-http:
          connections:
            my-mcp-server:
              url: http://localhost:18083/mcpserver
              endpoint: /api/mcp    # 必须以/开头!

关键配置说明

  • url:Server基础地址(不含端点路径)

  • endpoint:MCP端点路径(**/**必须以开头,否则工具发现失败)

4.2 使用ChatClient调用工具

ChatClient是Spring AI的高级API,能够自动结合LLM和MCP工具,实现智能工具调用

java 复制代码
@RestController
public class SmartChatController {

    @Autowired
    private ChatClient.Builder chatClientBuilder;

    @Autowired
    private ToolCallbackProvider toolCallbackProvider;

    @GetMapping("/smart-chat")
    public String smartChat(@RequestParam String message) {
        return chatClientBuilder
            .defaultTools("my-mcp-server")
            .build()
            .prompt()
            .user(message)
            .toolCallbacks(toolCallbackProvider)
            .call()
            .content();
    }
}

工作原理

用户输入:"北京天气怎么样?"

ChatClient将用户消息和工具列表发送给LLM

LLM分析:"需要调用getWeather工具"

Spring AI自动调用MCP Server的getWeather工具

获取工具结果:{"city":"北京","temperature":"26°C"}

LLM将工具结果总结为自然语言:"北京今天晴朗,气温26°C,适合出游。"

返回给用户



五、避坑指南:context-path的陷阱

5.1 问题现象

在企业级应用中,context-path非常常见。但配置不当时,会导致MCP客户端无法连接服务端:

错误日志:

HttpClientStreamableHttpTransport - Connection refused

实际请求地址:http://localhost:18083/api/mcp(404)

正确地址应该是:http://localhost:18083/mcpserver/api/mcp

5.2 根本原因

HttpClientStreamableHttpTransport底层使用Java的URI.resolve()方法拼接URL。这个方法的行为取决于endpoint是否以/开头:

配置 结果 说明
baseUrl=/mcpserver/ + endpoint=api/mcp /mcpserver/api/mcp 正确:相对路径附加到baseUrl
baseUrl=/mcpserver + endpoint=api/mcp /api/mcp 错误:替换了baseUrl最后一段
baseUrl=/mcpserver/ + endpoint=/api/mcp /api/mcp 错误:绝对路径覆盖了baseUrl

5.3 正确配置方式

方式一:Spring AI自动配置(推荐)

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        streamable-http:
          connections:
            mcp-server:
              url: http://localhost:18083/mcpserver
              endpoint: /api/mcp    # 必须以/开头

Spring AI会自动处理路径拼接,无需关注尾斜杠问题。

方式二:手动构建Transport

java 复制代码
// ✅ 正确写法:baseUrl以/结尾 + endpoint不以/开头
HttpClientStreamableHttpTransport transport = HttpClientStreamableHttpTransport
    .builder("http://127.0.0.1:18083/mcpserver/")   // 注意尾斜杠
    .endpoint("api/mcp")                              // 注意:没有前导/
    .build();

六、进阶技巧

6.1 工具过滤:只让LLM看到需要的工具

当MCP Server包含大量工具时,可以使用McpToolFilter过滤工具,减少LLM的上下文压力:

java 复制代码
// 只保留名称以"weather"开头的工具
SyncMcpToolCallbackProvider provider = SyncMcpToolCallbackProvider.builder()
    .mcpClients(clients)
    .toolFilter((conn, tool) -> tool.name().startsWith("weather"))
    .build();

过滤策略示例

java 复制代码
// 按工具名称白名单过滤
Set<String> allowedTools = Set.of("getWeather", "getCurrentTime");
.toolFilter((conn, tool) -> allowedTools.contains(tool.name()))

// 按Server连接名称过滤(多MCP Server场景)
.toolFilter((conn, tool) -> conn.serverInfo().name().equals("weather-server"))

// 黑名单过滤(排除危险工具)
Set<String> blockedTools = Set.of("deleteUser", "dropDatabase");
.toolFilter((conn, tool) -> !blockedTools.contains(tool.name()))

6.2 跳过LLM总结:直接返回工具结果

在某些场景下,工具返回的结构化数据就是最终答案,不需要LLM二次总结:

问题 :默认的returnDirect参数对MCP工具不生效!

原因SyncMcpToolCallback未实现getToolMetadata()方法,returnDirect参数无法透传。

解决方案 :自定义ToolCallback继承SyncMcpToolCallback,重写元数据获取逻辑:

java 复制代码
public class ReturnDirectSyncMcpToolCallback extends SyncMcpToolCallback {

    private final boolean returnDirect;

    @Override
    public ToolMetadata getToolMetadata() {
        return ToolMetadata.builder()
                .returnDirect(returnDirect)
                .build();
    }
}

适用场景

  • 多智能体协作(减少延迟和Token消耗)

  • 工具输出即最终答案(天气查询、订单查询)

  • AI工具已内置推理(LightRAG等)



七、总结

7.1 核心要点回顾

  1. MCP协议:AI工具调用的标准化协议,实现"即插即用"

  2. Streamable HTTP:基于HTTP/1.1的流式传输方式,兼顾性能与兼容性

  3. Spring AI集成 :通过@Tool注解和自动配置,快速构建MCP Server/Client

  4. 避坑要点endpoint必须以/开头,注意context-path的路径拼接

  5. 进阶技巧 :工具过滤减少上下文压力,returnDirect跳过LLM总结

7.2 技术选型建议

场景 推荐方案
简单工具调用 WebMVC + 自动配置
高并发流式场景 WebFlux + Streamable HTTP
多MCP Server 配置文件定义多个连接
需要智能判断 ChatClient + ToolCallbackProvider
直接调用工具 McpSyncClient

7.3 下一步学习

灵铠交互专注AI交互场景落地,深耕智能客服、音视频AI应用开发,为企业打造低延迟、高可用的智能化交互解决方案,助力业务AI数字化升级。



参考资源


标签#MCP#SpringAI#AI工具调用#StreamableHTTP#大模型应用

作者:灵铠互动技术团队

发布日期:2026年7月