Fix everything-server-based integration tests (#756)

* Pin `npx @modelcontextprotocol/server-everything` version to `2025.12.18`.
* Replace `tzolov/mcp-everything-server` Docker image with `node:lts-alpine` + `npx` command.
* Handle HTTP 202 special case.
* Fix test assertions.

The recent rollout of everything-server broke integration tests which take the latest version from the node registry.
This PR unifies the everything-server usage - the Testcontainers Docker setup uses the same version as the STDIO npx-based tests and no longer relies on tzolov/mcp-everything-server.

Signed-off-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com>
This commit is contained in:
Dariusz Jędrzejczyk 2026-01-20 16:09:41 +01:00 • committed by GitHub
commit af65356416
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 144 additions and 139 deletions

View file

@ -491,7 +491,9 @@ public class HttpClientStreamableHttpTransport implements McpClientTransport {
.firstValue(HttpHeaders.CONTENT_LENGTH) .firstValue(HttpHeaders.CONTENT_LENGTH)
.orElse(null); .orElse(null);
if (contentType.isBlank() || "0".equals(contentLength)) { // For empty content or HTTP code 202 (ACCEPTED), assume success
if (contentType.isBlank() || "0".equals(contentLength) || statusCode == 202) {
// if (contentType.isBlank() || "0".equals(contentLength)) {
logger.debug("No body returned for POST in session {}", sessionRepresentation); logger.debug("No body returned for POST in session {}", sessionRepresentation);
// No content type means no response body, so we can just // No content type means no response body, so we can just
// return an empty stream // return an empty stream

View file

@ -48,10 +48,9 @@ public abstract class AbstractMcpAsyncClientResiliencyTests {
static Network network = Network.newNetwork(); static Network network = Network.newNetwork();
static String host = "http://localhost:3001"; static String host = "http://localhost:3001";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js streamableHttp") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 streamableHttp")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withNetwork(network) .withNetwork(network)
.withNetworkAliases("everything-server") .withNetworkAliases("everything-server")

View file

@ -72,7 +72,7 @@ public abstract class AbstractMcpAsyncClientTests {
} }
protected Duration getInitializationTimeout() { protected Duration getInitializationTimeout() {
return Duration.ofSeconds(2); return Duration.ofSeconds(20);
} }
McpAsyncClient client(McpClientTransport transport) { McpAsyncClient client(McpClientTransport transport) {
@ -503,57 +503,64 @@ public abstract class AbstractMcpAsyncClientTests {
@Test @Test
void testReadResource() { void testReadResource() {
AtomicInteger resourceCount = new AtomicInteger();
withClient(createMcpTransport(), client -> { withClient(createMcpTransport(), client -> {
Flux<McpSchema.ReadResourceResult> resources = client.initialize() Flux<McpSchema.ReadResourceResult> resources = client.initialize()
.then(client.listResources(null)) .then(client.listResources(null))
.flatMapMany(r -> Flux.fromIterable(r.resources())) .flatMapMany(r -> {
List<Resource> l = r.resources();
resourceCount.set(l.size());
return Flux.fromIterable(l);
})
.flatMap(r -> client.readResource(r)); .flatMap(r -> client.readResource(r));
StepVerifier.create(resources).recordWith(ArrayList::new).consumeRecordedWith(readResourceResults -> { StepVerifier.create(resources)
.recordWith(ArrayList::new)
.thenConsumeWhile(res -> true)
.consumeRecordedWith(readResourceResults -> {
assertThat(readResourceResults.size()).isEqualTo(resourceCount.get());
for (ReadResourceResult result : readResourceResults) {
for (ReadResourceResult result : readResourceResults) { assertThat(result).isNotNull();
assertThat(result.contents()).isNotNull().isNotEmpty();
assertThat(result).isNotNull(); // Validate each content item
assertThat(result.contents()).isNotNull().isNotEmpty(); for (ResourceContents content : result.contents()) {
assertThat(content).isNotNull();
assertThat(content.uri()).isNotNull().isNotEmpty();
assertThat(content.mimeType()).isNotNull().isNotEmpty();
// Validate each content item // Validate content based on its type with more comprehensive
for (ResourceContents content : result.contents()) { // checks
assertThat(content).isNotNull(); switch (content.mimeType()) {
assertThat(content.uri()).isNotNull().isNotEmpty(); case "text/plain" -> {
assertThat(content.mimeType()).isNotNull().isNotEmpty(); TextResourceContents textContent = assertInstanceOf(TextResourceContents.class,
content);
// Validate content based on its type with more comprehensive assertThat(textContent.text()).isNotNull().isNotEmpty();
// checks assertThat(textContent.uri()).isNotEmpty();
switch (content.mimeType()) {
case "text/plain" -> {
TextResourceContents textContent = assertInstanceOf(TextResourceContents.class,
content);
assertThat(textContent.text()).isNotNull().isNotEmpty();
assertThat(textContent.uri()).isNotEmpty();
}
case "application/octet-stream" -> {
BlobResourceContents blobContent = assertInstanceOf(BlobResourceContents.class,
content);
assertThat(blobContent.blob()).isNotNull().isNotEmpty();
assertThat(blobContent.uri()).isNotNull().isNotEmpty();
// Validate base64 encoding format
assertThat(blobContent.blob()).matches("^[A-Za-z0-9+/]*={0,2}$");
}
default -> {
// Still validate basic properties
if (content instanceof TextResourceContents textContent) {
assertThat(textContent.text()).isNotNull();
} }
else if (content instanceof BlobResourceContents blobContent) { case "application/octet-stream" -> {
assertThat(blobContent.blob()).isNotNull(); BlobResourceContents blobContent = assertInstanceOf(BlobResourceContents.class,
content);
assertThat(blobContent.blob()).isNotNull().isNotEmpty();
assertThat(blobContent.uri()).isNotNull().isNotEmpty();
// Validate base64 encoding format
assertThat(blobContent.blob()).matches("^[A-Za-z0-9+/]*={0,2}$");
}
default -> {
// Still validate basic properties
if (content instanceof TextResourceContents textContent) {
assertThat(textContent.text()).isNotNull();
}
else if (content instanceof BlobResourceContents blobContent) {
assertThat(blobContent.blob()).isNotNull();
}
} }
} }
} }
} }
} })
})
.expectNextCount(10) // Expect 10 elements
.verifyComplete(); .verifyComplete();
}); });
} }
@ -693,7 +700,6 @@ public abstract class AbstractMcpAsyncClientTests {
assertThat(result.capabilities()).isNotNull(); assertThat(result.capabilities()).isNotNull();
}).verifyComplete()); }).verifyComplete());
} }
// --------------------------------------- // ---------------------------------------
// Logging Tests // Logging Tests
// --------------------------------------- // ---------------------------------------
@ -773,7 +779,7 @@ public abstract class AbstractMcpAsyncClientTests {
if (!(content instanceof McpSchema.TextContent text)) if (!(content instanceof McpSchema.TextContent text))
return; return;
assertThat(text.text()).endsWith(response); // Prefixed assertThat(text.text()).contains(response);
}); });
// Verify sampling request parameters received in our callback // Verify sampling request parameters received in our callback

