Skip to content

feat: add streamable-HTTP transport support for MCP client #3823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
<version>1.1.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<artifactId>spring-ai-autoconfigure-mcp-client</artifactId>
<artifactId>spring-ai-autoconfigure-mcp-client-common</artifactId>
<packaging>jar</packaging>
<name>Spring AI MCP Client Auto Configuration</name>
<description>Spring AI MCP Client Auto Configuration</description>
<name>Spring AI MCP Client Common Auto Configuration</name>
<description>Spring AI MCP Client Common Auto Configuration</description>
<url>https://github.com/spring-projects/spring-ai</url>

<scm>
Expand All @@ -35,11 +35,11 @@
<optional>true</optional>
</dependency>

<dependency>
<!-- <dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-spring-webflux</artifactId>
<optional>true</optional>
</dependency>
</dependency> -->

<dependency>
<groupId>org.springframework.boot</groupId>
Expand All @@ -53,15 +53,6 @@
<optional>true</optional>
</dependency>

<!-- NOTE: Currently the webmvc doesn't implement client transport.
We will add it in the future based on ResrtClient.
-->
<!-- <dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-spring-webmvc</artifactId>
<optional>true</optional>
</dependency> -->

<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.ai</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>
* These properties allow configuration of multiple named Streamable Http connections to
* MCP servers. Each connection is configured with a URL endpoint for communication.
*
* <p>
* Example configuration: <pre>
* spring.ai.mcp.client.streamable-http:
* connections-http:
* server1:
* url: http://localhost:8080/events
* server2:
* url: http://otherserver:8081/events
* </pre>
*
* @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.
* <p>
* The key represents the connection name, and the value contains the Streamable Http
* parameters for that connection.
*/
private final Map<String, ConnectionParameters> connections = new HashMap<>();

/**
* Returns the map of configured Streamable Http connections.
* @return map of connection names to their Streamable Http parameters
*/
public Map<String, ConnectionParameters> 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) {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.springframework.ai.mcp.client.common.autoconfigure.aot.McpClientAutoConfigurationRuntimeHints
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@
* 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;
import java.util.Set;

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Loading