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)
.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);
// No content type means no response body, so we can just
// return an empty stream

View file

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

View file

@ -72,7 +72,7 @@ public abstract class AbstractMcpAsyncClientTests {
}
protected Duration getInitializationTimeout() {
return Duration.ofSeconds(2);
return Duration.ofSeconds(20);
}
McpAsyncClient client(McpClientTransport transport) {
@ -503,57 +503,64 @@ public abstract class AbstractMcpAsyncClientTests {
@Test
void testReadResource() {
AtomicInteger resourceCount = new AtomicInteger();
withClient(createMcpTransport(), client -> {
Flux<McpSchema.ReadResourceResult> resources = client.initialize()
.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));
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();
assertThat(result.contents()).isNotNull().isNotEmpty();
// Validate each content item
for (ResourceContents content : result.contents()) {
assertThat(content).isNotNull();
assertThat(content.uri()).isNotNull().isNotEmpty();
assertThat(content.mimeType()).isNotNull().isNotEmpty();
// Validate each content item
for (ResourceContents content : result.contents()) {
assertThat(content).isNotNull();
assertThat(content.uri()).isNotNull().isNotEmpty();
assertThat(content.mimeType()).isNotNull().isNotEmpty();
// Validate content based on its type with more comprehensive
// checks
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();
// Validate content based on its type with more comprehensive
// checks
switch (content.mimeType()) {
case "text/plain" -> {
TextResourceContents textContent = assertInstanceOf(TextResourceContents.class,
content);
assertThat(textContent.text()).isNotNull().isNotEmpty();
assertThat(textContent.uri()).isNotEmpty();
}
else if (content instanceof BlobResourceContents blobContent) {
assertThat(blobContent.blob()).isNotNull();
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) {
assertThat(blobContent.blob()).isNotNull();
}
}
}
}
}
}
})
.expectNextCount(10) // Expect 10 elements
})
.verifyComplete();
});
}
@ -693,7 +700,6 @@ public abstract class AbstractMcpAsyncClientTests {
assertThat(result.capabilities()).isNotNull();
}).verifyComplete());
}
// ---------------------------------------
// Logging Tests
// ---------------------------------------
@ -773,7 +779,7 @@ public abstract class AbstractMcpAsyncClientTests {
if (!(content instanceof McpSchema.TextContent text))
return;
assertThat(text.text()).endsWith(response); // Prefixed
assertThat(text.text()).contains(response);
});
// Verify sampling request parameters received in our callback

View file

@ -625,7 +625,7 @@ public abstract class AbstractMcpSyncClientTests {
if (!(content instanceof McpSchema.TextContent text))
return;
assertThat(text.text()).endsWith(response); // Prefixed
assertThat(text.text()).contains(response);
});
// 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";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3")
.withCommand("node dist/index.js streamableHttp")
static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 streamableHttp")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

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

View file

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

View file

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

View file

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

View file

@ -10,10 +10,12 @@ public final class ServerParameterUtils {
public static ServerParameters createServerParameters() {
if (System.getProperty("os.name").toLowerCase().contains("win")) {
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();
}
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";
@SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3")
.withCommand("node dist/index.js sse")
static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 sse")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

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

View file

@ -318,7 +318,8 @@ public class WebClientStreamableHttpTransport implements McpClientTransport {
long contentLength = response.headers().contentLength().orElse(-1);
// Existing SDKs consume notifications with no response body nor
// 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 {}",
sessionRepresentation);
// 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";
// Uses the https://github.com/tzolov/mcp-everything-server-docker-image
@SuppressWarnings("resource")
static GenericContainer<?> container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v3")
.withCommand("node dist/index.js streamableHttp")
static GenericContainer<?> container = new GenericContainer<>("docker.io/node:lts-alpine3.23")
.withCommand("npx -y @modelcontextprotocol/server-everything@2025.12.18 streamableHttp")
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.withExposedPorts(3001)
.waitingFor(Wait.forHttp("/").forStatusCode(404));

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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