org.springframework.ai
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/McpClientAutoConfiguration.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfiguration.java
similarity index 91%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/McpClientAutoConfiguration.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfiguration.java
index 4de3b5d1f1f..28c628e5019 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/McpClientAutoConfiguration.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfiguration.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.common.autoconfigure;
import java.util.ArrayList;
import java.util.List;
@@ -24,9 +24,9 @@
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.spec.McpSchema;
-import org.springframework.ai.mcp.client.autoconfigure.configurer.McpAsyncClientConfigurer;
-import org.springframework.ai.mcp.client.autoconfigure.configurer.McpSyncClientConfigurer;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.configurer.McpAsyncClientConfigurer;
+import org.springframework.ai.mcp.client.common.autoconfigure.configurer.McpSyncClientConfigurer;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties;
import org.springframework.ai.mcp.customizer.McpAsyncClientCustomizer;
import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
import org.springframework.beans.factory.ObjectProvider;
@@ -98,8 +98,14 @@
* @see SseHttpClientTransportAutoConfiguration
* @see SseWebFluxTransportAutoConfiguration
*/
-@AutoConfiguration(after = { StdioTransportAutoConfiguration.class, SseHttpClientTransportAutoConfiguration.class,
- SseWebFluxTransportAutoConfiguration.class })
+@AutoConfiguration(afterName = {
+ "org.springframework.ai.mcp.client.common.autoconfigure.StdioTransportAutoConfiguration",
+ "org.springframework.ai.mcp.client.httpclient.autoconfigure.SseHttpClientTransportAutoConfiguration",
+ "org.springframework.ai.mcp.client.httpclient.autoconfigure.StreamableHttpHttpClientTransportAutoConfiguration",
+ "org.springframework.ai.mcp.client.webflux.autoconfigure.SseWebFluxTransportAutoConfiguration",
+ "org.springframework.ai.mcp.client.webflux.autoconfigure.StreamableHttpWebFluxTransportAutoConfiguration" })
+
+// @AutoConfiguration
@ConditionalOnClass({ McpSchema.class })
@EnableConfigurationProperties(McpClientCommonProperties.class)
@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/McpToolCallbackAutoConfiguration.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/McpToolCallbackAutoConfiguration.java
similarity index 95%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/McpToolCallbackAutoConfiguration.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/McpToolCallbackAutoConfiguration.java
index 53083e7620d..a477af8a47a 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/McpToolCallbackAutoConfiguration.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/McpToolCallbackAutoConfiguration.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.common.autoconfigure;
import java.util.List;
@@ -23,7 +23,7 @@
import org.springframework.ai.mcp.AsyncMcpToolCallbackProvider;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/NamedClientMcpTransport.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/NamedClientMcpTransport.java
similarity index 94%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/NamedClientMcpTransport.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/NamedClientMcpTransport.java
index 238e67ee566..55e840c0045 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/NamedClientMcpTransport.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/NamedClientMcpTransport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.common.autoconfigure;
import io.modelcontextprotocol.spec.McpClientTransport;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/StdioTransportAutoConfiguration.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/StdioTransportAutoConfiguration.java
similarity index 92%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/StdioTransportAutoConfiguration.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/StdioTransportAutoConfiguration.java
index 57b96e1ad89..bb3aefbb66b 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/StdioTransportAutoConfiguration.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/StdioTransportAutoConfiguration.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.common.autoconfigure;
import java.util.ArrayList;
import java.util.List;
@@ -24,8 +24,8 @@
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpStdioClientProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpStdioClientProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java
new file mode 100644
index 00000000000..c0f21e5a7c9
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java
@@ -0,0 +1,42 @@
+/*
+ * 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.client.common.autoconfigure.aot;
+
+import org.springframework.aot.hint.MemberCategory;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.RuntimeHintsRegistrar;
+
+import static org.springframework.ai.aot.AiRuntimeHints.findJsonAnnotatedClassesInPackage;
+
+/**
+ * @author Josh Long
+ * @author Soby Chacko
+ * @author Christian Tzolov
+ */
+public class McpClientAutoConfigurationRuntimeHints implements RuntimeHintsRegistrar {
+
+ @Override
+ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+ hints.resources().registerPattern("**.json");
+
+ var mcs = MemberCategory.values();
+ for (var tr : findJsonAnnotatedClassesInPackage("org.springframework.ai.mcp.client.common.autoconfigure")) {
+ hints.reflection().registerType(tr, mcs);
+ }
+ }
+
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/configurer/McpAsyncClientConfigurer.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/configurer/McpAsyncClientConfigurer.java
similarity index 94%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/configurer/McpAsyncClientConfigurer.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/configurer/McpAsyncClientConfigurer.java
index 5f57c90237d..7ba21e9a8b8 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/configurer/McpAsyncClientConfigurer.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/configurer/McpAsyncClientConfigurer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure.configurer;
+package org.springframework.ai.mcp.client.common.autoconfigure.configurer;
import java.util.List;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/configurer/McpSyncClientConfigurer.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/configurer/McpSyncClientConfigurer.java
similarity index 96%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/configurer/McpSyncClientConfigurer.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/configurer/McpSyncClientConfigurer.java
index 681b5fb0001..87520419cc1 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/configurer/McpSyncClientConfigurer.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/configurer/McpSyncClientConfigurer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure.configurer;
+package org.springframework.ai.mcp.client.common.autoconfigure.configurer;
import java.util.List;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpClientCommonProperties.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpClientCommonProperties.java
similarity index 98%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpClientCommonProperties.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpClientCommonProperties.java
index c53720bbe00..fcc534080aa 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpClientCommonProperties.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpClientCommonProperties.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure.properties;
+package org.springframework.ai.mcp.client.common.autoconfigure.properties;
import java.time.Duration;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpSseClientProperties.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpSseClientProperties.java
similarity index 96%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpSseClientProperties.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpSseClientProperties.java
index 54a1963d0d6..f23029ddd96 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpSseClientProperties.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpSseClientProperties.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure.properties;
+package org.springframework.ai.mcp.client.common.autoconfigure.properties;
import java.util.HashMap;
import java.util.Map;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpStdioClientProperties.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpStdioClientProperties.java
similarity index 98%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpStdioClientProperties.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpStdioClientProperties.java
index 2b18ce3cda0..7517f45e858 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpStdioClientProperties.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpStdioClientProperties.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure.properties;
+package org.springframework.ai.mcp.client.common.autoconfigure.properties;
import java.util.HashMap;
import java.util.List;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpStreamableHttpClientProperties.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpStreamableHttpClientProperties.java
new file mode 100644
index 00000000000..afa74ded003
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpStreamableHttpClientProperties.java
@@ -0,0 +1,74 @@
+/*
+ * 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.client.common.autoconfigure.properties;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Configuration properties for Streamable Http client connections.
+ *
+ *
+ * These properties allow configuration of multiple named Streamable Http connections to
+ * MCP servers. Each connection is configured with a URL endpoint for communication.
+ *
+ *
+ * Example configuration:
+ * spring.ai.mcp.client.streamable-http:
+ * connections-http:
+ * server1:
+ * url: http://localhost:8080/events
+ * server2:
+ * url: http://otherserver:8081/events
+ *
+ *
+ * @author Christian Tzolov
+ * @see ConnectionParameters
+ */
+@ConfigurationProperties(McpStreamableHttpClientProperties.CONFIG_PREFIX)
+public class McpStreamableHttpClientProperties {
+
+ public static final String CONFIG_PREFIX = "spring.ai.mcp.client.streamable-http";
+
+ /**
+ * Map of named Streamable Http connection configurations.
+ *
+ * The key represents the connection name, and the value contains the Streamable Http
+ * parameters for that connection.
+ */
+ private final Map connections = new HashMap<>();
+
+ /**
+ * Returns the map of configured Streamable Http connections.
+ * @return map of connection names to their Streamable Http parameters
+ */
+ public Map getConnections() {
+ return this.connections;
+ }
+
+ /**
+ * Parameters for configuring an Streamable Http connection to an MCP server.
+ *
+ * @param url the URL endpoint for Streamable Http communication with the MCP server
+ * @param endpoint the endpoint for the MCP server
+ */
+ public record ConnectionParameters(String url, String endpoint) {
+ }
+
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/resources/META-INF/spring/aot.factories b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/resources/META-INF/spring/aot.factories
new file mode 100644
index 00000000000..810b2a3164e
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/resources/META-INF/spring/aot.factories
@@ -0,0 +1,2 @@
+org.springframework.aot.hint.RuntimeHintsRegistrar=\
+ org.springframework.ai.mcp.client.common.autoconfigure.aot.McpClientAutoConfigurationRuntimeHints
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpClientAutoConfigurationIT.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfigurationIT.java
similarity index 96%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpClientAutoConfigurationIT.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfigurationIT.java
index 0b988f80cba..728188672c5 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpClientAutoConfigurationIT.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfigurationIT.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.common.autoconfigure;
import java.time.Duration;
import java.util.List;
@@ -30,8 +30,8 @@
import org.mockito.Mockito;
import reactor.core.publisher.Mono;
-import org.springframework.ai.mcp.client.autoconfigure.configurer.McpSyncClientConfigurer;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.configurer.McpSyncClientConfigurer;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties;
import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.boot.autoconfigure.AutoConfigurations;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpClientAutoConfigurationRuntimeHintsTests.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfigurationRuntimeHintsTests.java
similarity index 92%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpClientAutoConfigurationRuntimeHintsTests.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfigurationRuntimeHintsTests.java
index b5acb04af41..a514705c6b9 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpClientAutoConfigurationRuntimeHintsTests.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfigurationRuntimeHintsTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.common.autoconfigure;
import java.io.IOException;
import java.util.HashSet;
@@ -22,8 +22,8 @@
import org.junit.jupiter.api.Test;
-import org.springframework.ai.mcp.client.autoconfigure.aot.McpClientAutoConfigurationRuntimeHints;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpStdioClientProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.aot.McpClientAutoConfigurationRuntimeHints;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpStdioClientProperties;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference;
import org.springframework.core.io.Resource;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpToolCallbackAutoConfigurationConditionTests.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpToolCallbackAutoConfigurationConditionTests.java
similarity index 93%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpToolCallbackAutoConfigurationConditionTests.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpToolCallbackAutoConfigurationConditionTests.java
index 0fb5174ef6c..3708e0fa036 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpToolCallbackAutoConfigurationConditionTests.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpToolCallbackAutoConfigurationConditionTests.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.common.autoconfigure;
import org.junit.jupiter.api.Test;
-import org.springframework.ai.mcp.client.autoconfigure.McpToolCallbackAutoConfiguration.McpToolCallbackAutoConfigurationCondition;
+import org.springframework.ai.mcp.client.common.autoconfigure.McpToolCallbackAutoConfiguration.McpToolCallbackAutoConfigurationCondition;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpToolCallbackAutoConfigurationTests.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpToolCallbackAutoConfigurationTests.java
similarity index 97%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpToolCallbackAutoConfigurationTests.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpToolCallbackAutoConfigurationTests.java
index 8838857971d..9a55167fa31 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/McpToolCallbackAutoConfigurationTests.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpToolCallbackAutoConfigurationTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.common.autoconfigure;
import org.junit.jupiter.api.Test;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpClientCommonPropertiesTests.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpClientCommonPropertiesTests.java
similarity index 99%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpClientCommonPropertiesTests.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpClientCommonPropertiesTests.java
index 39c25c26327..18eb85e2c3f 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpClientCommonPropertiesTests.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpClientCommonPropertiesTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure.properties;
+package org.springframework.ai.mcp.client.common.autoconfigure.properties;
import java.time.Duration;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpSseClientPropertiesTests.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpSseClientPropertiesTests.java
similarity index 99%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpSseClientPropertiesTests.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpSseClientPropertiesTests.java
index 79f532c8f32..b3c72aa08b3 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/properties/McpSseClientPropertiesTests.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/properties/McpSseClientPropertiesTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure.properties;
+package org.springframework.ai.mcp.client.common.autoconfigure.properties;
import java.util.Map;
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/resources/application-test.properties b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/resources/application-test.properties
similarity index 100%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/resources/application-test.properties
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/resources/application-test.properties
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/resources/nested/nested-config.json b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/resources/nested/nested-config.json
similarity index 100%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/resources/nested/nested-config.json
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/resources/nested/nested-config.json
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/resources/test-config.json b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/resources/test-config.json
similarity index 100%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/resources/test-config.json
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/resources/test-config.json
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/pom.xml b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/pom.xml
new file mode 100644
index 00000000000..caef1b36103
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/pom.xml
@@ -0,0 +1,84 @@
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai-parent
+ 1.1.0-SNAPSHOT
+ ../../../pom.xml
+
+ spring-ai-autoconfigure-mcp-client-httpclient
+ jar
+ Spring AI MCP Client (HttpClient) Auto Configuration
+ Spring AI MCP Client (HttpClient) 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
+
+
+
+ org.springframework.ai
+ spring-ai-autoconfigure-mcp-client-common
+ ${project.parent.version}
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure-processor
+ true
+
+
+
+
+ org.springframework.ai
+ spring-ai-test
+ ${project.parent.version}
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+ org.testcontainers
+ junit-jupiter
+ ${testcontainers.version}
+ test
+
+
+
+
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfiguration.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/java/org/springframework/ai/mcp/client/httpclient/autoconfigure/SseHttpClientTransportAutoConfiguration.java
similarity index 85%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfiguration.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/java/org/springframework/ai/mcp/client/httpclient/autoconfigure/SseHttpClientTransportAutoConfiguration.java
index 79aabc0d2cc..1f713e75e88 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfiguration.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/java/org/springframework/ai/mcp/client/httpclient/autoconfigure/SseHttpClientTransportAutoConfiguration.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.httpclient.autoconfigure;
import java.net.http.HttpClient;
import java.util.ArrayList;
@@ -26,13 +26,13 @@
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpSseClientProperties;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpSseClientProperties.SseParameters;
+import org.springframework.ai.mcp.client.common.autoconfigure.NamedClientMcpTransport;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpSseClientProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpSseClientProperties.SseParameters;
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.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@@ -61,9 +61,8 @@
* @see HttpClientSseClientTransport
* @see McpSseClientProperties
*/
-@AutoConfiguration(after = SseWebFluxTransportAutoConfiguration.class)
+@AutoConfiguration
@ConditionalOnClass({ McpSchema.class, McpSyncClient.class })
-@ConditionalOnMissingClass("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport")
@EnableConfigurationProperties({ McpSseClientProperties.class, McpClientCommonProperties.class })
@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
matchIfMissing = true)
@@ -85,7 +84,7 @@ public class SseHttpClientTransportAutoConfiguration {
* @return list of named MCP transports
*/
@Bean
- public List mcpHttpClientTransports(McpSseClientProperties sseProperties,
+ public List sseHttpClientTransports(McpSseClientProperties sseProperties,
ObjectProvider objectMapperProvider) {
ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/java/org/springframework/ai/mcp/client/httpclient/autoconfigure/StreamableHttpHttpClientTransportAutoConfiguration.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/java/org/springframework/ai/mcp/client/httpclient/autoconfigure/StreamableHttpHttpClientTransportAutoConfiguration.java
new file mode 100644
index 00000000000..f18066553f8
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/java/org/springframework/ai/mcp/client/httpclient/autoconfigure/StreamableHttpHttpClientTransportAutoConfiguration.java
@@ -0,0 +1,118 @@
+/*
+ * 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.client.httpclient.autoconfigure;
+
+import java.net.http.HttpClient;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.ai.mcp.client.common.autoconfigure.NamedClientMcpTransport;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpStreamableHttpClientProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpStreamableHttpClientProperties.ConnectionParameters;
+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.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
+import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
+import io.modelcontextprotocol.spec.McpSchema;
+
+/**
+ * Auto-configuration for Streamable HTTP client transport in the Model Context Protocol
+ * (MCP).
+ *
+ *
+ * This configuration class sets up the necessary beans for Streamable HTTP client
+ * transport when WebFlux is not available. It provides HTTP client-based Streamable HTTP
+ * transport implementation for MCP client communication.
+ *
+ *
+ * The configuration is activated after the WebFlux Streamable HTTP transport
+ * auto-configuration to ensure proper fallback behavior when WebFlux is not available.
+ *
+ *
+ * Key features:
+ *
+ * - Creates HTTP client-based Streamable HTTP transports for configured MCP server
+ * connections
+ *
- Configures ObjectMapper for JSON serialization/deserialization
+ *
- Supports multiple named server connections with different URLs
+ *
+ *
+ * @see HttpClientStreamableHttpTransport
+ * @see McpStreamableHttpClientProperties
+ */
+@AutoConfiguration
+@ConditionalOnClass({ McpSchema.class, McpSyncClient.class })
+@EnableConfigurationProperties({ McpStreamableHttpClientProperties.class, McpClientCommonProperties.class })
+@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
+ matchIfMissing = true)
+public class StreamableHttpHttpClientTransportAutoConfiguration {
+
+ /**
+ * Creates a list of HTTP client-based Streamable HTTP transports for MCP
+ * communication.
+ *
+ *
+ * Each transport is configured with:
+ *
+ * - A new HttpClient instance
+ *
- Server URL from properties
+ *
- ObjectMapper for JSON processing
+ *
+ * @param streamableProperties the Streamable HTTP client properties containing server
+ * configurations
+ * @param objectMapperProvider the provider for ObjectMapper or a new instance if not
+ * available
+ * @return list of named MCP transports
+ */
+ @Bean
+ public List streamableHttpHttpClientTransports(
+ McpStreamableHttpClientProperties streamableProperties, ObjectProvider objectMapperProvider) {
+
+ ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
+
+ List streamableHttpTransports = new ArrayList<>();
+
+ for (Map.Entry serverParameters : streamableProperties.getConnections()
+ .entrySet()) {
+
+ String baseUrl = serverParameters.getValue().url();
+ String streamableHttpEndpoint = serverParameters.getValue().endpoint() != null
+ ? serverParameters.getValue().endpoint() : "/mcp";
+
+ HttpClientStreamableHttpTransport transport = HttpClientStreamableHttpTransport.builder(baseUrl)
+ .endpoint(streamableHttpEndpoint)
+ .clientBuilder(HttpClient.newBuilder())
+ .objectMapper(objectMapper)
+ .build();
+
+ streamableHttpTransports.add(new NamedClientMcpTransport(serverParameters.getKey(), transport));
+ }
+
+ return streamableHttpTransports;
+ }
+
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/java/org/springframework/ai/mcp/client/httpclient/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/java/org/springframework/ai/mcp/client/httpclient/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java
new file mode 100644
index 00000000000..c32a4d3ffc1
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/java/org/springframework/ai/mcp/client/httpclient/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java
@@ -0,0 +1,42 @@
+/*
+ * 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.client.httpclient.autoconfigure.aot;
+
+import org.springframework.aot.hint.MemberCategory;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.RuntimeHintsRegistrar;
+
+import static org.springframework.ai.aot.AiRuntimeHints.findJsonAnnotatedClassesInPackage;
+
+/**
+ * @author Josh Long
+ * @author Soby Chacko
+ * @author Christian Tzolov
+ */
+public class McpClientAutoConfigurationRuntimeHints implements RuntimeHintsRegistrar {
+
+ @Override
+ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+ hints.resources().registerPattern("**.json");
+
+ var mcs = MemberCategory.values();
+ for (var tr : findJsonAnnotatedClassesInPackage("org.springframework.ai.mcp.client.httpclient.autoconfigure")) {
+ hints.reflection().registerType(tr, mcs);
+ }
+ }
+
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/resources/META-INF/spring/aot.factories b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/resources/META-INF/spring/aot.factories
new file mode 100644
index 00000000000..63d01bc0352
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/resources/META-INF/spring/aot.factories
@@ -0,0 +1,2 @@
+org.springframework.aot.hint.RuntimeHintsRegistrar=\
+ org.springframework.ai.mcp.client.httpclient.autoconfigure.aot.McpClientAutoConfigurationRuntimeHints
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 00000000000..8011f6035ca
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/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.mcp.client.common.autoconfigure.StdioTransportAutoConfiguration
+org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration
+org.springframework.ai.mcp.client.common.autoconfigure.McpToolCallbackAutoConfiguration
+org.springframework.ai.mcp.client.httpclient.autoconfigure.SseHttpClientTransportAutoConfiguration
+org.springframework.ai.mcp.client.httpclient.autoconfigure.StreamableHttpHttpClientTransportAutoConfiguration
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfigurationIT.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfigurationIT.java
new file mode 100644
index 00000000000..70b9b2563a4
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfigurationIT.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024-2024 the original author or authors.
+ */
+
+package org.springframework.ai.mcp.client.autoconfigure;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration;
+import org.springframework.ai.mcp.client.httpclient.autoconfigure.SseHttpClientTransportAutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
+
+@Timeout(15)
+public class SseHttpClientTransportAutoConfigurationIT {
+
+ private static final Logger logger = LoggerFactory.getLogger(SseHttpClientTransportAutoConfigurationIT.class);
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withPropertyValues("spring.ai.mcp.client.initialized=false",
+ "spring.ai.mcp.client.sse.connections.server1.url=" + host)
+ .withConfiguration(
+ AutoConfigurations.of(McpClientAutoConfiguration.class, SseHttpClientTransportAutoConfiguration.class));
+
+ static String host = "http://localhost:3001";
+
+ // Uses the https://github.com/tzolov/mcp-everything-server-docker-image
+ @SuppressWarnings("resource")
+ static GenericContainer> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2")
+ .withCommand("node dist/index.js sse")
+ .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
+ .withExposedPorts(3001)
+ .waitingFor(Wait.forHttp("/").forStatusCode(404));
+
+ @BeforeAll
+ static void setUp() {
+ container.start();
+ int port = container.getMappedPort(3001);
+ host = "http://" + container.getHost() + ":" + port;
+ logger.info("Container started at host: {}", host);
+ }
+
+ @AfterAll
+ static void tearDown() {
+ container.stop();
+ }
+
+ @Test
+ void streamableHttpTest() {
+ this.contextRunner.run(context -> {
+ List mcpClients = (List) context.getBean("mcpSyncClients");
+
+ assertThat(mcpClients).isNotNull();
+ assertThat(mcpClients).hasSize(1);
+
+ McpSyncClient mcpClient = mcpClients.get(0);
+
+ mcpClient.ping();
+
+ System.out.println("mcpClient = " + mcpClient.getServerInfo());
+
+ ListToolsResult toolsResult = mcpClient.listTools();
+
+ assertThat(toolsResult).isNotNull();
+ assertThat(toolsResult.tools()).isNotEmpty();
+ assertThat(toolsResult.tools()).hasSize(8);
+
+ logger.info("tools = {}", toolsResult);
+
+ });
+ }
+
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfigurationTests.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfigurationTests.java
similarity index 73%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfigurationTests.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfigurationTests.java
index fadf71cec75..1e57a9551ea 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfigurationTests.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfigurationTests.java
@@ -16,21 +16,23 @@
package org.springframework.ai.mcp.client.autoconfigure;
+import static org.assertj.core.api.Assertions.assertThat;
+
import java.lang.reflect.Field;
import java.util.List;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
import org.junit.jupiter.api.Test;
-
+import org.springframework.ai.mcp.client.common.autoconfigure.NamedClientMcpTransport;
+import org.springframework.ai.mcp.client.httpclient.autoconfigure.SseHttpClientTransportAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
-import static org.assertj.core.api.Assertions.assertThat;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
/**
* Tests for {@link SseHttpClientTransportAutoConfiguration}.
@@ -42,47 +44,26 @@ public class SseHttpClientTransportAutoConfigurationTests {
private final ApplicationContextRunner applicationContext = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SseHttpClientTransportAutoConfiguration.class));
- @Test
- void mcpHttpClientTransportsNotPresentIfMissingWebFluxSseClientTransportPresent() {
- this.applicationContext.run(context -> assertThat(context.containsBean("mcpHttpClientTransports")).isFalse());
- }
-
- @Test
- void mcpHttpClientTransportsPresentIfMissingWebFluxSseClientTransportNotPresent() {
- this.applicationContext
- .withClassLoader(
- new FilteredClassLoader("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport"))
- .run(context -> assertThat(context.containsBean("mcpHttpClientTransports")).isTrue());
- }
-
@Test
void mcpHttpClientTransportsNotPresentIfMcpClientDisabled() {
- this.applicationContext
- .withClassLoader(
- new FilteredClassLoader("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport"))
- .withPropertyValues("spring.ai.mcp.client.enabled", "false")
- .run(context -> assertThat(context.containsBean("mcpHttpClientTransports")).isFalse());
+ this.applicationContext.withPropertyValues("spring.ai.mcp.client.enabled", "false")
+ .run(context -> assertThat(context.containsBean("sseHttpClientTransports")).isFalse());
}
@Test
void noTransportsCreatedWithEmptyConnections() {
- this.applicationContext
- .withClassLoader(
- new FilteredClassLoader("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport"))
- .run(context -> {
- List transports = context.getBean("mcpHttpClientTransports", List.class);
- assertThat(transports).isEmpty();
- });
+ this.applicationContext.run(context -> {
+ List transports = context.getBean("sseHttpClientTransports", List.class);
+ assertThat(transports).isEmpty();
+ });
}
@Test
void singleConnectionCreatesOneTransport() {
this.applicationContext
- .withClassLoader(
- new FilteredClassLoader("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport"))
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080")
.run(context -> {
- List transports = context.getBean("mcpHttpClientTransports", List.class);
+ List transports = context.getBean("sseHttpClientTransports", List.class);
assertThat(transports).hasSize(1);
assertThat(transports.get(0).name()).isEqualTo("server1");
assertThat(transports.get(0).transport()).isInstanceOf(HttpClientSseClientTransport.class);
@@ -92,12 +73,10 @@ void singleConnectionCreatesOneTransport() {
@Test
void multipleConnectionsCreateMultipleTransports() {
this.applicationContext
- .withClassLoader(
- new FilteredClassLoader("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport"))
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080",
"spring.ai.mcp.client.sse.connections.server2.url=http://otherserver:8081")
.run(context -> {
- List transports = context.getBean("mcpHttpClientTransports", List.class);
+ List transports = context.getBean("sseHttpClientTransports", List.class);
assertThat(transports).hasSize(2);
assertThat(transports).extracting("name").containsExactlyInAnyOrder("server1", "server2");
assertThat(transports).extracting("transport")
@@ -112,12 +91,10 @@ void multipleConnectionsCreateMultipleTransports() {
@Test
void customSseEndpointIsRespected() {
this.applicationContext
- .withClassLoader(
- new FilteredClassLoader("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport"))
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080",
"spring.ai.mcp.client.sse.connections.server1.sse-endpoint=/custom-sse")
.run(context -> {
- List transports = context.getBean("mcpHttpClientTransports", List.class);
+ List transports = context.getBean("sseHttpClientTransports", List.class);
assertThat(transports).hasSize(1);
assertThat(transports.get(0).name()).isEqualTo("server1");
assertThat(transports.get(0).transport()).isInstanceOf(HttpClientSseClientTransport.class);
@@ -129,14 +106,11 @@ void customSseEndpointIsRespected() {
@Test
void customObjectMapperIsUsed() {
- this.applicationContext
- .withClassLoader(
- new FilteredClassLoader("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport"))
- .withUserConfiguration(CustomObjectMapperConfiguration.class)
+ this.applicationContext.withUserConfiguration(CustomObjectMapperConfiguration.class)
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080")
.run(context -> {
assertThat(context.getBean(ObjectMapper.class)).isNotNull();
- List transports = context.getBean("mcpHttpClientTransports", List.class);
+ List transports = context.getBean("sseHttpClientTransports", List.class);
assertThat(transports).hasSize(1);
});
}
@@ -144,11 +118,9 @@ void customObjectMapperIsUsed() {
@Test
void defaultSseEndpointIsUsedWhenNotSpecified() {
this.applicationContext
- .withClassLoader(
- new FilteredClassLoader("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport"))
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080")
.run(context -> {
- List transports = context.getBean("mcpHttpClientTransports", List.class);
+ List transports = context.getBean("sseHttpClientTransports", List.class);
assertThat(transports).hasSize(1);
assertThat(transports.get(0).name()).isEqualTo("server1");
assertThat(transports.get(0).transport()).isInstanceOf(HttpClientSseClientTransport.class);
@@ -159,13 +131,11 @@ void defaultSseEndpointIsUsedWhenNotSpecified() {
@Test
void mixedConnectionsWithAndWithoutCustomSseEndpoint() {
this.applicationContext
- .withClassLoader(
- new FilteredClassLoader("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport"))
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080",
"spring.ai.mcp.client.sse.connections.server1.sse-endpoint=/custom-sse",
"spring.ai.mcp.client.sse.connections.server2.url=http://otherserver:8081")
.run(context -> {
- List transports = context.getBean("mcpHttpClientTransports", List.class);
+ List transports = context.getBean("sseHttpClientTransports", List.class);
assertThat(transports).hasSize(2);
assertThat(transports).extracting("name").containsExactlyInAnyOrder("server1", "server2");
assertThat(transports).extracting("transport")
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/java/org/springframework/ai/mcp/client/autoconfigure/StreamableHttpHttpClientTransportAutoConfigurationIT.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/java/org/springframework/ai/mcp/client/autoconfigure/StreamableHttpHttpClientTransportAutoConfigurationIT.java
new file mode 100644
index 00000000000..452373a711f
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/java/org/springframework/ai/mcp/client/autoconfigure/StreamableHttpHttpClientTransportAutoConfigurationIT.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2024-2024 the original author or authors.
+ */
+
+package org.springframework.ai.mcp.client.autoconfigure;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration;
+import org.springframework.ai.mcp.client.httpclient.autoconfigure.StreamableHttpHttpClientTransportAutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
+
+@Timeout(15)
+public class StreamableHttpHttpClientTransportAutoConfigurationIT {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(StreamableHttpHttpClientTransportAutoConfigurationIT.class);
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withPropertyValues("spring.ai.mcp.client.initialized=false",
+ "spring.ai.mcp.client.streamable-http.connections.server1.url=" + host)
+ .withConfiguration(AutoConfigurations.of(McpClientAutoConfiguration.class,
+ StreamableHttpHttpClientTransportAutoConfiguration.class));
+
+ static String host = "http://localhost:3001";
+
+ // Uses the https://github.com/tzolov/mcp-everything-server-docker-image
+ @SuppressWarnings("resource")
+ static GenericContainer> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2")
+ .withCommand("node dist/index.js streamableHttp")
+ .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
+ .withExposedPorts(3001)
+ .waitingFor(Wait.forHttp("/").forStatusCode(404));
+
+ @BeforeAll
+ static void setUp() {
+ container.start();
+ int port = container.getMappedPort(3001);
+ host = "http://" + container.getHost() + ":" + port;
+ logger.info("Container started at host: {}", host);
+ }
+
+ @AfterAll
+ static void tearDown() {
+ container.stop();
+ }
+
+ @Test
+ void streamableHttpTest() {
+ this.contextRunner.run(context -> {
+ List mcpClients = (List) context.getBean("mcpSyncClients");
+
+ assertThat(mcpClients).isNotNull();
+ assertThat(mcpClients).hasSize(1);
+
+ McpSyncClient mcpClient = mcpClients.get(0);
+
+ mcpClient.ping();
+
+ System.out.println("mcpClient = " + mcpClient.getServerInfo());
+
+ ListToolsResult toolsResult = mcpClient.listTools();
+
+ assertThat(toolsResult).isNotNull();
+ assertThat(toolsResult.tools()).isNotEmpty();
+ assertThat(toolsResult.tools()).hasSize(8);
+
+ logger.info("tools = {}", toolsResult);
+
+ });
+ }
+
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/resources/application-test.properties b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/resources/application-test.properties
new file mode 100644
index 00000000000..9107b9e407a
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/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/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/resources/nested/nested-config.json b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/resources/nested/nested-config.json
new file mode 100644
index 00000000000..7cd51d6d490
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/resources/nested/nested-config.json
@@ -0,0 +1,8 @@
+{
+ "name": "nested-config",
+ "description": "Test JSON file in nested subfolder of test resources",
+ "version": "1.0.0",
+ "nestedProperties": {
+ "nestedProperty1": "nestedValue1"
+ }
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/resources/test-config.json b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/resources/test-config.json
new file mode 100644
index 00000000000..57e2a46f20e
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient/src/test/resources/test-config.json
@@ -0,0 +1,8 @@
+{
+ "name": "test-config",
+ "description": "Test JSON file in root test resources folder",
+ "version": "1.0.0",
+ "properties": {
+ "testProperty1": "value1"
+ }
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/pom.xml b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/pom.xml
new file mode 100644
index 00000000000..0f6eb5758f8
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/pom.xml
@@ -0,0 +1,90 @@
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai-parent
+ 1.1.0-SNAPSHOT
+ ../../../pom.xml
+
+ spring-ai-autoconfigure-mcp-client-webflux
+ jar
+ Spring AI MCP WebFlux Client Auto Configuration
+ Spring AI MCP WebFlux 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
+
+
+
+ org.springframework.ai
+ spring-ai-autoconfigure-mcp-client-common
+ ${project.parent.version}
+
+
+
+ io.modelcontextprotocol.sdk
+ mcp-spring-webflux
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure-processor
+ true
+
+
+
+
+ org.springframework.ai
+ spring-ai-test
+ ${project.parent.version}
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+ org.testcontainers
+ junit-jupiter
+ ${testcontainers.version}
+ test
+
+
+
+
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/SseWebFluxTransportAutoConfiguration.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/autoconfigure/SseWebFluxTransportAutoConfiguration.java
similarity index 87%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/SseWebFluxTransportAutoConfiguration.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/autoconfigure/SseWebFluxTransportAutoConfiguration.java
index 4c064835e3b..595cd97dfa6 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/SseWebFluxTransportAutoConfiguration.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/autoconfigure/SseWebFluxTransportAutoConfiguration.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.webflux.autoconfigure;
import java.util.ArrayList;
import java.util.List;
@@ -23,9 +23,10 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpSseClientProperties;
-import org.springframework.ai.mcp.client.autoconfigure.properties.McpSseClientProperties.SseParameters;
+import org.springframework.ai.mcp.client.common.autoconfigure.NamedClientMcpTransport;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpSseClientProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpSseClientProperties.SseParameters;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -79,7 +80,7 @@ public class SseWebFluxTransportAutoConfiguration {
* @return list of named MCP transports
*/
@Bean
- public List webFluxClientTransports(McpSseClientProperties sseProperties,
+ public List sseWebFluxClientTransports(McpSseClientProperties sseProperties,
ObjectProvider webClientBuilderProvider,
ObjectProvider objectMapperProvider) {
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/autoconfigure/StreamableHttpWebFluxTransportAutoConfiguration.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/autoconfigure/StreamableHttpWebFluxTransportAutoConfiguration.java
new file mode 100644
index 00000000000..81d588e928b
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/autoconfigure/StreamableHttpWebFluxTransportAutoConfiguration.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.client.webflux.autoconfigure;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.ai.mcp.client.common.autoconfigure.NamedClientMcpTransport;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpClientCommonProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpStreamableHttpClientProperties;
+import org.springframework.ai.mcp.client.common.autoconfigure.properties.McpStreamableHttpClientProperties.ConnectionParameters;
+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.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport;
+
+/**
+ * Auto-configuration for WebFlux-based Streamable HTTP client transport in the Model
+ * Context Protocol (MCP).
+ *
+ *
+ * This configuration class sets up the necessary beans for Streamable HTTP-based WebFlux
+ * transport, providing reactive transport implementation for MCP client communication
+ * when WebFlux is available on the classpath.
+ *
+ *
+ * Key features:
+ *
+ * - Creates WebFlux-based Streamable HTTP 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 WebClientStreamableHttpTransport
+ * @see McpStreamableHttpClientProperties
+ */
+@AutoConfiguration
+@ConditionalOnClass({ WebClientStreamableHttpTransport.class, WebClient.class })
+@EnableConfigurationProperties({ McpStreamableHttpClientProperties.class, McpClientCommonProperties.class })
+@ConditionalOnProperty(prefix = McpClientCommonProperties.CONFIG_PREFIX, name = "enabled", havingValue = "true",
+ matchIfMissing = true)
+public class StreamableHttpWebFluxTransportAutoConfiguration {
+
+ /**
+ * Creates a list of WebFlux-based Streamable HTTP 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 streamableProperties the Streamable HTTP client properties containing server
+ * configurations
+ * @param webClientBuilderProvider the provider for WebClient.Builder
+ * @param objectMapperProvider the provider for ObjectMapper or a new instance if not
+ * available
+ * @return list of named MCP transports
+ */
+ @Bean
+ public List streamableHttpwebFluxClientTransports(
+ McpStreamableHttpClientProperties streamableProperties,
+ ObjectProvider webClientBuilderProvider,
+ ObjectProvider objectMapperProvider) {
+
+ List streamableHttpTransports = new ArrayList<>();
+
+ var webClientBuilderTemplate = webClientBuilderProvider.getIfAvailable(WebClient::builder);
+ var objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
+
+ for (Map.Entry serverParameters : streamableProperties.getConnections()
+ .entrySet()) {
+ var webClientBuilder = webClientBuilderTemplate.clone().baseUrl(serverParameters.getValue().url());
+ String streamableHttpEndpoint = serverParameters.getValue().endpoint() != null
+ ? serverParameters.getValue().endpoint() : "/mcp";
+
+ var transport = WebClientStreamableHttpTransport.builder(webClientBuilder)
+ .endpoint(streamableHttpEndpoint)
+ .objectMapper(objectMapper)
+ .build();
+
+ streamableHttpTransports.add(new NamedClientMcpTransport(serverParameters.getKey(), transport));
+ }
+
+ return streamableHttpTransports;
+ }
+
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java
similarity index 91%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java
index e551f57b837..a29c572087d 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/autoconfigure/aot/McpClientAutoConfigurationRuntimeHints.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure.aot;
+package org.springframework.ai.mcp.client.webflux.autoconfigure.aot;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
@@ -33,7 +33,7 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.resources().registerPattern("**.json");
var mcs = MemberCategory.values();
- for (var tr : findJsonAnnotatedClassesInPackage("org.springframework.ai.mcp.client.autoconfigure")) {
+ for (var tr : findJsonAnnotatedClassesInPackage("org.springframework.ai.mcp.client.webflux.autoconfigure")) {
hints.reflection().registerType(tr, mcs);
}
}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/resources/META-INF/spring/aot.factories b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/resources/META-INF/spring/aot.factories
new file mode 100644
index 00000000000..2e4c3886554
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/resources/META-INF/spring/aot.factories
@@ -0,0 +1,2 @@
+org.springframework.aot.hint.RuntimeHintsRegistrar=\
+ org.springframework.ai.mcp.client.webflux.autoconfigure.aot.McpClientAutoConfigurationRuntimeHints
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
similarity index 57%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index e740e0f7de3..abc775e076b 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -13,10 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-org.springframework.ai.mcp.client.autoconfigure.StdioTransportAutoConfiguration
-org.springframework.ai.mcp.client.autoconfigure.SseWebFluxTransportAutoConfiguration
-org.springframework.ai.mcp.client.autoconfigure.SseHttpClientTransportAutoConfiguration
-org.springframework.ai.mcp.client.autoconfigure.McpClientAutoConfiguration
-org.springframework.ai.mcp.client.autoconfigure.McpToolCallbackAutoConfiguration
-
+org.springframework.ai.mcp.client.common.autoconfigure.StdioTransportAutoConfiguration
+org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration
+org.springframework.ai.mcp.client.common.autoconfigure.McpToolCallbackAutoConfiguration
+org.springframework.ai.mcp.client.webflux.autoconfigure.SseWebFluxTransportAutoConfiguration
+org.springframework.ai.mcp.client.webflux.autoconfigure.StreamableHttpWebFluxTransportAutoConfiguration
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/SseWebFluxTransportAutoConfigurationIT.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/SseWebFluxTransportAutoConfigurationIT.java
new file mode 100644
index 00000000000..096218786f1
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/SseWebFluxTransportAutoConfigurationIT.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2024-2024 the original author or authors.
+ */
+
+package org.springframework.ai.mcp.client.webflux.autoconfigure;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
+
+@Timeout(15)
+public class SseWebFluxTransportAutoConfigurationIT {
+
+ private static final Logger logger = LoggerFactory.getLogger(SseWebFluxTransportAutoConfigurationIT.class);
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withPropertyValues("spring.ai.mcp.client.initialized=false",
+ "spring.ai.mcp.client.sse.connections.server1.url=" + host)
+ .withConfiguration(
+ AutoConfigurations.of(McpClientAutoConfiguration.class, SseWebFluxTransportAutoConfiguration.class));
+
+ static String host = "http://localhost:3001";
+
+ // Uses the https://github.com/tzolov/mcp-everything-server-docker-image
+ @SuppressWarnings("resource")
+ static GenericContainer> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2")
+ .withCommand("node dist/index.js sse")
+ .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
+ .withExposedPorts(3001)
+ .waitingFor(Wait.forHttp("/").forStatusCode(404));
+
+ @BeforeAll
+ static void setUp() {
+ container.start();
+ int port = container.getMappedPort(3001);
+ host = "http://" + container.getHost() + ":" + port;
+ logger.info("Container started at host: {}", host);
+ }
+
+ @AfterAll
+ static void tearDown() {
+ container.stop();
+ }
+
+ @Test
+ void streamableHttpTest() {
+ this.contextRunner.run(context -> {
+ List mcpClients = (List) context.getBean("mcpSyncClients");
+
+ assertThat(mcpClients).isNotNull();
+ assertThat(mcpClients).hasSize(1);
+
+ McpSyncClient mcpClient = mcpClients.get(0);
+
+ mcpClient.ping();
+
+ System.out.println("mcpClient = " + mcpClient.getServerInfo());
+
+ ListToolsResult toolsResult = mcpClient.listTools();
+
+ assertThat(toolsResult).isNotNull();
+ assertThat(toolsResult.tools()).isNotEmpty();
+ assertThat(toolsResult.tools()).hasSize(8);
+
+ logger.info("tools = {}", toolsResult);
+
+ });
+ }
+
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseWebFluxTransportAutoConfigurationTests.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/SseWebFluxTransportAutoConfigurationTests.java
similarity index 90%
rename from auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseWebFluxTransportAutoConfigurationTests.java
rename to auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/SseWebFluxTransportAutoConfigurationTests.java
index e1faef952b0..fbac5a2a15c 100644
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/test/java/org/springframework/ai/mcp/client/autoconfigure/SseWebFluxTransportAutoConfigurationTests.java
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/SseWebFluxTransportAutoConfigurationTests.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package org.springframework.ai.mcp.client.autoconfigure;
+package org.springframework.ai.mcp.client.webflux.autoconfigure;
+
+import static org.assertj.core.api.Assertions.assertThat;
import java.lang.reflect.Field;
import java.util.List;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
import org.junit.jupiter.api.Test;
-
+import org.springframework.ai.mcp.client.common.autoconfigure.NamedClientMcpTransport;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@@ -31,7 +31,9 @@
import org.springframework.util.ReflectionUtils;
import org.springframework.web.reactive.function.client.WebClient;
-import static org.assertj.core.api.Assertions.assertThat;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
/**
* Tests for {@link SseWebFluxTransportAutoConfiguration}.
@@ -45,7 +47,7 @@ public class SseWebFluxTransportAutoConfigurationTests {
@Test
void webFluxClientTransportsPresentIfWebFluxSseClientTransportPresent() {
- this.applicationContext.run(context -> assertThat(context.containsBean("webFluxClientTransports")).isTrue());
+ this.applicationContext.run(context -> assertThat(context.containsBean("sseWebFluxClientTransports")).isTrue());
}
@Test
@@ -53,19 +55,19 @@ void webFluxClientTransportsNotPresentIfMissingWebFluxSseClientTransportNotPrese
this.applicationContext
.withClassLoader(
new FilteredClassLoader("io.modelcontextprotocol.client.transport.WebFluxSseClientTransport"))
- .run(context -> assertThat(context.containsBean("webFluxClientTransports")).isFalse());
+ .run(context -> assertThat(context.containsBean("sseWebFluxClientTransports")).isFalse());
}
@Test
void webFluxClientTransportsNotPresentIfMcpClientDisabled() {
this.applicationContext.withPropertyValues("spring.ai.mcp.client.enabled", "false")
- .run(context -> assertThat(context.containsBean("webFluxClientTransports")).isFalse());
+ .run(context -> assertThat(context.containsBean("sseWebFluxClientTransports")).isFalse());
}
@Test
void noTransportsCreatedWithEmptyConnections() {
this.applicationContext.run(context -> {
- List transports = context.getBean("webFluxClientTransports", List.class);
+ List transports = context.getBean("sseWebFluxClientTransports", List.class);
assertThat(transports).isEmpty();
});
}
@@ -75,7 +77,7 @@ void singleConnectionCreatesOneTransport() {
this.applicationContext
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080")
.run(context -> {
- List transports = context.getBean("webFluxClientTransports", List.class);
+ List transports = context.getBean("sseWebFluxClientTransports", List.class);
assertThat(transports).hasSize(1);
assertThat(transports.get(0).name()).isEqualTo("server1");
assertThat(transports.get(0).transport()).isInstanceOf(WebFluxSseClientTransport.class);
@@ -88,7 +90,7 @@ void multipleConnectionsCreateMultipleTransports() {
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080",
"spring.ai.mcp.client.sse.connections.server2.url=http://otherserver:8081")
.run(context -> {
- List transports = context.getBean("webFluxClientTransports", List.class);
+ List transports = context.getBean("sseWebFluxClientTransports", List.class);
assertThat(transports).hasSize(2);
assertThat(transports).extracting("name").containsExactlyInAnyOrder("server1", "server2");
assertThat(transports).extracting("transport")
@@ -106,7 +108,7 @@ void customSseEndpointIsRespected() {
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080",
"spring.ai.mcp.client.sse.connections.server1.sse-endpoint=/custom-sse")
.run(context -> {
- List transports = context.getBean("webFluxClientTransports", List.class);
+ List transports = context.getBean("sseWebFluxClientTransports", List.class);
assertThat(transports).hasSize(1);
assertThat(transports.get(0).name()).isEqualTo("server1");
assertThat(transports.get(0).transport()).isInstanceOf(WebFluxSseClientTransport.class);
@@ -122,7 +124,7 @@ void customWebClientBuilderIsUsed() {
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080")
.run(context -> {
assertThat(context.getBean(WebClient.Builder.class)).isNotNull();
- List transports = context.getBean("webFluxClientTransports", List.class);
+ List transports = context.getBean("sseWebFluxClientTransports", List.class);
assertThat(transports).hasSize(1);
});
}
@@ -133,7 +135,7 @@ void customObjectMapperIsUsed() {
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080")
.run(context -> {
assertThat(context.getBean(ObjectMapper.class)).isNotNull();
- List transports = context.getBean("webFluxClientTransports", List.class);
+ List transports = context.getBean("sseWebFluxClientTransports", List.class);
assertThat(transports).hasSize(1);
});
}
@@ -143,7 +145,7 @@ void defaultSseEndpointIsUsedWhenNotSpecified() {
this.applicationContext
.withPropertyValues("spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8080")
.run(context -> {
- List transports = context.getBean("webFluxClientTransports", List.class);
+ List transports = context.getBean("sseWebFluxClientTransports", List.class);
assertThat(transports).hasSize(1);
assertThat(transports.get(0).name()).isEqualTo("server1");
assertThat(transports.get(0).transport()).isInstanceOf(WebFluxSseClientTransport.class);
@@ -158,7 +160,7 @@ void mixedConnectionsWithAndWithoutCustomSseEndpoint() {
"spring.ai.mcp.client.sse.connections.server1.sse-endpoint=/custom-sse",
"spring.ai.mcp.client.sse.connections.server2.url=http://otherserver:8081")
.run(context -> {
- List transports = context.getBean("webFluxClientTransports", List.class);
+ List transports = context.getBean("sseWebFluxClientTransports", List.class);
assertThat(transports).hasSize(2);
assertThat(transports).extracting("name").containsExactlyInAnyOrder("server1", "server2");
assertThat(transports).extracting("transport")
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/StreamableHttpHttpClientTransportAutoConfigurationIT.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/StreamableHttpHttpClientTransportAutoConfigurationIT.java
new file mode 100644
index 00000000000..b118bcd9dc7
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/StreamableHttpHttpClientTransportAutoConfigurationIT.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024-2024 the original author or authors.
+ */
+
+package org.springframework.ai.mcp.client.webflux.autoconfigure;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+import io.modelcontextprotocol.client.McpSyncClient;
+import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
+
+@Timeout(15)
+public class StreamableHttpHttpClientTransportAutoConfigurationIT {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(StreamableHttpHttpClientTransportAutoConfigurationIT.class);
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withPropertyValues("spring.ai.mcp.client.initialized=false",
+ "spring.ai.mcp.client.streamable-http.connections.server1.url=" + host)
+ .withConfiguration(AutoConfigurations.of(McpClientAutoConfiguration.class,
+ StreamableHttpWebFluxTransportAutoConfiguration.class));
+
+ static String host = "http://localhost:3001";
+
+ // Uses the https://github.com/tzolov/mcp-everything-server-docker-image
+ @SuppressWarnings("resource")
+ static GenericContainer> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2")
+ .withCommand("node dist/index.js streamableHttp")
+ .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
+ .withExposedPorts(3001)
+ .waitingFor(Wait.forHttp("/").forStatusCode(404));
+
+ @BeforeAll
+ static void setUp() {
+ container.start();
+ int port = container.getMappedPort(3001);
+ host = "http://" + container.getHost() + ":" + port;
+ logger.info("Container started at host: {}", host);
+ }
+
+ @AfterAll
+ static void tearDown() {
+ container.stop();
+ }
+
+ @Test
+ void streamableHttpTest() {
+ this.contextRunner.run(context -> {
+ List mcpClients = (List) context.getBean("mcpSyncClients");
+
+ assertThat(mcpClients).isNotNull();
+ assertThat(mcpClients).hasSize(1);
+
+ McpSyncClient mcpClient = mcpClients.get(0);
+
+ mcpClient.ping();
+
+ System.out.println("mcpClient = " + mcpClient.getServerInfo());
+
+ ListToolsResult toolsResult = mcpClient.listTools();
+
+ assertThat(toolsResult).isNotNull();
+ assertThat(toolsResult.tools()).isNotEmpty();
+ assertThat(toolsResult.tools()).hasSize(8);
+
+ logger.info("tools = {}", toolsResult);
+
+ });
+ }
+
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/StreamableHttpWebFluxTransportAutoConfigurationTests.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/StreamableHttpWebFluxTransportAutoConfigurationTests.java
new file mode 100644
index 00000000000..170db687cd4
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/java/org/springframework/ai/mcp/client/webflux/autoconfigure/StreamableHttpWebFluxTransportAutoConfigurationTests.java
@@ -0,0 +1,219 @@
+/*
+ * 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.client.webflux.autoconfigure;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.mcp.client.common.autoconfigure.NamedClientMcpTransport;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.FilteredClassLoader;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport;
+
+/**
+ * Tests for {@link StreamableHttpWebFluxTransportAutoConfiguration}.
+ *
+ * @author Christian Tzolov
+ */
+public class StreamableHttpWebFluxTransportAutoConfigurationTests {
+
+ private final ApplicationContextRunner applicationContext = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(StreamableHttpWebFluxTransportAutoConfiguration.class));
+
+ @Test
+ void webFluxClientTransportsPresentIfWebClientStreamableHttpTransportPresent() {
+ this.applicationContext
+ .run(context -> assertThat(context.containsBean("streamableHttpwebFluxClientTransports")).isTrue());
+ }
+
+ @Test
+ void webFluxClientTransportsNotPresentIfMissingWebClientStreamableHttpTransportNotPresent() {
+ this.applicationContext
+ .withClassLoader(new FilteredClassLoader(
+ "io.modelcontextprotocol.client.transport.WebClientStreamableHttpTransport"))
+ .run(context -> assertThat(context.containsBean("streamableHttpwebFluxClientTransports")).isFalse());
+ }
+
+ @Test
+ void webFluxClientTransportsNotPresentIfMcpClientDisabled() {
+ this.applicationContext.withPropertyValues("spring.ai.mcp.client.enabled", "false")
+ .run(context -> assertThat(context.containsBean("streamableHttpwebFluxClientTransports")).isFalse());
+ }
+
+ @Test
+ void noTransportsCreatedWithEmptyConnections() {
+ this.applicationContext.run(context -> {
+ List transports = context.getBean("streamableHttpwebFluxClientTransports",
+ List.class);
+ assertThat(transports).isEmpty();
+ });
+ }
+
+ @Test
+ void singleConnectionCreatesOneTransport() {
+ this.applicationContext
+ .withPropertyValues("spring.ai.mcp.client.streamable-http.connections.server1.url=http://localhost:8080")
+ .run(context -> {
+ List transports = context.getBean("streamableHttpwebFluxClientTransports",
+ List.class);
+ assertThat(transports).hasSize(1);
+ assertThat(transports.get(0).name()).isEqualTo("server1");
+ assertThat(transports.get(0).transport()).isInstanceOf(WebClientStreamableHttpTransport.class);
+ });
+ }
+
+ @Test
+ void multipleConnectionsCreateMultipleTransports() {
+ this.applicationContext
+ .withPropertyValues("spring.ai.mcp.client.streamable-http.connections.server1.url=http://localhost:8080",
+ "spring.ai.mcp.client.streamable-http.connections.server2.url=http://otherserver:8081")
+ .run(context -> {
+ List transports = context.getBean("streamableHttpwebFluxClientTransports",
+ List.class);
+ assertThat(transports).hasSize(2);
+ assertThat(transports).extracting("name").containsExactlyInAnyOrder("server1", "server2");
+ assertThat(transports).extracting("transport")
+ .allMatch(transport -> transport instanceof WebClientStreamableHttpTransport);
+ for (NamedClientMcpTransport transport : transports) {
+ assertThat(transport.transport()).isInstanceOf(WebClientStreamableHttpTransport.class);
+ assertThat(getStreamableHttpEndpoint((WebClientStreamableHttpTransport) transport.transport()))
+ .isEqualTo("/mcp");
+ }
+ });
+ }
+
+ @Test
+ void customStreamableHttpEndpointIsRespected() {
+ this.applicationContext
+ .withPropertyValues("spring.ai.mcp.client.streamable-http.connections.server1.url=http://localhost:8080",
+ "spring.ai.mcp.client.streamable-http.connections.server1.endpoint=/custom-mcp")
+ .run(context -> {
+ List transports = context.getBean("streamableHttpwebFluxClientTransports",
+ List.class);
+ assertThat(transports).hasSize(1);
+ assertThat(transports.get(0).name()).isEqualTo("server1");
+ assertThat(transports.get(0).transport()).isInstanceOf(WebClientStreamableHttpTransport.class);
+
+ assertThat(getStreamableHttpEndpoint((WebClientStreamableHttpTransport) transports.get(0).transport()))
+ .isEqualTo("/custom-mcp");
+ });
+ }
+
+ @Test
+ void customWebClientBuilderIsUsed() {
+ this.applicationContext.withUserConfiguration(CustomWebClientConfiguration.class)
+ .withPropertyValues("spring.ai.mcp.client.streamable-http.connections.server1.url=http://localhost:8080")
+ .run(context -> {
+ assertThat(context.getBean(WebClient.Builder.class)).isNotNull();
+ List transports = context.getBean("streamableHttpwebFluxClientTransports",
+ List.class);
+ assertThat(transports).hasSize(1);
+ });
+ }
+
+ @Test
+ void customObjectMapperIsUsed() {
+ this.applicationContext.withUserConfiguration(CustomObjectMapperConfiguration.class)
+ .withPropertyValues("spring.ai.mcp.client.streamable-http.connections.server1.url=http://localhost:8080")
+ .run(context -> {
+ assertThat(context.getBean(ObjectMapper.class)).isNotNull();
+ List transports = context.getBean("streamableHttpwebFluxClientTransports",
+ List.class);
+ assertThat(transports).hasSize(1);
+ });
+ }
+
+ @Test
+ void defaultStreamableHttpEndpointIsUsedWhenNotSpecified() {
+ this.applicationContext
+ .withPropertyValues("spring.ai.mcp.client.streamable-http.connections.server1.url=http://localhost:8080")
+ .run(context -> {
+ List transports = context.getBean("streamableHttpwebFluxClientTransports",
+ List.class);
+ assertThat(transports).hasSize(1);
+ assertThat(transports.get(0).name()).isEqualTo("server1");
+ assertThat(transports.get(0).transport()).isInstanceOf(WebClientStreamableHttpTransport.class);
+ // Default streamable HTTP endpoint is "/mcp" as specified in the
+ // configuration class
+ });
+ }
+
+ @Test
+ void mixedConnectionsWithAndWithoutCustomStreamableHttpEndpoint() {
+ this.applicationContext
+ .withPropertyValues("spring.ai.mcp.client.streamable-http.connections.server1.url=http://localhost:8080",
+ "spring.ai.mcp.client.streamable-http.connections.server1.endpoint=/custom-mcp",
+ "spring.ai.mcp.client.streamable-http.connections.server2.url=http://otherserver:8081")
+ .run(context -> {
+ List transports = context.getBean("streamableHttpwebFluxClientTransports",
+ List.class);
+ assertThat(transports).hasSize(2);
+ assertThat(transports).extracting("name").containsExactlyInAnyOrder("server1", "server2");
+ assertThat(transports).extracting("transport")
+ .allMatch(transport -> transport instanceof WebClientStreamableHttpTransport);
+ for (NamedClientMcpTransport transport : transports) {
+ assertThat(transport.transport()).isInstanceOf(WebClientStreamableHttpTransport.class);
+ if (transport.name().equals("server1")) {
+ assertThat(getStreamableHttpEndpoint((WebClientStreamableHttpTransport) transport.transport()))
+ .isEqualTo("/custom-mcp");
+ }
+ else {
+ assertThat(getStreamableHttpEndpoint((WebClientStreamableHttpTransport) transport.transport()))
+ .isEqualTo("/mcp");
+ }
+ }
+ });
+ }
+
+ private String getStreamableHttpEndpoint(WebClientStreamableHttpTransport transport) {
+ Field privateField = ReflectionUtils.findField(WebClientStreamableHttpTransport.class, "endpoint");
+ ReflectionUtils.makeAccessible(privateField);
+ return (String) ReflectionUtils.getField(privateField, transport);
+ }
+
+ @Configuration
+ static class CustomWebClientConfiguration {
+
+ @Bean
+ WebClient.Builder webClientBuilder() {
+ return WebClient.builder().baseUrl("http://custom-base-url");
+ }
+
+ }
+
+ @Configuration
+ static class CustomObjectMapperConfiguration {
+
+ @Bean
+ ObjectMapper objectMapper() {
+ return new ObjectMapper();
+ }
+
+ }
+
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/resources/application-test.properties b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/resources/application-test.properties
new file mode 100644
index 00000000000..9107b9e407a
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/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/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/resources/nested/nested-config.json b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/resources/nested/nested-config.json
new file mode 100644
index 00000000000..7cd51d6d490
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/resources/nested/nested-config.json
@@ -0,0 +1,8 @@
+{
+ "name": "nested-config",
+ "description": "Test JSON file in nested subfolder of test resources",
+ "version": "1.0.0",
+ "nestedProperties": {
+ "nestedProperty1": "nestedValue1"
+ }
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/resources/test-config.json b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/resources/test-config.json
new file mode 100644
index 00000000000..57e2a46f20e
--- /dev/null
+++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux/src/test/resources/test-config.json
@@ -0,0 +1,8 @@
+{
+ "name": "test-config",
+ "description": "Test JSON file in root test resources folder",
+ "version": "1.0.0",
+ "properties": {
+ "testProperty1": "value1"
+ }
+}
diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/resources/META-INF/spring/aot.factories b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/resources/META-INF/spring/aot.factories
deleted file mode 100644
index 306551e0d4e..00000000000
--- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/resources/META-INF/spring/aot.factories
+++ /dev/null
@@ -1,2 +0,0 @@
-org.springframework.aot.hint.RuntimeHintsRegistrar=\
- org.springframework.ai.mcp.client.autoconfigure.aot.McpClientAutoConfigurationRuntimeHints
diff --git a/pom.xml b/pom.xml
index 931409a7584..713d2f3335b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -114,7 +114,10 @@
auto-configurations/models/spring-ai-autoconfigure-model-zhipuai
auto-configurations/models/spring-ai-autoconfigure-model-deepseek
- auto-configurations/mcp/spring-ai-autoconfigure-mcp-client
+ auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common
+ auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-httpclient
+ auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-webflux
+
auto-configurations/mcp/spring-ai-autoconfigure-mcp-server
auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-azure
@@ -289,6 +292,7 @@
24.09
2.5.8
2.3.0
+ 1.20.4
4.0.1
4.29.3
diff --git a/spring-ai-bom/pom.xml b/spring-ai-bom/pom.xml
index 6d597e38415..b213d60005e 100644
--- a/spring-ai-bom/pom.xml
+++ b/spring-ai-bom/pom.xml
@@ -531,7 +531,19 @@
org.springframework.ai
- spring-ai-autoconfigure-mcp-client
+ spring-ai-autoconfigure-mcp-client-common
+ ${project.version}
+
+
+
+ org.springframework.ai
+ spring-ai-autoconfigure-mcp-client-httpclient
+ ${project.version}
+
+
+
+ org.springframework.ai
+ spring-ai-autoconfigure-mcp-client-webflux
${project.version}
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
index 63ab785fcd3..fd6974ad77c 100644
--- 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
@@ -1,24 +1,19 @@
= 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 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
+* Support for multiple named transports (STDIO, Http/SSE and Streamable HTTP)
* 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
-[NOTE]
-====
-There has been a significant change in the Spring AI auto-configuration, starter modules' artifact names.
-Please refer to the https://docs.spring.io/spring-ai/reference/upgrade-notes.html[upgrade notes] for more information.
-====
-
=== Standard MCP Client
[source,xml]
@@ -29,15 +24,15 @@ Please refer to the https://docs.spring.io/spring-ai/reference/upgrade-notes.htm
----
-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.
+The standard starter connects simultaneously to one or more MCP servers over `STDIO` (in-process), `SSE` and `Streamable Http` transports.
+The SSE and Streamable-Http transports use the JDK 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-starter-mcp-client-webflux`.
+For production deployment, we recommend using the WebFlux-based SSE & StreamableHttp connection with the `spring-ai-starter-mcp-client-webflux`.
=== WebFlux Client
-The WebFlux starter provides similar functionality to the standard starter but uses a WebFlux-based SSE transport implementation.
+The WebFlux starter provides similar functionality to the standard starter but uses a WebFlux-based SSE and Streamable-Http transport implementation.
[source,xml]
----
@@ -209,6 +204,44 @@ spring:
sse-endpoint: /custom-sse
----
+=== Streamable Http Transport Properties
+
+Properties for Streamable Http transport are prefixed with `spring.ai.mcp.client.streamable-http`:
+
+[cols="3,4,3"]
+|===
+|Property |Description | Default Value
+
+|`connections`
+|Map of named Streamable Http connection configurations
+|-
+
+|`connections.[name].url`
+|Base URL endpoint for Streamable-Http communication with the MCP server
+|-
+
+|`connections.[name].endpoint`
+|the streamable-http endpoint (as url suffix) to use for the connection
+|`/mcp`
+|===
+
+Example configuration:
+[source,yaml]
+----
+spring:
+ ai:
+ mcp:
+ client:
+ streamable-http:
+ connections:
+ server1:
+ url: http://localhost:8080
+ server2:
+ url: http://otherserver:8081
+ endpoint: /custom-sse
+----
+
+
== Features
=== Sync/Async Client Types
@@ -227,15 +260,16 @@ The auto-configuration provides extensive client spec customization capabilities
The following customization options are available:
* *Request Configuration* - Set custom request timeouts
-* link:https://spec.modelcontextprotocol.io/specification/2024-11-05/client/sampling/[*Custom Sampling Handlers*] - standardized way for servers to request LLM sampling (`completions` or `generations`) from LLMs via clients. This flow allows clients to maintain control over model access, selection, and permissions while enabling servers to leverage AI capabilities — with no server API keys necessary.
-* link:https://spec.modelcontextprotocol.io/specification/2024-11-05/client/roots/[*File system (Roots) Access*] - standardized way for clients to expose filesystem `roots` to servers.
+* link:https://modelcontextprotocol.io/specification/2025-06-18/client/sampling[*Custom Sampling Handlers*] - standardized way for servers to request LLM sampling (`completions` or `generations`) from LLMs via clients. This flow allows clients to maintain control over model access, selection, and permissions while enabling servers to leverage AI capabilities — with no server API keys necessary.
+* link:https://modelcontextprotocol.io/specification/2025-06-18/client/roots[*File system (Roots) Access*] - standardized way for clients to expose filesystem `roots` to servers.
Roots define the boundaries of where servers can operate within the filesystem, allowing them to understand which directories and files they have access to.
Servers can request the list of roots from supporting clients and receive notifications when that list changes.
+* link:https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation[*Elicitation Handlers*] - standardized way for servers to request additional information from users through the client during interactions.
* *Event Handlers* - client's handler to be notified when a certain server event occurs:
- Tools change notifications - when the list of available server tools changes
- Resources change notifications - when the list of available server resources changes.
- Prompts change notifications - when the list of available server prompts changes.
-* link:https://spec.modelcontextprotocol.io/specification/2024-11-05/server/utilities/logging/[*Logging Handlers*] - standardized way for servers to send structured log messages to clients.
+* link:https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging[*Logging Handlers*] - standardized way for servers to send structured log messages to clients.
Clients can control logging verbosity by setting minimum log levels
@@ -313,13 +347,14 @@ The MCP client auto-configuration automatically detects and applies any customiz
The auto-configuration supports multiple transport types:
-* Standard I/O (Stdio) (activated by the `spring-ai-starter-mcp-client`)
-* SSE HTTP (activated by the `spring-ai-starter-mcp-client`)
-* SSE WebFlux (activated by the `spring-ai-starter-mcp-client-webflux`)
+* Standard I/O (Stdio) (activated by the `spring-ai-starter-mcp-client` and `spring-ai-starter-mcp-client-webflux`)
+* (HttpClient) HTTP/SSE and StreamableHTTP (activated by the `spring-ai-starter-mcp-client`)
+* (WebFlux) HTTP/SSE and StreamableHTTP (activated by the `spring-ai-starter-mcp-client-webflux`)
=== Integration with Spring AI
-The starter can configure tool callbacks that integrate with Spring AI's tool execution framework, allowing MCP tools to be used as part of AI interactions. This integration is enabled by default and can be disabled by setting the `spring.ai.mcp.client.toolcallback.enabled=false` property.
+The starter can configure tool callbacks that integrate with Spring AI's tool execution framework, allowing MCP tools to be used as part of AI interactions.
+This integration is enabled by default and can be disabled by setting the `spring.ai.mcp.client.toolcallback.enabled=false` property.
== Usage Example
@@ -341,7 +376,12 @@ spring:
server1:
url: http://localhost:8080
server2:
- url: http://otherserver:8081
+ url: http://otherserver:8081
+ streamable-http:
+ connections:
+ server3:
+ url: http://localhost:8083
+ endpoint: /mcp
stdio:
root-change-notification: false
connections:
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
index 1fea2cb06fc..0e55acfd195 100644
--- 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
@@ -46,7 +46,7 @@