跳到主要内容
Spring AI MCP Server 集成指南与源码剖析 | 极客日志
Java AI java
Spring AI MCP Server 集成指南与源码剖析 综述由AI生成 Spring AI MCP Server 基于 Model Context Protocol 规范,为 Java 应用提供与大模型交互的工具调用能力。通过引入 Spring Boot Starter,开发者可快速构建支持 SSE 传输的 MCP 服务端或客户端。核心组件包括 McpServerAutoConfiguration 自动配置、ToolCallbackProvider 工具注册以及 WebMvc/WebFlux 传输层适配。结合 WeatherService 示例,演示了如何定义工具、初始化连接及调用流程,并深入分析了 McpSyncClient 与 McpAsyncServer 的内部实现机制,帮助理解协议握手、能力协商及工具调用的底层逻辑。
ServerBase 发布于 2026/3/15 更新于 2026/4/26 3 浏览概述
MCP (Model Context Protocol) 为 Java 应用提供了与大模型交互的标准接口。Spring AI 在此基础上扩展了 Spring Boot 的自动配置能力,使得开发者能够更便捷地构建支持 SSE(Server-Sent Events)传输的 MCP 服务端或客户端。
依赖引入
首先需要引入 MCP Java SDK 以及 Spring AI 的 Starter 依赖。SDK 本身提供了基础的 SSE 实现,而 Spring AI 则封装了这些功能以适配 Spring 生态。
<dependencyManagement >
<dependencies >
<dependency >
<groupId > io.modelcontextprotocol.sdk</groupId >
<artifactId > mcp-bom</artifactId >
<version > 0.8.0</version >
<type > pom</type >
<scope > import</scope >
</dependency >
</dependencies >
</dependencyManagement >
<dependency >
<groupId > io.modelcontextprotocol.sdk</groupId >
<artifactId > mcp</artifactId >
</dependency >
<dependency >
< > io.modelcontextprotocol.sdk
mcp-spring-webflux
io.modelcontextprotocol.sdk
mcp-spring-webmvc
groupId
</groupId >
<artifactId >
</artifactId >
</dependency >
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
</dependency >
在 Spring AI 侧,我们主要关注 spring-ai-starter-mcp-server 系列依赖,它根据传输层不同分为 WebMvc 和 WebFlux 版本。
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-starter-mcp-server-webmvc</artifactId >
</dependency >
工具定义与服务端实现 服务端的核心在于暴露工具(Tools)。我们可以利用 ToolCallbackProvider 将现有的 Service 方法注册为 MCP 工具。
工具服务示例 这里定义了一个 WeatherService,通过 @Tool 注解暴露天气查询和警报获取功能。注意 RestClient 的配置,确保请求头符合 API 规范。
@Service
public class WeatherService {
private static final String BASE_URL = "https://api.weather.gov" ;
private final RestClient restClient;
public WeatherService () {
this .restClient = RestClient.builder()
.baseUrl(BASE_URL)
.defaultHeader("Accept" , "application/geo+json" )
.defaultHeader("User-Agent" , "WeatherApiClient/1.0 ([email protected] )" )
.build();
}
@JsonIgnoreProperties(ignoreUnknown = true)
public record Points (@JsonProperty("properties") Props properties) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record Props (@JsonProperty("forecast") String forecast) { }
}
@JsonIgnoreProperties(ignoreUnknown = true)
public record Forecast (@JsonProperty("properties") Props properties) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record Props (@JsonProperty("periods") List<Period> periods) { }
@JsonIgnoreProperties(ignoreUnknown = true)
public record Period (
@JsonProperty("number") Integer number,
@JsonProperty("name") String name,
@JsonProperty("startTime") String startTime,
@JsonProperty("endTime") String endTime,
@JsonProperty("isDaytime") Boolean isDayTime,
@JsonProperty("temperature") Integer temperature,
@JsonProperty("temperatureUnit") String temperatureUnit,
@JsonProperty("temperatureTrend") String temperatureTrend,
@JsonProperty("probabilityOfPrecipitation") Map probabilityOfPrecipitation,
@JsonProperty("windSpeed") String windSpeed,
@JsonProperty("windDirection") String windDirection,
@JsonProperty("icon") String icon,
@JsonProperty("shortForecast") String shortForecast,
@JsonProperty("detailedForecast") String detailedForecast) { }
}
@JsonIgnoreProperties(ignoreUnknown = true)
public record Alert (@JsonProperty("features") List<Feature> features) {
@JsonIgnoreProperties(ignoreUnknown = true)
public record Feature (@JsonProperty("properties") Properties properties) { }
@JsonIgnoreProperties(ignoreUnknown = true)
public record Properties (
@JsonProperty("event") String event,
@JsonProperty("areaDesc") String areaDesc,
@JsonProperty("severity") String severity,
@JsonProperty("description") String description,
@JsonProperty("instruction") String instruction) { }
}
@Tool(description = "Get weather forecast for a specific latitude/longitude")
public String getWeatherForecastByLocation (double latitude, double longitude) {
var points = restClient.get()
.uri("/points/{latitude},{longitude}" , latitude, longitude)
.retrieve()
.body(Points.class);
var forecast = restClient.get().uri(points.properties().forecast()).retrieve().body(Forecast.class);
String forecastText = forecast.properties().periods().stream().map(p ->
String.format("%s: Temperature: %s %s Wind: %s %s Forecast: %s" ,
p.name(), p.temperature(), p.temperatureUnit(),
p.windSpeed(), p.windDirection(), p.detailedForecast()))
.collect(Collectors.joining());
return forecastText;
}
@Tool(description = "Get weather alerts for a US state. Input is Two-letter US state code (e.g. CA, NY)")
public String getAlerts (String state) {
Alert alert = restClient.get().uri("/alerts/active/area/{state}" , state).retrieve().body(Alert.class);
return alert.features().stream()
.map(f -> String.format("Event: %s Area: %s Severity: %s Description: %s Instructions: %s" ,
f.properties().event(), f.properties.areaDesc(), f.properties.severity(),
f.properties.description(), f.properties.instruction()))
.collect(Collectors.joining("\n" ));
}
}
配置 Bean 接下来需要在配置类中注册这些工具回调提供者。除了外部服务,我们也可以直接定义简单的函数工具。
@Bean
public ToolCallbackProvider weatherTools (WeatherService weatherService) {
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}
@Bean
public ToolCallback toUpperCase () {
return FunctionToolCallback.builder("toUpperCase" , (TextInput input) ->
input.input().toUpperCase())
.inputType(TextInput.class)
.description("Put the text to upper case" )
.build();
}
客户端调用演示 服务端启动后,客户端可以通过 HttpClientSseClientTransport 建立连接。典型的流程包括初始化、Ping 测试、列出可用工具以及调用具体工具。
public class SampleClient {
public static void main (String[] args) {
var transport = new HttpClientSseClientTransport ("http://localhost:8080" );
var client = McpClient.sync(transport).build();
client.initialize();
client.ping();
ListToolsResult toolsList = client.listTools();
System.out.println("Available Tools = " + toolsList);
CallToolResult weatherForcastResult = client.callTool(
new CallToolRequest ("getWeatherForecastByLocation" ,
Map.of("latitude" , "47.6062" , "longitude" , "-122.3321" )));
System.out.println("Weather Forcast: " + weatherForcastResult);
CallToolResult alertResult = client.callTool(
new CallToolRequest ("getAlerts" , Map.of("state" , "NY" )));
System.out.println("Alert Response = " + alertResult);
client.closeGracefully();
}
}
运行后的日志会显示协议版本协商结果及工具列表,随后输出具体的天气数据和警报信息。
核心源码解析 理解底层机制有助于排查问题。Spring AI 内部通过自动配置类组装了整个链路。
协议定义 (McpSchema) McpSchema 类集中定义了协议层面的常量,包括生命周期方法(initialize, ping)、工具方法(tools/list, tools/call)以及资源、提示词等相关方法名。这确保了客户端和服务端对消息类型的认知一致。
同步客户端 (McpSyncClient) McpSyncClient 是对异步内核 McpAsyncClient 的包装,提供了阻塞式 API。它实现了 AutoCloseable 接口,负责管理连接的生命周期。关键方法如 initialize() 用于握手,callTool() 执行实际的工具调用。内部通过 .block() 将响应流转换为同步结果。
自动配置 (McpServerAutoConfiguration) 这是 Spring Boot 集成的核心。该类根据配置属性决定创建 McpSyncServer 还是 McpAsyncServer。
传输层提供 :默认提供 StdioServerTransportProvider,若启用 Web 支持则注入 WebMvcSseServerTransportProvider。
工具注册 :扫描 ToolCallback 和 ToolCallbackProvider,将其转换为 SyncToolSpecification 或 AsyncToolSpecification。
能力构建 :收集工具、资源、提示词等能力,构建 ServerCapabilities。
Web MVC 适配 (McpWebMvcServerAutoConfiguration) 当使用 Web MVC 时,该配置类会自动注册 RouterFunction,将 /mcp 路径映射到 SSE 传输处理逻辑。handleSseConnection 方法负责建立会话,handleMessage 则分发消息给对应的处理器。
总结 Spring AI MCP Server 通过自动配置屏蔽了底层的协议细节,让开发者能专注于业务工具的编写。从依赖引入到工具定义,再到自动配置的装配,整个流程遵循 Spring 惯用模式。理解 McpServerAutoConfiguration 和传输层适配器的协作关系,对于自定义传输协议或扩展功能至关重要。
相关免费在线工具 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