View file

@ -625,7 +625,7 @@ public abstract class AbstractMcpSyncClientTests {
if (!(content instanceof McpSchema.TextContent text)) if (!(content instanceof McpSchema.TextContent text))
return; return;
assertThat(text.text()).endsWith(response); // Prefixed assertThat(text.text()).contains(response);
}); });
// Verify sampling request parameters received in our callback // Verify sampling request parameters received in our callback

View file

@ -17,10 +17,9 @@ public class HttpClientStreamableHttpAsyncClientTests extends AbstractMcpAsyncCl
private static String host = "http://localhost:3001"; private static String host = "http://localhost:3001";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js streamableHttp") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 streamableHttp")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -30,10 +30,9 @@ public class HttpClientStreamableHttpSyncClientTests extends AbstractMcpSyncClie
static String host = "http://localhost:3001"; static String host = "http://localhost:3001";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js streamableHttp") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 streamableHttp")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -36,10 +36,9 @@ public class HttpSseMcpAsyncClientLostConnectionTests {
static Network network = Network.newNetwork(); static Network network = Network.newNetwork();
static String host = "http://localhost:3001"; static String host = "http://localhost:3001";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js sse") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 sse")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withNetwork(network) .withNetwork(network)
.withNetworkAliases("everything-server") .withNetworkAliases("everything-server")

View file

