Testcontainers Java 的 Docker MCP Gateway 模块:在测试中启动 MCP 网关容器
【免费下载链接】testcontainers-javaTestcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.项目地址: https://gitcode.com/GitHub_Trending/te/testcontainers-java
导读
本文讲解 Testcontainers for Java 提供的DockerMcpGatewayContainer模块,它允许你在 JUnit 测试中以真实 Docker 容器的方式启动 Docker MCP Gateway,将各种 MCP(Model Context Protocol)服务器聚合到一个统一的 SSE 端点下,供测试代码通过 HTTP 调用。读完本文,你将掌握:如何引入依赖、如何在任意 Java 应用中启动网关容器、如何注册 MCP 服务器并选择暴露的工具、如何注入密钥(secrets)、如何获取网关端点地址,以及容器底层是如何完成启动与就绪检测的。
模块概览
DockerMcpGatewayContainer是 Testcontainers 核心库(org.testcontainers:testcontainers)内置的一个专用容器类,其实现位于 core/src/main/java/org/testcontainers/containers/DockerMcpGatewayContainer.java。它继承自GenericContainer<DockerMcpGatewayContainer>,因此具备 Testcontainers 通用的生命周期管理能力(start()、stop()、try-with-resources 自动关闭等)。
从源码看,该模块有几个关键默认值:
- 默认镜像:
docker/mcp-gateway(见 DockerMcpGatewayContainer.java); - 默认暴露端口:
8811(见 DockerMcpGatewayContainer.java); - 默认以 SSE 传输模式启动:
--transport=sse(见 DockerMcpGatewayContainer.java); - 自动将宿主机 Docker 套接字挂载到容器内的
/var/run/docker.sock,使网关能够通过 Docker 发现本地 MCP 服务器(见 DockerMcpGatewayContainer.java); - 启动就绪条件:等待容器日志中出现
.*Start sse server on port.*(见 DockerMcpGatewayContainer.java)。
注意:由于该模块会挂载宿主机 Docker 套接字,请仅在受信任的测试/开发环境中使用,并留意镜像
docker/mcp-gateway的版本行为(测试中通常使用docker/mcp-gateway:latest,生产化测试建议固定具体版本号)。
使用示例
你可以从任意 Java 应用中通过如下方式启动一个 Docker MCP Gateway 容器实例:
DockerMcpGatewayContainer gateway = new DockerMcpGatewayContainer("docker/mcp-gateway:latest"); gateway.start();上面的示例取自 core/src/test/java/org/testcontainers/containers/DockerMcpGatewayContainerTest.java,测试通过gateway.isRunning()验证容器确实成功启动。
更完整的典型用法是:创建容器后链式调用withServer(...)注册 MCP 服务器,并通过withSecret(...)/withSecrets(...)注入各服务器所需的 API Key 等机密信息,然后启动容器:
try ( DockerMcpGatewayContainer gateway = new DockerMcpGatewayContainer("docker/mcp-gateway:latest") .withServer("curl", "curl") .withServer("brave", "brave_local_search", "brave_web_search") .withServer("github-official", Collections.singletonList("add_issue_comment")) .withSecret("brave.api_key", "test_key") .withSecrets(Collections.singletonMap("github.personal_access_token", "test_token")) ) { gateway.start(); assertThat(gateway.getLogs()).contains("4 tools listed"); }该示例同样来自 DockerMcpGatewayContainerTest.java:通过 try-with-resources 确保测试结束后容器自动停止;注册了curl、brave、github-official三个服务器共 4 个工具(curl、brave_local_search、brave_web_search、add_issue_comment),并在启动后断言网关日志输出了4 tools listed,从而验证服务器与工具的注册过程真实生效。
添加模块依赖
Docker MCP Gateway 支持包含在 Testcontainers 核心库中,无需额外添加独立模块依赖。只需将org.testcontainers:testcontainers加入测试依赖即可(将{{latest_version}}替换为实际使用的版本号):
=== "Gradle"groovy testImplementation "org.testcontainers:testcontainers:{{latest_version}}"
=== "Maven"xml <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <version>{{latest_version}}</version> <scope>test</scope> </dependency>
此外,还需要确保测试环境具备可用的 Docker 运行时(Docker daemon 或兼容的容器运行时),因为 Testcontainers 的一切容器操作都依赖 Docker 客户端。
配置 MCP 服务器与工具
网关本身是一个聚合层:它把多个 MCP 服务器的工具暴露在一个统一的入口后面。DockerMcpGatewayContainer提供两个重载的withServer方法(见 DockerMcpGatewayContainer.java):
public DockerMcpGatewayContainer withServer(String server, List<String> tools) public DockerMcpGatewayContainer withServer(String server, String... tools)- 第一个参数
server是要注册的 MCP 服务器标识(如curl、brave、github-official); - 第二个参数是希望从该服务器暴露给网关的工具名列表(如
brave_local_search、add_issue_comment)。
两个重载方法内部都将服务器名加入servers列表、把工具名加入tools列表,因此可以连续多次调用withServer注册多个服务器。在容器真正启动时(configure()阶段),这些值会被拼装为命令行参数--servers=<server>与--tools=<tool>传给网关进程(见 DockerMcpGatewayContainer.java),并配合固定的--transport=sse参数一起以单条 command 方式启动。
注入密钥(Secrets)
许多 MCP 服务器(如 Brave Search、GitHub)需要 API Key 或访问令牌。容器类提供了两种注入方式(见 DockerMcpGatewayContainer.java):
public DockerMcpGatewayContainer withSecrets(Map<String, String> secrets) public DockerMcpGatewayContainer withSecret(String secretKey, String secretValue)withSecret("brave.api_key", "test_key"):逐条注入单个密钥;withSecrets(Collections.singletonMap("github.personal_access_token", "test_token")):一次注入多个密钥。
底层实现上,所有密钥被汇总到内部的secrets映射中;当存在任何密钥时,启动命令会追加--secrets=/testcontainers/app/secrets(见 DockerMcpGatewayContainer.java)。而在容器创建完成后(containerIsCreated回调,见 DockerMcpGatewayContainer.java),容器类会把密钥按key=value格式逐行写入一个文本文件,并通过copyFileToContainer复制到容器内的/testcontainers/app/secrets路径,供网关读取。这种"先启动、后注入文件"的顺序,确保了密钥文件在网关读取配置时已经就位。
获取网关端点
网关容器启动后,测试代码通常需要连接它的 SSE 端点来调用工具。容器类提供getEndpoint()方法(见 DockerMcpGatewayContainer.java):
public String getEndpoint() { return "http://" + getHost() + ":" + getMappedPort(DEFAULT_PORT); }它基于GenericContainer#getHost()与getMappedPort(8811)返回宿主机可访问的地址,格式为http://<host>:<mappedPort>。因为 Testcontainers 会在运行时将容器内端口8811映射到宿主机随机可用端口,测试代码应始终使用getEndpoint()(或getMappedPort(8811))而非硬编码端口,以保证测试在不同机器与 CI 环境下的可移植性。SSE 端点即getEndpoint() + "/sse"(对应容器启动时输出的Start sse server on port日志)。
工作原理与就绪检测
从源码可以梳理出该容器完整的启动链路:
- 构造时:解析并校验镜像名与
docker/mcp-gateway兼容,暴露端口8811,挂载 Docker 套接字,并注册日志等待策略Wait.forLogMessage(".*Start sse server on port.*", 1)(见 DockerMcpGatewayContainer.java); configure():把传输模式、服务器列表、工具列表、密钥路径拼成启动命令;containerIsCreated():容器创建后立即写入密钥文件;- 等待策略:Testcontainers 持续读取容器日志,直到匹配到 SSE 服务启动日志,
start()才会返回,保证测试在网关真正可服务之后才继续执行。
因此测试中在start()之后立即getEndpoint()、发起 MCP 调用是安全可靠的。
小结
- Docker MCP Gateway 支持内置于 Testcontainers 核心库,通过
org.testcontainers:testcontainers即可使用; DockerMcpGatewayContainer默认使用docker/mcp-gateway镜像、暴露端口8811、以 SSE 模式启动,并自动挂载 Docker 套接字;- 通过
withServer(server, tools...)注册 MCP 服务器与工具,通过withSecret/withSecrets注入密钥; - 使用
getEndpoint()获取可访问的网关地址,结合日志等待策略确保启动完成后才进行业务调用; - 相关源码与测试可分别在 DockerMcpGatewayContainer.java 与 DockerMcpGatewayContainerTest.java 中查看,作为自定义扩展与进一步研究的起点。
【免费下载链接】testcontainers-javaTestcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.项目地址: https://gitcode.com/GitHub_Trending/te/testcontainers-java
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考