Skip to content

Commit 2bdd299

Browse files
authored
Add DockerMcpGatewayContainer (#10364)
1 parent 7d83019 commit 2bdd299

File tree

4 files changed

+175
-0
lines changed

4 files changed

+175
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package org.testcontainers.containers;
2+
3+
import org.testcontainers.DockerClientFactory;
4+
import org.testcontainers.containers.wait.strategy.Wait;
5+
import org.testcontainers.images.builder.Transferable;
6+
import org.testcontainers.utility.DockerImageName;
7+
8+
import java.util.ArrayList;
9+
import java.util.Arrays;
10+
import java.util.HashMap;
11+
import java.util.List;
12+
import java.util.Map;
13+
14+
/**
15+
* Testcontainers implementation of the Docker MCP Gateway container.
16+
* <p>
17+
* Supported images: {@code docker/agents_gateway}
18+
* <p>
19+
* Exposed ports: 8811
20+
*/
21+
public class DockerMcpGatewayContainer extends GenericContainer<DockerMcpGatewayContainer> {
22+
23+
private static final String DOCKER_AGENT_GATEWAY_IMAGE = "docker/agents_gateway";
24+
25+
private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(DOCKER_AGENT_GATEWAY_IMAGE);
26+
27+
private static final int DEFAULT_PORT = 8811;
28+
29+
private static final String SECRETS_PATH = "/testcontainers/app/secrets";
30+
31+
private final List<String> servers = new ArrayList<>();
32+
33+
private final List<String> tools = new ArrayList<>();
34+
35+
private final Map<String, String> secrets = new HashMap<>();
36+
37+
public DockerMcpGatewayContainer(String dockerImageName) {
38+
this(DockerImageName.parse(dockerImageName));
39+
}
40+
41+
public DockerMcpGatewayContainer(DockerImageName dockerImageName) {
42+
super(dockerImageName);
43+
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
44+
withExposedPorts(DEFAULT_PORT);
45+
withFileSystemBind(DockerClientFactory.instance().getRemoteDockerUnixSocketPath(), "/var/run/docker.sock");
46+
waitingFor(Wait.forLogMessage(".*Start sse server on port.*", 1));
47+
}
48+
49+
@Override
50+
protected void configure() {
51+
List<String> command = new ArrayList<>();
52+
command.add("--transport=sse");
53+
for (String server : this.servers) {
54+
if (!server.isEmpty()) {
55+
command.add("--servers=" + server);
56+
}
57+
}
58+
for (String tool : this.tools) {
59+
if (!tool.isEmpty()) {
60+
command.add("--tools=" + tool);
61+
}
62+
}
63+
if (this.secrets != null && !this.secrets.isEmpty()) {
64+
command.add("--secrets=" + SECRETS_PATH);
65+
}
66+
withCommand(String.join(" ", command));
67+
}
68+
69+
@Override
70+
protected void containerIsCreated(String containerId) {
71+
if (this.secrets != null && !this.secrets.isEmpty()) {
72+
StringBuilder secretsFile = new StringBuilder();
73+
for (Map.Entry<String, String> entry : this.secrets.entrySet()) {
74+
secretsFile.append(entry.getKey()).append("=").append(entry.getValue()).append("\n");
75+
}
76+
copyFileToContainer(Transferable.of(secretsFile.toString()), SECRETS_PATH);
77+
}
78+
}
79+
80+
public DockerMcpGatewayContainer withServer(String server, List<String> tools) {
81+
this.servers.add(server);
82+
this.tools.addAll(tools);
83+
return this;
84+
}
85+
86+
public DockerMcpGatewayContainer withServer(String server, String... tools) {
87+
this.servers.add(server);
88+
this.tools.addAll(Arrays.asList(tools));
89+
return this;
90+
}
91+
92+
public DockerMcpGatewayContainer withSecrets(Map<String, String> secrets) {
93+
this.secrets.putAll(secrets);
94+
return this;
95+
}
96+
97+
public DockerMcpGatewayContainer withSecret(String secretKey, String secretValue) {
98+
this.secrets.put(secretKey, secretValue);
99+
return this;
100+
}
101+
102+
public String getEndpoint() {
103+
return "http://" + getHost() + ":" + getMappedPort(DEFAULT_PORT);
104+
}
105+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.testcontainers.containers;
2+
3+
import org.junit.Test;
4+
5+
import java.util.Collections;
6+
7+
import static org.assertj.core.api.Assertions.assertThat;
8+
9+
public class DockerMcpGatewayContainerTest {
10+
11+
@Test
12+
public void serviceSuccessfullyStarts() {
13+
try (DockerMcpGatewayContainer gateway = new DockerMcpGatewayContainer("docker/agents_gateway:v2")) {
14+
gateway.start();
15+
16+
assertThat(gateway.isRunning()).isTrue();
17+
}
18+
}
19+
20+
@Test
21+
public void gatewayStartsWithServers() {
22+
try (
23+
// container {
24+
DockerMcpGatewayContainer gateway = new DockerMcpGatewayContainer("docker/agents_gateway:v2")
25+
.withServer("curl", "curl")
26+
.withServer("brave", "brave_local_search", "brave_web_search")
27+
.withServer("github-official", Collections.singletonList("add_issue_comment"))
28+
.withSecret("brave.api_key", "test_key")
29+
.withSecrets(Collections.singletonMap("github.personal_access_token", "test_token"))
30+
// }
31+
) {
32+
gateway.start();
33+
34+
assertThat(gateway.getLogs()).contains("4 tools listed");
35+
}
36+
}
37+
}

docs/modules/docker_mcp_gateway.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Docker MCP Gateway
2+
3+
Testcontainers module for [Docker MCP Gateway](https://hub.docker.com/r/docker/agents_gateway).
4+
5+
## DockerMcpGatewayContainer's usage examples
6+
7+
You can start a Docker MCP Gateway container instance from any Java application by using:
8+
9+
<!--codeinclude-->
10+
[Create a DockerMcpGatewayContainer](../../core/src/test/java/org/testcontainers/containers/DockerMcpGatewayContainerTest.java) inside_block:container
11+
<!--/codeinclude-->
12+
13+
## Adding this module to your project dependencies
14+
15+
*Docker MCP Gateway support is part of the core Testcontainers library.*
16+
17+
Add the following dependency to your `pom.xml`/`build.gradle` file:
18+
19+
=== "Gradle"
20+
```groovy
21+
testImplementation "org.testcontainers:testcontainers:{{latest_version}}"
22+
```
23+
=== "Maven"
24+
```xml
25+
<dependency>
26+
<groupId>org.testcontainers</groupId>
27+
<artifactId>testcontainers</artifactId>
28+
<version>{{latest_version}}</version>
29+
<scope>test</scope>
30+
</dependency>
31+
```
32+

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ nav:
8282
- modules/chromadb.md
8383
- modules/consul.md
8484
- modules/docker_compose.md
85+
- modules/docker_mcp_gateway.md
8586
- modules/docker_model_runner.md
8687
- modules/elasticsearch.md
8788
- modules/gcloud.md

0 commit comments

Comments
 (0)