跳到主要内容 基于 SpringBoot 和 Streamable-HTTP 构建 MCP Server | 极客日志
Java AI java
基于 SpringBoot 和 Streamable-HTTP 构建 MCP Server 如何使用 Spring Boot 基于 Streamable-HTTP 协议构建 MCP Server。内容包括开发环境配置、核心依赖引入、MCP 工具类编写、自动配置类创建、启动类及配置文件说明,并通过 curl 命令演示了 initialize、tools/list 和 tools/call 接口的测试流程。重点讲解了 Spring AI Starter 在异步模式下的配置要求及协议选择。
HadoopMan 发布于 2026/3/26 更新于 2026/4/17 10K 浏览背景
最近几年 AI 应用越来越广泛,Anthropic 公司在 2024 年 11 月提出了 MCP 协议,随着时间推移,支持 MCP 协议的厂商越来越多。
MCP 的 client 端和 server 端通讯的协议也在逐步演进,一开始主流的是 SSE 协议,随后又诞生了 Streamable-HTTP 协议,用于取代 SSE 协议。
本文将介绍如何使用 SpringBoot 基于 Streamable-HTTP 构建 MCP-Server 服务端。
SSE 与 Streamable-HTTP
MCP(Model Context Protocol)协议通过 PR #206 引入的 Streamable HTTP 传输层,是针对 AI 模型与外部工具通信场景的一次底层革新。它并非简单替代传统 HTTP+SSE 方案,而是通过 '统一通信范式、动态传输适配、状态化增强' 三大核心设计,解决了企业级 AI 应用在高并发、长周期任务、复杂基础设施环境下的通信痛点,同时降低开发与维护成本,成为 MCP 协议规模化落地的关键支撑。
本文开发环境介绍
开发依赖 版本 Spring Boot 4.0.1 spring-ai-bom 2.0.0-M1 spring-ai-starter-mcp-server-webflux 2.0.0-M1
pom 核心依赖
<dependencyManagement >
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-bom</artifactId >
<version > ${spring-ai.version}</version >
<type > pom</type >
<scope > import</scope >
</dependency >
</dependencyManagement >
org.springframework.ai
spring-ai-starter-mcp-server-webflux
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
</dependency >
spring-ai-starter-mcp-server-webflux和 spring-ai-starter-mcp-server-webmvc都可以,一种是响应式架构,一种是非响应式架构,两者只能二选一。
MCP 工具类 package com.wen3.demo.ai.mcp.server.tools;
import lombok.extern.slf4j.Slf4j;
import org.springaicommunity.mcp.annotation.McpTool;
import org.springaicommunity.mcp.annotation.McpToolParam;
import reactor.core.publisher.Mono;
import java.util.Map;
@Slf4j
public class DemoTool {
@McpTool(name = "hello", description = "根据城市名称获取天气预报")
public Mono<String> hello (@McpToolParam(description = "城市名称,比如:广州") String city) {
log.info("city: {}" , city);
Map<String, String> mockData = Map.of("西安" , "晴天" , "北京" , "小雨" , "上海" , "大雨" );
return Mono.just(mockData.getOrDefault(city, "抱歉:未查询到对应城市!" ));
}
@McpTool(description = "根据名字算八字")
public Mono<String> helloWithName (@McpToolParam(description = "姓名") String name) {
log.info("name: {}" , name);
return Mono.just("五行缺火!" );
}
}
创建自动配置类 package com.wen3.demo.ai.mcp.server.autoconfigure;
import com.wen3.demo.ai.mcp.server.tools.DemoTool;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class McpServerAutoConfiguration {
@ConditionalOnMissingBean
@Bean
DemoTool demoTool () {
return new DemoTool ();
}
}
创建启动类
spring.ai.mcp.server.annotation-scanner.enabled=true默认是 true,所以会自动扫描 @McpTool标注的类,并注册为工具对象。不需要再使用 ToolCallbackProvider 进行注册,否则会重复注册。
配置文件 server:
port: 9090
spring:
ai:
mcp:
server:
name: demo-mcp-server
version: 1.0 .0
type: ASYNC
protocol: STREAMABLE
streamable-http:
mcp-endpoint: /mcp
keep-alive-interval: 30s
必须配置项 spring.ai.mcp.server.protocol=STREAMABLE
必须把 spring.ai.mcp.server.protocol设置为 STREAMABLE,才能开启 Streamable-HTTP 协议
注意配置项 spring.ai.mcp.server.type=ASYNC
如果 spring.ai.mcp.server.type配置 ASYNC,则工具的返回值类型必须是如下类型
Mono<T>
Flux<T>
Publisher<T>
如果 spring.ai.mcp.server.type配置 SYNC,则工具的返回值类型必须是如下类型
Primitive types (int, double, boolean)
Object types (String, Integer, custom POJOs)
MCP types (CallToolResult, ReadResourceResult, GetPromptResult, CompleteResult)
Collections (List<String>, Map<String, Object>)
相关配置类 org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerStreamableHttpProperties
前缀为 spring.ai.mcp.server.streamable-http的配置项,请求端点默认是 /mcp
org.springframework.ai.mcp.server.common.autoconfigure.annotations.McpServerAnnotationScannerProperties
前缀为 spring.ai.mcp.server.annotation-scanner的配置项,默认为扫描 @McpTool注解进行工具的注册
org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerProperties
前缀为 spring.ai.mcp.server的配置项
核心逻辑
io.modelcontextprotocol.server.transport.WebFluxStreamableServerTransportProvider#handlePost
这个方法是收到 POST 请求的处理逻辑
Accept: application/json、Accept: text/event-stream,同时包含这两个请求头,请求才会继续往下走
io.modelcontextprotocol.spec.McpSchema.deserializeJsonRpcMessage
这个方法是解析 json 参数的过程
同时传 method 和 id,就是 JSONRPCRequest 请求
protocolVersion这个字段不能是 null,但可以是空字符串
io.modelcontextprotocol.spec.McpStreamableServerSession#responseStream
这里是根据不同 method,使用不同的 McpRequestHandler 进行处理
io.modelcontextprotocol.server.McpAsyncServer#prepareRequestHandlers
McpRequestHandler 是在这个逻辑里添加的
curl 测试 curl -ik -H "Accept: application/json" -H "Accept: text/event-stream" -XPOST --url "http://localhost:9090/mcp" -d '{"id": "stream-1","method": "initialize", "params": {"capabilities":{}, "clientInfo": {}, "protocolVersion": ""}}'
initialize 请求的响应如下,从响应中拿到 Mcp-Session-Id
{
"jsonrpc" : "2.0" ,
"id" : "stream-1" ,
"result" : {
"protocolVersion" : "2025-06-18" ,
"capabilities" : {
"completions" : { } ,
"logging" : { } ,
"prompts" : { "listChanged" : true } ,
"resources" : { "subscribe" : false , "listChanged" : true } ,
"tools" : { "listChanged" : true }
} ,
"serverInfo" : {
"name" : "demo-mcp-server" ,
"version" : "1.0.0"
}
}
}
curl -ik -H "Mcp-Session-Id: d6d67425-2d58-4758-9bbd-9b2c7521e033" -H "Accept: application/json" -H "Accept: text/event-stream" -XPOST --url "http://localhost:9090/mcp" -d '{"id": "stream-1","method": "tools/list", "params": {}}'
{
"jsonrpc" : "2.0" ,
"id" : "stream-1" ,
"result" : {
"tools" : [
{
"name" : "hello" ,
"title" : "hello" ,
"description" : "根据城市名称获取天气预报" ,
"inputSchema" : {
"type" : "object" ,
"properties" : {
"city" : {
"type" : "string" ,
"description" : "城市名称,比如:广州"
}
} ,
"required" : [ "city" ]
} ,
"annotations" : {
"title" : "" ,
"readOnlyHint" : false ,
"destructiveHint" : true ,
"idempotentHint" : false ,
"openWorldHint" : true
}
}
]
}
}
curl -ik -H "Mcp-Session-Id: d6d67425-2d58-4758-9bbd-9b2c7521e033" -H "Content-Type: application/json;charset=GBK" -H "Accept: application/json" -H "Accept: text/event-stream" -XPOST --url "http://localhost:9090/mcp" -d '{"id": "stream-1","method": "tools/call", "params": {"name":"hello", "arguments": {"city": "北京"}}}'
Content-Type: application/json;charset=GBK 这个请求头主要用来指定编码格式,如果参数是 GBK 编码,则需要添加这个请求头,否则会出现乱码
{
"jsonrpc" : "2.0" ,
"id" : "stream-1" ,
"result" : {
"content" : [ { "type" : "text" , "text" : "小雨" } ] ,
"isError" : false
}
}
curl -ik -H "Mcp-Session-Id: d6d67425-2d58-4758-9bbd-9b2c7521e033" -H "Content-Type: application/json;charset=GBK" -H "Accept: application/json" -H "Accept: text/event-stream" -XPOST --url "http://localhost:9090/mcp" -d '{"id": "stream-1","method": "tools/call", "params": {"name":"helloWithName", "arguments": {"name": "Lucy"}}}'
{
"jsonrpc" : "2.0" ,
"id" : "stream-1xx" ,
"result" : {
"content" : [ { "type" : "text" , "text" : "五行缺火!" } ] ,
"isError" : false
}
}