跳到主要内容 Spring AI MCP Server 集成与使用指南 | 极客日志
Java AI java
Spring AI MCP Server 集成与使用指南 Spring AI MCP Server 扩展了 MCP Java SDK,提供 Spring Boot 集成。通过 WebFlux 或 WebMvc 实现 SSE 传输,支持工具注册与调用。示例展示了如何定义 WeatherService 工具,并通过 McpClient 连接服务器进行工具列表查询和调用。源码分析揭示了自动配置类 McpServerAutoConfiguration 如何构建同步或异步服务器会话,对接 TransportProvider。该方案简化了 LLM 工具调用的服务端开发流程。
云朵棉花糖 发布于 2026/2/15 更新于 2026/4/21 0 浏览概述
本文主要研究 Spring AI 的 MCP Server。
MCP Java SDK
MCP 提供了 Java SDK,同时还提供了 Spring WebFlux 及 MVC 的 SSE 实现。
<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 >
<groupId > io.modelcontextprotocol.sdk</groupId >
<artifactId > mcp-spring-webflux</artifactId >
</dependency >
<dependency >
<groupId > io.modelcontextprotocol.sdk</groupId >
<artifactId > mcp-spring-webmvc</artifactId >
</dependency >
Spring AI MCP
Spring AI MCP 扩展了 MCP Java SDK,提供了 Spring Boot 的集成。
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-starter-mcp-client</artifactId >
</dependency >
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-starter-mcp-client-webflux</artifactId >
</dependency >
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-starter-mcp-server</artifactId >
</dependency >
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-starter-mcp-server-webmvc</artifactId >
</dependency >
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-starter-mcp-server-webflux</artifactId >
其中 mcp-server 提供了 webmvc 及 webflux 两个版本。
示例
pom.xml
<dependency >
<groupId > org.springframework.ai</groupId >
<artifactId > spring-ai-starter-mcp-server-webmvc</artifactId >
</dependency >
配置
@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();
}
WeatherService
@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,
String name,
String startTime,
String endTime,
Boolean isDayTime,
Integer temperature,
String temperatureUnit,
String temperatureTrend,
Map probabilityOfPrecipitation,
String windSpeed,
String windDirection,
String icon,
String shortForecast,
String detailedForecast) { }
}
List<Feature> features) {
Properties properties) { }
String event,
String areaDesc,
String severity,
String description,
String instruction) { }
}
String {
restClient.get()
.uri( , latitude, longitude)
.retrieve()
.body(Points.class);
restClient.get().uri(points.properties().forecast()).retrieve().body(Forecast.class);
forecast.properties().periods().stream().map(p -> {
String.format(
Event: %s Area: %s Severity: %s Description: %s Instructions: %s \n NY
WeatherService 使用 RestClient 去请求 api.weather.gov,它使用@Tool 注解了 getWeatherForecastByLocation、getAlerts 方法。
客户端调用
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();
}
}
这里构建了 HttpClientSseClientTransport,然后通过 McpClient.sync(transport).build() 创建了 McpSyncClient;示例先调用 listTools 查看有哪些 tool,之后构建 CallToolRequest 去请求 getWeatherForecastByLocation、getAlerts 方法。
输出示例:
16 :18 :34.707 [HttpClient - 1 - Worker - 0 ] INFO io.modelcontextprotocol.client.McpAsyncClient -- Server response with Protocol : 2024 - 11 - 05 , Capabilities : ServerCapabilities [experimental= null, logging= LoggingCapabilities [], prompts= null, resources= null, tools= ToolCapabilities [listChanged= true ]], Info : Implementation [name= my- weather- server, version= 0.0 .1 ] and Instructions null
Available Tools = ListToolsResult [tools= [Tool [name= toUpperCase, description= Put the text to upper case , inputSchema= JsonSchema [type= object, properties= {input= {type= string}}, [input], additionalProperties ]], [name getAlerts, description weather alerts a state. letter state code (e.g. , ), inputSchema [type object, properties {state {type string}}, [state], additionalProperties ]], [name getWeatherForecastByLocation, description weather forecast a specific latitude longitude, inputSchema [type object, properties {latitude {type number, format double}, longitude {type number, format double}}, [latitude, longitude], additionalProperties ]]], nextCursor null]
: [content [ [audience null, priority null, text ]], isError ]
[content [ [audience null, priority null,]], isError ]
源码分析
McpSchema
io/modelcontextprotocol/spec/McpSchema.java
public final class McpSchema {
private static final Logger logger = LoggerFactory.getLogger(McpSchema.class);
private McpSchema () { }
public static final String LATEST_PROTOCOL_VERSION = "2024-11-05" ;
public static final String JSONRPC_VERSION = "2.0" ;
public static final String METHOD_INITIALIZE = "initialize" ;
public static final String METHOD_NOTIFICATION_INITIALIZED = "notifications/initialized" ;
public static final String METHOD_PING = "ping" ;
public ;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
();
}
McpSchema 定义了 Lifecycle 方法(initialize, notifications/initialized, ping),Tool 方法(tools/list, tools/call, notifications/tools/list_changed),Resources 方法(resources/list, resources/read, notifications/resources/list_changed, resources/templates/list, resources/subscribe, resources/unsubscribe),Prompt 方法(prompts/list, prompts/get, notifications/prompts/list_changed);Logging 方法(logging/setLevel, notifications/message),Roots 方法(roots/list, notifications/roots/list_changed),Sampling 方法(sampling/createMessage)。
McpSyncClient
io/modelcontextprotocol/client/McpSyncClient.java
public class McpSyncClient implements AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(McpSyncClient.class);
private static final long DEFAULT_CLOSE_TIMEOUT_MS = 10_000L ;
private final McpAsyncClient delegate;
@Deprecated
public McpSyncClient (McpAsyncClient delegate) {
Assert.notNull(delegate, "The delegate can not be null" );
this .delegate = delegate;
}
public McpSchema.ServerCapabilities getServerCapabilities () {
return this .delegate.getServerCapabilities();
}
public McpSchema.Implementation getServerInfo () {
return this .delegate.getServerInfo();
}
public ClientCapabilities getClientCapabilities () {
return this .delegate.getClientCapabilities();
}
McpSchema.Implementation {
.delegate.getClientInfo();
}
{
.delegate.close();
}
{
{
.delegate.closeGracefully().block(Duration.ofMillis(DEFAULT_CLOSE_TIMEOUT_MS));
} (RuntimeException e) {
logger.warn( , DEFAULT_CLOSE_TIMEOUT_MS, e);
;
}
;
}
McpSchema.InitializeResult {
.delegate.initialize().block();
}
{
.delegate.rootsListChangedNotification().block();
}
{
.delegate.addRoot(root).block();
}
{
.delegate.removeRoot(rootUri).block();
}
Object {
.delegate.ping().block();
}
McpSchema.CallToolResult {
.delegate.callTool(callToolRequest).block();
}
McpSchema.ListToolsResult {
.delegate.listTools().block();
}
McpSchema.ListToolsResult {
.delegate.listTools(cursor).block();
}
McpSchema.ListResourcesResult {
.delegate.listResources(cursor).block();
}
McpSchema.ListResourcesResult {
.delegate.listResources().block();
}
McpSchema.ReadResourceResult {
.delegate.readResource(resource).block();
}
McpSchema.ReadResourceResult {
.delegate.readResource(readResourceRequest).block();
}
McpSchema.ListResourceTemplatesResult {
.delegate.listResourceTemplates(cursor).block();
}
McpSchema.ListResourceTemplatesResult {
.delegate.listResourceTemplates().block();
}
{
.delegate.subscribeResource(subscribeRequest).block();
}
{
.delegate.unsubscribeResource(unsubscribeRequest).block();
}
ListPromptsResult {
.delegate.listPrompts(cursor).block();
}
ListPromptsResult {
.delegate.listPrompts().block();
}
GetPromptResult {
.delegate.getPrompt(getPromptRequest).block();
}
{
.delegate.setLoggingLevel(loggingLevel).block();
}
}
McpSyncClient 实现了 AutoCloseable 接口,它主要是使用 McpAsyncClient 进行了包装,包装为同步方法;它提供了 getServerCapabilities、getServerInfo、getClientCapabilities、getClientInfo、initialize、rootsListChangedNotification、addRoot、removeRoot、ping、callTool、listTools、listResources、readResource、listResourceTemplates、subscribeResource、unsubscribeResource、listPrompts、getPrompt、setLoggingLevel。
McpServerAutoConfiguration
org/springframework/ai/mcp/server/autoconfigure/McpServerAutoConfiguration.java
@AutoConfiguration(after = { McpWebMvcServerAutoConfiguration.class, McpWebFluxServerAutoConfiguration.class })
@ConditionalOnClass({ McpSchema.class, McpSyncServer.class })
@EnableConfigurationProperties(McpServerProperties.class)
@Import(McpBackwardCompatibility.class)
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class McpServerAutoConfiguration {
private static final LogAccessor logger = new LogAccessor (McpServerAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean
public McpServerTransportProvider stdioServerTransport () {
return new StdioServerTransportProvider ();
}
@Bean
@ConditionalOnMissingBean
public McpSchema.ServerCapabilities.Builder capabilitiesBuilder () {
return McpSchema.ServerCapabilities.builder();
}
@Bean
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC", matchIfMissing = true)
public List<McpServerFeatures.SyncToolSpecification> syncTools(ObjectProvider<List<ToolCallback>> toolCalls, List<ToolCallback> toolCallbacksList, McpServerProperties serverProperties) {
List<ToolCallback> tools = new ArrayList <>(toolCalls.stream().flatMap(List::stream).toList());
if (!CollectionUtils.isEmpty(toolCallbacksList)) {
tools.addAll(toolCallbacksList);
}
return this .toSyncToolSpecifications(tools, serverProperties);
}
List<McpServerFeatures.SyncToolSpecification> toSyncToolSpecifications(List<ToolCallback> tools, McpServerProperties serverProperties) {
tools.stream().map(tool -> {
tool.getToolDefinition().name();
(serverProperties.getToolResponseMimeType().containsKey(toolName)) ? MimeType.valueOf(serverProperties.getToolResponseMimeType().get(toolName)) : ;
McpToolUtils.toSyncToolSpecification(tool, mimeType);
}).toList();
}
McpSyncServer {
McpSchema. (serverProperties.getName(), serverProperties.getVersion());
McpServer.sync(transportProvider).serverInfo(serverInfo);
List<SyncToolSpecification> toolSpecifications = <>(tools.stream().flatMap(List::stream).toList());
List<ToolCallback> providerToolCallbacks = toolCallbackProvider.stream()
.map(pr -> List.of(pr.getToolCallbacks()))
.flatMap(List::stream)
.filter(fc -> fc ToolCallback)
.map(fc -> (ToolCallback) fc)
.toList();
toolSpecifications.addAll( .toSyncToolSpecifications(providerToolCallbacks, serverProperties));
(!CollectionUtils.isEmpty(toolSpecifications)) {
serverBuilder.tools(toolSpecifications);
capabilitiesBuilder.tools(serverProperties.isToolChangeNotification());
logger.info( + toolSpecifications.size() + + serverProperties.isToolChangeNotification());
}
List<SyncResourceSpecification> resourceSpecifications = resources.stream().flatMap(List::stream).toList();
(!CollectionUtils.isEmpty(resourceSpecifications)) {
serverBuilder.resources(resourceSpecifications);
capabilitiesBuilder.resources( , serverProperties.isResourceChangeNotification());
logger.info( + resourceSpecifications.size() + + serverProperties.isResourceChangeNotification());
}
List<SyncPromptSpecification> promptSpecifications = prompts.stream().flatMap(List::stream).toList();
(!CollectionUtils.isEmpty(promptSpecifications)) {
serverBuilder.prompts(promptSpecifications);
capabilitiesBuilder.prompts(serverProperties.isPromptChangeNotification());
logger.info( + promptSpecifications.size() + + serverProperties.isPromptChangeNotification());
}
rootsChangeConsumers.ifAvailable(consumer -> {
serverBuilder.rootsChangeHandler((exchange, roots) -> {
consumer.accept(exchange, roots);
});
logger.info( );
});
serverBuilder.capabilities(capabilitiesBuilder.build());
serverBuilder.build();
}
List<McpServerFeatures.AsyncToolSpecification> asyncTools(ObjectProvider<List<ToolCallback>> toolCalls, List<ToolCallback> toolCallbackList, McpServerProperties serverProperties) {
List<ToolCallback> tools = <>(toolCalls.stream().flatMap(List::stream).toList());
(!CollectionUtils.isEmpty(toolCallbackList)) {
tools.addAll(toolCallbackList);
}
.toAsyncToolSpecification(tools, serverProperties);
}
List<McpServerFeatures.AsyncToolSpecification> toAsyncToolSpecification(List<ToolCallback> tools, McpServerProperties serverProperties) {
tools.stream().map(tool -> {
tool.getToolDefinition().name();
(serverProperties.getToolResponseMimeType().containsKey(toolName)) ? MimeType.valueOf(serverProperties.getToolResponseMimeType().get(toolName)) : ;
McpToolUtils.toAsyncToolSpecification(tool, mimeType);
}).toList();
}
McpAsyncServer {
McpSchema. (serverProperties.getName(), serverProperties.getVersion());
McpServer.async(transportProvider).serverInfo(serverInfo);
List<AsyncToolSpecification> toolSpecifications = <>(tools.stream().flatMap(List::stream).toList());
List<ToolCallback> providerToolCallbacks = toolCallbackProvider.stream()
.map(pr -> List.of(pr.getToolCallbacks()))
.flatMap(List::stream)
.filter(fc -> fc ToolCallback)
.map(fc -> (ToolCallback) fc)
.toList();
toolSpecifications.addAll( .toAsyncToolSpecification(providerToolCallbacks, serverProperties));
(!CollectionUtils.isEmpty(toolSpecifications)) {
serverBuilder.tools(toolSpecifications);
capabilitiesBuilder.tools(serverProperties.isToolChangeNotification());
logger.info( + toolSpecifications.size() + + serverProperties.isToolChangeNotification());
}
List<AsyncResourceSpecification> resourceSpecifications = resources.stream().flatMap(List::stream).toList();
(!CollectionUtils.isEmpty(resourceSpecifications)) {
serverBuilder.resources(resourceSpecifications);
capabilitiesBuilder.resources( , serverProperties.isResourceChangeNotification());
logger.info( + resourceSpecifications.size() + + serverProperties.isResourceChangeNotification());
}
List<AsyncPromptSpecification> promptSpecifications = prompts.stream().flatMap(List::stream).toList();
(!CollectionUtils.isEmpty(promptSpecifications)) {
serverBuilder.prompts(promptSpecifications);
capabilitiesBuilder.prompts(serverProperties.isPromptChangeNotification());
logger.info( + promptSpecifications.size() + + serverProperties.isPromptChangeNotification());
}
rootsChangeConsumer.ifAvailable(consumer -> {
BiFunction<McpAsyncServerExchange, List<McpSchema.Root>, Mono<Void>> asyncConsumer = (exchange, roots) -> {
consumer.accept(exchange, roots);
Mono.empty();
};
serverBuilder.rootsChangeHandler(asyncConsumer);
logger.info( );
});
serverBuilder.capabilities(capabilitiesBuilder.build());
serverBuilder.build();
}
}
McpServerAutoConfiguration 主要是提供 McpSyncServer 亦或是 McpAsyncServer,它依赖于 McpServerTransportProvider、McpSchema.ServerCapabilities.Builder、McpServerProperties、List<SyncToolSpecification>、List<SyncResourceSpecification>、List<SyncPromptSpecification>、BiConsumer<McpSyncServerExchange, List<McpSchema.Root>>、List<ToolCallbackProvider>;McpSyncServer 内部是包装 McpAsyncServer 来实现的。
McpWebMvcServerAutoConfiguration
org/springframework/ai/mcp/server/autoconfigure/McpWebMvcServerAutoConfiguration.java
@AutoConfiguration
@ConditionalOnClass({ WebMvcSseServerTransportProvider.class })
@ConditionalOnMissingBean(McpServerTransportProvider.class)
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "stdio", havingValue = "false", matchIfMissing = true)
public class McpWebMvcServerAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider (ObjectMapper objectMapper, McpServerProperties serverProperties) {
return new WebMvcSseServerTransportProvider (objectMapper, serverProperties.getSseMessageEndpoint());
}
@Bean
public RouterFunction<ServerResponse> mvcMcpRouterFunction (WebMvcSseServerTransportProvider transportProvider) {
return transportProvider.getRouterFunction();
}
}
McpWebMvcServerAutoConfiguration 自动配置了 WebMvcSseServerTransportProvider、mvcMcpRouterFunction。
小结
MCP 提供了 Java SDK,同时还提供了 Spring WebFlux 及 MVC 的 SSE 实现;Spring AI MCP 扩展了 MCP Java SDK,提供了 Spring Boot 的集成,其中 mcp-server 提供了 webmvc 及 webflux 两个版本。McpAsyncServer 内部实现了 MCP 协议定义的方法的 requestHandlers,之后创建 McpServerSession.Factory,设置给 McpServerTransportProvider,与 Spring 提供的 WebMvcSseServerTransportProvider 衔接起来。WebMvcSseServerTransportProvider 通过 McpServerSession.Factory 来集成 MCP,其构造器定义了 routerFunction(注册到 Spring 中),其 handleSseConnection 方法主要是构建 McpServerSession,其 handleMessage 方法获取 McpServerSession,执行其 handle 方法。
参考资料
相关免费在线工具 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 >
@JsonProperty("name")
@JsonProperty("startTime")
@JsonProperty("endTime")
@JsonProperty("isDaytime")
@JsonProperty("temperature")
@JsonProperty("temperatureUnit")
@JsonProperty("temperatureTrend")
@JsonProperty("probabilityOfPrecipitation")
@JsonProperty("windSpeed")
@JsonProperty("windDirection")
@JsonProperty("icon")
@JsonProperty("shortForecast")
@JsonProperty("detailedForecast")
@JsonIgnoreProperties(ignoreUnknown = true)
public
record
Alert
(@JsonProperty("features")
@JsonIgnoreProperties(ignoreUnknown = true)
public
record
Feature
(@JsonProperty("properties")
@JsonIgnoreProperties(ignoreUnknown = true)
public
record
Properties
(
@JsonProperty("event")
@JsonProperty("areaDesc")
@JsonProperty("severity")
@JsonProperty("description")
@JsonProperty("instruction")
@Tool(description = "Get weather forecast for a specific latitude/longitude")
public
getWeatherForecastByLocation
(double latitude, double longitude)
var
points
=
"/points/{latitude},{longitude}"
var
forecast
=
String
forecastText
=
return
"""
%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;
}
/**
* Get alerts for a specific area
* @param state Area code. Two-letter US state code (e.g. CA, NY)
* @return Human readable alert information
* @throws RestClientException if the request fails
*/
@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("""
",
f.properties().event(), f.properties.areaDesc(), f.properties.severity(), f.properties.description(), f.properties.instruction()))
.collect(Collectors.joining("
"));
}
public static void main(String[] args) {
WeatherService client = new WeatherService();
System.out.println(client.getWeatherForecastByLocation(47.6062, -122.3321));
System.out.println(client.getAlerts("
"));
}
}
required
=
=
false
Tool
=
=
Get
for
US
Input
is
Two
-
US
CA
NY
=
JsonSchema
=
=
=
=
required
=
=
false
Tool
=
=
Get
for
/
=
JsonSchema
=
=
=
=
=
=
=
=
required
=
=
false
=
Weather
Forcast
CallToolResult
=
TextContent
=
=
=
"Overnight:\n Temperature: 50 F\n Wind: 5 mph N\n Forecast: Mostly cloudy, with a low around 50. North wind around 5 mph.\n Wednesday:\n Temperature: 70 F\n Wind: 2 to 7 mph NW\n Forecast: A chance of rain showers between 9am and 5pm, then showers and thunderstorms. Some of the storms could be severe. Mostly cloudy. High near 70, with temperatures falling to around 68 in the afternoon. Northwest wind 2 to 7 mph. Chance of precipitation is 100%. New rainfall amounts between a quarter and half of an inch possible.\n Wednesday Night:\n Temperature: 48 F\n Wind: 7 to 10 mph SSW\n Forecast: Showers and thunderstorms before 5am, then rain. Some of the storms could be severe. Cloudy. Low around 48, with temperatures rising to around 50 overnight. South southwest wind 7 to 10 mph, with gusts as high as 21 mph. Chance of precipitation is 100%. New rainfall amounts between a quarter and half of an inch possible.\n Thursday:\n Temperature: 59 F\n Wind: 9 mph S\n Forecast: Rain. Cloudy, with a high near 59. South wind around 9 mph, with gusts as high as 21 mph. Chance of precipitation is 90%. New rainfall amounts between a tenth and quarter of an inch possible.\n Thursday Night:\n Temperature: 47 F\n Wind: 9 mph S\n Forecast: Rain. Cloudy, with a low around 47. South wind around 9 mph, with gusts as high as 22 mph. Chance of precipitation is 90%. New rainfall amounts between a quarter and half of an inch possible.\n Friday:\n Temperature: 55 F\n Wind: 9 to 13 mph S\n Forecast: Rain. Cloudy, with a high near 55. Chance of precipitation is 100%. New rainfall amounts between a tenth and quarter of an inch possible.\n Friday Night:\n Temperature: 44 F\n Wind: 6 to 10 mph S\n Forecast: Rain. Mostly cloudy, with a low around 44. Chance of precipitation is 80%.\n Saturday:\n Temperature: 55 F\n Wind: 7 mph S\n Forecast: Rain likely. Partly sunny, with a high near 55.\n Saturday Night:\n Temperature: 41 F\n Wind: 2 to 6 mph SE\n Forecast: A chance of rain before 11pm. Mostly cloudy, with a low around 41.\n Sunday:\n Temperature: 59 F\n Wind: 2 to 6 mph NNE\n Forecast: A chance of rain after 11am. Mostly cloudy, with a high near 59.\n Sunday Night:\n Temperature: 45 F\n Wind: 6 mph ESE\n Forecast: Rain likely. Mostly cloudy, with a low around 45.\n Monday:\n Temperature: 54 F\n Wind: 5 to 8 mph S\n Forecast: Rain. Mostly cloudy, with a high near 54.\n Monday Night:\n Temperature: 42 F\n Wind: 5 to 8 mph S\n Forecast: Rain likely. Mostly cloudy, with a low around 42.\n Tuesday:\n Temperature: 54 F\n Wind: 7 mph SSW\n Forecast: Rain likely. Mostly cloudy, with a high near 54.\n "
=
false
Alert
Response
=
CallToolResult
=
TextContent
=
=
=
false
static
final
String
METHOD_TOOLS_LIST
=
"tools/list"
public
static
final
String
METHOD_TOOLS_CALL
=
"tools/call"
public
static
final
String
METHOD_NOTIFICATION_TOOLS_LIST_CHANGED
=
"notifications/tools/list_changed"
public
static
final
String
METHOD_RESOURCES_LIST
=
"resources/list"
public
static
final
String
METHOD_RESOURCES_READ
=
"resources/read"
public
static
final
String
METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED
=
"notifications/resources/list_changed"
public
static
final
String
METHOD_RESOURCES_TEMPLATES_LIST
=
"resources/templates/list"
public
static
final
String
METHOD_RESOURCES_SUBSCRIBE
=
"resources/subscribe"
public
static
final
String
METHOD_RESOURCES_UNSUBSCRIBE
=
"resources/unsubscribe"
public
static
final
String
METHOD_PROMPT_LIST
=
"prompts/list"
public
static
final
String
METHOD_PROMPT_GET
=
"prompts/get"
public
static
final
String
METHOD_NOTIFICATION_PROMPTS_LIST_CHANGED
=
"notifications/prompts/list_changed"
public
static
final
String
METHOD_LOGGING_SET_LEVEL
=
"logging/setLevel"
public
static
final
String
METHOD_NOTIFICATION_MESSAGE
=
"notifications/message"
public
static
final
String
METHOD_ROOTS_LIST
=
"roots/list"
public
static
final
String
METHOD_NOTIFICATION_ROOTS_LIST_CHANGED
=
"notifications/roots/list_changed"
public
static
final
String
METHOD_SAMPLING_CREATE_MESSAGE
=
"sampling/createMessage"
private
static
final
ObjectMapper
OBJECT_MAPPER
=
new
ObjectMapper
public
getClientInfo
()
return
this
@Override
public
void
close
()
this
public
boolean
closeGracefully
()
try
this
catch
"Client didn't close within timeout of {} ms."
return
false
return
true
public
initialize
()
return
this
public
void
rootsListChangedNotification
()
this
public
void
addRoot
(McpSchema.Root root)
this
public
void
removeRoot
(String rootUri)
this
public
ping
()
return
this
public
callTool
(McpSchema.CallToolRequest callToolRequest)
return
this
public
listTools
()
return
this
public
listTools
(String cursor)
return
this
public
listResources
(String cursor)
return
this
public
listResources
()
return
this
public
readResource
(McpSchema.Resource resource)
return
this
public
readResource
(McpSchema.ReadResourceRequest readResourceRequest)
return
this
public
listResourceTemplates
(String cursor)
return
this
public
listResourceTemplates
()
return
this
public
void
subscribeResource
(McpSchema.SubscribeRequest subscribeRequest)
this
public
void
unsubscribeResource
(McpSchema.UnsubscribeRequest unsubscribeRequest)
this
public
listPrompts
(String cursor)
return
this
public
listPrompts
()
return
this
public
getPrompt
(GetPromptRequest getPromptRequest)
return
this
public
void
setLoggingLevel
(McpSchema.LoggingLevel loggingLevel)
this
private
return
String
toolName
=
MimeType
mimeType
=
null
return
@Bean
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC", matchIfMissing = true)
public
mcpSyncServer
(McpServerTransportProvider transportProvider, McpSchema.ServerCapabilities.Builder capabilitiesBuilder, McpServerProperties serverProperties, ObjectProvider<List<SyncToolSpecification>> tools, ObjectProvider<List<SyncResourceSpecification>> resources, ObjectProvider<List<SyncPromptSpecification>> prompts, ObjectProvider<BiConsumer<McpSyncServerExchange, List<McpSchema.Root>>> rootsChangeConsumers, List<ToolCallbackProvider> toolCallbackProvider)
Implementation
serverInfo
=
new
Implementation
SyncSpecification
serverBuilder
=
new
ArrayList
instanceof
this
if
"Registered tools: "
", notification: "
if
false
"Registered resources: "
", notification: "
if
"Registered prompts: "
", notification: "
"Registered roots change consumer"
return
@Bean
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
public
new
ArrayList
if
return
this
private
return
String
toolName
=
MimeType
mimeType
=
null
return
@Bean
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
public
mcpAsyncServer
(McpServerTransportProvider transportProvider, McpSchema.ServerCapabilities.Builder capabilitiesBuilder, McpServerProperties serverProperties, ObjectProvider<List<AsyncToolSpecification>> tools, ObjectProvider<List<AsyncResourceSpecification>> resources, ObjectProvider<List<AsyncPromptSpecification>> prompts, ObjectProvider<BiConsumer<McpAsyncServerExchange, List<McpSchema.Root>>> rootsChangeConsumer, List<ToolCallbackProvider> toolCallbackProvider)
Implementation
serverInfo
=
new
Implementation
AsyncSpecification
serverBuilder
=
new
ArrayList
instanceof
this
if
"Registered tools: "
", notification: "
if
false
"Registered resources: "
", notification: "
if
"Registered prompts: "
", notification: "
return
"Registered roots change consumer"
return