@ -23,10 +23,9 @@ class HttpSseMcpAsyncClientTests extends AbstractMcpAsyncClientTests {
private static String host = "http://localhost:3004"; private static String host = "http://localhost:3004";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js sse") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 sse")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -36,10 +36,9 @@ class HttpSseMcpSyncClientTests extends AbstractMcpSyncClientTests {
static String host = "http://localhost:3003"; static String host = "http://localhost:3003";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js sse") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 sse")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -10,10 +10,12 @@ public final class ServerParameterUtils {
public static ServerParameters createServerParameters() { public static ServerParameters createServerParameters() {
if (System.getProperty("os.name").toLowerCase().contains("win")) { if (System.getProperty("os.name").toLowerCase().contains("win")) {
return ServerParameters.builder("cmd.exe") return ServerParameters.builder("cmd.exe")
.args("/c", "npx.cmd", "-y", "@modelcontextprotocol/server-everything", "stdio") .args("/c", "npx.cmd", "-y", "@modelcontextprotocol/server-everything@2025.12.18", "stdio")
.build(); .build();
} }
return ServerParameters.builder("npx").args("-y", "@modelcontextprotocol/server-everything", "stdio").build(); return ServerParameters.builder("npx")
.args("-y", "@modelcontextprotocol/server-everything@2025.12.18", "stdio")
.build();
} }
} }

View file

@ -58,8 +58,8 @@ class HttpClientSseClientTransportTests {
static String host = "http://localhost:3001"; static String host = "http://localhost:3001";
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js sse") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 sse")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -40,8 +40,8 @@ class HttpClientStreamableHttpTransportTest {
.create(Map.of("test-transport-context-key", "some-value")); .create(Map.of("test-transport-context-key", "some-value"));
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js streamableHttp") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 streamableHttp")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -318,7 +318,8 @@ public class WebClientStreamableHttpTransport implements McpClientTransport {
long contentLength = response.headers().contentLength().orElse(-1); long contentLength = response.headers().contentLength().orElse(-1);
// Existing SDKs consume notifications with no response body nor // Existing SDKs consume notifications with no response body nor
// content type // content type
if (contentType.isEmpty() || contentLength == 0) { if (contentType.isEmpty() || contentLength == 0
|| response.statusCode().equals(HttpStatus.ACCEPTED)) {
logger.trace("Message was successfully sent via POST for session {}", logger.trace("Message was successfully sent via POST for session {}",
sessionRepresentation); sessionRepresentation);
// signal the caller that the message was successfully // signal the caller that the message was successfully

View file

@ -19,10 +19,9 @@ public class WebClientStreamableHttpAsyncClientTests extends AbstractMcpAsyncCli
static String host = "http://localhost:3001"; static String host = "http://localhost:3001";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js streamableHttp") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 streamableHttp")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -19,10 +19,9 @@ public class WebClientStreamableHttpSyncClientTests extends AbstractMcpSyncClien
static String host = "http://localhost:3001"; static String host = "http://localhost:3001";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js streamableHttp") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 streamableHttp")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -26,13 +26,12 @@ class WebFluxSseMcpAsyncClientTests extends AbstractMcpAsyncClientTests {
static String host = "http://localhost:3001"; static String host = "http://localhost:3001";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js sse") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 sse")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404).forPort(3001));
@Override @Override
protected McpClientTransport createMcpTransport() { protected McpClientTransport createMcpTransport() {

View file

@ -25,10 +25,9 @@ class WebFluxSseMcpSyncClientTests extends AbstractMcpSyncClientTests {
static String host = "http://localhost:3001"; static String host = "http://localhost:3001";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js sse") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 sse")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -20,8 +20,8 @@ class WebClientStreamableHttpTransportTest {
static WebClient.Builder builder; static WebClient.Builder builder;
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js streamableHttp") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 streamableHttp")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -47,8 +47,8 @@ class WebFluxSseClientTransportTests {
static String host = "http://localhost:3001"; static String host = "http://localhost:3001";
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js sse") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 sse")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001) .withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404)); .waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

@ -47,10 +47,9 @@ public abstract class AbstractMcpAsyncClientResiliencyTests {
static Network network = Network.newNetwork(); static Network network = Network.newNetwork();
static String host = "http://localhost:3001"; static String host = "http://localhost:3001";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource") @SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3") static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("node dist/index.js streamableHttp") .withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 streamableHttp")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withNetwork(network) .withNetwork(network)
.withNetworkAliases("everything-server") .withNetworkAliases("everything-server")

Some files were not shown because too many files have changed in this diff Show more