diff --git a/auto-configurations/spring-ai-mcp-client/pom.xml b/auto-configurations/spring-ai-mcp-client/pom.xml
new file mode 100644
index 00000000000..da1b08b6784
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+ spring-ai-mcp-client-spring-boot-autoconfigure
+ jar
+ Spring AI MCP Client Auto Configuration
+ Spring AI MCP Client Auto Configuration
+ https://github.com/spring-projects/spring-ai
+
+
+ https://github.com/spring-projects/spring-ai
+ git://github.com/spring-projects/spring-ai.git
+ git@github.com:spring-projects/spring-ai.git
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.ai
+ spring-ai-mcp
+ ${project.parent.version}
+ true
+
+
+
+ io.modelcontextprotocol.sdk
+ mcp-spring-webflux
+ true
+
+
+
+
+
+
+
+ org.springframework.ai
+ spring-ai-test
+ ${project.parent.version}
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
diff --git a/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/McpClientAutoConfiguration.java b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/McpClientAutoConfiguration.java
new file mode 100644
index 00000000000..23a3d10130c
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/McpClientAutoConfiguration.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ai.autoconfigure.mcp.client;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.modelcontextprotocol.client.McpAsyncClient;
+import io.modelcontextprotocol.client.McpClient;
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.spec.McpSchema;
+
+import org.springframework.ai.autoconfigure.mcp.client.configurer.McpAsyncClientConfigurer;
+import org.springframework.ai.autoconfigure.mcp.client.configurer.McpSyncClientConfigurer;
+import org.springframework.ai.autoconfigure.mcp.client.properties.McpClientCommonProperties;
+import org.springframework.ai.mcp.McpToolUtils;
+import org.springframework.ai.mcp.customizer.McpAsyncClientCustomizer;
+import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
+import org.springframework.ai.tool.ToolCallback;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * Auto-configuration for Model Context Protocol (MCP) client support.
+ *
+ *
+ * This configuration class sets up the necessary beans for MCP client functionality,
+ * including both synchronous and asynchronous clients along with their respective tool
+ * callbacks. It is automatically enabled when the required classes are present on the
+ * classpath and can be explicitly disabled through properties.
+ *
+ *
{@code spring.ai.mcp.client.initialized} - Whether to initialize clients on
+ * creation
+ *
+ *
+ *
+ * The configuration is activated after the transport-specific auto-configurations (Stdio,
+ * SSE HTTP, and SSE WebFlux) to ensure proper initialization order. At least one
+ * transport must be available for the clients to be created.
+ *
+ *
+ * Key features:
+ *
+ *
Synchronous and Asynchronous Client Support:
+ *
+ *
Creates and configures MCP clients based on available transports
+ *
Supports both blocking (sync) and non-blocking (async) operations
+ *
Automatic client initialization if enabled
+ *
+ *
Integration Support:
+ *
+ *
Sets up tool callbacks for Spring AI integration
+ *
Supports multiple named transports
+ *
Proper lifecycle management with automatic cleanup
+ *
+ *
Customization Options:
+ *
+ *
Extensible through {@link McpSyncClientCustomizer} and
+ * {@link McpAsyncClientCustomizer}
+ *
Configurable timeouts and client information
+ *
Support for custom transport implementations
+ *
+ *
+ *
+ * @see McpSyncClient
+ * @see McpAsyncClient
+ * @see McpClientCommonProperties
+ * @see McpSyncClientCustomizer
+ * @see McpAsyncClientCustomizer
+ * @see StdioTransportAutoConfiguration
+ * @see SseHttpClientTransportAutoConfiguration
+ * @see SseWebFluxTransportAutoConfiguration
+ */
+@AutoConfiguration(after = { StdioTransportAutoConfiguration.class, SseHttpClientTransportAutoConfiguration.class,
+ SseWebFluxTransportAutoConfiguration.class })
+@ConditionalOnClass({ McpSchema.class })
+@EnableConfigurationProperties(McpClientCommonProperties.class)
+@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
+ matchIfMissing = true)
+public class McpClientAutoConfiguration {
+
+ /**
+ * Creates a list of {@link McpSyncClient} instances based on the available
+ * transports.
+ *
+ *
+ * Each client is configured with:
+ *
+ *
Client information (name and version) from common properties
+ *
Request timeout settings
+ *
Custom configurations through {@link McpSyncClientConfigurer}
+ *
+ *
+ *
+ * If initialization is enabled in properties, the clients are automatically
+ * initialized.
+ * @param mcpSyncClientConfigurer the configurer for customizing client creation
+ * @param commonProperties common MCP client properties
+ * @param transportsProvider provider of named MCP transports
+ * @return list of configured MCP sync clients
+ */
+ @Bean
+ @ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
+ matchIfMissing = true)
+ public List mcpSyncClients(McpSyncClientConfigurer mcpSyncClientConfigurer,
+ McpClientCommonProperties commonProperties,
+ ObjectProvider> transportsProvider) {
+
+ List mcpSyncClients = new ArrayList<>();
+
+ List namedTransports = transportsProvider.stream().flatMap(List::stream).toList();
+
+ if (!CollectionUtils.isEmpty(namedTransports)) {
+ for (NamedClientMcpTransport namedTransport : namedTransports) {
+
+ McpSchema.Implementation clientInfo = new McpSchema.Implementation(commonProperties.getName(),
+ commonProperties.getVersion());
+
+ McpClient.SyncSpec syncSpec = McpClient.sync(namedTransport.transport())
+ .clientInfo(clientInfo)
+ .requestTimeout(commonProperties.getRequestTimeout());
+
+ syncSpec = mcpSyncClientConfigurer.configure(namedTransport.name(), syncSpec);
+
+ var syncClient = syncSpec.build();
+
+ if (commonProperties.isInitialized()) {
+ syncClient.initialize();
+ }
+
+ mcpSyncClients.add(syncClient);
+ }
+ }
+
+ return mcpSyncClients;
+ }
+
+ /**
+ * Creates tool callbacks for all configured MCP clients.
+ *
+ *
+ * These callbacks enable integration with Spring AI's tool execution framework,
+ * allowing MCP tools to be used as part of AI interactions.
+ * @param mcpClientsProvider provider of MCP sync clients
+ * @return list of tool callbacks for MCP integration
+ */
+ @Bean
+ @ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
+ matchIfMissing = true)
+ public List toolCallbacks(ObjectProvider> mcpClientsProvider) {
+ List mcpClients = mcpClientsProvider.stream().flatMap(List::stream).toList();
+ return McpToolUtils.getToolCallbacksFromSyncClients(mcpClients);
+ }
+
+ /**
+ * Record class that implements {@link AutoCloseable} to ensure proper cleanup of MCP
+ * clients.
+ *
+ *
+ * This class is responsible for closing all MCP sync clients when the application
+ * context is closed, preventing resource leaks.
+ */
+ public record ClosebleMcpSyncClients(List clients) implements AutoCloseable {
+
+ @Override
+ public void close() {
+ this.clients.forEach(McpSyncClient::close);
+ }
+ }
+
+ /**
+ * Creates a closeable wrapper for MCP sync clients to ensure proper resource cleanup.
+ * @param clients the list of MCP sync clients to manage
+ * @return a closeable wrapper for the clients
+ */
+ @Bean
+ @ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
+ matchIfMissing = true)
+ public ClosebleMcpSyncClients makeSyncClientsClosable(List clients) {
+ return new ClosebleMcpSyncClients(clients);
+ }
+
+ /**
+ * Creates the default {@link McpSyncClientConfigurer} if none is provided.
+ *
+ *
+ * This configurer aggregates all available {@link McpSyncClientCustomizer} instances
+ * to allow for customization of MCP sync client creation.
+ * @param customizerProvider provider of MCP sync client customizers
+ * @return the configured MCP sync client configurer
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
+ matchIfMissing = true)
+ McpSyncClientConfigurer mcpSyncClientConfigurer(ObjectProvider customizerProvider) {
+ return new McpSyncClientConfigurer(customizerProvider.orderedStream().toList());
+ }
+
+ // Async client configuration
+
+ @Bean
+ @ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
+ public List mcpAsyncClients(McpAsyncClientConfigurer mcpSyncClientConfigurer,
+ McpClientCommonProperties commonProperties,
+ ObjectProvider> transportsProvider) {
+
+ List mcpSyncClients = new ArrayList<>();
+
+ List namedTransports = transportsProvider.stream().flatMap(List::stream).toList();
+
+ if (!CollectionUtils.isEmpty(namedTransports)) {
+ for (NamedClientMcpTransport namedTransport : namedTransports) {
+
+ McpSchema.Implementation clientInfo = new McpSchema.Implementation(commonProperties.getName(),
+ commonProperties.getVersion());
+
+ McpClient.AsyncSpec syncSpec = McpClient.async(namedTransport.transport())
+ .clientInfo(clientInfo)
+ .requestTimeout(commonProperties.getRequestTimeout());
+
+ syncSpec = mcpSyncClientConfigurer.configure(namedTransport.name(), syncSpec);
+
+ var syncClient = syncSpec.build();
+
+ if (commonProperties.isInitialized()) {
+ syncClient.initialize();
+ }
+
+ mcpSyncClients.add(syncClient);
+ }
+ }
+
+ return mcpSyncClients;
+ }
+
+ @Bean
+ @ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
+ public List asyncToolCallbacks(ObjectProvider> mcpClientsProvider) {
+ List mcpClients = mcpClientsProvider.stream().flatMap(List::stream).toList();
+ return McpToolUtils.getToolCallbacksFromAsyncClinents(mcpClients);
+ }
+
+ public record ClosebleMcpAsyncClients(List clients) implements AutoCloseable {
+ @Override
+ public void close() {
+ this.clients.forEach(McpAsyncClient::close);
+ }
+ }
+
+ @Bean
+ @ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
+ public ClosebleMcpAsyncClients makeAsynClientsClosable(List clients) {
+ return new ClosebleMcpAsyncClients(clients);
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
+ McpAsyncClientConfigurer mcpAsyncClientConfigurer(ObjectProvider customizerProvider) {
+ return new McpAsyncClientConfigurer(customizerProvider.orderedStream().toList());
+ }
+
+}
diff --git a/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/NamedClientMcpTransport.java b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/NamedClientMcpTransport.java
new file mode 100644
index 00000000000..de2bb7c60c5
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/NamedClientMcpTransport.java
@@ -0,0 +1,32 @@
+/*
+* Copyright 2024 - 2024 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.springframework.ai.autoconfigure.mcp.client;
+
+import io.modelcontextprotocol.spec.ClientMcpTransport;
+
+/**
+ * A named MCP client transport. Usually created by the transport auto-configurations, but
+ * you can also create them manually. Expose the list castom NamedClientMcpTransport
+ * as @Bean.
+ *
+ * @param name the name of the transport. Usually the name of the server connection.
+ * @param transport the MCP client transport.
+ * @author Christian Tzolov
+ * @since 1.0.0
+ */
+public record NamedClientMcpTransport(String name, ClientMcpTransport transport) {
+
+}
diff --git a/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/SseHttpClientTransportAutoConfiguration.java b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/SseHttpClientTransportAutoConfiguration.java
new file mode 100644
index 00000000000..31aaa2244b1
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/SseHttpClientTransportAutoConfiguration.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ai.autoconfigure.mcp.client;
+
+import java.net.http.HttpClient;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
+import io.modelcontextprotocol.spec.McpSchema;
+
+import org.springframework.ai.autoconfigure.mcp.client.properties.McpClientCommonProperties;
+import org.springframework.ai.autoconfigure.mcp.client.properties.McpSseClientProperties;
+import org.springframework.ai.autoconfigure.mcp.client.properties.McpSseClientProperties.SseParameters;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * Auto-configuration for Server-Sent Events (SSE) HTTP client transport in the Model
+ * Context Protocol (MCP).
+ *
+ *
+ * This configuration class sets up the necessary beans for SSE-based HTTP client
+ * transport when WebFlux is not available. It provides HTTP client-based SSE transport
+ * implementation for MCP client communication.
+ *
+ *
+ * The configuration is activated after the WebFlux SSE transport auto-configuration to
+ * ensure proper fallback behavior when WebFlux is not available.
+ *
+ *
+ * Key features:
+ *
+ *
Creates HTTP client-based SSE transports for configured MCP server connections
+ *
Configures ObjectMapper for JSON serialization/deserialization
+ *
Supports multiple named server connections with different URLs
+ *
+ *
+ * @see HttpClientSseClientTransport
+ * @see McpSseClientProperties
+ */
+@AutoConfiguration(after = SseWebFluxTransportAutoConfiguration.class)
+@ConditionalOnClass({ McpSchema.class, McpSyncClient.class })
+@ConditionalOnMissingClass("io.modelcontextprotocol.client.transport.public class WebFluxSseClientTransport")
+@EnableConfigurationProperties({ McpSseClientProperties.class, McpClientCommonProperties.class })
+@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
+ matchIfMissing = true)
+public class SseHttpClientTransportAutoConfiguration {
+
+ /**
+ * Creates a list of HTTP client-based SSE transports for MCP communication.
+ *
+ *
+ * Each transport is configured with:
+ *
+ *
A new HttpClient instance
+ *
Server URL from properties
+ *
ObjectMapper for JSON processing
+ *
+ * @param sseProperties the SSE client properties containing server configurations
+ * @param objectMapper the ObjectMapper for JSON serialization/deserialization
+ * @return list of named MCP transports
+ */
+ @Bean
+ public List mcpHttpClientTransports(McpSseClientProperties sseProperties,
+ ObjectMapper objectMapper) {
+
+ List sseTransports = new ArrayList<>();
+
+ for (Map.Entry serverParameters : sseProperties.getConnections().entrySet()) {
+
+ var transport = new HttpClientSseClientTransport(HttpClient.newBuilder(), serverParameters.getValue().url(),
+ objectMapper);
+ sseTransports.add(new NamedClientMcpTransport(serverParameters.getKey(), transport));
+ }
+
+ return sseTransports;
+ }
+
+ /**
+ * Creates the default ObjectMapper if none is provided.
+ *
+ *
+ * This ObjectMapper is used for JSON serialization and deserialization in the SSE
+ * transport implementation.
+ * @return the configured ObjectMapper instance
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public ObjectMapper objectMapper() {
+ return new ObjectMapper();
+ }
+
+}
diff --git a/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/SseWebFluxTransportAutoConfiguration.java b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/SseWebFluxTransportAutoConfiguration.java
new file mode 100644
index 00000000000..d92520e5a67
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/SseWebFluxTransportAutoConfiguration.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ai.autoconfigure.mcp.client;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
+
+import org.springframework.ai.autoconfigure.mcp.client.properties.McpClientCommonProperties;
+import org.springframework.ai.autoconfigure.mcp.client.properties.McpSseClientProperties;
+import org.springframework.ai.autoconfigure.mcp.client.properties.McpSseClientProperties.SseParameters;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.reactive.function.client.WebClient;
+
+/**
+ * Auto-configuration for WebFlux-based Server-Sent Events (SSE) client transport in the
+ * Model Context Protocol (MCP).
+ *
+ *
+ * This configuration class sets up the necessary beans for SSE-based WebFlux transport,
+ * providing reactive transport implementation for MCP client communication when WebFlux
+ * is available on the classpath.
+ *
+ *
+ * Key features:
+ *
+ *
Creates WebFlux-based SSE transports for configured MCP server connections
+ *
Configures WebClient.Builder for HTTP client operations
+ *
Sets up ObjectMapper for JSON serialization/deserialization
+ *
Supports multiple named server connections with different base URLs
+ *
+ *
+ * @see WebFluxSseClientTransport
+ * @see McpSseClientProperties
+ */
+@AutoConfiguration
+@ConditionalOnClass(WebFluxSseClientTransport.class)
+@EnableConfigurationProperties({ McpSseClientProperties.class, McpClientCommonProperties.class })
+@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
+ matchIfMissing = true)
+public class SseWebFluxTransportAutoConfiguration {
+
+ /**
+ * Creates a list of WebFlux-based SSE transports for MCP communication.
+ *
+ *
+ * Each transport is configured with:
+ *
+ *
A cloned WebClient.Builder with server-specific base URL
+ *
ObjectMapper for JSON processing
+ *
Server connection parameters from properties
+ *
+ * @param sseProperties the SSE client properties containing server configurations
+ * @param webClientBuilderTemplate the template WebClient.Builder to clone for each
+ * connection
+ * @param objectMapper the ObjectMapper for JSON serialization/deserialization
+ * @return list of named MCP transports
+ */
+ @Bean
+ public List webFluxClientTransports(McpSseClientProperties sseProperties,
+ WebClient.Builder webClientBuilderTemplate, ObjectMapper objectMapper) {
+
+ List sseTransports = new ArrayList<>();
+
+ for (Map.Entry serverParameters : sseProperties.getConnections().entrySet()) {
+ var webClientBuilder = webClientBuilderTemplate.clone().baseUrl(serverParameters.getValue().url());
+ var transport = new WebFluxSseClientTransport(webClientBuilder, objectMapper);
+ sseTransports.add(new NamedClientMcpTransport(serverParameters.getKey(), transport));
+ }
+
+ return sseTransports;
+ }
+
+ /**
+ * Creates the default WebClient.Builder if none is provided.
+ *
+ *
+ * This builder serves as a template for creating server-specific WebClient instances
+ * used in SSE transport implementation.
+ * @return the configured WebClient.Builder instance
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public WebClient.Builder webClientBuilder() {
+ return WebClient.builder();
+ }
+
+ /**
+ * Creates the default ObjectMapper if none is provided.
+ *
+ *
+ * This ObjectMapper is used for JSON serialization and deserialization in the SSE
+ * transport implementation.
+ * @return the configured ObjectMapper instance
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public ObjectMapper objectMapper() {
+ return new ObjectMapper();
+ }
+
+}
diff --git a/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/StdioTransportAutoConfiguration.java b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/StdioTransportAutoConfiguration.java
new file mode 100644
index 00000000000..bc5e8d3c952
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/StdioTransportAutoConfiguration.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ai.autoconfigure.mcp.client;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import io.modelcontextprotocol.client.transport.ServerParameters;
+import io.modelcontextprotocol.client.transport.StdioClientTransport;
+import io.modelcontextprotocol.spec.McpSchema;
+
+import org.springframework.ai.autoconfigure.mcp.client.properties.McpClientCommonProperties;
+import org.springframework.ai.autoconfigure.mcp.client.properties.McpStdioClientProperties;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * Auto-configuration for Standard Input/Output (stdio) transport in the Model Context
+ * Protocol (MCP).
+ *
+ *
+ * This configuration class sets up the necessary beans for stdio-based transport,
+ * enabling communication with MCP servers through standard input and output streams.
+ *
+ *
+ * Key features:
+ *
+ *
Creates stdio transports for configured MCP server connections
+ *
Supports multiple named server connections with different parameters
+ *
Configures transport with server-specific parameters
+ *
+ *
+ * @see StdioClientTransport
+ * @see McpStdioClientProperties
+ */
+@AutoConfiguration
+@ConditionalOnClass({ McpSchema.class })
+@EnableConfigurationProperties({ McpStdioClientProperties.class, McpClientCommonProperties.class })
+@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
+ matchIfMissing = true)
+public class StdioTransportAutoConfiguration {
+
+ /**
+ * Creates a list of stdio-based transports for MCP communication.
+ *
+ *
+ * Each transport is configured with:
+ *
+ *
Server-specific parameters from properties
+ *
Unique connection name for identification
+ *
+ * @param sdioProperties the stdio client properties containing server configurations
+ * @return list of named MCP transports
+ */
+ @Bean
+ public List stdioTransports(McpStdioClientProperties sdioProperties) {
+
+ List stdoiTransports = new ArrayList<>();
+
+ for (Map.Entry serverParameters : sdioProperties.toServerParameters().entrySet()) {
+ var transport = new StdioClientTransport(serverParameters.getValue());
+ stdoiTransports.add(new NamedClientMcpTransport(serverParameters.getKey(), transport));
+
+ }
+
+ return stdoiTransports;
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/McpSyncClientConfigurer.java b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/configurer/McpAsyncClientConfigurer.java
similarity index 62%
rename from spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/McpSyncClientConfigurer.java
rename to auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/configurer/McpAsyncClientConfigurer.java
index 159a38057b0..70b33d7a0f6 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/McpSyncClientConfigurer.java
+++ b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/configurer/McpAsyncClientConfigurer.java
@@ -14,30 +14,30 @@
* limitations under the License.
*/
-package org.springframework.ai.autoconfigure.mcp.client.stdio;
+package org.springframework.ai.autoconfigure.mcp.client.configurer;
import java.util.List;
import io.modelcontextprotocol.client.McpClient;
-import org.springframework.ai.mcp.McpSyncClientCustomizer;
+import org.springframework.ai.mcp.customizer.McpAsyncClientCustomizer;
-public class McpSyncClientConfigurer {
+public class McpAsyncClientConfigurer {
- private List customizers;
+ private List customizers;
- void setCustomizers(List customizers) {
+ public McpAsyncClientConfigurer(List customizers) {
this.customizers = customizers;
}
- public McpClient.SyncSpec configure(String name, McpClient.SyncSpec spec) {
+ public McpClient.AsyncSpec configure(String name, McpClient.AsyncSpec spec) {
applyCustomizers(name, spec);
return spec;
}
- private void applyCustomizers(String name, McpClient.SyncSpec spec) {
+ private void applyCustomizers(String name, McpClient.AsyncSpec spec) {
if (this.customizers != null) {
- for (McpSyncClientCustomizer customizer : this.customizers) {
+ for (McpAsyncClientCustomizer customizer : this.customizers) {
customizer.customize(name, spec);
}
}
diff --git a/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/configurer/McpSyncClientConfigurer.java b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/configurer/McpSyncClientConfigurer.java
new file mode 100644
index 00000000000..cc8c331cd9f
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/configurer/McpSyncClientConfigurer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ai.autoconfigure.mcp.client.configurer;
+
+import java.util.List;
+
+import io.modelcontextprotocol.client.McpClient;
+
+import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
+
+/**
+ * Configurer class for customizing MCP synchronous clients.
+ *
+ *
+ * This class manages a collection of {@link McpSyncClientCustomizer} instances that can
+ * be applied to customize the configuration of MCP synchronous clients during their
+ * creation.
+ *
+ *
+ * The configurer applies customizations in the order they are registered, allowing for
+ * sequential modifications to the client specifications.
+ *
+ * @see McpSyncClientCustomizer
+ * @see McpClient.SyncSpec
+ */
+public class McpSyncClientConfigurer {
+
+ private List customizers;
+
+ public McpSyncClientConfigurer(List customizers) {
+ this.customizers = customizers;
+ }
+
+ /**
+ * Configures an MCP sync client specification by applying all registered customizers.
+ * @param name the name of the client being configured
+ * @param spec the specification to customize
+ * @return the customized specification
+ */
+ public McpClient.SyncSpec configure(String name, McpClient.SyncSpec spec) {
+ applyCustomizers(name, spec);
+ return spec;
+ }
+
+ /**
+ * Applies all registered customizers to the given specification.
+ *
+ *
+ * Customizers are applied in the order they were registered. If no customizers are
+ * registered, this method has no effect.
+ * @param name the name of the client being customized
+ * @param spec the specification to customize
+ */
+ private void applyCustomizers(String name, McpClient.SyncSpec spec) {
+ if (this.customizers != null) {
+ for (McpSyncClientCustomizer customizer : this.customizers) {
+ customizer.customize(name, spec);
+ }
+ }
+ }
+
+}
diff --git a/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/properties/McpClientCommonProperties.java b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/properties/McpClientCommonProperties.java
new file mode 100644
index 00000000000..b7c73aae12b
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/properties/McpClientCommonProperties.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ai.autoconfigure.mcp.client.properties;
+
+import java.time.Duration;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Common Configuration properties for the Model Context Protocol (MCP) clients shared for
+ * all transport types.
+ *
+ * @author Christian Tzolov
+ * @since 1.0.0
+ */
+@ConfigurationProperties(McpClientCommonProperties.CONFIG_PREFIX)
+public class McpClientCommonProperties {
+
+ public static final String CONFIG_PREFIX = "spring.ai.mcp.client";
+
+ /**
+ * Enable/disable the MCP client.
+ *
+ * When set to false, the MCP client and all its components will not be initialized.
+ */
+ private boolean enabled = true;
+
+ /**
+ * The name of the MCP client instance.
+ *
+ * This name is reported to clients and used for compatibility checks.
+ */
+ private String name = "spring-ai-mcp-client";
+
+ /**
+ * The version of the MCP client instance.
+ *
+ * This version is reported to clients and used for compatibility checks.
+ */
+ private String version = "1.0.0";
+
+ /**
+ * Flag to indicate if the MCP client has to be initialized.
+ */
+ private boolean initialized = true;
+
+ /**
+ * The timeout duration for MCP client requests.
+ *
+ * Defaults to 20 seconds.
+ */
+ private Duration requestTimeout = Duration.ofSeconds(20);
+
+ /**
+ * The type of client to use for MCP client communication.
+ *
+ * When enabled, the client will be notified of changes to the root configuration.
+ * Defaults to true.
+ */
+ private boolean rootChangeNotification = true;
+
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getVersion() {
+ return this.version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public boolean isInitialized() {
+ return this.initialized;
+ }
+
+ public void setInitialized(boolean initialized) {
+ this.initialized = initialized;
+ }
+
+ public Duration getRequestTimeout() {
+ return this.requestTimeout;
+ }
+
+ public void setRequestTimeout(Duration requestTimeout) {
+ this.requestTimeout = requestTimeout;
+ }
+
+ public ClientType getType() {
+ return this.type;
+ }
+
+ public void setType(ClientType type) {
+ this.type = type;
+ }
+
+ public boolean isRootChangeNotification() {
+ return this.rootChangeNotification;
+ }
+
+ public void setRootChangeNotification(boolean rootChangeNotification) {
+ this.rootChangeNotification = rootChangeNotification;
+ }
+
+}
diff --git a/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/properties/McpSseClientProperties.java b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/properties/McpSseClientProperties.java
new file mode 100644
index 00000000000..ed5575a1422
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/properties/McpSseClientProperties.java
@@ -0,0 +1,73 @@
+/*
+* Copyright 2024 - 2024 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.springframework.ai.autoconfigure.mcp.client.properties;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Configuration properties for Server-Sent Events (SSE) based MCP client connections.
+ *
+ *
+ * These properties allow configuration of multiple named SSE connections to MCP servers.
+ * Each connection is configured with a URL endpoint for SSE communication.
+ *
+ *
+ *
+ * @author Christian Tzolov
+ * @since 1.0.0
+ * @see SseParameters
+ */
+@ConfigurationProperties(McpSseClientProperties.CONFIG_PREFIX)
+public class McpSseClientProperties {
+
+ public static final String CONFIG_PREFIX = "spring.ai.mcp.client.sse";
+
+ /**
+ * Parameters for configuring an SSE connection to an MCP server.
+ *
+ * @param url the URL endpoint for SSE communication with the MCP server
+ */
+ public record SseParameters(String url) {
+ }
+
+ /**
+ * Map of named SSE connection configurations.
+ *
+ * The key represents the connection name, and the value contains the SSE parameters
+ * for that connection.
+ */
+ private final Map connections = new HashMap<>();
+
+ /**
+ * Returns the map of configured SSE connections.
+ * @return map of connection names to their SSE parameters
+ */
+ public Map getConnections() {
+ return this.connections;
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/McpStdioClientProperties.java b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/properties/McpStdioClientProperties.java
similarity index 66%
rename from spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/McpStdioClientProperties.java
rename to auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/properties/McpStdioClientProperties.java
index 34a72f67aaa..1284b9d5100 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/McpStdioClientProperties.java
+++ b/auto-configurations/spring-ai-mcp-client/src/main/java/org/springframework/ai/autoconfigure/mcp/client/properties/McpStdioClientProperties.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.autoconfigure.mcp.client.stdio;
+package org.springframework.ai.autoconfigure.mcp.client.properties;
import java.time.Duration;
import java.util.HashMap;
@@ -47,35 +47,6 @@ public class McpStdioClientProperties {
public static final String CONFIG_PREFIX = "spring.ai.mcp.client.stdio";
- /**
- * Enable/disable the MCP client.
- *
- * When set to false, the MCP client and all its components will not be initialized.
- */
- private boolean enabled = false;
-
- /**
- * The version of the MCP client instance.
- *
- * This version is reported to clients and used for compatibility checks.
- */
- private String version = "1.0.0";
-
- /**
- * The timeout duration for MCP client requests.
- *
- * Defaults to 20 seconds.
- */
- private Duration requestTimeout = Duration.ofSeconds(20);
-
- /**
- * Flag to enable/disable root change notifications.
- *
- * When enabled, the client will be notified of changes to the root configuration.
- * Defaults to true.
- */
- private boolean rootChangeNotification = true;
-
/**
* Resource containing the MCP servers configuration.
*
@@ -90,12 +61,7 @@ public class McpStdioClientProperties {
* Each entry represents a named connection with its specific configuration
* parameters.
*/
- private final Map stdioConnections = new HashMap<>();
-
- /**
- * Flag to indicate if the MCP client has to be initialized.
- */
- private boolean initialize = true;
+ private final Map connections = new HashMap<>();
public Resource getServersConfiguration() {
return this.serversConfiguration;
@@ -105,50 +71,8 @@ public void setServersConfiguration(Resource stdioConnectionResources) {
this.serversConfiguration = stdioConnectionResources;
}
- public Map getStdioConnections() {
- return this.stdioConnections;
- }
-
- public boolean isRootChangeNotification() {
- return this.rootChangeNotification;
- }
-
- public void setRootChangeNotification(boolean rootChangeNotification) {
- this.rootChangeNotification = rootChangeNotification;
- }
-
- public Duration getRequestTimeout() {
- return this.requestTimeout;
- }
-
- public void setRequestTimeout(Duration requestTimeout) {
- Assert.notNull(requestTimeout, "Request timeout must not be null");
- this.requestTimeout = requestTimeout;
- }
-
- public boolean isEnabled() {
- return this.enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public String getVersion() {
- return this.version;
- }
-
- public void setVersion(String version) {
- Assert.hasText(version, "Version must not be empty");
- this.version = version;
- }
-
- public boolean isInitialize() {
- return this.initialize;
- }
-
- public void setInitialize(boolean initialize) {
- this.initialize = initialize;
+ public Map getConnections() {
+ return this.connections;
}
/**
@@ -170,6 +94,11 @@ public record Parameters(
* Map of environment variables for the server process.
*/
@JsonProperty("env") Map env) {
+
+ public ServerParameters toServerParameters() {
+ return ServerParameters.builder(this.command()).args(this.args()).env(this.env()).build();
+ }
+
}
private Map resourceToServerParameters() {
@@ -200,7 +129,7 @@ public Map toServerParameters() {
serverParameters.putAll(resourceToServerParameters());
}
- for (Map.Entry entry : this.stdioConnections.entrySet()) {
+ for (Map.Entry entry : this.connections.entrySet()) {
serverParameters.put(entry.getKey(), entry.getValue().toServerParameters());
}
return serverParameters;
diff --git a/auto-configurations/spring-ai-mcp-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/auto-configurations/spring-ai-mcp-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 00000000000..f13b5f74875
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,21 @@
+#
+# Copyright 2025-2025 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+org.springframework.ai.autoconfigure.mcp.client.StdioTransportAutoConfiguration
+org.springframework.ai.autoconfigure.mcp.client.SseWebFluxTransportAutoConfiguration
+org.springframework.ai.autoconfigure.mcp.client.SseHttpClientTransportAutoConfiguration
+org.springframework.ai.autoconfigure.mcp.client.McpClientAutoConfiguration
+
+
diff --git a/auto-configurations/spring-ai-mcp-client/src/test/java/org/springframework/ai/autoconfigure/mcp/client/McpClientAutoConfigurationIT.java b/auto-configurations/spring-ai-mcp-client/src/test/java/org/springframework/ai/autoconfigure/mcp/client/McpClientAutoConfigurationIT.java
new file mode 100644
index 00000000000..780d105d973
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/test/java/org/springframework/ai/autoconfigure/mcp/client/McpClientAutoConfigurationIT.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ai.autoconfigure.mcp.client;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.function.Function;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import io.modelcontextprotocol.client.McpAsyncClient;
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.spec.ClientMcpTransport;
+import io.modelcontextprotocol.spec.McpSchema;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import reactor.core.publisher.Mono;
+
+import org.springframework.ai.autoconfigure.mcp.client.configurer.McpSyncClientConfigurer;
+import org.springframework.ai.autoconfigure.mcp.client.properties.McpClientCommonProperties;
+import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
+import org.springframework.ai.tool.ToolCallback;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Disabled
+public class McpClientAutoConfigurationIT {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(McpClientAutoConfiguration.class));
+
+ @Test
+ void defaultConfiguration() {
+ this.contextRunner.withUserConfiguration(TestTransportConfiguration.class).run(context -> {
+ List clients = context.getBean("mcpSyncClients", List.class);
+ assertThat(clients).hasSize(1);
+
+ McpClientCommonProperties properties = context.getBean(McpClientCommonProperties.class);
+ assertThat(properties.getName()).isEqualTo("mcp-client");
+ assertThat(properties.getVersion()).isEqualTo("1.0.0");
+ assertThat(properties.getType()).isEqualTo(McpClientCommonProperties.ClientType.SYNC);
+ assertThat(properties.getRequestTimeout()).isEqualTo(Duration.ofSeconds(30));
+ assertThat(properties.isInitialized()).isTrue();
+ });
+ }
+
+ @Test
+ void asyncConfiguration() {
+ this.contextRunner
+ .withPropertyValues("spring.ai.mcp.client.type=ASYNC", "spring.ai.mcp.client.name=test-client",
+ "spring.ai.mcp.client.version=2.0.0", "spring.ai.mcp.client.request-timeout=60s",
+ "spring.ai.mcp.client.initialized=false")
+ .withUserConfiguration(TestTransportConfiguration.class)
+ .run(context -> {
+ List clients = context.getBean("mcpAsyncClients", List.class);
+ assertThat(clients).hasSize(1);
+
+ McpClientCommonProperties properties = context.getBean(McpClientCommonProperties.class);
+ assertThat(properties.getName()).isEqualTo("test-client");
+ assertThat(properties.getVersion()).isEqualTo("2.0.0");
+ assertThat(properties.getType()).isEqualTo(McpClientCommonProperties.ClientType.ASYNC);
+ assertThat(properties.getRequestTimeout()).isEqualTo(Duration.ofSeconds(60));
+ assertThat(properties.isInitialized()).isFalse();
+ });
+ }
+
+ @Test
+ void disabledConfiguration() {
+ this.contextRunner.withPropertyValues("spring.ai.mcp.client.enabled=false").run(context -> {
+ assertThat(context).doesNotHaveBean(McpSyncClient.class);
+ assertThat(context).doesNotHaveBean(McpAsyncClient.class);
+ assertThat(context).doesNotHaveBean(ToolCallback.class);
+ });
+ }
+
+ @Test
+ void customTransportConfiguration() {
+ this.contextRunner.withUserConfiguration(CustomTransportConfiguration.class).run(context -> {
+ List transports = context.getBean("customTransports", List.class);
+ assertThat(transports).hasSize(1);
+ assertThat(transports.get(0).transport()).isInstanceOf(CustomClientTransport.class);
+ });
+ }
+
+ @Test
+ void clientCustomization() {
+ this.contextRunner.withUserConfiguration(TestTransportConfiguration.class, CustomizerConfiguration.class)
+ .run(context -> {
+ assertThat(context).hasSingleBean(McpSyncClientConfigurer.class);
+ List clients = context.getBean("mcpSyncClients", List.class);
+ assertThat(clients).hasSize(1);
+ });
+ }
+
+ @Test
+ void toolCallbacksCreation() {
+ this.contextRunner.withUserConfiguration(TestTransportConfiguration.class).run(context -> {
+ assertThat(context).hasSingleBean(List.class);
+ List callbacks = context.getBean("toolCallbacks", List.class);
+ assertThat(callbacks).isNotEmpty();
+ });
+ }
+
+ @Test
+ void closeableWrappersCreation() {
+ this.contextRunner.withUserConfiguration(TestTransportConfiguration.class).run(context -> {
+ assertThat(context).hasSingleBean(McpClientAutoConfiguration.ClosebleMcpSyncClients.class);
+ });
+ }
+
+ @Configuration
+ static class TestTransportConfiguration {
+
+ @Bean
+ List testTransports() {
+ return List.of(new NamedClientMcpTransport("test", Mockito.mock(ClientMcpTransport.class)));
+ }
+
+ }
+
+ @Configuration
+ static class CustomTransportConfiguration {
+
+ @Bean
+ List customTransports() {
+ return List.of(new NamedClientMcpTransport("custom", new CustomClientTransport()));
+ }
+
+ }
+
+ @Configuration
+ static class CustomizerConfiguration {
+
+ @Bean
+ McpSyncClientCustomizer testCustomizer() {
+ return (name, spec) -> {
+ /* no-op */ };
+ }
+
+ }
+
+ static class CustomClientTransport implements ClientMcpTransport {
+
+ @Override
+ public void close() {
+ // Test implementation
+ }
+
+ @Override
+ public Mono connect(
+ Function, Mono> messageHandler) {
+ return Mono.empty(); // Test implementation
+ }
+
+ @Override
+ public Mono sendMessage(McpSchema.JSONRPCMessage message) {
+ return Mono.empty(); // Test implementation
+ }
+
+ @Override
+ public T unmarshalFrom(Object value, TypeReference type) {
+ return null; // Test implementation
+ }
+
+ @Override
+ public Mono closeGracefully() {
+ return Mono.empty(); // Test implementation
+ }
+
+ }
+
+}
diff --git a/auto-configurations/spring-ai-mcp-client/src/test/resources/application-test.properties b/auto-configurations/spring-ai-mcp-client/src/test/resources/application-test.properties
new file mode 100644
index 00000000000..9107b9e407a
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-client/src/test/resources/application-test.properties
@@ -0,0 +1,10 @@
+# Test MCP STDIO client configuration
+spring.ai.mcp.client.stdio.enabled=true
+spring.ai.mcp.client.stdio.version=test-version
+spring.ai.mcp.client.stdio.request-timeout=15s
+spring.ai.mcp.client.stdio.root-change-notification=false
+
+# Test server configuration
+spring.ai.mcp.client.stdio.stdio-connections.test-server.command=echo
+spring.ai.mcp.client.stdio.stdio-connections.test-server.args[0]=test
+spring.ai.mcp.client.stdio.stdio-connections.test-server.env.TEST_ENV=test-value
diff --git a/auto-configurations/spring-ai-mcp-server/pom.xml b/auto-configurations/spring-ai-mcp-server/pom.xml
new file mode 100644
index 00000000000..751884f8aec
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-server/pom.xml
@@ -0,0 +1,62 @@
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+ spring-ai-mcp-server-spring-boot-autoconfigure
+ jar
+ Spring AI MCP Server Auto Configuration
+ Spring AI MCP Server Auto Configuration
+ https://github.com/spring-projects/spring-ai
+
+
+ https://github.com/spring-projects/spring-ai
+ git://github.com/spring-projects/spring-ai.git
+ git@github.com:spring-projects/spring-ai.git
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.ai
+ spring-ai-mcp
+ ${project.parent.version}
+ true
+
+
+
+ io.modelcontextprotocol.sdk
+ mcp-spring-webflux
+ true
+
+
+
+ io.modelcontextprotocol.sdk
+ mcp-spring-webmvc
+ true
+
+
+
+
+
+ org.springframework.ai
+ spring-ai-test
+ ${project.parent.version}
+ test
+
+
+
+
+
+
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/McpServerProperties.java b/auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/McpServerProperties.java
similarity index 82%
rename from spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/McpServerProperties.java
rename to auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/McpServerProperties.java
index 5be80b131c6..00867c089d4 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/McpServerProperties.java
+++ b/auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/McpServerProperties.java
@@ -25,7 +25,6 @@
* These properties control the behavior and configuration of the MCP server, including:
*
*
Server identification (name and version)
- *
Transport type (STDIO, WEBMVC, or WEBFLUX)
*
Change notification settings for tools, resources, and prompts
*
Web transport endpoint configuration
*
@@ -46,7 +45,15 @@ public class McpServerProperties {
*
* When set to false, the MCP server and all its components will not be initialized.
*/
- private boolean enabled = false;
+ private boolean enabled = true;
+
+ /**
+ * Enable/disable the standard input/output (stdio) transport.
+ *
+ * When enabled, the server will listen for incoming messages on the standard input
+ * and write responses to the standard output.
+ */
+ private boolean stdio = false;
/**
* The name of the MCP server instance.
@@ -88,18 +95,6 @@ public class McpServerProperties {
*/
private boolean promptChangeNotification = true;
- /**
- * The transport type to use for MCP server communication.
- *
- * Supported types are:
- *
- *
STDIO - Standard input/output transport (default)
- *
WEBMVC - Spring MVC Server-Sent Events transport
- *
WEBFLUX - Spring WebFlux Server-Sent Events transport
- *
- */
- private Transport transport = Transport.STDIO;
-
/**
* The endpoint path for Server-Sent Events (SSE) when using web transports.
*
@@ -118,31 +113,6 @@ public class McpServerProperties {
*/
private ServerType type = ServerType.SYNC;
- /**
- * Transport types supported by the MCP server.
- */
- public enum Transport {
-
- /**
- * Standard input/output transport, suitable for command-line tools and local
- * development.
- */
- STDIO,
-
- /**
- * Spring MVC Server-Sent Events transport, requires spring-boot-starter-web and
- * mcp-spring-webmvc.
- */
- WEBMVC,
-
- /**
- * Spring WebFlux Server-Sent Events transport, requires
- * spring-boot-starter-webflux and mcp-spring-webflux.
- */
- WEBFLUX
-
- }
-
/**
* Server types supported by the MCP server.
*/
@@ -160,6 +130,14 @@ public enum ServerType {
}
+ public boolean isStdio() {
+ return this.stdio;
+ }
+
+ public void setStdio(boolean stdio) {
+ this.stdio = stdio;
+ }
+
public boolean isEnabled() {
return this.enabled;
}
@@ -210,15 +188,6 @@ public void setPromptChangeNotification(boolean promptChangeNotification) {
this.promptChangeNotification = promptChangeNotification;
}
- public Transport getTransport() {
- return this.transport;
- }
-
- public void setTransport(Transport transport) {
- Assert.notNull(transport, "Transport must not be null");
- this.transport = transport;
- }
-
public String getSseMessageEndpoint() {
return this.sseMessageEndpoint;
}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcServerAutoConfiguration.java b/auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcServerAutoConfiguration.java
similarity index 72%
rename from spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcServerAutoConfiguration.java
rename to auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcServerAutoConfiguration.java
index 1293f3d97a8..dfb8f9760f7 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcServerAutoConfiguration.java
+++ b/auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcServerAutoConfiguration.java
@@ -20,19 +20,23 @@
import java.util.function.Consumer;
import java.util.function.Function;
+import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.McpServer;
-import reactor.core.publisher.Mono;
-import io.modelcontextprotocol.server.McpServer.SyncSpec;
import io.modelcontextprotocol.server.McpServer.AsyncSpec;
+import io.modelcontextprotocol.server.McpServer.SyncSpec;
import io.modelcontextprotocol.server.McpServerFeatures;
-import io.modelcontextprotocol.server.McpServerFeatures.SyncToolRegistration;
+import io.modelcontextprotocol.server.McpServerFeatures.AsyncPromptRegistration;
+import io.modelcontextprotocol.server.McpServerFeatures.AsyncResourceRegistration;
import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolRegistration;
+import io.modelcontextprotocol.server.McpServerFeatures.SyncPromptRegistration;
+import io.modelcontextprotocol.server.McpServerFeatures.SyncResourceRegistration;
+import io.modelcontextprotocol.server.McpServerFeatures.SyncToolRegistration;
import io.modelcontextprotocol.server.McpSyncServer;
-import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.Implementation;
import io.modelcontextprotocol.spec.ServerMcpTransport;
+import reactor.core.publisher.Mono;
import org.springframework.ai.mcp.McpToolUtils;
import org.springframework.ai.tool.ToolCallback;
@@ -45,6 +49,7 @@
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.log.LogAccessor;
+import org.springframework.util.CollectionUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for the Model Context Protocol (MCP)
@@ -94,18 +99,17 @@
* @see MpcWebMvcServerAutoConfiguration
* @see org.springframework.ai.mcp.ToolCallback
*/
-@AutoConfiguration
+@AutoConfiguration(after = { MpcWebMvcServerAutoConfiguration.class, MpcWebFluxServerAutoConfiguration.class })
@ConditionalOnClass({ McpSchema.class, McpSyncServer.class })
@EnableConfigurationProperties(McpServerProperties.class)
-@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true")
+@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
+ matchIfMissing = true)
public class MpcServerAutoConfiguration {
private static final LogAccessor logger = new LogAccessor(MpcServerAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean
- @ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "transport", havingValue = "STDIO",
- matchIfMissing = true)
public ServerMcpTransport stdioServerTransport() {
return new StdioServerTransport();
}
@@ -119,8 +123,9 @@ public McpSchema.ServerCapabilities.Builder capabilitiesBuilder() {
@Bean
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
matchIfMissing = true)
- public List syncTools(List toolCalls) {
- return McpToolUtils.toSyncToolRegistration(toolCalls);
+ public List syncTools(ObjectProvider> toolCalls) {
+ var tools = toolCalls.stream().flatMap(List::stream).toList();
+ return McpToolUtils.toSyncToolRegistration(tools);
}
@Bean
@@ -128,9 +133,8 @@ public List syncTools(List
matchIfMissing = true)
public McpSyncServer mcpSyncServer(ServerMcpTransport transport,
McpSchema.ServerCapabilities.Builder capabilitiesBuilder, McpServerProperties serverProperties,
- ObjectProvider> tools,
- ObjectProvider> resources,
- ObjectProvider> prompts,
+ ObjectProvider> tools, ObjectProvider> resources,
+ ObjectProvider> prompts,
ObjectProvider>> rootsChangeConsumers) {
McpSchema.Implementation serverInfo = new Implementation(serverProperties.getName(),
@@ -139,26 +143,29 @@ public McpSyncServer mcpSyncServer(ServerMcpTransport transport,
// Create the server with both tool and resource capabilities
SyncSpec serverBuilder = McpServer.sync(transport).serverInfo(serverInfo);
- tools.ifAvailable(toolsList -> {
- serverBuilder.tools(toolsList);
+ List toolResgistrations = tools.stream().flatMap(List::stream).toList();
+ if (!CollectionUtils.isEmpty(toolResgistrations)) {
+ serverBuilder.tools(toolResgistrations);
capabilitiesBuilder.tools(serverProperties.isToolChangeNotification());
- logger.info("Registered tools" + toolsList.size() + " notification: "
+ logger.info("Registered tools" + toolResgistrations.size() + " notification: "
+ serverProperties.isToolChangeNotification());
- });
+ }
- resources.ifAvailable(resourceList -> {
- serverBuilder.resources(resourceList);
+ List resourceResgistrations = resources.stream().flatMap(List::stream).toList();
+ if (!CollectionUtils.isEmpty(resourceResgistrations)) {
+ serverBuilder.resources(resourceResgistrations);
capabilitiesBuilder.resources(false, serverProperties.isResourceChangeNotification());
- logger.info("Registered resources" + resourceList.size() + " notification: "
+ logger.info("Registered resources" + resourceResgistrations.size() + " notification: "
+ serverProperties.isResourceChangeNotification());
- });
+ }
- prompts.ifAvailable(promptList -> {
- serverBuilder.prompts(promptList);
+ List promptResgistrations = prompts.stream().flatMap(List::stream).toList();
+ if (!CollectionUtils.isEmpty(promptResgistrations)) {
+ serverBuilder.prompts(promptResgistrations);
capabilitiesBuilder.prompts(serverProperties.isPromptChangeNotification());
- logger.info("Registered prompts" + promptList.size() + " notification: "
+ logger.info("Registered prompts" + promptResgistrations.size() + " notification: "
+ serverProperties.isPromptChangeNotification());
- });
+ }
rootsChangeConsumers.ifAvailable(consumer -> {
serverBuilder.rootsChangeConsumer(consumer);
@@ -172,8 +179,9 @@ public McpSyncServer mcpSyncServer(ServerMcpTransport transport,
@Bean
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "ASYNC")
- public List asyncTools(List toolCalls) {
- return McpToolUtils.toAsyncToolRegistration(toolCalls);
+ public List asyncTools(ObjectProvider> toolCalls) {
+ var tools = toolCalls.stream().flatMap(List::stream).toList();
+ return McpToolUtils.toAsyncToolRegistration(tools);
}
@Bean
@@ -181,8 +189,8 @@ public List asyncTools(List> tools,
- ObjectProvider> resources,
- ObjectProvider> prompts,
+ ObjectProvider> resources,
+ ObjectProvider> prompts,
ObjectProvider>> rootsChangeConsumer) {
McpSchema.Implementation serverInfo = new Implementation(serverProperties.getName(),
@@ -191,26 +199,29 @@ public McpAsyncServer mcpAsyncServer(ServerMcpTransport transport,
// Create the server with both tool and resource capabilities
AsyncSpec serverBilder = McpServer.async(transport).serverInfo(serverInfo);
- tools.ifAvailable(toolsList -> {
- serverBilder.tools(toolsList);
+ List toolResgistrations = tools.stream().flatMap(List::stream).toList();
+ if (!CollectionUtils.isEmpty(toolResgistrations)) {
+ serverBilder.tools(toolResgistrations);
capabilitiesBuilder.tools(serverProperties.isToolChangeNotification());
- logger.info("Registered tools" + toolsList.size() + " notification: "
+ logger.info("Registered tools" + toolResgistrations.size() + " notification: "
+ serverProperties.isToolChangeNotification());
- });
+ }
- resources.ifAvailable(resourceList -> {
- serverBilder.resources(resourceList);
+ List resourceResgistrations = resources.stream().flatMap(List::stream).toList();
+ if (!CollectionUtils.isEmpty(resourceResgistrations)) {
+ serverBilder.resources(resourceResgistrations);
capabilitiesBuilder.resources(false, serverProperties.isResourceChangeNotification());
- logger.info("Registered resources" + resourceList.size() + " notification: "
+ logger.info("Registered resources" + resourceResgistrations.size() + " notification: "
+ serverProperties.isResourceChangeNotification());
- });
+ }
- prompts.ifAvailable(promptList -> {
- serverBilder.prompts(promptList);
+ List promptResgistrations = prompts.stream().flatMap(List::stream).toList();
+ if (!CollectionUtils.isEmpty(promptResgistrations)) {
+ serverBilder.prompts(promptResgistrations);
capabilitiesBuilder.prompts(serverProperties.isPromptChangeNotification());
- logger.info("Registered prompts" + promptList.size() + " notification: "
+ logger.info("Registered prompts" + promptResgistrations.size() + " notification: "
+ serverProperties.isPromptChangeNotification());
- });
+ }
rootsChangeConsumer.ifAvailable(consumer -> {
Function, Mono> asyncConsumer = roots -> {
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebFluxServerAutoConfiguration.java b/auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebFluxServerAutoConfiguration.java
similarity index 94%
rename from spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebFluxServerAutoConfiguration.java
rename to auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebFluxServerAutoConfiguration.java
index a1ce31bca5b..703ee484350 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebFluxServerAutoConfiguration.java
+++ b/auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebFluxServerAutoConfiguration.java
@@ -18,6 +18,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.transport.WebFluxSseServerTransport;
+import io.modelcontextprotocol.spec.ServerMcpTransport;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -64,7 +65,9 @@
*/
@AutoConfiguration
@ConditionalOnClass({ WebFluxSseServerTransport.class })
-@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "transport", havingValue = "WEBFLUX")
+@ConditionalOnMissingBean(ServerMcpTransport.class)
+@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "stdio", havingValue = "false",
+ matchIfMissing = true)
public class MpcWebFluxServerAutoConfiguration {
@Bean
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebMvcServerAutoConfiguration.java b/auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebMvcServerAutoConfiguration.java
similarity index 94%
rename from spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebMvcServerAutoConfiguration.java
rename to auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebMvcServerAutoConfiguration.java
index 5759cc4f68c..55d19df49d9 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebMvcServerAutoConfiguration.java
+++ b/auto-configurations/spring-ai-mcp-server/src/main/java/org/springframework/ai/autoconfigure/mcp/server/MpcWebMvcServerAutoConfiguration.java
@@ -18,6 +18,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.transport.WebMvcSseServerTransport;
+import io.modelcontextprotocol.spec.ServerMcpTransport;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -48,10 +49,6 @@
*
* Required dependencies:
{@code
*
- * io.modelcontextprotocol.sdk
- * mcp-spring-webmvc
- *
- *
* org.springframework.boot
* spring-boot-starter-web
*
@@ -64,7 +61,9 @@
*/
@AutoConfiguration
@ConditionalOnClass({ WebMvcSseServerTransport.class })
-@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "transport", havingValue = "WEBMVC")
+@ConditionalOnMissingBean(ServerMcpTransport.class)
+@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "stdio", havingValue = "false",
+ matchIfMissing = true)
public class MpcWebMvcServerAutoConfiguration {
@Bean
diff --git a/auto-configurations/spring-ai-mcp-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/auto-configurations/spring-ai-mcp-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 00000000000..eee7e32e69c
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,20 @@
+#
+# Copyright 2025-2025 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.springframework.ai.autoconfigure.mcp.server.MpcServerAutoConfiguration
+org.springframework.ai.autoconfigure.mcp.server.MpcWebMvcServerAutoConfiguration
+org.springframework.ai.autoconfigure.mcp.server.MpcWebFluxServerAutoConfiguration
+
diff --git a/auto-configurations/spring-ai-mcp-server/src/test/java/org/springframework/ai/autoconfigure/mcp/server/McpServerAutoConfigurationIT.java b/auto-configurations/spring-ai-mcp-server/src/test/java/org/springframework/ai/autoconfigure/mcp/server/McpServerAutoConfigurationIT.java
new file mode 100644
index 00000000000..34490ea5393
--- /dev/null
+++ b/auto-configurations/spring-ai-mcp-server/src/test/java/org/springframework/ai/autoconfigure/mcp/server/McpServerAutoConfigurationIT.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ai.autoconfigure.mcp.server;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.server.McpAsyncServer;
+import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolRegistration;
+import io.modelcontextprotocol.server.McpServerFeatures.SyncToolRegistration;
+import io.modelcontextprotocol.server.McpServerFeatures.SyncResourceRegistration;
+import io.modelcontextprotocol.server.McpServerFeatures.SyncPromptRegistration;
+import io.modelcontextprotocol.server.McpSyncServer;
+import io.modelcontextprotocol.server.transport.StdioServerTransport;
+import io.modelcontextprotocol.spec.McpSchema;
+import io.modelcontextprotocol.spec.ServerMcpTransport;
+import org.mockito.Mockito;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.mcp.SyncMcpToolCallback;
+import org.springframework.ai.tool.ToolCallback;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class McpServerAutoConfigurationIT {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(MpcServerAutoConfiguration.class));
+
+ @Test
+ void defaultConfiguration() {
+ this.contextRunner.run(context -> {
+ assertThat(context).hasSingleBean(McpSyncServer.class);
+ assertThat(context).hasSingleBean(ServerMcpTransport.class);
+ assertThat(context.getBean(ServerMcpTransport.class)).isInstanceOf(StdioServerTransport.class);
+
+ McpServerProperties properties = context.getBean(McpServerProperties.class);
+ assertThat(properties.getName()).isEqualTo("mcp-server");
+ assertThat(properties.getVersion()).isEqualTo("1.0.0");
+ assertThat(properties.getType()).isEqualTo(McpServerProperties.ServerType.SYNC);
+ assertThat(properties.isToolChangeNotification()).isTrue();
+ assertThat(properties.isResourceChangeNotification()).isTrue();
+ assertThat(properties.isPromptChangeNotification()).isTrue();
+ });
+ }
+
+ @Test
+ void asyncConfiguration() {
+ this.contextRunner
+ .withPropertyValues("spring.ai.mcp.server.type=ASYNC", "spring.ai.mcp.server.name=test-server",
+ "spring.ai.mcp.server.version=2.0.0")
+ .run(context -> {
+ assertThat(context).hasSingleBean(McpAsyncServer.class);
+ assertThat(context).doesNotHaveBean(McpSyncServer.class);
+
+ McpServerProperties properties = context.getBean(McpServerProperties.class);
+ assertThat(properties.getName()).isEqualTo("test-server");
+ assertThat(properties.getVersion()).isEqualTo("2.0.0");
+ assertThat(properties.getType()).isEqualTo(McpServerProperties.ServerType.ASYNC);
+ });
+ }
+
+ @Test
+ void transportConfiguration() {
+ this.contextRunner.withUserConfiguration(CustomTransportConfiguration.class).run(context -> {
+ assertThat(context).hasSingleBean(ServerMcpTransport.class);
+ assertThat(context.getBean(ServerMcpTransport.class)).isInstanceOf(CustomServerTransport.class);
+ });
+ }
+
+ @Test
+ void serverNotificationConfiguration() {
+ this.contextRunner
+ .withPropertyValues("spring.ai.mcp.server.tool-change-notification=false",
+ "spring.ai.mcp.server.resource-change-notification=false")
+ .run(context -> {
+ McpServerProperties properties = context.getBean(McpServerProperties.class);
+ assertThat(properties.isToolChangeNotification()).isFalse();
+ assertThat(properties.isResourceChangeNotification()).isFalse();
+ });
+ }
+
+ // @Test
+ void invalidConfigurationThrowsException() {
+ this.contextRunner.withPropertyValues("spring.ai.mcp.server.version=invalid-version").run(context -> {
+ assertThat(context).hasFailed();
+ assertThat(context).getFailure()
+ .hasRootCauseInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("Invalid version format");
+ });
+ }
+
+ @Test
+ void disabledConfiguration() {
+ this.contextRunner.withPropertyValues("spring.ai.mcp.server.enabled=false").run(context -> {
+ assertThat(context).doesNotHaveBean(McpSyncServer.class);
+ assertThat(context).doesNotHaveBean(McpAsyncServer.class);
+ assertThat(context).doesNotHaveBean(ServerMcpTransport.class);
+ });
+ }
+
+ @Test
+ void notificationConfiguration() {
+ this.contextRunner
+ .withPropertyValues("spring.ai.mcp.server.tool-change-notification=false",
+ "spring.ai.mcp.server.resource-change-notification=false",
+ "spring.ai.mcp.server.prompt-change-notification=false")
+ .run(context -> {
+ McpServerProperties properties = context.getBean(McpServerProperties.class);
+ assertThat(properties.isToolChangeNotification()).isFalse();
+ assertThat(properties.isResourceChangeNotification()).isFalse();
+ assertThat(properties.isPromptChangeNotification()).isFalse();
+ });
+ }
+
+ @Test
+ void stdioConfiguration() {
+ this.contextRunner.withPropertyValues("spring.ai.mcp.server.stdio=true").run(context -> {
+ McpServerProperties properties = context.getBean(McpServerProperties.class);
+ assertThat(properties.isStdio()).isTrue();
+ });
+ }
+
+ @Test
+ void serverCapabilitiesConfiguration() {
+ this.contextRunner.run(context -> {
+ assertThat(context).hasSingleBean(McpSchema.ServerCapabilities.Builder.class);
+ McpSchema.ServerCapabilities.Builder builder = context.getBean(McpSchema.ServerCapabilities.Builder.class);
+ assertThat(builder).isNotNull();
+ });
+ }
+
+ @Test
+ void toolRegistrationConfiguration() {
+ this.contextRunner.withUserConfiguration(TestToolConfiguration.class).run(context -> {
+ List tools = context.getBean("syncTools", List.class);
+ assertThat(tools).hasSize(1);
+ });
+ }
+
+ @Test
+ void resourceRegistrationConfiguration() {
+ this.contextRunner.withUserConfiguration(TestResourceConfiguration.class).run(context -> {
+ McpSyncServer server = context.getBean(McpSyncServer.class);
+ assertThat(server).isNotNull();
+ });
+ }
+
+ @Test
+ void promptRegistrationConfiguration() {
+ this.contextRunner.withUserConfiguration(TestPromptConfiguration.class).run(context -> {
+ McpSyncServer server = context.getBean(McpSyncServer.class);
+ assertThat(server).isNotNull();
+ });
+ }
+
+ @Test
+ void asyncToolRegistrationConfiguration() {
+ this.contextRunner.withPropertyValues("spring.ai.mcp.server.type=ASYNC")
+ .withUserConfiguration(TestToolConfiguration.class)
+ .run(context -> {
+ List tools = context.getBean("asyncTools", List.class);
+ assertThat(tools).hasSize(1);
+ });
+ }
+
+ @Test
+ void customCapabilitiesBuilder() {
+ this.contextRunner.withUserConfiguration(CustomCapabilitiesConfiguration.class).run(context -> {
+ assertThat(context).hasSingleBean(McpSchema.ServerCapabilities.Builder.class);
+ assertThat(context.getBean(McpSchema.ServerCapabilities.Builder.class))
+ .isInstanceOf(CustomCapabilitiesBuilder.class);
+ });
+ }
+
+ @Test
+ void rootsChangeConsumerConfiguration() {
+ this.contextRunner.withUserConfiguration(TestRootsChangeConfiguration.class).run(context -> {
+ McpSyncServer server = context.getBean(McpSyncServer.class);
+ assertThat(server).isNotNull();
+ });
+ }
+
+ @Configuration
+ static class TestResourceConfiguration {
+
+ @Bean
+ List testResources() {
+ return List.of();
+ }
+
+ }
+
+ @Configuration
+ static class TestPromptConfiguration {
+
+ @Bean
+ List testPrompts() {
+ return List.of();
+ }
+
+ }
+
+ @Configuration
+ static class CustomCapabilitiesConfiguration {
+
+ @Bean
+ McpSchema.ServerCapabilities.Builder customCapabilitiesBuilder() {
+ return new CustomCapabilitiesBuilder();
+ }
+
+ }
+
+ static class CustomCapabilitiesBuilder extends McpSchema.ServerCapabilities.Builder {
+
+ // Custom implementation for testing
+
+ }
+
+ @Configuration
+ static class TestToolConfiguration {
+
+ @Bean
+ List testTool() {
+ McpSyncClient mockClient = Mockito.mock(McpSyncClient.class);
+ McpSchema.Tool mockTool = Mockito.mock(McpSchema.Tool.class);
+ McpSchema.CallToolResult mockResult = Mockito.mock(McpSchema.CallToolResult.class);
+
+ Mockito.when(mockTool.name()).thenReturn("test-tool");
+ Mockito.when(mockTool.description()).thenReturn("Test Tool");
+ Mockito.when(mockClient.callTool(Mockito.any(McpSchema.CallToolRequest.class))).thenReturn(mockResult);
+
+ return List.of(new SyncMcpToolCallback(mockClient, mockTool));
+ }
+
+ }
+
+ @Configuration
+ static class TestRootsChangeConfiguration {
+
+ @Bean
+ Consumer> rootsChangeConsumer() {
+ return roots -> {
+ // Test implementation
+ };
+ }
+
+ }
+
+ static class CustomServerTransport implements ServerMcpTransport {
+
+ @Override
+ public Mono connect(
+ Function, Mono> messageHandler) {
+ return Mono.empty(); // Test implementation
+ }
+
+ @Override
+ public Mono sendMessage(McpSchema.JSONRPCMessage message) {
+ return Mono.empty(); // Test implementation
+ }
+
+ @Override
+ public T unmarshalFrom(Object value, TypeReference type) {
+ return null; // Test implementation
+ }
+
+ @Override
+ public void close() {
+ // Test implementation
+ }
+
+ @Override
+ public Mono closeGracefully() {
+ return Mono.empty(); // Test implementation
+ }
+
+ }
+
+ @Configuration
+ static class CustomTransportConfiguration {
+
+ @Bean
+ ServerMcpTransport customTransport() {
+ return new CustomServerTransport();
+ }
+
+ }
+
+}
diff --git a/mcp-client-boot-starter-docs.adoc b/mcp-client-boot-starter-docs.adoc
new file mode 100644
index 00000000000..a43ec00a64a
--- /dev/null
+++ b/mcp-client-boot-starter-docs.adoc
@@ -0,0 +1,340 @@
+= Spring AI MCP Client Boot Starter
+
+The Spring AI MCP (Model Context Protocol) Client Boot Starter provides auto-configuration for MCP client functionality in Spring Boot applications. It supports both synchronous and asynchronous client implementations with various transport options.
+
+The MCP Client Boot Starter provides:
+
+* Automatic client initialization (if enabled)
+* Support for multiple named transports
+* Integration with Spring AI's tool execution framework
+* Proper lifecycle management with automatic cleanup
+* Customizable client creation through customizers
+
+
+== Dependencies
+
+=== Core Starter
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mcp-client-spring-boot-starter
+ ${spring-ai.version}
+
+----
+
+It will connect, simultaneously, to one or more MCP Servers over `STDIO` (in-process) and/or `SSE` (remote) transports.
+The SSE connection uses the HttpClient-based transport implementation.
+Every connection to an MCP Server creates a new MCP Client instance.
+You can opt for either `SYNC` or `ASYNC` MCP Clients (Note: you cannot mix sync and async clients).
+For more enterprise-ready deployment, it is recommended to use the WebFlux-based SSE connection using the `spring-ai-mcp-client-webflux-spring-boot-starter` starter.
+
+=== WebFlux Starter
+
+Similar to the core starter, it allows configuring one or more STDIO and SSE connections, but uses WebFlux-based SSE transport implementation.
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mcp-client-webflux-spring-boot-starter
+ ${spring-ai.version}
+
+----
+
+== Configuration Properties
+
+=== Common Properties
+
+All common configuration properties are prefixed with `spring.ai.mcp.client`:
+
+[cols="3,4,3"]
+|===
+|Property |Description |Default Value
+
+|`enabled`
+|Enable/disable the MCP client
+|`true`
+
+|`name`
+|Name of the MCP client instance (used for compatibility checks)
+|`spring-ai-mcp-client`
+
+|`version`
+|Version of the MCP client instance
+|`1.0.0`
+
+|`initialized`
+|Whether to initialize clients on creation
+|`true`
+
+|`request-timeout`
+|Timeout duration for MCP client requests
+|`20s`
+
+|`type`
+|Client type (SYNC or ASYNC). You can not mix client type. All clients can be either sync or async
+|`SYNC`
+
+|`root-change-notification`
+|Enable/disable root change notifications for all clients
+|`true`
+|===
+
+=== SSE Transport Properties
+
+Properties for Server-Sent Events (SSE) transport are prefixed with `spring.ai.mcp.client.sse`:
+
+[cols="2,4"]
+|===
+|Property |Description
+
+|`connections`
+|Map of named SSE connection configurations
+
+|`connections.[name].url`
+|URL endpoint for SSE communication with the MCP server
+|===
+
+Example configuration:
+[source,yaml]
+----
+spring:
+ ai:
+ mcp:
+ client:
+ sse:
+ connections:
+ server1:
+ url: http://localhost:8080
+ server2:
+ url: http://otherserver:8081
+----
+
+=== Stdio Transport Properties
+
+Properties for Standard I/O transport are prefixed with `spring.ai.mcp.client.stdio`:
+
+[cols="3,4,3"]
+|===
+|Property |Description |Default Value
+
+|`servers-configuration`
+|Resource containing the MCP servers configuration in JSON format
+|-
+
+|`connections`
+|Map of named stdio connection configurations
+|-
+
+|`connections.[name].command`
+|The command to execute for the MCP server
+|-
+
+|`connections.[name].args`
+|List of command arguments
+|-
+
+|`connections.[name].env`
+|Map of environment variables for the server process
+|-
+|===
+
+Example configuration:
+[source,yaml]
+----
+spring:
+ ai:
+ mcp:
+ client:
+ stdio:
+ root-change-notification: true
+ connections:
+ server1:
+ command: /path/to/server
+ args:
+ - --port=8080
+ - --mode=production
+ env:
+ API_KEY: your-api-key
+ DEBUG: "true"
+----
+
+Alternatively, you can configure stdio connections using an external JSON file using the link:https://modelcontextprotocol.io/quickstart/user[Claude Desctop format]:
+
+[source,yaml]
+----
+spring:
+ ai:
+ mcp:
+ client:
+ stdio:
+ servers-configuration: classpath:mcp-servers.json
+----
+
+The Claude Destop format looks like this:
+
+[source,json]
+----
+{
+ "mcpServers": {
+ "filesystem": {
+ "command": "npx",
+ "args": [
+ "-y",
+ "@modelcontextprotocol/server-filesystem",
+ "/Users/username/Desktop",
+ "/Users/username/Downloads"
+ ]
+ }
+ }
+}
+----
+Currently the Claude Destop supports only STDIO connection types.
+
+== Features
+
+=== Client Types
+
+The starter supports two types of clients:
+
+1. *Synchronous Client (SYNC)*
+ * Default client type
+ * Blocking operations
+ * Suitable for traditional request-response patterns
+
+2. *Asynchronous Client (ASYNC)*
+ * Non-blocking operations
+ * Suitable for reactive applications
+ * Must be explicitly configured using `spring.ai.mcp.client.type=ASYNC`
+
+=== Client Customization
+
+The auto-configuration supports customization through:
+
+* `McpSyncClientCustomizer` for synchronous clients
+* `McpAsyncClientCustomizer` for asynchronous clients
+
+== Usage Example
+
+1. Add the appropriate starter dependency to your project.
+
+2. Configure the client in `application.properties` or `application.yml`:
+
+[source,yaml]
+----
+spring:
+ ai:
+ mcp:
+ client:
+ enabled: true
+ name: my-mcp-client
+ version: 1.0.0
+ request-timeout: 30s
+ type: SYNC # or ASYNC for reactive applications
+ sse:
+ connections:
+ server1:
+ url: http://localhost:8080
+ server2:
+ url: http://otherserver:8081
+ stdio:
+ root-change-notification: false
+ connections:
+ server1:
+ command: /path/to/server
+ args:
+ - --port=8080
+ - --mode=production
+ env:
+ API_KEY: your-api-key
+ DEBUG: "true"
+----
+
+3. The MCP client beans will be automatically configured and available for injection:
+
+[source,java]
+----
+@Autowired
+private List mcpSyncClients; // For sync client
+
+// OR
+
+@Autowired
+private List mcpAsyncClients; // For async client
+----
+
+== Transport Support
+
+The auto-configuration supports multiple transport types:
+
+* Standard I/O (Stdio)
+* SSE HTTP
+* SSE WebFlux (requires `spring-ai-starter-mcp-client-webflux`)
+
+At least one transport must be available for the clients to be created.
+
+== Integration with Spring AI
+
+The starter automatically configures tool callbacks that integrate with Spring AI's tool execution framework, allowing MCP tools to be used as part of AI interactions.
+
+== Lifecycle Management
+
+The auto-configuration includes proper lifecycle management:
+
+* Automatic initialization of clients (if enabled)
+* Proper cleanup of resources when the application context is closed
+* Management of multiple client instances
+
+== Best Practices
+
+1. Choose the appropriate client type based on your application's needs:
+ * Use SYNC client for traditional applications
+ * Use ASYNC client for reactive applications
+
+2. Configure appropriate timeout values based on your use case:
+[source,yaml]
+----
+spring:
+ ai:
+ mcp:
+ client:
+ request-timeout: 30s
+----
+
+3. Use customizers for advanced client configuration:
+[source,java]
+----
+@Component
+public class MyMcpClientCustomizer implements McpSyncClientCustomizer {
+ @Override
+ public void customize(String name, McpClient.SyncSpec clientSpec) {
+ // Custom configuration
+ }
+}
+----
+
+== Troubleshooting
+
+Common issues and solutions:
+
+1. *Client Not Created*
+ * Verify that at least one transport is available
+ * Check if the client is enabled in configuration
+ * Ensure required dependencies are present
+
+2. *Timeout Issues*
+ * Adjust the `request-timeout` property
+ * Check network connectivity
+ * Verify server response times
+
+3. *Integration Issues*
+ * Ensure proper transport configuration
+ * Check client initialization status
+
+== Additional Resources
+
+* link:https://docs.spring.io/spring-ai/reference/[Spring AI Documentation]
+* link:https://modelcontextprotocol.github.io/specification/[Model Context Protocol Specification]
+* link:https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration[Spring Boot Auto-configuration]
diff --git a/mcp-client-boot-starter-docs.md b/mcp-client-boot-starter-docs.md
new file mode 100644
index 00000000000..ea7cc8600cc
--- /dev/null
+++ b/mcp-client-boot-starter-docs.md
@@ -0,0 +1,284 @@
+# Spring AI MCP Client Boot Starter
+
+The Spring AI MCP (Model Context Protocol) Client Boot Starter provides auto-configuration for MCP client functionality in Spring Boot applications. It supports both synchronous and asynchronous client implementations with various transport options.
+
+The MCP Client Boot Starter provides:
+
+- Automatic client initialization (if enabled)
+- Support for multiple named transports
+- Integration with Spring AI's tool execution framework
+- Proper lifecycle management with automatic cleanup
+- Customizable client creation through customizers
+
+
+## Dependencies
+
+### Core Starter
+
+```xml
+
+ org.springframework.ai
+ spring-ai-mcp-client-spring-boot-starter
+ ${spring-ai.version}
+
+```
+
+It will connect, simultaneously, to one or more MCP Servers over `STDIO` (in-process) and/or `SSE` (remote) transports.
+The SSE connection uses the HttpClient-based transport implementation.
+Every connection to an MCP Server creates a new MCP Client instance.
+You can opt for either `SYNC` or `ASYNC` MCP Clients (Note: you cannot mix sync and async clients).
+For more enterprise-ready deployment, it is recommended to use the WebFlux-based SSE connection using the `spring-ai-mcp-client-webflux-spring-boot-starter` starter.
+
+### WebFlux Starter
+
+Similar to the core starter, it allows configuring one or more STDIO and SSE connections, but uses WebFlux-based SSE transport implementation.
+
+```xml
+
+ org.springframework.ai
+ spring-ai-mcp-client-webflux-spring-boot-starter
+ ${spring-ai.version}
+
+```
+
+## Configuration Properties
+
+### Common Properties
+
+All common configuration properties are prefixed with `spring.ai.mcp.client`:
+
+| Property | Description | Default Value |
+|----------|-------------|---------------|
+| `enabled` | Enable/disable the MCP client | `true` |
+| `name` | Name of the MCP client instance (used for compatibility checks) | `spring-ai-mcp-client` |
+| `version` | Version of the MCP client instance | `1.0.0` |
+| `initialized` | Whether to initialize clients on creation | `true` |
+| `request-timeout` | Timeout duration for MCP client requests | `20s` |
+| `type` | Client type (SYNC or ASYNC). You can not mix client type. All clients can be either sync or async | `SYNC` |
+| `root-change-notification` | Enable/disable root change notifications for all clients| `true` |
+
+### SSE Transport Properties
+
+Properties for Server-Sent Events (SSE) transport are prefixed with `spring.ai.mcp.client.sse`:
+
+| Property | Description |
+|----------|-------------|
+| `connections` | Map of named SSE connection configurations |
+| `connections.[name].url` | URL endpoint for SSE communication with the MCP server |
+
+Example configuration:
+```yaml
+spring:
+ ai:
+ mcp:
+ client:
+ sse:
+ connections:
+ server1:
+ url: http://localhost:8080
+ server2:
+ url: http://otherserver:8081
+```
+
+### Stdio Transport Properties
+
+Properties for Standard I/O transport are prefixed with `spring.ai.mcp.client.stdio`:
+
+| Property | Description | Default Value |
+|----------|-------------|---------------|
+| `servers-configuration` | Resource containing the MCP servers configuration in JSON format | - |
+| `connections` | Map of named stdio connection configurations | - |
+| `connections.[name].command` | The command to execute for the MCP server | - |
+| `connections.[name].args` | List of command arguments | - |
+| `connections.[name].env` | Map of environment variables for the server process | - |
+
+Example configuration:
+```yaml
+spring:
+ ai:
+ mcp:
+ client:
+ stdio:
+ root-change-notification: true
+ connections:
+ server1:
+ command: /path/to/server
+ args:
+ - --port=8080
+ - --mode=production
+ env:
+ API_KEY: your-api-key
+ DEBUG: "true"
+```
+
+Alternatively, you can configure stdio connections using an external JSON file using the [Claude Desctop format](https://modelcontextprotocol.io/quickstart/user):
+
+```yaml
+spring:
+ ai:
+ mcp:
+ client:
+ stdio:
+ servers-configuration: classpath:mcp-servers.json
+```
+
+The Claude Destop format looks like this:
+
+```json
+{
+ "mcpServers": {
+ "filesystem": {
+ "command": "npx",
+ "args": [
+ "-y",
+ "@modelcontextprotocol/server-filesystem",
+ "/Users/username/Desktop",
+ "/Users/username/Downloads"
+ ]
+ }
+ }
+}
+```
+Currently the Claude Destop supports only STDIO connection types.
+
+## Features
+
+### Client Types
+
+The starter supports two types of clients:
+
+1. **Synchronous Client (SYNC)**
+ - Default client type
+ - Blocking operations
+ - Suitable for traditional request-response patterns
+
+2. **Asynchronous Client (ASYNC)**
+ - Non-blocking operations
+ - Suitable for reactive applications
+ - Must be explicitly configured using `spring.ai.mcp.client.type=ASYNC`
+
+### Client Customization
+
+The auto-configuration supports customization through:
+
+- `McpSyncClientCustomizer` for synchronous clients
+- `McpAsyncClientCustomizer` for asynchronous clients
+
+## Usage Example
+
+1. Add the appropriate starter dependency to your project.
+
+2. Configure the client in `application.properties` or `application.yml`:
+
+```yaml
+spring:
+ ai:
+ mcp:
+ client:
+ enabled: true
+ name: my-mcp-client
+ version: 1.0.0
+ request-timeout: 30s
+ type: SYNC # or ASYNC for reactive applications
+ sse:
+ connections:
+ server1:
+ url: http://localhost:8080
+ server2:
+ url: http://otherserver:8081
+ stdio:
+ root-change-notification: false
+ connections:
+ server1:
+ command: /path/to/server
+ args:
+ - --port=8080
+ - --mode=production
+ env:
+ API_KEY: your-api-key
+ DEBUG: "true"
+```
+
+3. The MCP client beans will be automatically configured and available for injection:
+
+```java
+@Autowired
+private List mcpSyncClients; // For sync client
+
+// OR
+
+@Autowired
+private List mcpAsyncClients; // For async client
+```
+
+## Transport Support
+
+The auto-configuration supports multiple transport types:
+
+- Standard I/O (Stdio)
+- SSE HTTP
+- SSE WebFlux (requires `spring-ai-starter-mcp-client-webflux`)
+
+At least one transport must be available for the clients to be created.
+
+## Integration with Spring AI
+
+The starter automatically configures tool callbacks that integrate with Spring AI's tool execution framework, allowing MCP tools to be used as part of AI interactions.
+
+## Lifecycle Management
+
+The auto-configuration includes proper lifecycle management:
+
+- Automatic initialization of clients (if enabled)
+- Proper cleanup of resources when the application context is closed
+- Management of multiple client instances
+
+## Best Practices
+
+1. Choose the appropriate client type based on your application's needs:
+ - Use SYNC client for traditional applications
+ - Use ASYNC client for reactive applications
+
+2. Configure appropriate timeout values based on your use case:
+ ```yaml
+ spring:
+ ai:
+ mcp:
+ client:
+ request-timeout: 30s
+ ```
+
+3. Use customizers for advanced client configuration:
+ ```java
+ @Component
+ public class MyMcpClientCustomizer implements McpSyncClientCustomizer {
+ @Override
+ public void customize(String name, McpClient.SyncSpec clientSpec) {
+ // Custom configuration
+ }
+ }
+ ```
+
+## Troubleshooting
+
+Common issues and solutions:
+
+1. **Client Not Created**
+ - Verify that at least one transport is available
+ - Check if the client is enabled in configuration
+ - Ensure required dependencies are present
+
+2. **Timeout Issues**
+ - Adjust the `request-timeout` property
+ - Check network connectivity
+ - Verify server response times
+
+3. **Integration Issues**
+ - Ensure proper transport configuration
+ - Check client initialization status
+
+## Additional Resources
+
+- [Spring AI Documentation](https://docs.spring.io/spring-ai/reference/)
+- [Model Context Protocol Specification](https://modelcontextprotocol.github.io/specification/)
+- [Spring Boot Auto-configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration)
diff --git a/mcp-helpers.adoc b/mcp-helpers.adoc
new file mode 100644
index 00000000000..532816eb1a3
--- /dev/null
+++ b/mcp-helpers.adoc
@@ -0,0 +1,168 @@
+= Spring AI MCP Common Utilities
+:page-title: Spring AI MCP Common Utilities
+
+This document provides reference documentation for the common utilities in Spring AI's Model Context Protocol (MCP) integration.
+
+== Overview
+
+The MCP common utilities provide foundational support for integrating Model Context Protocol with Spring AI applications. These utilities enable seamless communication between Spring AI's tool system and MCP servers, supporting both synchronous and asynchronous operations.
+
+== Core Components
+
+=== Tool Callbacks
+
+==== AsyncMcpToolCallback
+
+Adapts MCP tools to Spring AI's tool interface with asynchronous execution support.
+
+[source,java]
+----
+McpAsyncClient mcpClient = // obtain MCP client
+Tool mcpTool = // obtain MCP tool definition
+ToolCallback callback = new AsyncMcpToolCallback(mcpClient, mcpTool);
+
+// Use the tool through Spring AI's interfaces
+ToolDefinition definition = callback.getToolDefinition();
+String result = callback.call("{\"param\": \"value\"}");
+----
+
+==== SyncMcpToolCallback
+
+Similar to AsyncMcpToolCallback but provides synchronous execution semantics.
+
+[source,java]
+----
+McpSyncClient mcpClient = // obtain MCP client
+Tool mcpTool = // obtain MCP tool definition
+ToolCallback callback = new SyncMcpToolCallback(mcpClient, mcpTool);
+
+// Use the tool through Spring AI's interfaces
+ToolDefinition definition = callback.getToolDefinition();
+String result = callback.call("{\"param\": \"value\"}");
+----
+
+=== Tool Callback Providers
+
+==== AsyncMcpToolCallbackProvider
+
+Discovers and provides MCP tools asynchronously.
+
+[source,java]
+----
+McpAsyncClient mcpClient = // obtain MCP client
+ToolCallbackProvider provider = new AsyncMcpToolCallbackProvider(mcpClient);
+
+// Get all available tools
+ToolCallback[] tools = provider.getToolCallbacks();
+----
+
+The provider also offers a utility method for working with multiple clients:
+
+[source,java]
+----
+List clients = // obtain list of clients
+Flux callbacks = AsyncMcpToolCallbackProvider.asyncToolCallbacks(clients);
+----
+
+==== SyncMcpToolCallbackProvider
+
+Similar to AsyncMcpToolCallbackProvider but works with synchronous clients.
+
+[source,java]
+----
+McpSyncClient mcpClient = // obtain MCP client
+ToolCallbackProvider provider = new SyncMcpToolCallbackProvider(mcpClient);
+
+// Get all available tools
+ToolCallback[] tools = provider.getToolCallbacks();
+----
+
+For multiple clients:
+
+[source,java]
+----
+List clients = // obtain list of clients
+List callbacks = SyncMcpToolCallbackProvider.syncToolCallbacks(clients);
+----
+
+=== Client Customization
+
+==== McpAsyncClientCustomizer
+
+Allows customization of asynchronous MCP client configurations.
+
+[source,java]
+----
+@Component
+public class CustomMcpAsyncClientCustomizer implements McpAsyncClientCustomizer {
+ @Override
+ public void customize(String name, McpClient.AsyncSpec spec) {
+ // Customize the async client configuration
+ spec.requestTimeout(Duration.ofSeconds(30));
+ }
+}
+----
+
+==== McpSyncClientCustomizer
+
+Similar to McpAsyncClientCustomizer but for synchronous clients.
+
+[source,java]
+----
+@Component
+public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {
+ @Override
+ public void customize(String name, McpClient.SyncSpec spec) {
+ // Customize the sync client configuration
+ spec.requestTimeout(Duration.ofSeconds(30));
+ }
+}
+----
+
+=== Utility Classes
+
+==== McpToolUtils
+
+Provides helper methods for working with MCP tools in a Spring AI environment.
+
+Converting Spring AI tool callbacks to MCP tool registrations:
+
+[source,java]
+----
+// For synchronous tools
+List toolCallbacks = // obtain tool callbacks
+List syncRegs = McpToolUtils.toSyncToolRegistration(toolCallbacks);
+
+// For asynchronous tools
+List asyncRegs = McpToolUtils.toAsyncToolRegistration(toolCallbacks);
+----
+
+Getting tool callbacks from MCP clients:
+
+[source,java]
+----
+// From sync clients
+List syncClients = // obtain sync clients
+List syncCallbacks = McpToolUtils.getToolCallbacksFromSyncClients(syncClients);
+
+// From async clients
+List asyncClients = // obtain async clients
+List asyncCallbacks = McpToolUtils.getToolCallbacksFromAsyncClients(asyncClients);
+----
+
+=== Native Image Support
+
+==== McpHints
+
+Provides GraalVM native image hints for MCP schema classes.
+
+[source,java]
+----
+@Configuration
+@ImportRuntimeHints(McpHints.class)
+public class MyConfiguration {
+ // Configuration code
+}
+----
+
+This class automatically registers all necessary reflection hints for MCP schema classes when building native images.
diff --git a/mcp-helpers.md b/mcp-helpers.md
new file mode 100644
index 00000000000..90b643e2d7f
--- /dev/null
+++ b/mcp-helpers.md
@@ -0,0 +1,156 @@
+# Spring AI MCP Common Utilities
+
+This document provides reference documentation for the common utilities in Spring AI's Model Context Protocol (MCP) integration.
+
+## Overview
+
+The MCP common utilities provide foundational support for integrating Model Context Protocol with Spring AI applications. These utilities enable seamless communication between Spring AI's tool system and MCP servers, supporting both synchronous and asynchronous operations.
+
+## Core Components
+
+### Tool Callbacks
+
+#### AsyncMcpToolCallback
+
+Adapts MCP tools to Spring AI's tool interface with asynchronous execution support.
+
+```java
+McpAsyncClient mcpClient = // obtain MCP client
+Tool mcpTool = // obtain MCP tool definition
+ToolCallback callback = new AsyncMcpToolCallback(mcpClient, mcpTool);
+
+// Use the tool through Spring AI's interfaces
+ToolDefinition definition = callback.getToolDefinition();
+String result = callback.call("{\"param\": \"value\"}");
+```
+
+#### SyncMcpToolCallback
+
+Similar to AsyncMcpToolCallback but provides synchronous execution semantics.
+
+```java
+McpSyncClient mcpClient = // obtain MCP client
+Tool mcpTool = // obtain MCP tool definition
+ToolCallback callback = new SyncMcpToolCallback(mcpClient, mcpTool);
+
+// Use the tool through Spring AI's interfaces
+ToolDefinition definition = callback.getToolDefinition();
+String result = callback.call("{\"param\": \"value\"}");
+```
+
+### Tool Callback Providers
+
+#### AsyncMcpToolCallbackProvider
+
+Discovers and provides MCP tools asynchronously.
+
+```java
+McpAsyncClient mcpClient = // obtain MCP client
+ToolCallbackProvider provider = new AsyncMcpToolCallbackProvider(mcpClient);
+
+// Get all available tools
+ToolCallback[] tools = provider.getToolCallbacks();
+```
+
+The provider also offers a utility method for working with multiple clients:
+
+```java
+List clients = // obtain list of clients
+Flux callbacks = AsyncMcpToolCallbackProvider.asyncToolCallbacks(clients);
+```
+
+#### SyncMcpToolCallbackProvider
+
+Similar to AsyncMcpToolCallbackProvider but works with synchronous clients.
+
+```java
+McpSyncClient mcpClient = // obtain MCP client
+ToolCallbackProvider provider = new SyncMcpToolCallbackProvider(mcpClient);
+
+// Get all available tools
+ToolCallback[] tools = provider.getToolCallbacks();
+```
+
+For multiple clients:
+
+```java
+List clients = // obtain list of clients
+List callbacks = SyncMcpToolCallbackProvider.syncToolCallbacks(clients);
+```
+
+### Client Customization
+
+#### McpAsyncClientCustomizer
+
+Allows customization of asynchronous MCP client configurations.
+
+```java
+@Component
+public class CustomMcpAsyncClientCustomizer implements McpAsyncClientCustomizer {
+ @Override
+ public void customize(String name, McpClient.AsyncSpec spec) {
+ // Customize the async client configuration
+ spec.requestTimeout(Duration.ofSeconds(30));
+ }
+}
+```
+
+#### McpSyncClientCustomizer
+
+Similar to McpAsyncClientCustomizer but for synchronous clients.
+
+```java
+@Component
+public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {
+ @Override
+ public void customize(String name, McpClient.SyncSpec spec) {
+ // Customize the sync client configuration
+ spec.requestTimeout(Duration.ofSeconds(30));
+ }
+}
+```
+
+### Utility Classes
+
+#### McpToolUtils
+
+Provides helper methods for working with MCP tools in a Spring AI environment.
+
+Converting Spring AI tool callbacks to MCP tool registrations:
+
+```java
+// For synchronous tools
+List toolCallbacks = // obtain tool callbacks
+List syncRegs = McpToolUtils.toSyncToolRegistration(toolCallbacks);
+
+// For asynchronous tools
+List asyncRegs = McpToolUtils.toAsyncToolRegistration(toolCallbacks);
+```
+
+Getting tool callbacks from MCP clients:
+
+```java
+// From sync clients
+List syncClients = // obtain sync clients
+List syncCallbacks = McpToolUtils.getToolCallbacksFromSyncClients(syncClients);
+
+// From async clients
+List asyncClients = // obtain async clients
+List asyncCallbacks = McpToolUtils.getToolCallbacksFromAsyncClients(asyncClients);
+```
+
+### Native Image Support
+
+#### McpHints
+
+Provides GraalVM native image hints for MCP schema classes.
+
+```java
+@Configuration
+@ImportRuntimeHints(McpHints.class)
+public class MyConfiguration {
+ // Configuration code
+}
+```
+
+This class automatically registers all necessary reflection hints for MCP schema classes when building native images.
\ No newline at end of file
diff --git a/mcp-server-boot-starter-docs.adoc b/mcp-server-boot-starter-docs.adoc
new file mode 100644
index 00000000000..cf9784e9da1
--- /dev/null
+++ b/mcp-server-boot-starter-docs.adoc
@@ -0,0 +1,310 @@
+= Spring AI MCP Server Boot Starter
+
+The Spring AI MCP (Model Context Protocol) Server Boot Starter provides auto-configuration for setting up an MCP server in Spring Boot applications. It enables seamless integration of MCP server capabilities with Spring Boot's auto-configuration system.
+
+== Overview
+
+The MCP Server Boot Starter offers:
+
+* Automatic configuration of MCP server components
+* Support for both synchronous and asynchronous operation modes
+* Multiple transport layer options
+* Flexible tool, resource, and prompt registration
+* Change notification capabilities
+
+== Starter Dependencies
+
+Choose one of the following starters based on your transport requirements:
+
+=== 1. Standard MCP Server
+
+Full MCP Server features support with `STDIO` server transport.
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mcp-server-spring-boot-starter
+ ${spring-ai.version}
+
+----
+
+=== 2. WebMVC Server
+
+Full MCP Server features support with `SSE` (Server-Sent Events) server transport based on Spring MVC and an optional `STDIO` transport.
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mcp-server-webmvc-spring-boot-starter
+ ${spring-ai.version}
+
+----
+
+This starter includes:
+
+* spring-boot-starter-web
+* mcp-spring-webmvc
+(optionally allows `stdio` transport deployment)
+
+=== 3. WebFlux Server
+
+Full MCP Server features support with `SSE` (Server-Sent Events) server transport based on Spring WebFlux and an optional `STDIO` transport.
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mcp-server-webflux-spring-boot-starter
+ ${spring-ai.version}
+
+----
+
+This starter includes:
+
+* spring-boot-starter-webflux
+* mcp-spring-webflux
+(optionally allows `stdio` transport deployment)
+
+== Configuration Properties
+
+All properties are prefixed with `spring.ai.mcp.server`:
+
+[options="header"]
+|===
+|Property |Description |Default
+|`enabled` |Enable/disable the MCP server |`true`
+|`stdio` |Enable/disable stdio transport |`false`
+|`name` |Server name for identification |`mcp-server`
+|`version` |Server version |`1.0.0`
+|`type` |Server type (SYNC/ASYNC) |`SYNC`
+|`resource-change-notification` |Enable resource change notifications |`true`
+|`tool-change-notification` |Enable tool change notifications |`true`
+|`prompt-change-notification` |Enable prompt change notifications |`true`
+|`sse-message-endpoint` |SSE endpoint path for web transport |`/mcp/message`
+|===
+
+== Server Types
+
+=== Synchronous Server
+* Default server type
+* Uses `McpSyncServer`
+* Suitable for straightforward request-response patterns
+* Configure with `spring.ai.mcp.server.type=SYNC`
+* Automatically configures synchronous tool registrations
+
+=== Asynchronous Server
+* Uses `McpAsyncServer`
+* Suitable for non-blocking operations
+* Configure with `spring.ai.mcp.server.type=ASYNC`
+* Automatically configures asynchronous tool registrations with Project Reactor support
+
+== Transport Options
+
+The MCP Server supports three transport mechanisms, each with its dedicated starter:
+
+=== 1. Standard Input/Output (STDIO)
+* Use `spring-ai-mcp-server-spring-boot-starter`
+* Default transport when using the standard starter
+* Suitable for command-line tools and testing
+* No additional web dependencies required
+
+=== 2. Spring MVC (Server-Sent Events)
+* Use `spring-ai-mcp-server-webmvc-spring-boot-starter`
+* Provides HTTP-based transport using Spring MVC
+* Uses `WebMvcSseServerTransport`
+* Automatically configures SSE endpoints
+* Ideal for traditional web applications
+* Optionally you can deploy `STDIO` transport by setting the `spring.ai.mcp.server.stdio=true` property.
+
+=== 3. Spring WebFlux (Reactive SSE)
+* Use `spring-ai-mcp-server-webflux-spring-boot-starter`
+* Provides reactive transport using Spring WebFlux
+* Uses `WebFluxSseServerTransport`
+* Automatically configures reactive SSE endpoints
+* Ideal for reactive applications with non-blocking requirements
+* Optionally you can deploy `STDIO` transport by setting the `spring.ai.mcp.server.stdio=true` property.
+
+== Features and Capabilities
+
+=== 1. Tools Registration
+* Support for both sync and async tool execution
+* Automatic tool registration through Spring beans
+* Change notification support
+* Tools are automatically converted to sync/async registrations based on server type
+
+=== 2. Resource Management
+* Static and dynamic resource registration
+* Optional change notifications
+* Support for resource templates
+* Automatic conversion between sync/async resource registrations
+
+=== 3. Prompt Templates
+* Configurable prompt registration
+* Change notification support
+* Template versioning
+* Automatic conversion between sync/async prompt registrations
+
+=== 4. Root Change Consumers
+* Support for monitoring root changes
+* Automatic conversion to async consumers for reactive applications
+* Optional registration through Spring beans
+
+== Usage Examples
+
+=== 1. Standard STDIO Server Configuration
+[source,yaml]
+----
+# Using spring-ai-mcp-server-spring-boot-starter
+spring:
+ ai:
+ mcp:
+ server:
+ name: stdio-mcp-server
+ version: 1.0.0
+ type: SYNC
+ stdio: true
+----
+
+=== 2. WebMVC Server Configuration
+[source,yaml]
+----
+# Using spring-ai-mcp-server-webmvc-spring-boot-starter
+spring:
+ ai:
+ mcp:
+ server:
+ name: webmvc-mcp-server
+ version: 1.0.0
+ type: SYNC
+ stdio: false
+ sse-message-endpoint: /mcp/messages
+----
+
+=== 3. WebFlux Server Configuration
+[source,yaml]
+----
+# Using spring-ai-mcp-server-webflux-spring-boot-starter
+spring:
+ ai:
+ mcp:
+ server:
+ name: webflux-mcp-server
+ version: 1.0.0
+ type: ASYNC # Recommended for reactive applications
+ stdio: false
+ sse-message-endpoint: /mcp/messages
+----
+
+=== Tool Registration Examples
+
+==== 1. Synchronous Tool (for SYNC server type)
+[source,java]
+----
+@Configuration
+public class SyncToolConfig {
+
+ @Bean
+ public ToolCallback syncTool() {
+ return new ToolCallback() {
+ @Override
+ public String getName() {
+ return "syncTool";
+ }
+
+ @Override
+ public Object execute(Map params) {
+ // Synchronous implementation
+ return result;
+ }
+ };
+ }
+}
+----
+
+==== 2. Asynchronous Tool (for ASYNC server type)
+[source,java]
+----
+@Configuration
+public class AsyncToolConfig {
+
+ @Bean
+ public ToolCallback asyncTool() {
+ return new ToolCallback() {
+ @Override
+ public String getName() {
+ return "asyncTool";
+ }
+
+ @Override
+ public Object execute(Map params) {
+ // Asynchronous implementation using Project Reactor
+ return Mono.just("result")
+ .map(r -> processResult(r))
+ .subscribeOn(Schedulers.boundedElastic());
+ }
+ };
+ }
+}
+----
+
+== Auto-configuration Classes
+
+The starter provides several auto-configuration classes:
+
+1. `MpcServerAutoConfiguration`: Core server configuration
+* Configures basic server components
+* Handles tool, resource, and prompt registrations
+* Manages server capabilities and change notifications
+* Provides both sync and async server implementations
+
+2. `MpcWebMvcServerAutoConfiguration`: Spring MVC transport
+* Configures SSE endpoints for web transport
+* Integrates with Spring MVC infrastructure
+
+3. `MpcWebFluxServerAutoConfiguration`: Spring WebFlux transport
+* Configures reactive SSE endpoints
+* Integrates with Spring WebFlux infrastructure
+
+These classes are conditionally enabled based on the classpath and configuration properties.
+
+== Conditional Configuration
+
+The auto-configuration is activated when:
+
+* Required MCP classes are on the classpath
+* `spring.ai.mcp.server.enabled=true` (default)
+* Appropriate transport dependencies are available
+
+== Best Practices
+
+1. Choose the appropriate server type based on your use case:
+* Use SYNC for simple request-response patterns
+* Use ASYNC for non-blocking operations and reactive applications
+
+2. Select the transport mechanism based on your application type:
+* Use STDIO for command-line tools and testing
+* Use WebMvc for traditional web applications
+* Use WebFlux for reactive applications
+
+3. Configure change notifications based on your needs:
+* Enable only the notifications you need
+* Consider performance implications of notifications
+* Use appropriate consumers for root changes
+
+4. Properly version your server and tools:
+* Use semantic versioning
+* Document version changes
+* Handle version compatibility
+
+5. Tool Implementation:
+* Implement tools as Spring beans for automatic registration
+* Return Mono/Flux for async operations in ASYNC mode
+* Use appropriate error handling strategies
+
+== Additional Resources
+
+* link:https://docs.spring.io/spring-ai/reference/[Spring AI Documentation]
+* link:https://modelcontextprotocol.github.io/specification/[Model Context Protocol Specification]
+* link:https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration[Spring Boot Auto-configuration]
diff --git a/mcp-server-boot-starter-docs.md b/mcp-server-boot-starter-docs.md
new file mode 100644
index 00000000000..393c6935ea5
--- /dev/null
+++ b/mcp-server-boot-starter-docs.md
@@ -0,0 +1,294 @@
+# Spring AI MCP Server Boot Starter
+
+The Spring AI MCP (Model Context Protocol) Server Boot Starter provides auto-configuration for setting up an MCP server in Spring Boot applications. It enables seamless integration of MCP server capabilities with Spring Boot's auto-configuration system.
+
+## Overview
+
+The MCP Server Boot Starter offers:
+- Automatic configuration of MCP server components
+- Support for both synchronous and asynchronous operation modes
+- Multiple transport layer options
+- Flexible tool, resource, and prompt registration
+- Change notification capabilities
+
+## Starter Dependencies
+
+Choose one of the following starters based on your transport requirements:
+
+### 1. Standard MCP Server
+
+Full MCP Server features support with `STDIO` server transport.
+
+```xml
+
+ org.springframework.ai
+ spring-ai-mcp-server-spring-boot-starter
+ ${spring-ai.version}
+
+```
+
+### 2. WebMVC Server
+
+Full MCP Server features support with `SSE` (Server-Sent Events) server transport based on Spring MVC and an optional `STDIO` transport.
+
+```xml
+
+ org.springframework.ai
+ spring-ai-mcp-server-webmvc-spring-boot-starter
+ ${spring-ai.version}
+
+```
+This starter includes:
+- spring-boot-starter-web
+- mcp-spring-webmvc
+(optionally allows `stdio` transport deployment)
+
+### 3. WebFlux Server
+
+Full MCP Server features support with `SSE` (Server-Sent Events) server transport based on Spring WebFlux and an optional `STDIO` transport.
+
+```xml
+
+ org.springframework.ai
+ spring-ai-mcp-server-webflux-spring-boot-starter
+ ${spring-ai.version}
+
+```
+This starter includes:
+- spring-boot-starter-webflux
+- mcp-spring-webflux
+(optionally allows `stdio` transport deployment)
+
+## Configuration Properties
+
+All properties are prefixed with `spring.ai.mcp.server`:
+
+| Property | Description | Default |
+|----------|-------------|---------|
+| `enabled` | Enable/disable the MCP server | `true` |
+| `stdio` | Enable/disable stdio transport | `false` |
+| `name` | Server name for identification | `mcp-server` |
+| `version` | Server version | `1.0.0` |
+| `type` | Server type (SYNC/ASYNC) | `SYNC` |
+| `resource-change-notification` | Enable resource change notifications | `true` |
+| `tool-change-notification` | Enable tool change notifications | `true` |
+| `prompt-change-notification` | Enable prompt change notifications | `true` |
+| `sse-message-endpoint` | SSE endpoint path for web transport | `/mcp/message` |
+
+## Server Types
+
+### Synchronous Server
+- Default server type
+- Uses `McpSyncServer`
+- Suitable for straightforward request-response patterns
+- Configure with `spring.ai.mcp.server.type=SYNC`
+- Automatically configures synchronous tool registrations
+
+### Asynchronous Server
+- Uses `McpAsyncServer`
+- Suitable for non-blocking operations
+- Configure with `spring.ai.mcp.server.type=ASYNC`
+- Automatically configures asynchronous tool registrations with Project Reactor support
+
+## Transport Options
+
+The MCP Server supports three transport mechanisms, each with its dedicated starter:
+
+### 1. Standard Input/Output (STDIO)
+- Use `spring-ai-mcp-server-spring-boot-starter`
+- Default transport when using the standard starter
+- Suitable for command-line tools and testing
+- No additional web dependencies required
+
+### 2. Spring MVC (Server-Sent Events)
+- Use `spring-ai-mcp-server-webmvc-spring-boot-starter`
+- Provides HTTP-based transport using Spring MVC
+- Uses `WebMvcSseServerTransport`
+- Automatically configures SSE endpoints
+- Ideal for traditional web applications
+- Optionally you can deploy `STDIO` transport by setting the `spring.ai.mcp.server.stdio=true` property.
+
+### 3. Spring WebFlux (Reactive SSE)
+- Use `spring-ai-mcp-server-webflux-spring-boot-starter`
+- Provides reactive transport using Spring WebFlux
+- Uses `WebFluxSseServerTransport`
+- Automatically configures reactive SSE endpoints
+- Ideal for reactive applications with non-blocking requirements
+- Optionally you can deploy `STDIO` transport by setting the `spring.ai.mcp.server.stdio=true` property.
+
+## Features and Capabilities
+
+### 1. Tools Registration
+- Support for both sync and async tool execution
+- Automatic tool registration through Spring beans
+- Change notification support
+- Tools are automatically converted to sync/async registrations based on server type
+
+### 2. Resource Management
+- Static and dynamic resource registration
+- Optional change notifications
+- Support for resource templates
+- Automatic conversion between sync/async resource registrations
+
+### 3. Prompt Templates
+- Configurable prompt registration
+- Change notification support
+- Template versioning
+- Automatic conversion between sync/async prompt registrations
+
+### 4. Root Change Consumers
+- Support for monitoring root changes
+- Automatic conversion to async consumers for reactive applications
+- Optional registration through Spring beans
+
+## Usage Examples
+
+### 1. Standard STDIO Server Configuration
+```yaml
+# Using spring-ai-mcp-server-spring-boot-starter
+spring:
+ ai:
+ mcp:
+ server:
+ name: stdio-mcp-server
+ version: 1.0.0
+ type: SYNC
+ stdio: true
+```
+
+### 2. WebMVC Server Configuration
+```yaml
+# Using spring-ai-mcp-server-webmvc-spring-boot-starter
+spring:
+ ai:
+ mcp:
+ server:
+ name: webmvc-mcp-server
+ version: 1.0.0
+ type: SYNC
+ stdio: false
+ sse-message-endpoint: /mcp/messages
+```
+
+### 3. WebFlux Server Configuration
+```yaml
+# Using spring-ai-mcp-server-webflux-spring-boot-starter
+spring:
+ ai:
+ mcp:
+ server:
+ name: webflux-mcp-server
+ version: 1.0.0
+ type: ASYNC # Recommended for reactive applications
+ stdio: false
+ sse-message-endpoint: /mcp/messages
+```
+
+### Tool Registration Examples
+
+#### 1. Synchronous Tool (for SYNC server type)
+```java
+@Configuration
+public class SyncToolConfig {
+
+ @Bean
+ public ToolCallback syncTool() {
+ return new ToolCallback() {
+ @Override
+ public String getName() {
+ return "syncTool";
+ }
+
+ @Override
+ public Object execute(Map params) {
+ // Synchronous implementation
+ return result;
+ }
+ };
+ }
+}
+```
+
+#### 2. Asynchronous Tool (for ASYNC server type)
+```java
+@Configuration
+public class AsyncToolConfig {
+
+ @Bean
+ public ToolCallback asyncTool() {
+ return new ToolCallback() {
+ @Override
+ public String getName() {
+ return "asyncTool";
+ }
+
+ @Override
+ public Object execute(Map params) {
+ // Asynchronous implementation using Project Reactor
+ return Mono.just("result")
+ .map(r -> processResult(r))
+ .subscribeOn(Schedulers.boundedElastic());
+ }
+ };
+ }
+}
+```
+
+## Auto-configuration Classes
+
+The starter provides several auto-configuration classes:
+
+1. `MpcServerAutoConfiguration`: Core server configuration
+ - Configures basic server components
+ - Handles tool, resource, and prompt registrations
+ - Manages server capabilities and change notifications
+ - Provides both sync and async server implementations
+
+2. `MpcWebMvcServerAutoConfiguration`: Spring MVC transport
+ - Configures SSE endpoints for web transport
+ - Integrates with Spring MVC infrastructure
+
+3. `MpcWebFluxServerAutoConfiguration`: Spring WebFlux transport
+ - Configures reactive SSE endpoints
+ - Integrates with Spring WebFlux infrastructure
+
+These classes are conditionally enabled based on the classpath and configuration properties.
+
+## Conditional Configuration
+
+The auto-configuration is activated when:
+- Required MCP classes are on the classpath
+- `spring.ai.mcp.server.enabled=true` (default)
+- Appropriate transport dependencies are available
+
+## Best Practices
+
+1. Choose the appropriate server type based on your use case:
+ - Use SYNC for simple request-response patterns
+ - Use ASYNC for non-blocking operations and reactive applications
+
+2. Select the transport mechanism based on your application type:
+ - Use STDIO for command-line tools and testing
+ - Use WebMvc for traditional web applications
+ - Use WebFlux for reactive applications
+
+3. Configure change notifications based on your needs:
+ - Enable only the notifications you need
+ - Consider performance implications of notifications
+ - Use appropriate consumers for root changes
+
+4. Properly version your server and tools:
+ - Use semantic versioning
+ - Document version changes
+ - Handle version compatibility
+
+5. Tool Implementation:
+ - Implement tools as Spring beans for automatic registration
+ - Return Mono/Flux for async operations in ASYNC mode
+ - Use appropriate error handling strategies
+
+## Additional Resources
+
+- [Spring AI Documentation](https://docs.spring.io/spring-ai/reference/)
+- [Model Context Protocol Specification](https://modelcontextprotocol.github.io/specification/)
+- [Spring Boot Auto-configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration)
diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallback.java b/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallback.java
new file mode 100644
index 00000000000..121e1d9b867
--- /dev/null
+++ b/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallback.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ai.mcp;
+
+import java.util.Map;
+
+import io.modelcontextprotocol.client.McpAsyncClient;
+import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
+import io.modelcontextprotocol.spec.McpSchema.Tool;
+
+import org.springframework.ai.model.ModelOptionsUtils;
+import org.springframework.ai.tool.ToolCallback;
+import org.springframework.ai.tool.definition.ToolDefinition;
+
+/**
+ * Implementation of {@link ToolCallback} that adapts MCP tools to Spring AI's tool
+ * interface with asynchronous execution support.
+ *
+ * This class acts as a bridge between the Model Context Protocol (MCP) and Spring AI's
+ * tool system, allowing MCP tools to be used seamlessly within Spring AI applications.
+ * It:
+ *
+ *
Converts MCP tool definitions to Spring AI tool definitions
+ *
Handles the asynchronous execution of tool calls through the MCP client
+ *
Manages JSON serialization/deserialization of tool inputs and outputs
+ *
+ *
+ * Example usage:
{@code
+ * McpAsyncClient mcpClient = // obtain MCP client
+ * Tool mcpTool = // obtain MCP tool definition
+ * ToolCallback callback = new AsyncMcpToolCallback(mcpClient, mcpTool);
+ *
+ * // Use the tool through Spring AI's interfaces
+ * ToolDefinition definition = callback.getToolDefinition();
+ * String result = callback.call("{\"param\": \"value\"}");
+ * }
+ *
+ * @author Christian Tzolov
+ * @see ToolCallback
+ * @see McpAsyncClient
+ * @see Tool
+ */
+public class AsyncMcpToolCallback implements ToolCallback {
+
+ private final McpAsyncClient asyncMcpClient;
+
+ private final Tool tool;
+
+ /**
+ * Creates a new {@code AsyncMcpToolCallback} instance.
+ * @param mcpClient the MCP client to use for tool execution
+ * @param tool the MCP tool definition to adapt
+ */
+ public AsyncMcpToolCallback(McpAsyncClient mcpClient, Tool tool) {
+ this.asyncMcpClient = mcpClient;
+ this.tool = tool;
+ }
+
+ /**
+ * Returns a Spring AI tool definition adapted from the MCP tool.
+ *
+ * The tool definition includes:
+ *
+ *
The tool's name from the MCP definition
+ *
The tool's description from the MCP definition
+ *
The input schema converted to JSON format
+ *
+ * @return the Spring AI tool definition
+ */
+ @Override
+ public ToolDefinition getToolDefinition() {
+ return ToolDefinition.builder()
+ .name(this.tool.name())
+ .description(this.tool.description())
+ .inputSchema(ModelOptionsUtils.toJsonString(this.tool.inputSchema()))
+ .build();
+ }
+
+ /**
+ * Executes the tool with the provided input asynchronously.
+ *
+ * This method:
+ *
+ *
Converts the JSON input string to a map of arguments
+ *
Calls the tool through the MCP client asynchronously
+ *
Converts the tool's response content to a JSON string
+ *
+ * @param functionInput the tool input as a JSON string
+ * @return the tool's response as a JSON string
+ */
+ @Override
+ public String call(String functionInput) {
+ Map arguments = ModelOptionsUtils.jsonToMap(functionInput);
+ return this.asyncMcpClient.callTool(new CallToolRequest(this.getToolDefinition().name(), arguments))
+ .map(response -> ModelOptionsUtils.toJsonString(response.content()))
+ .block();
+ }
+
+}
diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallbackProvider.java b/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallbackProvider.java
new file mode 100644
index 00000000000..f8f1bb1ebc0
--- /dev/null
+++ b/mcp/common/src/main/java/org/springframework/ai/mcp/AsyncMcpToolCallbackProvider.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.mcp;
+
+import java.util.List;
+
+import io.modelcontextprotocol.client.McpAsyncClient;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import org.springframework.ai.tool.ToolCallback;
+import org.springframework.ai.tool.ToolCallbackProvider;
+import org.springframework.ai.tool.util.ToolUtils;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * Implementation of {@link ToolCallbackProvider} that discovers and provides MCP tools
+ * asynchronously.
+ *
+ * This class acts as a tool provider for Spring AI, automatically discovering tools from
+ * an MCP server and making them available as Spring AI tools. It:
+ *
+ *
Connects to an MCP server through an async client
+ *
Lists and retrieves available tools from the server
+ *
Creates {@link AsyncMcpToolCallback} instances for each discovered tool
+ *
Validates tool names to prevent duplicates
+ *
+ *
+ * Example usage:
{@code
+ * McpAsyncClient mcpClient = // obtain MCP client
+ * ToolCallbackProvider provider = new AsyncMcpToolCallbackProvider(mcpClient);
+ *
+ * // Get all available tools
+ * ToolCallback[] tools = provider.getToolCallbacks();
+ * }
+ *
+ * @author Christian Tzolov
+ * @since 1.0.0
+ * @see ToolCallbackProvider
+ * @see AsyncMcpToolCallback
+ * @see McpAsyncClient
+ */
+public class AsyncMcpToolCallbackProvider implements ToolCallbackProvider {
+
+ private final McpAsyncClient mcpClient;
+
+ /**
+ * Creates a new {@code AsyncMcpToolCallbackProvider} instance.
+ * @param mcpClient the MCP client to use for discovering tools
+ */
+ public AsyncMcpToolCallbackProvider(McpAsyncClient mcpClient) {
+ this.mcpClient = mcpClient;
+ }
+
+ /**
+ * Discovers and returns all available tools from the MCP server asynchronously.
+ *
+ * This method:
+ *
+ *
Retrieves the list of tools from the MCP server
+ *
Creates a {@link AsyncMcpToolCallback} for each tool
+ *
Validates that there are no duplicate tool names
+ *
+ * @return an array of tool callbacks, one for each discovered tool
+ * @throws IllegalStateException if duplicate tool names are found
+ */
+ @Override
+ public ToolCallback[] getToolCallbacks() {
+ var toolCallbacks = this.mcpClient.listTools()
+ .map(response -> response.tools()
+ .stream()
+ .map(tool -> new AsyncMcpToolCallback(this.mcpClient, tool))
+ .toArray(ToolCallback[]::new))
+ .block();
+
+ validateToolCallbacks(toolCallbacks);
+
+ return toolCallbacks;
+ }
+
+ /**
+ * Validates that there are no duplicate tool names in the provided callbacks.
+ *
+ * This method ensures that each tool has a unique name, which is required for proper
+ * tool resolution and execution.
+ * @param toolCallbacks the tool callbacks to validate
+ * @throws IllegalStateException if duplicate tool names are found
+ */
+ private void validateToolCallbacks(ToolCallback[] toolCallbacks) {
+ List duplicateToolNames = ToolUtils.getDuplicateToolNames(toolCallbacks);
+ if (!duplicateToolNames.isEmpty()) {
+ throw new IllegalStateException(
+ "Multiple tools with the same name (%s)".formatted(String.join(", ", duplicateToolNames)));
+ }
+ }
+
+ /**
+ * Creates a reactive stream of tool callbacks from multiple MCP clients.
+ *
+ * This utility method:
+ *
+ *
Takes a list of MCP clients
+ *
Creates a provider for each client
+ *
Retrieves and flattens all tool callbacks into a single stream
+ *
+ * @param mcpClients the list of MCP clients to create callbacks from
+ * @return a Flux of tool callbacks from all provided clients
+ */
+ public static Flux asyncToolCallbacks(List mcpClients) {
+ if (CollectionUtils.isEmpty(mcpClients)) {
+ return Flux.empty();
+ }
+
+ return Flux.fromIterable(mcpClients)
+ .flatMap(mcpClient -> Mono.just(new AsyncMcpToolCallbackProvider(mcpClient).getToolCallbacks()))
+ .flatMap(callbacks -> Flux.fromArray(callbacks));
+ }
+
+}
diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/McpSyncClientCustomizer.java b/mcp/common/src/main/java/org/springframework/ai/mcp/McpSyncClientCustomizer.java
deleted file mode 100644
index 0e1a7ae2441..00000000000
--- a/mcp/common/src/main/java/org/springframework/ai/mcp/McpSyncClientCustomizer.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2025-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.ai.mcp;
-
-import io.modelcontextprotocol.client.McpClient;
-
-/**
- * @author Christian Tzolov
- * @since 1.0.0
- */
-public interface McpSyncClientCustomizer {
-
- void customize(String name, McpClient.SyncSpec sync);
-
-}
diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java b/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java
index 821ff153e01..5a69b92b8e5 100644
--- a/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java
+++ b/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java
@@ -17,6 +17,7 @@
import java.util.List;
+import io.modelcontextprotocol.client.McpAsyncClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolRegistration;
@@ -180,11 +181,32 @@ public static McpServerFeatures.AsyncToolRegistration toAsyncToolRegistration(To
.subscribeOn(Schedulers.boundedElastic()));
}
- public static List getToolCallbacks(McpSyncClient... mcpClients) {
- return getToolCallbacks(List.of(mcpClients));
+ /**
+ * Convenience method to get tool callbacks from multiple synchronous MCP clients.
+ *
+ * This is a varargs wrapper around {@link #getToolCallbacksFromSyncClients(List)} for
+ * easier usage when working with individual clients.
+ * @param mcpClients the synchronous MCP clients to get callbacks from
+ * @return a list of tool callbacks from all provided clients
+ * @see #getToolCallbacksFromSyncClients(List)
+ */
+ public static List getToolCallbacksFromSyncClients(McpSyncClient... mcpClients) {
+ return getToolCallbacksFromSyncClients(List.of(mcpClients));
}
- public static List getToolCallbacks(List mcpClients) {
+ /**
+ * Gets tool callbacks from a list of synchronous MCP clients.
+ *
+ * This method:
+ *
+ *
Takes a list of synchronous MCP clients
+ *
Creates a provider for each client
+ *
Retrieves and combines all tool callbacks into a single list
+ *
+ * @param mcpClients the list of synchronous MCP clients to get callbacks from
+ * @return a list of tool callbacks from all provided clients
+ */
+ public static List getToolCallbacksFromSyncClients(List mcpClients) {
if (CollectionUtils.isEmpty(mcpClients)) {
return List.of();
@@ -195,4 +217,40 @@ public static List getToolCallbacks(List mcpClients
.toList();
}
+ /**
+ * Convenience method to get tool callbacks from multiple asynchronous MCP clients.
+ *
+ * This is a varargs wrapper around {@link #getToolCallbacksFromAsyncClinents(List)}
+ * for easier usage when working with individual clients.
+ * @param asynMcpClients the asynchronous MCP clients to get callbacks from
+ * @return a list of tool callbacks from all provided clients
+ * @see #getToolCallbacksFromAsyncClinents(List)
+ */
+ public static List getToolCallbacksFromAsyncClients(McpAsyncClient... asynMcpClients) {
+ return getToolCallbacksFromAsyncClinents(List.of(asynMcpClients));
+ }
+
+ /**
+ * Gets tool callbacks from a list of asynchronous MCP clients.
+ *
+ * This method:
+ *
+ *
Takes a list of asynchronous MCP clients
+ *
Creates a provider for each client
+ *
Retrieves and combines all tool callbacks into a single list
+ *
+ * @param asynMcpClients the list of asynchronous MCP clients to get callbacks from
+ * @return a list of tool callbacks from all provided clients
+ */
+ public static List getToolCallbacksFromAsyncClinents(List asynMcpClients) {
+
+ if (CollectionUtils.isEmpty(asynMcpClients)) {
+ return List.of();
+ }
+ return asynMcpClients.stream()
+ .map(mcpClient -> List.of((new AsyncMcpToolCallbackProvider(mcpClient).getToolCallbacks())))
+ .flatMap(List::stream)
+ .toList();
+ }
+
}
diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolCallback.java b/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallback.java
similarity index 95%
rename from mcp/common/src/main/java/org/springframework/ai/mcp/McpToolCallback.java
rename to mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallback.java
index b4e3ada139f..ffbe09303d6 100644
--- a/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolCallback.java
+++ b/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallback.java
@@ -55,19 +55,18 @@
* @see McpSyncClient
* @see Tool
*/
-public class McpToolCallback implements ToolCallback {
+public class SyncMcpToolCallback implements ToolCallback {
private final McpSyncClient mcpClient;
private final Tool tool;
/**
- * Creates a new {@code McpToolCallback} instance.
+ * Creates a new {@code SyncMcpToolCallback} instance.
* @param mcpClient the MCP client to use for tool execution
* @param tool the MCP tool definition to adapt
*/
-
- public McpToolCallback(McpSyncClient mcpClient, Tool tool) {
+ public SyncMcpToolCallback(McpSyncClient mcpClient, Tool tool) {
this.mcpClient = mcpClient;
this.tool = tool;
}
diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallbackProvider.java b/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallbackProvider.java
index 3051ad7f008..ea18baf42e3 100644
--- a/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallbackProvider.java
+++ b/mcp/common/src/main/java/org/springframework/ai/mcp/SyncMcpToolCallbackProvider.java
@@ -32,13 +32,13 @@
*
*
Connects to an MCP server through a sync client
*
Lists and retrieves available tools from the server
- *
Creates {@link McpToolCallback} instances for each discovered tool
+ *
Creates {@link SyncMcpToolCallback} instances for each discovered tool
*
Validates tool names to prevent duplicates
*
*
* Example usage:
{@code
* McpSyncClient mcpClient = // obtain MCP client
- * ToolCallbackProvider provider = new McpToolCallbackProvider(mcpClient);
+ * ToolCallbackProvider provider = new SyncMcpToolCallbackProvider(mcpClient);
*
* // Get all available tools
* ToolCallback[] tools = provider.getToolCallbacks();
@@ -47,7 +47,7 @@
* @author Christian Tzolov
* @since 1.0.0
* @see ToolCallbackProvider
- * @see McpToolCallback
+ * @see SyncMcpToolCallback
* @see McpSyncClient
*/
@@ -56,7 +56,7 @@ public class SyncMcpToolCallbackProvider implements ToolCallbackProvider {
private final McpSyncClient mcpClient;
/**
- * Creates a new {@code McpToolCallbackProvider} instance.
+ * Creates a new {@code SyncMcpToolCallbackProvider} instance.
* @param mcpClient the MCP client to use for discovering tools
*/
public SyncMcpToolCallbackProvider(McpSyncClient mcpClient) {
@@ -69,7 +69,7 @@ public SyncMcpToolCallbackProvider(McpSyncClient mcpClient) {
* This method:
*
*
Retrieves the list of tools from the MCP server
- *
Creates a {@link McpToolCallback} for each tool
+ *
Creates a {@link SyncMcpToolCallback} for each tool
*
Validates that there are no duplicate tool names
*
* @return an array of tool callbacks, one for each discovered tool
@@ -81,7 +81,7 @@ public ToolCallback[] getToolCallbacks() {
var toolCallbacks = this.mcpClient.listTools()
.tools()
.stream()
- .map(tool -> new McpToolCallback(this.mcpClient, tool))
+ .map(tool -> new SyncMcpToolCallback(this.mcpClient, tool))
.toArray(ToolCallback[]::new);
validateToolCallbacks(toolCallbacks);
@@ -106,6 +106,18 @@ private void validateToolCallbacks(ToolCallback[] toolCallbacks) {
}
}
+ /**
+ * Creates a list of tool callbacks from multiple MCP clients.
+ *
+ * This utility method:
+ *
+ *
Takes a list of MCP clients
+ *
Creates a provider for each client
+ *
Retrieves and combines all tool callbacks into a single list
+ *
+ * @param mcpClients the list of MCP clients to create callbacks from
+ * @return a list of tool callbacks from all provided clients
+ */
public static List syncToolCallbacks(List mcpClients) {
if (CollectionUtils.isEmpty(mcpClients)) {
diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/McpHints.java b/mcp/common/src/main/java/org/springframework/ai/mcp/aot/McpHints.java
similarity index 54%
rename from mcp/common/src/main/java/org/springframework/ai/mcp/McpHints.java
rename to mcp/common/src/main/java/org/springframework/ai/mcp/aot/McpHints.java
index cc6b617b392..fcf8c81d2e5 100644
--- a/mcp/common/src/main/java/org/springframework/ai/mcp/McpHints.java
+++ b/mcp/common/src/main/java/org/springframework/ai/mcp/aot/McpHints.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.ai.mcp;
+package org.springframework.ai.mcp.aot;
import java.util.ArrayList;
import java.util.Arrays;
@@ -30,12 +30,36 @@
import org.springframework.lang.Nullable;
/**
+ * Runtime hints registrar for Model Context Protocol (MCP) schema classes.
+ *
+ * This class provides GraalVM native image hints for MCP schema classes to ensure proper
+ * reflection access in native images. It:
+ *
+ *
Registers all nested classes of {@link McpSchema} for reflection
+ *
Enables all member categories (fields, methods, etc.) for registered types
+ *
Ensures proper serialization/deserialization in native images
+ *
+ *
* @author Josh Long
* @since 1.0.0
+ * @see RuntimeHintsRegistrar
+ * @see McpSchema
*/
@SuppressWarnings("unused")
public class McpHints implements RuntimeHintsRegistrar {
+ /**
+ * Registers runtime hints for MCP schema classes.
+ *
+ * This method:
+ *
+ *
Discovers all nested classes within {@link McpSchema}
+ *
Registers each discovered class for reflection access
+ *
Enables all member categories for complete reflection support
+ *
+ * @param hints the hints instance to register hints with
+ * @param classLoader the classloader to use (may be null)
+ */
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
var mcs = MemberCategory.values();
@@ -45,12 +69,32 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader)
}
}
+ /**
+ * Discovers all inner classes of a given class.
+ *
+ * This method recursively finds all nested classes (both declared and inherited) of
+ * the provided class and converts them to type references.
+ * @param clazz the class to find inner classes for
+ * @return a set of type references for all discovered inner classes
+ */
private Set innerClasses(Class> clazz) {
var indent = new HashSet();
this.findNestedClasses(clazz, indent);
return indent.stream().map(TypeReference::of).collect(Collectors.toSet());
}
+ /**
+ * Recursively finds all nested classes of a given class.
+ *
+ * This method:
+ *
+ *
Collects both declared and inherited nested classes
+ *
Recursively processes each nested class
+ *
Adds the class names to the provided set
+ *
+ * @param clazz the class to find nested classes for
+ * @param indent the set to collect class names in
+ */
private void findNestedClasses(Class> clazz, Set indent) {
var classes = new ArrayList>();
classes.addAll(Arrays.asList(clazz.getDeclaredClasses()));
diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/customizer/McpAsyncClientCustomizer.java b/mcp/common/src/main/java/org/springframework/ai/mcp/customizer/McpAsyncClientCustomizer.java
new file mode 100644
index 00000000000..b059bcbe3c5
--- /dev/null
+++ b/mcp/common/src/main/java/org/springframework/ai/mcp/customizer/McpAsyncClientCustomizer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.mcp.customizer;
+
+import io.modelcontextprotocol.client.McpClient;
+
+/**
+ * Interface for customizing asynchronous MCP client configurations.
+ *
+ * This interface allows for customization of MCP client behavior through Spring's
+ * customizer pattern. Implementations can modify the client's configuration before it is
+ * used in the application.
+ *
+ * This method is called for each async MCP client being created, allowing for
+ * client-specific customizations based on the client's name and specification.
+ * @param name the name of the MCP client being customized
+ * @param spec the async specification to customize
+ */
+ void customize(String name, McpClient.AsyncSpec spec);
+
+}
diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/customizer/McpSyncClientCustomizer.java b/mcp/common/src/main/java/org/springframework/ai/mcp/customizer/McpSyncClientCustomizer.java
new file mode 100644
index 00000000000..98c0a1cdca8
--- /dev/null
+++ b/mcp/common/src/main/java/org/springframework/ai/mcp/customizer/McpSyncClientCustomizer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2025-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.ai.mcp.customizer;
+
+import io.modelcontextprotocol.client.McpClient;
+
+/**
+ * Interface for customizing synchronous MCP client configurations.
+ *
+ * This interface allows for customization of MCP client behavior through Spring's
+ * customizer pattern. Implementations can modify the client's configuration before it is
+ * used in the application.
+ *
+ * This method is called for each sync MCP client being created, allowing for
+ * client-specific customizations based on the client's name and specification.
+ * @param name the name of the MCP client being customized
+ * @param spec the sync specification to customize
+ */
+ void customize(String name, McpClient.SyncSpec spec);
+
+}
diff --git a/mcp/common/src/main/java/org/springframework/ai/mcp/package-info.java b/mcp/common/src/main/java/org/springframework/ai/mcp/package-info.java
index fdd7a9d995b..df28217aff9 100644
--- a/mcp/common/src/main/java/org/springframework/ai/mcp/package-info.java
+++ b/mcp/common/src/main/java/org/springframework/ai/mcp/package-info.java
@@ -14,6 +14,26 @@
* limitations under the License.
*/
+/**
+ * Core support for Model Context Protocol (MCP) integration in Spring AI.
+ *
+ * This package provides the foundational classes and utilities for integrating MCP with
+ * Spring AI's tool system. It includes:
+ *
+ *
Tool callback implementations for both synchronous and asynchronous MCP
+ * operations
+ *
Tool callback providers that discover and expose MCP tools
+ *
Utility classes for converting between Spring AI and MCP tool representations
+ *
Support for customizing MCP client behavior
+ *
+ *
+ * The classes in this package enable seamless integration between Spring AI applications
+ * and MCP servers, allowing language models to discover and invoke tools through a
+ * standardized protocol.
+ *
+ * @author Christian Tzolov
+ * @since 1.0.0
+ */
@NonNullApi
@NonNullFields
package org.springframework.ai.mcp;
diff --git a/mcp/common/src/main/resources/META-INF/spring/aot.factories b/mcp/common/src/main/resources/META-INF/spring/aot.factories
index dbf904981fb..b961ce5bcfb 100644
--- a/mcp/common/src/main/resources/META-INF/spring/aot.factories
+++ b/mcp/common/src/main/resources/META-INF/spring/aot.factories
@@ -1,2 +1,2 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
- org.springframework.ai.mcp.McpHints
\ No newline at end of file
+ org.springframework.ai.mcp.aot.McpHints
\ No newline at end of file
diff --git a/mcp/common/src/test/java/org/springframework/ai/mcp/McpToolCallbackProviderTests.java b/mcp/common/src/test/java/org/springframework/ai/mcp/SyncMcpToolCallbackProviderTests.java
similarity index 98%
rename from mcp/common/src/test/java/org/springframework/ai/mcp/McpToolCallbackProviderTests.java
rename to mcp/common/src/test/java/org/springframework/ai/mcp/SyncMcpToolCallbackProviderTests.java
index 0fc86888df7..608ae9f2017 100644
--- a/mcp/common/src/test/java/org/springframework/ai/mcp/McpToolCallbackProviderTests.java
+++ b/mcp/common/src/test/java/org/springframework/ai/mcp/SyncMcpToolCallbackProviderTests.java
@@ -33,7 +33,7 @@
import io.modelcontextprotocol.spec.McpSchema.Tool;
@ExtendWith(MockitoExtension.class)
-class McpToolCallbackProviderTests {
+class SyncMcpToolCallbackProviderTests {
@Mock
private McpSyncClient mcpClient;
diff --git a/mcp/common/src/test/java/org/springframework/ai/mcp/McpToolCallbackTests.java b/mcp/common/src/test/java/org/springframework/ai/mcp/SyncMcpToolCallbackTests.java
similarity index 92%
rename from mcp/common/src/test/java/org/springframework/ai/mcp/McpToolCallbackTests.java
rename to mcp/common/src/test/java/org/springframework/ai/mcp/SyncMcpToolCallbackTests.java
index d0eefaf75e5..7131385258b 100644
--- a/mcp/common/src/test/java/org/springframework/ai/mcp/McpToolCallbackTests.java
+++ b/mcp/common/src/test/java/org/springframework/ai/mcp/SyncMcpToolCallbackTests.java
@@ -31,7 +31,7 @@
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
-class McpToolCallbackTests {
+class SyncMcpToolCallbackTests {
@Mock
private McpSyncClient mcpClient;
@@ -45,7 +45,7 @@ void getToolDefinitionShouldReturnCorrectDefinition() {
when(tool.name()).thenReturn("testTool");
when(tool.description()).thenReturn("Test tool description");
- McpToolCallback callback = new McpToolCallback(mcpClient, tool);
+ SyncMcpToolCallback callback = new SyncMcpToolCallback(mcpClient, tool);
// Act
var toolDefinition = callback.getToolDefinition();
@@ -62,7 +62,7 @@ void callShouldHandleJsonInputAndOutput() {
CallToolResult callResult = mock(CallToolResult.class);
when(mcpClient.callTool(any(CallToolRequest.class))).thenReturn(callResult);
- McpToolCallback callback = new McpToolCallback(mcpClient, tool);
+ SyncMcpToolCallback callback = new SyncMcpToolCallback(mcpClient, tool);
// Act
String response = callback.call("{\"param\":\"value\"}");
diff --git a/pom.xml b/pom.xml
index 2136b768b93..f47ff2f5786 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,7 +34,12 @@
spring-ai-bomspring-ai-corespring-ai-test
+
spring-ai-spring-boot-autoconfigure
+
+ auto-configurations/spring-ai-mcp-client
+ auto-configurations/spring-ai-mcp-server
+
spring-ai-retryspring-ai-spring-boot-docker-composespring-ai-spring-boot-testcontainers
@@ -127,7 +132,11 @@
spring-ai-spring-boot-starters/spring-ai-starter-zhipuaispring-ai-spring-boot-starters/spring-ai-starter-moonshot
- spring-ai-spring-boot-starters/spring-ai-starter-mcp
+ spring-ai-spring-boot-starters/spring-ai-starter-mcp-client
+ spring-ai-spring-boot-starters/spring-ai-starter-mcp-server
+ spring-ai-spring-boot-starters/spring-ai-starter-mcp-client-webflux
+ spring-ai-spring-boot-starters/spring-ai-starter-mcp-server-webflux
+ spring-ai-spring-boot-starters/spring-ai-starter-mcp-server-webmvcspring-ai-integration-tests
@@ -890,6 +899,16 @@
lead
+
+ tzolov
+ Christian Tzolov
+ christian tzolov at broadcom.com
+ Broadcom
+ http://www.spring.io
+
+ lead
+
+
diff --git a/spring-ai-bom/pom.xml b/spring-ai-bom/pom.xml
index 5979f150857..093caee4dc5 100644
--- a/spring-ai-bom/pom.xml
+++ b/spring-ai-bom/pom.xml
@@ -310,6 +310,18 @@
${project.version}
+
+ org.springframework.ai
+ spring-ai-mcp-client-spring-boot-autoconfigure
+ ${project.version}
+
+
+
+ org.springframework.ai
+ spring-ai-mcp-server-spring-boot-autoconfigure
+ ${project.version}
+
+
org.springframework.ai
@@ -589,10 +601,35 @@
org.springframework.ai
- spring-ai-mcp-spring-boot-starter
+ spring-ai-mcp-client-spring-boot-starter
+ ${project.version}
+
+
+
+ org.springframework.ai
+ spring-ai-mcp-server-spring-boot-starter${project.version}
+
+ org.springframework.ai
+ spring-ai-mcp-client-webflux-spring-boot-starter
+ ${project.version}
+
+
+
+ org.springframework.ai
+ spring-ai-mcp-server-webflux-spring-boot-starter
+ ${project.version}
+
+
+
+ org.springframework.ai
+ spring-ai-mcp-server-webmvc-spring-boot-starter
+ ${project.version}
+
+
+
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/java-mcp-client-architecture.jpg b/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/java-mcp-client-architecture.jpg
new file mode 100644
index 00000000000..688a2b4ad0f
Binary files /dev/null and b/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/java-mcp-client-architecture.jpg differ
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/java-mcp-server-architecture.jpg b/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/java-mcp-server-architecture.jpg
new file mode 100644
index 00000000000..4b05ca139ac
Binary files /dev/null and b/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/java-mcp-server-architecture.jpg differ
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/java-mcp-uml-classdiagram.svg b/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/java-mcp-uml-classdiagram.svg
new file mode 100644
index 00000000000..f83a586e7b0
--- /dev/null
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/java-mcp-uml-classdiagram.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/mcp-stack.svg b/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/mcp-stack.svg
new file mode 100644
index 00000000000..3847eaa8d21
--- /dev/null
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/images/mcp/mcp-stack.svg
@@ -0,0 +1,197 @@
+
+
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc
index ba9efa25469..f94c7ea0843 100644
--- a/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc
@@ -95,10 +95,13 @@
* xref:api/prompt.adoc[]
* xref:api/structured-output-converter.adoc[Structured Output]
* xref:api/tools.adoc[Tool Calling]
-* xref:api/model-context-protocol.adoc[Model Context Protocol (MCP)]
* xref:api/functions.adoc[Function Calling (Deprecated)]
** xref:api/function-callback.adoc[FunctionCallback API (Deprecated)]
** xref:api/tools-migration.adoc[Migrating to ToolCallback API]
+* xref:api/mcp/mcp-overview.adoc[Model Context Protocol (MCP)]
+** xref:api/mcp/mcp-client-boot-starter-docs.adoc[MCP Client Boot Starters]
+** xref:api/mcp/mcp-server-boot-starter-docs.adoc[MCP Server Boot Starters]
+** xref:api/mcp/mcp-helpers.adoc[MCP Utilities]
* xref:api/multimodality.adoc[Multimodality]
* xref:api/etl-pipeline.adoc[]
* xref:api/testing.adoc[AI Model Evaluation]
@@ -112,4 +115,3 @@
* Appendices
** xref:upgrade-notes.adoc[]
-
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-client-boot-starter-docs.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-client-boot-starter-docs.adoc
new file mode 100644
index 00000000000..ec0fc25315c
--- /dev/null
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-client-boot-starter-docs.adoc
@@ -0,0 +1,353 @@
+= MCP Client Boot Starter
+
+The Spring AI MCP (Model Context Protocol) Client Boot Starter provides auto-configuration for MCP client functionality in Spring Boot applications. It supports both synchronous and asynchronous client implementations with various transport options.
+
+The MCP Client Boot Starter provides:
+
+* Management of multiple client instances
+* Automatic client initialization (if enabled)
+* Support for multiple named transports
+* Integration with Spring AI's tool execution framework
+* Proper lifecycle management with automatic cleanup of resources when the application context is closed
+* Customizable client creation through customizers
+
+== Starters
+
+=== Standard MCP Client
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mcp-client-spring-boot-starter
+
+----
+
+The standard starter connects simultaneously to one or more MCP servers over `STDIO` (in-process) and/or `SSE` (remote) transports.
+The SSE connection uses the HttpClient-based transport implementation.
+Each connection to an MCP server creates a new MCP client instance.
+You can choose either `SYNC` or `ASYNC` MCP clients (note: you cannot mix sync and async clients).
+For production deployment, we recommend using the WebFlux-based SSE connection with the `spring-ai-mcp-client-webflux-spring-boot-starter`.
+
+=== WebFlux Client
+
+The WebFlux starter provides similar functionality to the standard starter but uses a WebFlux-based SSE transport implementation.
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mcp-client-webflux-spring-boot-starter
+
+----
+
+== Configuration Properties
+
+=== Common Properties
+
+The common properties are prefixed with `spring.ai.mcp.client`:
+
+[cols="3,4,3"]
+|===
+|Property |Description |Default Value
+
+|`enabled`
+|Enable/disable the MCP client
+|`true`
+
+|`name`
+|Name of the MCP client instance (used for compatibility checks)
+|`spring-ai-mcp-client`
+
+|`version`
+|Version of the MCP client instance
+|`1.0.0`
+
+|`initialized`
+|Whether to initialize clients on creation
+|`true`
+
+|`request-timeout`
+|Timeout duration for MCP client requests
+|`20s`
+
+|`type`
+|Client type (SYNC or ASYNC). All clients must be either sync or async; mixing is not supported
+|`SYNC`
+
+|`root-change-notification`
+|Enable/disable root change notifications for all clients
+|`true`
+|===
+
+=== Stdio Transport Properties
+
+Properties for Standard I/O transport are prefixed with `spring.ai.mcp.client.stdio`:
+
+[cols="3,4,3"]
+|===
+|Property |Description |Default Value
+
+|`servers-configuration`
+|Resource containing the MCP servers configuration in JSON format
+|-
+
+|`connections`
+|Map of named stdio connection configurations
+|-
+
+|`connections.[name].command`
+|The command to execute for the MCP server
+|-
+
+|`connections.[name].args`
+|List of command arguments
+|-
+
+|`connections.[name].env`
+|Map of environment variables for the server process
+|-
+|===
+
+Example configuration:
+[source,yaml]
+----
+spring:
+ ai:
+ mcp:
+ client:
+ stdio:
+ root-change-notification: true
+ connections:
+ server1:
+ command: /path/to/server
+ args:
+ - --port=8080
+ - --mode=production
+ env:
+ API_KEY: your-api-key
+ DEBUG: "true"
+----
+
+Alternatively, you can configure stdio connections using an external JSON file using the link:https://modelcontextprotocol.io/quickstart/user[Claude Desktop format]:
+
+[source,yaml]
+----
+spring:
+ ai:
+ mcp:
+ client:
+ stdio:
+ servers-configuration: classpath:mcp-servers.json
+----
+
+The Claude Desktop format looks like this:
+
+[source,json]
+----
+{
+ "mcpServers": {
+ "filesystem": {
+ "command": "npx",
+ "args": [
+ "-y",
+ "@modelcontextprotocol/server-filesystem",
+ "/Users/username/Desktop",
+ "/Users/username/Downloads"
+ ]
+ }
+ }
+}
+----
+
+Currently, the Claude Desktop format supports only STDIO connection types.
+
+=== SSE Transport Properties
+
+Properties for Server-Sent Events (SSE) transport are prefixed with `spring.ai.mcp.client.sse`:
+
+[cols="2,4"]
+|===
+|Property |Description
+
+|`connections`
+|Map of named SSE connection configurations
+
+|`connections.[name].url`
+|URL endpoint for SSE communication with the MCP server
+|===
+
+Example configuration:
+[source,yaml]
+----
+spring:
+ ai:
+ mcp:
+ client:
+ sse:
+ connections:
+ server1:
+ url: http://localhost:8080
+ server2:
+ url: http://otherserver:8081
+----
+
+== Features
+
+=== Sync/Async Client Types
+
+The starter supports two types of clients:
+
+* Synchronous - default client type, suitable for traditional request-response patterns with blocking operations
+* Asynchronous - suitable for reactive applications with non-blocking operations, configured using `spring.ai.mcp.client.type=ASYNC`
+
+=== Client Customization
+
+The auto-configuration supports client spec customization.
+Implement the `McpSyncClientCustomizer` callback interface to customize the `McpClient.SyncSpec` spec for a named server connection.
+Similarly, the `McpAsyncClientCustomizer` interface allows customizing the `McpClient.AsyncSpec` spec for asynchronous clients.
+
+[tabs]
+======
+Sync::
++
+[source,java]
+----
+@Component
+public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {
+ @Override
+ public void customize(String name, McpClient.SyncSpec spec) {
+
+ // Customize the sync client configuration
+ spec.requestTimeout(Duration.ofSeconds(30));
+
+ // Adds a consumer to be notified when the available tools change. This allows the
+ // client to react to changes in the server's tool capabilities, such as tools
+ // being added or removed.
+ spec.toolsChangeConsumer((List tools) -> {
+ // Handle tools change
+ });
+
+ // Adds a consumer to be notified when the available resources change. This allows the
+ // client to react to changes in the server's resource capabilities, such as resources
+ // being added or removed.
+ spec.resourcesChangeConsumer((List resources) -> {
+ // Handle resources change
+ });
+
+ // Adds a consumer to be notified when the available prompts change. This allows the
+ // client to react to changes in the server's prompt capabilities, such as prompts
+ // being added or removed.
+ spec.promptsChangeConsumer((List prompts) -> {
+ // Handle prompts change
+ });
+
+ // Adds a consumer to be notified when logging messages are received from the server.
+ spec.loggingConsumer((McpSchema.LoggingMessageNotification log) -> {
+ // Handle log messages
+ });
+
+ // Sets a custom sampling handler for processing message creation requests.
+ spec.sampling((CreateMessageRequest messageRequest) -> {
+ // Handle sampling
+ CreateMessageResult result = ...
+ return result;
+ });
+
+ // Sets the root URIs that the server connecto this client can access.
+ // Roots define the base URIs for resources that the server can request.
+ // For example, a root might be "file://workspace" for accessing workspace files.
+ spec.roots(roots);
+ }
+}
+----
+
+Async::
++
+[source,java]
+----
+@Component
+public class CustomMcpAsyncClientCustomizer implements McpAsyncClientCustomizer {
+ @Override
+ public void customize(String name, McpClient.AsyncSpec spec) {
+ // Customize the async client configuration
+ spec.requestTimeout(Duration.ofSeconds(30));
+ }
+}
+----
+======
+
+The MCP client auto-configuration automatically detects and applies any customizers found in the application context.
+
+=== Transport Support
+
+The auto-configuration supports multiple transport types:
+
+* Standard I/O (Stdio) (activated by the `spring-ai-mcp-client-spring-boot-starter`)
+* SSE HTTP (activated by the `spring-ai-mcp-client-spring-boot-starter`)
+* SSE WebFlux (activated by the `spring-ai-starter-mcp-client-webflux`)
+
+=== Integration with Spring AI
+
+The starter automatically configures tool callbacks that integrate with Spring AI's tool execution framework, allowing MCP tools to be used as part of AI interactions.
+
+== Usage Example
+
+Add the appropriate starter dependency to your project and configure the client in `application.properties` or `application.yml`:
+
+[source,yaml]
+----
+spring:
+ ai:
+ mcp:
+ client:
+ enabled: true
+ name: my-mcp-client
+ version: 1.0.0
+ request-timeout: 30s
+ type: SYNC # or ASYNC for reactive applications
+ sse:
+ connections:
+ server1:
+ url: http://localhost:8080
+ server2:
+ url: http://otherserver:8081
+ stdio:
+ root-change-notification: false
+ connections:
+ server1:
+ command: /path/to/server
+ args:
+ - --port=8080
+ - --mode=production
+ env:
+ API_KEY: your-api-key
+ DEBUG: "true"
+----
+
+The MCP client beans will be automatically configured and available for injection:
+
+[source,java]
+----
+@Autowired
+private List mcpSyncClients; // For sync client
+
+// OR
+
+@Autowired
+private List mcpAsyncClients; // For async client
+----
+
+Additionally, the registered MCP Tools with all MCP clients are provided as a list of ToolCallback instances:
+
+[source,java]
+----
+@Autowired
+private List toolCallbacks;
+----
+
+== Additional Resources
+
+* link:https://docs.spring.io/spring-ai/reference/[Spring AI Documentation]
+* link:https://modelcontextprotocol.github.io/specification/[Model Context Protocol Specification]
+* link:https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration[Spring Boot Auto-configuration]
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-helpers.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-helpers.adoc
new file mode 100644
index 00000000000..ce756511f40
--- /dev/null
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-helpers.adoc
@@ -0,0 +1,156 @@
+= MCP Utilities
+:page-title: Spring AI MCP Utilities
+
+The MCP utilities provide foundational support for integrating Model Context Protocol with Spring AI applications.
+These utilities enable seamless communication between Spring AI's tool system and MCP servers, supporting both synchronous and asynchronous operations.
+They are typically used for programmatic MCP Client and Server configuration and interaction.
+For a more streamlined configuration, consider using the boot starters.
+
+== ToolCallback Utility
+
+=== Tool Callback Adapter
+
+Adapts MCP tools to Spring AI's tool interface with both synchronous and asynchronous execution support.
+
+[tabs]
+======
+Sync::
++
+[source,java]
+----
+McpSyncClient mcpClient = // obtain MCP client
+Tool mcpTool = // obtain MCP tool definition
+ToolCallback callback = new SyncMcpToolCallback(mcpClient, mcpTool);
+
+// Use the tool through Spring AI's interfaces
+ToolDefinition definition = callback.getToolDefinition();
+String result = callback.call("{\"param\": \"value\"}");
+----
+
+Async::
++
+[source,java]
+----
+McpAsyncClient mcpClient = // obtain MCP client
+Tool mcpTool = // obtain MCP tool definition
+ToolCallback callback = new AsyncMcpToolCallback(mcpClient, mcpTool);
+
+// Use the tool through Spring AI's interfaces
+ToolDefinition definition = callback.getToolDefinition();
+String result = callback.call("{\"param\": \"value\"}");
+----
+======
+
+=== Tool Callback Providers
+
+Discovers and provides MCP tools from MCP clients.
+
+[tabs]
+======
+Sync::
++
+[source,java]
+----
+McpSyncClient mcpClient = // obtain MCP client
+ToolCallbackProvider provider = new SyncMcpToolCallbackProvider(mcpClient);
+
+// Get all available tools
+ToolCallback[] tools = provider.getToolCallbacks();
+----
++
+For multiple clients:
++
+[source,java]
+----
+List clients = // obtain list of clients
+List callbacks = SyncMcpToolCallbackProvider.syncToolCallbacks(clients);
+----
+
+Async::
++
+[source,java]
+----
+McpAsyncClient mcpClient = // obtain MCP client
+ToolCallbackProvider provider = new AsyncMcpToolCallbackProvider(mcpClient);
+
+// Get all available tools
+ToolCallback[] tools = provider.getToolCallbacks();
+----
++
+For multiple clients:
++
+[source,java]
+----
+List clients = // obtain list of clients
+Flux callbacks = AsyncMcpToolCallbackProvider.asyncToolCallbacks(clients);
+----
+======
+
+== McpToolUtils
+
+=== ToolCallbacks to ToolRegistrations
+
+Converting Spring AI tool callbacks to MCP tool registrations:
+
+[tabs]
+======
+Sync::
++
+[source,java]
+----
+List toolCallbacks = // obtain tool callbacks
+List syncToolRegs = McpToolUtils.toSyncToolRegistration(toolCallbacks);
+----
++
+then you can use the `McpServer.SyncSpec` to register the tool registrations:
++
+[source,java]
+----
+McpServer.SyncSpec syncSpec = ...
+syncSpec.tools(syncToolRegs);
+----
+
+Async::
++
+[source,java]
+----
+List toolCallbacks = // obtain tool callbacks
+List asyncToolRegs = McpToolUtils.toAsyncToolRegistration(toolCallbacks);
+----
++
+then you can use the `McpServer.AsyncSpec` to register the tool registrations:
++
+[source,java]
+----
+McpServer.AsyncSpec asyncSpec = ...
+asyncSpec.tools(asyncToolRegs);
+----
+======
+
+=== MCP Clients to ToolCallbacks
+
+Getting tool callbacks from MCP clients
+
+[tabs]
+======
+Sync::
++
+[source,java]
+----
+List syncClients = // obtain sync clients
+List syncCallbacks = McpToolUtils.getToolCallbacksFromSyncClients(syncClients);
+----
+
+Async::
++
+[source,java]
+----
+List asyncClients = // obtain async clients
+List asyncCallbacks = McpToolUtils.getToolCallbacksFromAsyncClients(asyncClients);
+----
+======
+
+== Native Image Support
+
+The `McpHints` class provides GraalVM native image hints for MCP schema classes.
+This class automatically registers all necessary reflection hints for MCP schema classes when building native images.
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-overview.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-overview.adoc
new file mode 100644
index 00000000000..f0f2fec69a1
--- /dev/null
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-overview.adoc
@@ -0,0 +1,90 @@
+= Model Context Protocol (MCP)
+
+The link:https://modelcontextprotocol.org/docs/concepts/architecture[Model Context Protocol] (MCP) is a standardized protocol that enables AI models to interact with external tools and resources in a structured way.
+It supports multiple transport mechanisms for flexibility in different environments.
+
+The link:https://modelcontextprotocol.github.io/sdk/java[Java MCP] provides a Java SDK implementation of the Model Context Protocol, enabling standardized interaction with AI models and tools through both synchronous and asynchronous communication.
+
+== MCP Java SDK
+
+The Java MCP implementation follows a three-layer architecture:
+
+// [cols="2,10"]
+|===
+| |
+^a| image::mcp/mcp-stack.svg[MCP Stack Architecture]
+a| * *Client/Server Layer*: The McpClient handles client-side and the McpServer manages server-side protocol operations.
+Both utilize McpSession for operations
+* *Session Layer (McpSession)*: Manages communication patterns and state.
+Uses the DefaultMcpSession implementation.
+* *Transport Layer (McpTransport)*: Handles JSON-RPC message serialization/deserialization.
+Supports multiple transport implementations.
+|===
+
+// [cols="10,2"]
+|===
+| MCP Client |
+
+a| The MCP Client is a key component in the Model Context Protocol (MCP) architecture, responsible for establishing and managing connections with MCP servers. It implements the client-side of the protocol, handling:
+
+* Protocol version negotiation to ensure compatibility with servers
+* Capability negotiation to determine available features
+* Message transport and JSON-RPC communication
+* Tool discovery and execution
+* Resource access and management
+* Prompt system interactions
+* Optional features like roots management and sampling support
+* Synchronous and asynchronous operations
+* Multiple transport options:
+** Stdio-based transport for process-based communication
+** Java HttpClient-based SSE client transport
+** WebFlux SSE client transport for reactive HTTP streaming
+
+^a| image::mcp/java-mcp-client-architecture.jpg[Java MCP Client Architecture, width=500]
+|===
+
+// [cols="10,2"]
+|===
+| MCP Server |
+
+a| The MCP Server is a foundational component in the Model Context Protocol (MCP) architecture that provides tools, resources, and capabilities to clients. It implements the server-side of the protocol, responsible for:
+
+* Exposing tools that clients can discover and execute
+* Managing resources with URI-based access patterns
+* Providing prompt templates and handling prompt requests
+* Supporting capability negotiation with clients
+* Implementing server-side protocol operations
+* Managing concurrent client connections
+* Providing structured logging and notifications
+* Supporting both synchronous and asynchronous APIs for flexible integration
+* Multiple transport options:
+** Stdio-based transport for process-based communication
+** Servlet-based SSE server transport
+** WebFlux SSE server transport for reactive HTTP streaming
+** WebMVC SSE server transport for servlet-based HTTP streaming
+
+^a| image::mcp/java-mcp-server-architecture.jpg[Java MCP Server Architecture, width=600]
+|===
+
+For manual SDK implementation, refer to the link:https://modelcontextprotocol.github.io/sdk/java[MCP Java SDK documentation].
+For simplified setup, use the Spring AI MCP Boot Starters below.
+
+== Spring AI MCP Integration
+
+The Spring AI MCP integration is provided through Spring Boot starters:
+
+=== link:mcp-client-boot-starter-docs.html[Client Starters]
+* `spring-ai-mcp-client-spring-boot-starter` - Core starter with STDIO and HTTP-based SSE support
+* `spring-ai-mcp-client-webflux-spring-boot-starter` - WebFlux-based SSE transport
+
+=== link:mcp-server-boot-starter-docs.html[Server Starters]
+* `spring-ai-mcp-server-spring-boot-starter` - Core server with STDIO transport
+* `spring-ai-mcp-server-webmvc-spring-boot-starter` - Spring MVC-based SSE transport
+* `spring-ai-mcp-server-webflux-spring-boot-starter` - WebFlux-based SSE transport
+
+== Additional Resources
+
+* link:mcp-client-boot-starter-docs.html[MCP Client Boot Starters Documentation]
+* link:mcp-server-boot-starter-docs.html[MCP Server Boot Starters Documentation]
+* link:mcp-helpers.html[MCP Utilities Documentation]
+* link:https://modelcontextprotocol.github.io/specification/[Model Context Protocol Specification]
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-server-boot-starter-docs.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-server-boot-starter-docs.adoc
new file mode 100644
index 00000000000..4e80db98628
--- /dev/null
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/mcp/mcp-server-boot-starter-docs.adoc
@@ -0,0 +1,218 @@
+= MCP Server Boot Starter
+
+The Spring AI MCP (Model Context Protocol) Server Boot Starter provides auto-configuration for setting up an MCP server in Spring Boot applications. It enables seamless integration of MCP server capabilities with Spring Boot's auto-configuration system.
+
+The MCP Server Boot Starter offers:
+
+* Automatic configuration of MCP server components
+* Support for both synchronous and asynchronous operation modes
+* Multiple transport layer options
+* Flexible tool, resource, and prompt registration
+* Change notification capabilities
+
+== Starters
+
+Choose one of the following starters based on your transport requirements:
+
+=== Standard MCP Server
+
+Full MCP Server features support with `STDIO` server transport.
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mcp-server-spring-boot-starter
+
+----
+
+* Suitable for command-line and desktop tools
+* No additional web dependencies required
+
+The starter activates the `McpServerAutoConfiguration` auto-configuration responsible for:
+
+* Configuring the basic server components
+* Handling tool, resource, and prompt registrations
+* Managing server capabilities and change notifications
+* Providing both sync and async server implementations
+
+=== WebMVC Server Transport
+
+Full MCP Server features support with `SSE` (Server-Sent Events) server transport based on Spring MVC and an optional `STDIO` transport.
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mcp-server-webmvc-spring-boot-starter
+
+----
+
+The starter activates the `McpWebMvcServerAutoConfiguration` and `McpServerAutoConfiguration` auto-configurations to provide:
+
+* HTTP-based transport using Spring MVC (`WebMvcSseServerTransport`)
+* Automatically configured SSE endpoints
+* Optional `STDIO` transport (enabled by setting `spring.ai.mcp.server.stdio=true`)
+* Included `spring-boot-starter-web` and `mcp-spring-webmvc` dependencies
+
+=== WebFlux Server Transport
+
+Full MCP Server features support with `SSE` (Server-Sent Events) server transport based on Spring WebFlux and an optional `STDIO` transport.
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mcp-server-webflux-spring-boot-starter
+
+----
+
+The starter activates the `McpWebFluxServerAutoConfiguration` and `McpServerAutoConfiguration` auto-configurations to provide:
+
+* Reactive transport using Spring WebFlux (`WebFluxSseServerTransport`)
+* Automatically configured reactive SSE endpoints
+* Optional `STDIO` transport (enabled by setting `spring.ai.mcp.server.stdio=true`)
+* Included `spring-boot-starter-webflux` and `mcp-spring-webflux` dependencies
+
+== Configuration Properties
+
+All properties are prefixed with `spring.ai.mcp.server`:
+
+[options="header"]
+|===
+|Property |Description |Default
+|`enabled` |Enable/disable the MCP server |`true`
+|`stdio` |Enable/disable stdio transport |`false`
+|`name` |Server name for identification |`mcp-server`
+|`version` |Server version |`1.0.0`
+|`type` |Server type (SYNC/ASYNC) |`SYNC`
+|`resource-change-notification` |Enable resource change notifications |`true`
+|`tool-change-notification` |Enable tool change notifications |`true`
+|`prompt-change-notification` |Enable prompt change notifications |`true`
+|`sse-message-endpoint` |SSE endpoint path for web transport |`/mcp/message`
+|===
+
+== Sync/Async Server Types
+
+* **Synchronous Server** - The default server type implemented using `McpSyncServer`.
+It is designed for straightforward request-response patterns in your applications.
+To enable this server type, set `spring.ai.mcp.server.type=SYNC` in your configuration.
+When activated, it automatically handles the configuration of synchronous tool registrations.
+
+* **Asynchronous Server** - The asynchronous server implementation uses `McpAsyncServer` and is optimized for non-blocking operations.
+To enable this server type, configure your application with `spring.ai.mcp.server.type=ASYNC`.
+This server type automatically sets up asynchronous tool registrations with built-in Project Reactor support.
+
+== Transport Options
+
+The MCP Server supports three transport mechanisms, each with its dedicated starter:
+
+* Standard Input/Output (STDIO) - `spring-ai-mcp-server-spring-boot-starter`
+* Spring MVC (Server-Sent Events) - `spring-ai-mcp-server-webmvc-spring-boot-starter`
+* Spring WebFlux (Reactive SSE) - `spring-ai-mcp-server-webflux-spring-boot-starter`
+
+== Features and Capabilities
+
+=== Tools Registration
+* Support for both sync and async tool execution
+* Automatic tool registration through Spring beans
+* Change notification support
+* Tools are automatically converted to sync/async registrations based on server type
+
+=== Resource Management
+* Static and dynamic resource registration
+* Optional change notifications
+* Support for resource templates
+* Automatic conversion between sync/async resource registrations
+
+=== Prompt Templates
+* Configurable prompt registration
+* Change notification support
+* Template versioning
+* Automatic conversion between sync/async prompt registrations
+
+=== Root Change Consumers
+* Support for monitoring root changes
+* Automatic conversion to async consumers for reactive applications
+* Optional registration through Spring beans
+
+== Usage Examples
+
+=== Standard STDIO Server Configuration
+[source,yaml]
+----
+# Using spring-ai-mcp-server-spring-boot-starter
+spring:
+ ai:
+ mcp:
+ server:
+ name: stdio-mcp-server
+ version: 1.0.0
+ type: SYNC
+----
+
+=== WebMVC Server Configuration
+[source,yaml]
+----
+# Using spring-ai-mcp-server-webmvc-spring-boot-starter
+spring:
+ ai:
+ mcp:
+ server:
+ name: webmvc-mcp-server
+ version: 1.0.0
+ type: SYNC
+ sse-message-endpoint: /mcp/messages
+----
+
+=== WebFlux Server Configuration
+[source,yaml]
+----
+# Using spring-ai-mcp-server-webflux-spring-boot-starter
+spring:
+ ai:
+ mcp:
+ server:
+ name: webflux-mcp-server
+ version: 1.0.0
+ type: ASYNC # Recommended for reactive applications
+ sse-message-endpoint: /mcp/messages
+----
+
+=== Create Spring Boot application with MCP Server
+
+[source,java]
+----
+@Service
+public class WeatherService {
+
+ @Tool(description = "Get weather information by city name")
+ public String getBooks(String cityName) {
+ // Implementation
+ }
+}
+
+@SpringBootApplication
+public class McpServerApplication {
+
+ private static final Logger logger = LoggerFactory.getLogger(McpServerApplication.class);
+
+ public static void main(String[] args) {
+ SpringApplication.run(McpServerApplication.class, args);
+ }
+
+ @Bean
+ public List tools(WeatherService weatherService) {
+ return ToolCallbacks.from(weatherService);
+ }
+}
+----
+
+The auto-configuration will automatically register the toolcallbacs as MCP tools.
+You can have multiple beans producing list of ToolCallbacks. The autoconfiguration will merge them.
+
+== Additional Resources
+
+* link:https://docs.spring.io/spring-ai/reference/[Spring AI Documentation]
+* link:https://modelcontextprotocol.github.io/specification/[Model Context Protocol Specification]
+* link:https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration[Spring Boot Auto-configuration]
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/model-context-protocol.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/model-context-protocol.adoc
deleted file mode 100644
index 056ad242ac9..00000000000
--- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/model-context-protocol.adoc
+++ /dev/null
@@ -1,117 +0,0 @@
-[[MCP]]
-= Model Context Protocol (MCP)
-
-The link:https://modelcontextprotocol.io/introduction[Model Context Protocol (MCP)] is an open protocol that standardizes how applications provide context to Large Language Models (LLMs).
-MCP provides an unified way to connect AI models to different data sources and tools, making integration seamless and consistent.
-It helps you build agents and complex workflows on top of LLMs. LLMs frequently need to integrate with data and tools, and MCP provides:
-- A growing list of pre-built integrations that your LLM can directly plug into
-- The flexibility to switch between LLM providers and vendors
-
-== Spring AI MCP
-
-NOTE: Spring AI MCP is an experimental project and subject to change.
-
-link:https://github.com/spring-projects-experimental/spring-ai-mcp[Spring AI MCP] is an experimental project that provides Java and Spring Framework integration for the Model Context Protocol.
-It enables Spring AI applications to interact with different data sources and tools, through a standardized interface, supporting both synchronous and asynchronous communication patterns.
-
-image::spring-ai-mcp-architecture.jpg[SpringAIMCP, 800]
-
-The Spring AI MCP implements a modular architecture with the following components:
-
-- Spring AI Application: Uses Spring AI framework to build Generative AI applications that want to access data through MCP
-- Spring MCP Clients: Spring AI implementation of the MCP protocol that maintain 1:1 connections with servers
-- MCP Servers: Lightweight programs that each expose specific capabilities through the standardized Model Context Protocol
-- Local Data Sources: Your computer's files, databases, and services that MCP servers can securely access
-- Remote Services: External systems available over the internet (e.g., through APIs) that MCP servers can connect to
-
-The architecture supports a wide range of use cases, from simple file system access to complex multi-model AI interactions with database and internet connectivity.
-
-== Getting Started
-
-Add the SDK to your Maven project:
-
-
-[tabs]
-======
-Maven::
-+
-[source,xml,indent=0,subs="verbatim,quotes"]
-----
-
- org.springframework.experimental
- spring-ai-mcp
- 0.2.0
-
-----
-
-Gradle::
-+
-[source,groovy,indent=0,subs="verbatim,quotes"]
-----
-dependencies {
- implementation 'org.springframework.experimental:spring-ai-mcp:0.2.0'
-}
-----
-======
-
-[NOTE]
-====
-The Spring AI MCP milestones are not available in the Maven Central Repository yet.
-Please add tehe Spring Milestone Repository to your build file to access the Spring AI MCP artifacts:
-
-[source,xml,indent=0,subs="verbatim,quotes"]
-----
-
-
- spring-milestones
- Spring Milestones
- https://repo.spring.io/libs-milestone-local
-
- false
-
-
-
-----
-====
-
-The latter builds on top of mcp-core to provide some useful Spring AI abstractions, such as `McpFunctionCallback`.
-
-Now create an `McpClient` to regester the MCP Brave server tools with your ChatClient and let the LLM call them:
-
-[source,java]
-----
-// https://github.com/modelcontextprotocol/servers/tree/main/src/brave-search
-var stdioParams = ServerParameters.builder("npx")
- .args("-y", "@modelcontextprotocol/server-brave-search")
- .addEnvVar("BRAVE_API_KEY", System.getenv("BRAVE_API_KEY"))
- .build();
-
-var mcpClient = McpClient.using(new StdioClientTransport(stdioParams)).sync();
-
-var init = mcpClient.initialize();
-
-var chatClient = chatClientBuilder
- .defaultFunctions(mcpClient.listTools(null)
- .tools()
- .stream()
- .map(tool -> new McpFunctionCallback(mcpClient, tool))
- .toArray(McpFunctionCallback[]::new))
- .build();
-
-String response = chatClient
- .prompt("Does Spring AI supports the Model Context Protocol? Please provide some references.")
- .call().content();
-----
-
-
-== Example Demos
-
-There is a growing link:https://github.com/modelcontextprotocol/servers[list of MCP Servers] that you can use with Spring AI MCP.
-Explore these MCP examples in the link:https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol[spring-ai-examples/model-context-protocol] repository:
-
-- link:https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/sqlite/simple[SQLite Simple] - Demonstrates LLM integration with a database
-- link:https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/sqlite/chatbot[SQLite Chatbot] - Interactive chatbot with SQLite database interaction
-- https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/filesystem[Filesystem] - Enables LLM interaction with local filesystem folders and files
-- https://github.com/spring-projects/spring-ai-examples/tree/main/model-context-protocol/brave[Brave] - Enables natural language interactions with Brave Search, allowing you to perform internet searches.
-
-
diff --git a/spring-ai-spring-boot-autoconfigure/pom.xml b/spring-ai-spring-boot-autoconfigure/pom.xml
index b876c536f18..ac2df801d4c 100644
--- a/spring-ai-spring-boot-autoconfigure/pom.xml
+++ b/spring-ai-spring-boot-autoconfigure/pom.xml
@@ -20,9 +20,6 @@
git@github.com:spring-projects/spring-ai.git
-
-
-
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/McpStdioConnection.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/McpStdioConnection.java
deleted file mode 100644
index 54b7630d171..00000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/McpStdioConnection.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2025-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.ai.autoconfigure.mcp.client.stdio;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import io.modelcontextprotocol.client.transport.ServerParameters;
-
-public class McpStdioConnection {
-
- private String command;
-
- private List args = new ArrayList<>();
-
- private Map env;
-
- public String getCommand() {
- return this.command;
- }
-
- public void setCommand(String command) {
- this.command = command;
- }
-
- public List getArgs() {
- return this.args;
- }
-
- public void setArgs(List args) {
- this.args = args;
- }
-
- public Map getEnv() {
- return this.env;
- }
-
- public void setEnv(Map env) {
- this.env = env;
- }
-
- public ServerParameters toServerParameters() {
- return ServerParameters.builder(this.command).args(this.args).env(this.env).build();
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/MpcStdioClientAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/MpcStdioClientAutoConfiguration.java
deleted file mode 100644
index cd7c4127f1a..00000000000
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/mcp/client/stdio/MpcStdioClientAutoConfiguration.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2025-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.ai.autoconfigure.mcp.client.stdio;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import io.modelcontextprotocol.client.McpClient;
-import io.modelcontextprotocol.client.McpSyncClient;
-import io.modelcontextprotocol.client.transport.ServerParameters;
-import io.modelcontextprotocol.client.transport.StdioClientTransport;
-import io.modelcontextprotocol.spec.McpSchema;
-
-import org.springframework.ai.mcp.McpSyncClientCustomizer;
-import org.springframework.ai.mcp.McpToolCallback;
-import org.springframework.ai.mcp.McpToolUtils;
-import org.springframework.ai.tool.ToolCallback;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.context.annotation.Bean;
-
-/**
- * Auto-configuration for Model Context Protocol (MCP) STDIO clients.
- *
- *
- * This configuration is responsible for setting up MCP clients that communicate with MCP
- * servers through standard input/output (STDIO). It creates and configures
- * {@link McpSyncClient} instances based on the provided configuration properties.
- *
- *
- * The configuration is conditionally enabled when:
- *
- *
Required classes ({@link McpSchema} and {@link McpSyncClient}) are present on the
- * classpath
- *
The 'spring.ai.mcp.client.stdio.enabled' property is set to 'true'
- *
- *
- *
- * This auto-configuration provides:
- *
- *
A {@code List} bean configured for STDIO communication
- *
A {@link McpSyncClientConfigurer} bean for customizing the MCP sync client
- * configuration
- *
A {@code List} bean containing tool callbacks from the MCP
- * clients
- *
- *
- * @author Christian Tzolov
- * @since 1.0.0
- * @see McpStdioClientProperties
- * @see McpSyncClient
- * @see McpToolCallback
- */
-@AutoConfiguration
-@ConditionalOnClass({ McpSchema.class, McpSyncClient.class })
-@EnableConfigurationProperties(McpStdioClientProperties.class)
-@ConditionalOnProperty(prefix = McpStdioClientProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true")
-public class MpcStdioClientAutoConfiguration {
-
- @Bean
- public List mcpSyncClients(McpSyncClientConfigurer mcpSyncClientConfigurer,
- McpStdioClientProperties clientProperties) {
-
- List clients = new ArrayList<>();
-
- for (Map.Entry serverParameters : clientProperties.toServerParameters().entrySet()) {
-
- var transport = new StdioClientTransport(serverParameters.getValue());
-
- McpSchema.Implementation clientInfo = new McpSchema.Implementation(serverParameters.getKey(),
- clientProperties.getVersion());
-
- McpClient.SyncSpec syncSpec = McpClient.sync(transport)
- .clientInfo(clientInfo)
- .requestTimeout(clientProperties.getRequestTimeout());
-
- syncSpec = mcpSyncClientConfigurer.configure(serverParameters.getKey(), syncSpec);
-
- var syncClient = syncSpec.build();
-
- if (clientProperties.isInitialize()) {
- syncClient.initialize();
- }
-
- clients.add(syncClient);
-
- }
-
- return clients;
- }
-
- @Bean
- public List toolCallbacks(List mcpClients) {
- return McpToolUtils.getToolCallbacks(mcpClients);
- }
-
- public record ClosebleMcpSyncClients(List clients) implements AutoCloseable {
-
- @Override
- public void close() {
- this.clients.forEach(McpSyncClient::close);
- }
- }
-
- @Bean
- public ClosebleMcpSyncClients makeThemClosable(List clients) {
- return new ClosebleMcpSyncClients(clients);
- }
-
- @Bean
- @ConditionalOnMissingBean
- McpSyncClientConfigurer mcpSyncClientConfigurer(ObjectProvider customizerProvider) {
- McpSyncClientConfigurer configurer = new McpSyncClientConfigurer();
- configurer.setCustomizers(customizerProvider.orderedStream().toList());
- return configurer;
- }
-
-}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index aa9a96d02b7..022b4a8b5e7 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -60,8 +60,3 @@ org.springframework.ai.autoconfigure.minimax.MiniMaxAutoConfiguration
org.springframework.ai.autoconfigure.vertexai.embedding.VertexAiEmbeddingAutoConfiguration
org.springframework.ai.autoconfigure.chat.memory.cassandra.CassandraChatMemoryAutoConfiguration
org.springframework.ai.autoconfigure.vectorstore.observation.VectorStoreObservationAutoConfiguration
-org.springframework.ai.autoconfigure.mcp.server.MpcServerAutoConfiguration
-org.springframework.ai.autoconfigure.mcp.server.MpcWebMvcServerAutoConfiguration
-org.springframework.ai.autoconfigure.mcp.server.MpcWebFluxServerAutoConfiguration
-org.springframework.ai.autoconfigure.mcp.client.stdio.MpcStdioClientAutoConfiguration
-
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mcp/server/McpServerAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mcp/server/McpServerAutoConfigurationIT.java
deleted file mode 100644
index c2580bb82c5..00000000000
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mcp/server/McpServerAutoConfigurationIT.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2025-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.ai.autoconfigure.mcp.server;
-
-import io.modelcontextprotocol.server.McpAsyncServer;
-import io.modelcontextprotocol.server.McpSyncServer;
-import io.modelcontextprotocol.server.transport.StdioServerTransport;
-import io.modelcontextprotocol.spec.ServerMcpTransport;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class McpServerAutoConfigurationIT {
-
- private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withPropertyValues("spring.ai.mcp.server.enabled=true")
- .withConfiguration(AutoConfigurations.of(MpcServerAutoConfiguration.class));
-
- @Test
- void defaultConfiguration() {
- this.contextRunner.run(context -> {
- assertThat(context).hasSingleBean(McpSyncServer.class);
- assertThat(context).hasSingleBean(ServerMcpTransport.class);
- assertThat(context.getBean(ServerMcpTransport.class)).isInstanceOf(StdioServerTransport.class);
-
- McpServerProperties properties = context.getBean(McpServerProperties.class);
- assertThat(properties.getName()).isEqualTo("mcp-server");
- assertThat(properties.getVersion()).isEqualTo("1.0.0");
- assertThat(properties.getTransport()).isEqualTo(McpServerProperties.Transport.STDIO);
- assertThat(properties.getType()).isEqualTo(McpServerProperties.ServerType.SYNC);
- assertThat(properties.isToolChangeNotification()).isTrue();
- assertThat(properties.isResourceChangeNotification()).isTrue();
- assertThat(properties.isPromptChangeNotification()).isTrue();
- });
- }
-
- @Test
- void asyncConfiguration() {
- this.contextRunner
- .withPropertyValues("spring.ai.mcp.server.type=ASYNC", "spring.ai.mcp.server.name=test-server",
- "spring.ai.mcp.server.version=2.0.0")
- .run(context -> {
- assertThat(context).hasSingleBean(McpAsyncServer.class);
- assertThat(context).doesNotHaveBean(McpSyncServer.class);
-
- McpServerProperties properties = context.getBean(McpServerProperties.class);
- assertThat(properties.getName()).isEqualTo("test-server");
- assertThat(properties.getVersion()).isEqualTo("2.0.0");
- assertThat(properties.getType()).isEqualTo(McpServerProperties.ServerType.ASYNC);
- });
- }
-
- @Test
- void disabledConfiguration() {
- this.contextRunner.withPropertyValues("spring.ai.mcp.server.enabled=false").run(context -> {
- assertThat(context).doesNotHaveBean(McpSyncServer.class);
- assertThat(context).doesNotHaveBean(McpAsyncServer.class);
- assertThat(context).doesNotHaveBean(ServerMcpTransport.class);
- });
- }
-
- @Test
- void notificationConfiguration() {
- this.contextRunner
- .withPropertyValues("spring.ai.mcp.server.tool-change-notification=false",
- "spring.ai.mcp.server.resource-change-notification=false",
- "spring.ai.mcp.server.prompt-change-notification=false")
- .run(context -> {
- McpServerProperties properties = context.getBean(McpServerProperties.class);
- assertThat(properties.isToolChangeNotification()).isFalse();
- assertThat(properties.isResourceChangeNotification()).isFalse();
- assertThat(properties.isPromptChangeNotification()).isFalse();
- });
- }
-
-}
diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-mcp-client-webflux/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-mcp-client-webflux/pom.xml
new file mode 100644
index 00000000000..0d2c8b210e8
--- /dev/null
+++ b/spring-ai-spring-boot-starters/spring-ai-starter-mcp-client-webflux/pom.xml
@@ -0,0 +1,66 @@
+
+
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+ spring-ai-mcp-client-webflux-spring-boot-starter
+ jar
+ Spring AI Starter - MCP Client Webflux
+ Spring AI MCP Client WebFlux Auto Configuration
+ https://github.com/spring-projects/spring-ai
+
+
+ https://github.com/spring-projects/spring-ai
+ git://github.com/spring-projects/spring-ai.git
+ git@github.com:spring-projects/spring-ai.git
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.ai
+ spring-ai-mcp-client-spring-boot-autoconfigure
+ ${project.parent.version}
+
+
+
+ org.springframework.ai
+ spring-ai-mcp
+ ${project.parent.version}
+
+
+
+ io.modelcontextprotocol.sdk
+ mcp-spring-webflux
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-mcp/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-mcp-client/pom.xml
similarity index 88%
rename from spring-ai-spring-boot-starters/spring-ai-starter-mcp/pom.xml
rename to spring-ai-spring-boot-starters/spring-ai-starter-mcp-client/pom.xml
index e7e54a69b22..06a22b85a89 100644
--- a/spring-ai-spring-boot-starters/spring-ai-starter-mcp/pom.xml
+++ b/spring-ai-spring-boot-starters/spring-ai-starter-mcp-client/pom.xml
@@ -23,10 +23,10 @@
1.0.0-SNAPSHOT../../pom.xml
- spring-ai-mcp-spring-boot-starter
+ spring-ai-mcp-client-spring-boot-starterjar
- Spring AI Starter - MCP
- Spring AI MCP Auto Configuration
+ Spring AI Starter - MCP Client
+ Spring AI MCP Client Auto Configurationhttps://github.com/spring-projects/spring-ai
@@ -44,7 +44,7 @@
org.springframework.ai
- spring-ai-spring-boot-autoconfigure
+ spring-ai-mcp-client-spring-boot-autoconfigure${project.parent.version}
diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-mcp-server-webflux/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-mcp-server-webflux/pom.xml
new file mode 100644
index 00000000000..e847b730fe9
--- /dev/null
+++ b/spring-ai-spring-boot-starters/spring-ai-starter-mcp-server-webflux/pom.xml
@@ -0,0 +1,72 @@
+
+
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+ spring-ai-mcp-server-webflux-spring-boot-starter
+ jar
+ Spring AI Starter - MCP Server Webflux
+ Spring AI MCP Server WebFlux Auto Configuration
+ https://github.com/spring-projects/spring-ai
+
+
+ https://github.com/spring-projects/spring-ai
+ git://github.com/spring-projects/spring-ai.git
+ git@github.com:spring-projects/spring-ai.git
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.ai
+ spring-ai-mcp-server-spring-boot-autoconfigure
+ ${project.parent.version}
+
+
+
+ org.springframework.ai
+ spring-ai-mcp
+ ${project.parent.version}
+
+
+
+ io.modelcontextprotocol.sdk
+ mcp-spring-webflux
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-mcp-server-webmvc/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-mcp-server-webmvc/pom.xml
new file mode 100644
index 00000000000..6441d943ef8
--- /dev/null
+++ b/spring-ai-spring-boot-starters/spring-ai-starter-mcp-server-webmvc/pom.xml
@@ -0,0 +1,71 @@
+
+
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+ spring-ai-mcp-server-webmvc-spring-boot-starter
+ jar
+ Spring AI Starter - MCP Server WebMvc
+ Spring AI MCP Server WebMvc Auto Configuration
+ https://github.com/spring-projects/spring-ai
+
+
+ https://github.com/spring-projects/spring-ai
+ git://github.com/spring-projects/spring-ai.git
+ git@github.com:spring-projects/spring-ai.git
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.ai
+ spring-ai-spring-boot-autoconfigure
+ ${project.parent.version}
+
+
+
+ org.springframework.ai
+ spring-ai-mcp
+ ${project.parent.version}
+
+
+
+ io.modelcontextprotocol.sdk
+ mcp-spring-webmvc
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-mcp-server/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-mcp-server/pom.xml
new file mode 100644
index 00000000000..15d0b79fc56
--- /dev/null
+++ b/spring-ai-spring-boot-starters/spring-ai-starter-mcp-server/pom.xml
@@ -0,0 +1,58 @@
+
+
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+ spring-ai-mcp-server-spring-boot-starter
+ jar
+ Spring AI Starter - MCP Server
+ Spring AI MCP Server Auto Configuration
+ https://github.com/spring-projects/spring-ai
+
+
+ https://github.com/spring-projects/spring-ai
+ git://github.com/spring-projects/spring-ai.git
+ git@github.com:spring-projects/spring-ai.git
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.ai
+ spring-ai-mcp-server-spring-boot-autoconfigure
+ ${project.parent.version}
+
+
+
+ org.springframework.ai
+ spring-ai-mcp
+ ${project.parent.version}
+
+
+
+