diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/McpSseClientConnectionDetails.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/McpSseClientConnectionDetails.java new file mode 100644 index 00000000000..2712e3d6ab1 --- /dev/null +++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/McpSseClientConnectionDetails.java @@ -0,0 +1,28 @@ +/* + * 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.autoconfigure; + +import org.springframework.ai.mcp.client.autoconfigure.properties.McpSseClientProperties; +import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; + +import java.util.Map; + +public interface McpSseClientConnectionDetails extends ConnectionDetails { + + Map getConnections(); + +} diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/PropertiesMcpSseClientConnectionDetails.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/PropertiesMcpSseClientConnectionDetails.java new file mode 100644 index 00000000000..0818aa497b3 --- /dev/null +++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client/src/main/java/org/springframework/ai/mcp/client/autoconfigure/PropertiesMcpSseClientConnectionDetails.java @@ -0,0 +1,36 @@ +/* + * 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.autoconfigure; + +import org.springframework.ai.mcp.client.autoconfigure.properties.McpSseClientProperties; + +import java.util.Map; + +class PropertiesMcpSseClientConnectionDetails implements McpSseClientConnectionDetails { + + private final McpSseClientProperties properties; + + PropertiesMcpSseClientConnectionDetails(McpSseClientProperties properties) { + this.properties = properties; + } + + @Override + public Map getConnections() { + return this.properties.getConnections(); + } + +} 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/src/main/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfiguration.java index 79aabc0d2cc..7b5bf3c995e 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/src/main/java/org/springframework/ai/mcp/client/autoconfigure/SseHttpClientTransportAutoConfiguration.java @@ -69,6 +69,11 @@ matchIfMissing = true) public class SseHttpClientTransportAutoConfiguration { + @Bean + PropertiesMcpSseClientConnectionDetails mcpSseClientConnectionDetails(McpSseClientProperties sseProperties) { + return new PropertiesMcpSseClientConnectionDetails(sseProperties); + } + /** * Creates a list of HTTP client-based SSE transports for MCP communication. * @@ -79,20 +84,21 @@ public class SseHttpClientTransportAutoConfiguration { *
  • Server URL from properties *
  • ObjectMapper for JSON processing * - * @param sseProperties the SSE client properties containing server configurations + * @param connectionDetails the SSE client connection details 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 mcpHttpClientTransports(McpSseClientProperties sseProperties, + public List mcpHttpClientTransports(McpSseClientConnectionDetails connectionDetails, ObjectProvider objectMapperProvider) { ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new); List sseTransports = new ArrayList<>(); - for (Map.Entry serverParameters : sseProperties.getConnections().entrySet()) { + for (Map.Entry serverParameters : connectionDetails.getConnections().entrySet()) { String baseUrl = serverParameters.getValue().url(); String sseEndpoint = serverParameters.getValue().sseEndpoint() != null 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/src/main/java/org/springframework/ai/mcp/client/autoconfigure/SseWebFluxTransportAutoConfiguration.java index 4c064835e3b..e47669bacd8 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/src/main/java/org/springframework/ai/mcp/client/autoconfigure/SseWebFluxTransportAutoConfiguration.java @@ -62,6 +62,11 @@ matchIfMissing = true) public class SseWebFluxTransportAutoConfiguration { + @Bean + PropertiesMcpSseClientConnectionDetails mcpSseClientConnectionDetails(McpSseClientProperties sseProperties) { + return new PropertiesMcpSseClientConnectionDetails(sseProperties); + } + /** * Creates a list of WebFlux-based SSE transports for MCP communication. * @@ -79,7 +84,7 @@ public class SseWebFluxTransportAutoConfiguration { * @return list of named MCP transports */ @Bean - public List webFluxClientTransports(McpSseClientProperties sseProperties, + public List webFluxClientTransports(McpSseClientConnectionDetails connectionDetails, ObjectProvider webClientBuilderProvider, ObjectProvider objectMapperProvider) { @@ -88,7 +93,7 @@ public List webFluxClientTransports(McpSseClientPropert var webClientBuilderTemplate = webClientBuilderProvider.getIfAvailable(WebClient::builder); var objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new); - for (Map.Entry serverParameters : sseProperties.getConnections().entrySet()) { + for (Map.Entry serverParameters : connectionDetails.getConnections().entrySet()) { var webClientBuilder = webClientBuilderTemplate.clone().baseUrl(serverParameters.getValue().url()); String sseEndpoint = serverParameters.getValue().sseEndpoint() != null ? serverParameters.getValue().sseEndpoint() : "/sse"; diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/docker-compose.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/docker-compose.adoc index 2ed52679277..e96cca6b958 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/docker-compose.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/docker-compose.adoc @@ -54,4 +54,7 @@ The following service connection factories are provided in the `spring-ai-spring | `WeaviateConnectionDetails` | Containers named `semitechnologies/weaviate`, `cr.weaviate.io/semitechnologies/weaviate` + +| `McpSseClientConnectionDetails` +| Containers named `docker/mcp-gateway` |==== diff --git a/spring-ai-spring-boot-docker-compose/pom.xml b/spring-ai-spring-boot-docker-compose/pom.xml index 7f77cb1cbcd..78e2877e7ac 100644 --- a/spring-ai-spring-boot-docker-compose/pom.xml +++ b/spring-ai-spring-boot-docker-compose/pom.xml @@ -81,6 +81,13 @@ true + + org.springframework.ai + spring-ai-autoconfigure-mcp-client + ${project.version} + true + + diff --git a/spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/docker/DockerMcpGatewayDockerComposeConnectionDetailsFactory.java b/spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/docker/DockerMcpGatewayDockerComposeConnectionDetailsFactory.java new file mode 100644 index 00000000000..6fb9a3ff96b --- /dev/null +++ b/spring-ai-spring-boot-docker-compose/src/main/java/org/springframework/ai/docker/compose/service/connection/docker/DockerMcpGatewayDockerComposeConnectionDetailsFactory.java @@ -0,0 +1,69 @@ +/* + * Copyright 2023-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.docker.compose.service.connection.docker; + +import org.springframework.ai.mcp.client.autoconfigure.McpSseClientConnectionDetails; +import org.springframework.ai.mcp.client.autoconfigure.properties.McpSseClientProperties; +import org.springframework.boot.docker.compose.core.RunningService; +import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory; +import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource; + +import java.util.Map; + +/** + * A {@link DockerComposeConnectionDetailsFactory} implementation that creates + * {@link McpSseClientConnectionDetails} for a Docker MCP Gateway instance running in a + * Docker container. + * + * @author EddĂș MelĂ©ndez + */ +class DockerMcpGatewayDockerComposeConnectionDetailsFactory + extends DockerComposeConnectionDetailsFactory { + + private static final int GATEWAY_PORT = 8811; + + protected DockerMcpGatewayDockerComposeConnectionDetailsFactory() { + super("docker/mcp-gateway"); + } + + @Override + protected McpSseClientConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) { + return new DockerAgentsGatewayContainerConnectionDetails(source.getRunningService()); + } + + /** + * {@link McpSseClientConnectionDetails} backed by a {@code Docker MCP Gateway} + * {@link RunningService}. + */ + static class DockerAgentsGatewayContainerConnectionDetails extends DockerComposeConnectionDetails + implements McpSseClientConnectionDetails { + + private final String url; + + DockerAgentsGatewayContainerConnectionDetails(RunningService service) { + super(service); + this.url = String.format("http://%s:%d", service.host(), service.ports().get(GATEWAY_PORT)); + } + + @Override + public Map getConnections() { + return Map.of("gateway", new McpSseClientProperties.SseParameters(url, "/sse")); + } + + } + +} diff --git a/spring-ai-spring-boot-docker-compose/src/main/resources/META-INF/spring.factories b/spring-ai-spring-boot-docker-compose/src/main/resources/META-INF/spring.factories index d6949d5de09..c32faeaff29 100644 --- a/spring-ai-spring-boot-docker-compose/src/main/resources/META-INF/spring.factories +++ b/spring-ai-spring-boot-docker-compose/src/main/resources/META-INF/spring.factories @@ -16,6 +16,7 @@ org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\ org.springframework.ai.docker.compose.service.connection.chroma.ChromaDockerComposeConnectionDetailsFactory,\ +org.springframework.ai.docker.compose.service.connection.docker.DockerMcpGatewayDockerComposeConnectionDetailsFactory,\ org.springframework.ai.docker.compose.service.connection.mongo.MongoDbAtlasLocalDockerComposeConnectionDetailsFactory,\ org.springframework.ai.docker.compose.service.connection.ollama.OllamaDockerComposeConnectionDetailsFactory,\ org.springframework.ai.docker.compose.service.connection.opensearch.AwsOpenSearchDockerComposeConnectionDetailsFactory,\ diff --git a/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/docker/DockerMcpGatewayDockerComposeConnectionDetailsFactoryIT.java b/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/docker/DockerMcpGatewayDockerComposeConnectionDetailsFactoryIT.java new file mode 100644 index 00000000000..1e2af7475c0 --- /dev/null +++ b/spring-ai-spring-boot-docker-compose/src/test/java/org/springframework/ai/docker/compose/service/connection/docker/DockerMcpGatewayDockerComposeConnectionDetailsFactoryIT.java @@ -0,0 +1,40 @@ +/* + * Copyright 2023-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ai.docker.compose.service.connection.docker; + +import org.junit.jupiter.api.Test; +import org.springframework.ai.mcp.client.autoconfigure.McpSseClientConnectionDetails; +import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIT; +import org.testcontainers.utility.DockerImageName; + +import static org.assertj.core.api.Assertions.assertThat; + +class DockerMcpGatewayDockerComposeConnectionDetailsFactoryIT extends AbstractDockerComposeIT { + + protected DockerMcpGatewayDockerComposeConnectionDetailsFactoryIT() { + super("docker-agents-gateway-compose.yaml", DockerImageName.parse("docker/mcp-gateway")); + } + + @Test + void runCreatesConnectionDetails() { + McpSseClientConnectionDetails connectionDetails = run(McpSseClientConnectionDetails.class); + assertThat(connectionDetails.getConnections()).hasSize(1); + assertThat(connectionDetails.getConnections().get("gateway").url()).startsWith("http://"); + assertThat(connectionDetails.getConnections().get("gateway").sseEndpoint()).contains("/sse"); + } + +} diff --git a/spring-ai-spring-boot-docker-compose/src/test/resources/org/springframework/ai/docker/compose/service/connection/docker/docker-agents-gateway-compose.yaml b/spring-ai-spring-boot-docker-compose/src/test/resources/org/springframework/ai/docker/compose/service/connection/docker/docker-agents-gateway-compose.yaml new file mode 100644 index 00000000000..15b2afe3ca9 --- /dev/null +++ b/spring-ai-spring-boot-docker-compose/src/test/resources/org/springframework/ai/docker/compose/service/connection/docker/docker-agents-gateway-compose.yaml @@ -0,0 +1,9 @@ +services: + mcp-gateway: + image: '{imageName}' + ports: + - 8811 + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + command: + - --transport=sse