Revert "Clean up AbstractMcpClientServerIntegrationTests"
This reverts commit 439dc90d25.
Spring AI still uses the old API from these tests because they have
muliple clients.
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
This commit is contained in:
parent
c4dd4715ca
commit
fb029c8c76
3 changed files with 304 additions and 146 deletions
|
|
@ -12,6 +12,7 @@ import java.time.Duration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
@ -49,10 +50,11 @@ import io.modelcontextprotocol.spec.McpSchema.Root;
|
||||||
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
|
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
|
||||||
import io.modelcontextprotocol.spec.McpSchema.TextContent;
|
import io.modelcontextprotocol.spec.McpSchema.TextContent;
|
||||||
import io.modelcontextprotocol.spec.McpSchema.Tool;
|
import io.modelcontextprotocol.spec.McpSchema.Tool;
|
||||||
import io.modelcontextprotocol.spec.McpTransportException;
|
|
||||||
import io.modelcontextprotocol.util.Utils;
|
import io.modelcontextprotocol.util.Utils;
|
||||||
import net.javacrumbs.jsonunit.core.Option;
|
import net.javacrumbs.jsonunit.core.Option;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import static io.modelcontextprotocol.util.ToolsUtils.EMPTY_JSON_SCHEMA;
|
import static io.modelcontextprotocol.util.ToolsUtils.EMPTY_JSON_SCHEMA;
|
||||||
|
|
@ -72,20 +74,26 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
protected static final int MAX_REQUEST_SIZE = 2048;
|
protected static final int MAX_REQUEST_SIZE = 2048;
|
||||||
|
|
||||||
|
protected ConcurrentHashMap<String, McpClient.SyncSpec> clientBuilders = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
abstract protected void prepareClients(int port, String mcpEndpoint);
|
||||||
|
|
||||||
abstract protected McpServer.AsyncSpecification<?> prepareAsyncServerBuilder();
|
abstract protected McpServer.AsyncSpecification<?> prepareAsyncServerBuilder();
|
||||||
|
|
||||||
abstract protected McpServer.SyncSpecification<?> prepareSyncServerBuilder();
|
abstract protected McpServer.SyncSpecification<?> prepareSyncServerBuilder();
|
||||||
|
|
||||||
abstract protected McpClient.SyncSpec getMcpClientBuilder();
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
|
@MethodSource("clientsForTesting")
|
||||||
|
void simple(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
@Test
|
|
||||||
void simple() {
|
|
||||||
var server = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
var server = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||||
.requestTimeout(Duration.ofSeconds(1000))
|
.requestTimeout(Duration.ofSeconds(1000))
|
||||||
.build();
|
.build();
|
||||||
try (
|
try (
|
||||||
// Create client without sampling capabilities
|
// Create client without sampling capabilities
|
||||||
var client = getMcpClientBuilder()
|
var client = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
|
||||||
.requestTimeout(Duration.ofSeconds(1000))
|
.requestTimeout(Duration.ofSeconds(1000))
|
||||||
.build()) {
|
.build()) {
|
||||||
|
|
@ -101,8 +109,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
// Sampling Tests
|
// Sampling Tests
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateMessageWithoutSamplingCapabilities() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateMessageWithoutSamplingCapabilities(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||||
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
|
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
|
||||||
.callHandler((exchange, request) -> {
|
.callHandler((exchange, request) -> {
|
||||||
|
|
@ -115,7 +127,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
try (
|
try (
|
||||||
// Create client without sampling capabilities
|
// Create client without sampling capabilities
|
||||||
var client = getMcpClientBuilder()
|
var client = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
|
||||||
.build()) {
|
.build()) {
|
||||||
|
|
||||||
|
|
@ -134,8 +146,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateMessageSuccess() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateMessageSuccess(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
|
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
|
||||||
assertThat(request.messages()).hasSize(1);
|
assertThat(request.messages()).hasSize(1);
|
||||||
assertThat(request.messages().get(0).content()).isInstanceOf(McpSchema.TextContent.class);
|
assertThat(request.messages().get(0).content()).isInstanceOf(McpSchema.TextContent.class);
|
||||||
|
|
@ -176,7 +192,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder()
|
try (var mcpClient = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
||||||
.capabilities(ClientCapabilities.builder().sampling().build())
|
.capabilities(ClientCapabilities.builder().sampling().build())
|
||||||
.sampling(samplingHandler)
|
.sampling(samplingHandler)
|
||||||
|
|
@ -205,8 +221,14 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateMessageWithRequestTimeoutSuccess() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateMessageWithRequestTimeoutSuccess(String clientType) throws InterruptedException {
|
||||||
|
|
||||||
|
// Client
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
|
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
|
||||||
assertThat(request.messages()).hasSize(1);
|
assertThat(request.messages()).hasSize(1);
|
||||||
assertThat(request.messages().get(0).content()).isInstanceOf(McpSchema.TextContent.class);
|
assertThat(request.messages().get(0).content()).isInstanceOf(McpSchema.TextContent.class);
|
||||||
|
|
@ -256,7 +278,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.requestTimeout(Duration.ofSeconds(4))
|
.requestTimeout(Duration.ofSeconds(4))
|
||||||
.tools(tool)
|
.tools(tool)
|
||||||
.build();
|
.build();
|
||||||
try (var mcpClient = getMcpClientBuilder()
|
try (var mcpClient = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
||||||
.capabilities(ClientCapabilities.builder().sampling().build())
|
.capabilities(ClientCapabilities.builder().sampling().build())
|
||||||
.sampling(samplingHandler)
|
.sampling(samplingHandler)
|
||||||
|
|
@ -285,8 +307,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateMessageWithRequestTimeoutFail() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateMessageWithRequestTimeoutFail(String clientType) throws InterruptedException {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
|
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
|
||||||
assertThat(request.messages()).hasSize(1);
|
assertThat(request.messages()).hasSize(1);
|
||||||
assertThat(request.messages().get(0).content()).isInstanceOf(McpSchema.TextContent.class);
|
assertThat(request.messages().get(0).content()).isInstanceOf(McpSchema.TextContent.class);
|
||||||
|
|
@ -331,7 +357,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool)
|
.tools(tool)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder()
|
try (var mcpClient = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
||||||
.capabilities(ClientCapabilities.builder().sampling().build())
|
.capabilities(ClientCapabilities.builder().sampling().build())
|
||||||
.sampling(samplingHandler)
|
.sampling(samplingHandler)
|
||||||
|
|
@ -352,8 +378,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
// Elicitation Tests
|
// Elicitation Tests
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateElicitationWithoutElicitationCapabilities() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateElicitationWithoutElicitationCapabilities(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||||
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
|
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
|
||||||
.callHandler((exchange, request) -> exchange.createElicitation(mock(McpSchema.ElicitFormRequest.class))
|
.callHandler((exchange, request) -> exchange.createElicitation(mock(McpSchema.ElicitFormRequest.class))
|
||||||
|
|
@ -363,8 +393,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
var server = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
var server = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
||||||
|
|
||||||
// Create client without elicitation capabilities
|
// Create client without elicitation capabilities
|
||||||
try (var client = getMcpClientBuilder()
|
try (var client = clientBuilder.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
|
||||||
.build()) {
|
.build()) {
|
||||||
|
|
||||||
assertThat(client.initialize()).isNotNull();
|
assertThat(client.initialize()).isNotNull();
|
||||||
|
|
@ -382,8 +411,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateElicitationSuccess() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateElicitationSuccess(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> formElicitationHandler = request -> {
|
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> formElicitationHandler = request -> {
|
||||||
assertThat(request.message()).isNotEmpty();
|
assertThat(request.message()).isNotEmpty();
|
||||||
assertThat(request.requestedSchema()).isNotNull();
|
assertThat(request.requestedSchema()).isNotNull();
|
||||||
|
|
@ -416,7 +449,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder()
|
try (var mcpClient = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
||||||
.capabilities(ClientCapabilities.builder().elicitation().build())
|
.capabilities(ClientCapabilities.builder().elicitation().build())
|
||||||
.elicitation(formElicitationHandler)
|
.elicitation(formElicitationHandler)
|
||||||
|
|
@ -441,8 +474,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateElicitationWithApplyDefaults() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateElicitationWithApplyDefaults(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Client handler returns empty content — SDK should apply defaults
|
// Client handler returns empty content — SDK should apply defaults
|
||||||
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> elicitationHandler = request -> {
|
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> elicitationHandler = request -> {
|
||||||
assertThat(request.message()).isNotEmpty();
|
assertThat(request.message()).isNotEmpty();
|
||||||
|
|
@ -476,7 +513,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
|
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
|
||||||
.capabilities(ClientCapabilities.builder().elicitation(true, false).build())
|
.capabilities(ClientCapabilities.builder().elicitation(true, false).build())
|
||||||
.elicitation(elicitationHandler)
|
.elicitation(elicitationHandler)
|
||||||
.applyElicitationDefaults(true)
|
.applyElicitationDefaults(true)
|
||||||
|
|
@ -502,8 +539,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateElicitationWithApplyDefaultsAndUnmodifiableMap() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateElicitationWithApplyDefaultsAndUnmodifiableMap(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Client handler returns an unmodifiable map (Map.of()) — SDK must copy into a
|
// Client handler returns an unmodifiable map (Map.of()) — SDK must copy into a
|
||||||
// mutable map before applying defaults.
|
// mutable map before applying defaults.
|
||||||
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> elicitationHandler = request -> new McpSchema.ElicitResult(
|
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> elicitationHandler = request -> new McpSchema.ElicitResult(
|
||||||
|
|
@ -534,7 +575,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
|
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
|
||||||
.capabilities(ClientCapabilities.builder().elicitation(true, false).build())
|
.capabilities(ClientCapabilities.builder().elicitation(true, false).build())
|
||||||
.elicitation(elicitationHandler)
|
.elicitation(elicitationHandler)
|
||||||
.applyElicitationDefaults(true)
|
.applyElicitationDefaults(true)
|
||||||
|
|
@ -558,8 +599,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateElicitationApplyDefaultsDisabledLeavesContentUntouched() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateElicitationApplyDefaultsDisabledLeavesContentUntouched(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> elicitationHandler = request -> new McpSchema.ElicitResult(
|
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> elicitationHandler = request -> new McpSchema.ElicitResult(
|
||||||
McpSchema.ElicitResult.Action.ACCEPT, new HashMap<>());
|
McpSchema.ElicitResult.Action.ACCEPT, new HashMap<>());
|
||||||
|
|
||||||
|
|
@ -586,7 +631,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
||||||
|
|
||||||
// applyElicitationDefaults intentionally NOT called — default false.
|
// applyElicitationDefaults intentionally NOT called — default false.
|
||||||
try (var mcpClient = getMcpClientBuilder().clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
|
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
|
||||||
.capabilities(ClientCapabilities.builder().elicitation(true, false).build())
|
.capabilities(ClientCapabilities.builder().elicitation(true, false).build())
|
||||||
.elicitation(elicitationHandler)
|
.elicitation(elicitationHandler)
|
||||||
.build()) {
|
.build()) {
|
||||||
|
|
@ -608,8 +653,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateElicitationApplyDefaultsSkippedOnDecline() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateElicitationApplyDefaultsSkippedOnDecline(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> elicitationHandler = request -> new McpSchema.ElicitResult(
|
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> elicitationHandler = request -> new McpSchema.ElicitResult(
|
||||||
McpSchema.ElicitResult.Action.DECLINE, new HashMap<>());
|
McpSchema.ElicitResult.Action.DECLINE, new HashMap<>());
|
||||||
|
|
||||||
|
|
@ -635,7 +684,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
|
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
|
||||||
.capabilities(ClientCapabilities.builder().elicitation(true, false).build())
|
.capabilities(ClientCapabilities.builder().elicitation(true, false).build())
|
||||||
.elicitation(elicitationHandler)
|
.elicitation(elicitationHandler)
|
||||||
.applyElicitationDefaults(true)
|
.applyElicitationDefaults(true)
|
||||||
|
|
@ -658,8 +707,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateElicitationApplyDefaultsPreservesMeta() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateElicitationApplyDefaultsPreservesMeta(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
Map<String, Object> meta = Map.of("trace-id", "abc-123");
|
Map<String, Object> meta = Map.of("trace-id", "abc-123");
|
||||||
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> elicitationHandler = request -> new McpSchema.ElicitResult(
|
Function<McpSchema.ElicitFormRequest, McpSchema.ElicitResult> elicitationHandler = request -> new McpSchema.ElicitResult(
|
||||||
McpSchema.ElicitResult.Action.ACCEPT, new HashMap<>(), meta);
|
McpSchema.ElicitResult.Action.ACCEPT, new HashMap<>(), meta);
|
||||||
|
|
@ -686,7 +739,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
|
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
|
||||||
.capabilities(ClientCapabilities.builder().elicitation(true, false).build())
|
.capabilities(ClientCapabilities.builder().elicitation(true, false).build())
|
||||||
.elicitation(elicitationHandler)
|
.elicitation(elicitationHandler)
|
||||||
.applyElicitationDefaults(true)
|
.applyElicitationDefaults(true)
|
||||||
|
|
@ -710,8 +763,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateElicitationWithRequestTimeoutSuccess() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateElicitationWithRequestTimeoutSuccess(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
Function<ElicitFormRequest, ElicitResult> elicitationHandler = request -> {
|
Function<ElicitFormRequest, ElicitResult> elicitationHandler = request -> {
|
||||||
assertThat(request.message()).isNotEmpty();
|
assertThat(request.message()).isNotEmpty();
|
||||||
assertThat(((McpSchema.ElicitFormRequest) request).requestedSchema()).isNotNull();
|
assertThat(((McpSchema.ElicitFormRequest) request).requestedSchema()).isNotNull();
|
||||||
|
|
@ -746,7 +803,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool)
|
.tools(tool)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder()
|
try (var mcpClient = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
||||||
.capabilities(ClientCapabilities.builder().elicitation().build())
|
.capabilities(ClientCapabilities.builder().elicitation().build())
|
||||||
.elicitation(elicitationHandler)
|
.elicitation(elicitationHandler)
|
||||||
|
|
@ -771,10 +828,14 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateElicitationWithRequestTimeoutFail() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateElicitationWithRequestTimeoutFail(String clientType) {
|
||||||
|
|
||||||
var latch = new CountDownLatch(1);
|
var latch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
Function<ElicitFormRequest, ElicitResult> elicitationHandler = request -> {
|
Function<ElicitFormRequest, ElicitResult> elicitationHandler = request -> {
|
||||||
assertThat(request.message()).isNotEmpty();
|
assertThat(request.message()).isNotEmpty();
|
||||||
assertThat(request.requestedSchema()).isNotNull();
|
assertThat(request.requestedSchema()).isNotNull();
|
||||||
|
|
@ -818,7 +879,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool)
|
.tools(tool)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder()
|
try (var mcpClient = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
||||||
.capabilities(ClientCapabilities.builder().elicitation().build())
|
.capabilities(ClientCapabilities.builder().elicitation().build())
|
||||||
.elicitation(elicitationHandler)
|
.elicitation(elicitationHandler)
|
||||||
|
|
@ -839,12 +900,15 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testCreateUrlElicitationSuccess() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCreateUrlElicitationSuccess(String clientType) {
|
||||||
var elicitationRequest = McpSchema.ElicitUrlRequest
|
var elicitationRequest = McpSchema.ElicitUrlRequest
|
||||||
.builder("Test message", "https://example.com/auth", "elicitation-123")
|
.builder("Test message", "https://example.com/auth", "elicitation-123")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
Function<McpSchema.ElicitUrlRequest, McpSchema.ElicitResult> urlElicitationHandler = request -> {
|
Function<McpSchema.ElicitUrlRequest, McpSchema.ElicitResult> urlElicitationHandler = request -> {
|
||||||
assertThat(request.message()).isEqualTo("Test message");
|
assertThat(request.message()).isEqualTo("Test message");
|
||||||
assertThat(request.url()).isEqualTo("https://example.com/auth");
|
assertThat(request.url()).isEqualTo("https://example.com/auth");
|
||||||
|
|
@ -868,7 +932,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder()
|
try (var mcpClient = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
||||||
.capabilities(ClientCapabilities.builder().elicitation(false, true).build())
|
.capabilities(ClientCapabilities.builder().elicitation(false, true).build())
|
||||||
.urlElicitation(urlElicitationHandler)
|
.urlElicitation(urlElicitationHandler)
|
||||||
|
|
@ -888,8 +952,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testElicitationCompleteNotification() throws InterruptedException {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testElicitationCompleteNotification(String clientType) throws InterruptedException {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
CountDownLatch notificationLatch = new CountDownLatch(1);
|
CountDownLatch notificationLatch = new CountDownLatch(1);
|
||||||
AtomicReference<McpSchema.ElicitationCompleteNotification> notificationRef = new AtomicReference<>();
|
AtomicReference<McpSchema.ElicitationCompleteNotification> notificationRef = new AtomicReference<>();
|
||||||
AtomicReference<String> sessionId = new AtomicReference<>();
|
AtomicReference<String> sessionId = new AtomicReference<>();
|
||||||
|
|
@ -914,7 +982,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder()
|
try (var mcpClient = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
||||||
.elicitationCompleteConsumer(elicitationCompleteConsumer)
|
.elicitationCompleteConsumer(elicitationCompleteConsumer)
|
||||||
// enable elicitation so that we can register an elicitation complete consumer
|
// enable elicitation so that we can register an elicitation complete consumer
|
||||||
|
|
@ -940,8 +1008,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testElicitationRequiredError() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testElicitationRequiredError(String clientType) throws InterruptedException {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Capture the session ID so we can trigger an "elicitation complete" notification
|
// Capture the session ID so we can trigger an "elicitation complete" notification
|
||||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||||
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
|
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
|
||||||
|
|
@ -957,7 +1029,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
Function<McpSchema.ElicitUrlRequest, ElicitResult> elicitationHandler = request -> ElicitResult
|
Function<McpSchema.ElicitUrlRequest, ElicitResult> elicitationHandler = request -> ElicitResult
|
||||||
.builder(ElicitResult.Action.ACCEPT)
|
.builder(ElicitResult.Action.ACCEPT)
|
||||||
.build();
|
.build();
|
||||||
try (var mcpClient = getMcpClientBuilder()
|
try (var mcpClient = clientBuilder
|
||||||
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
|
||||||
.urlElicitation(elicitationHandler)
|
.urlElicitation(elicitationHandler)
|
||||||
.build()) {
|
.build()) {
|
||||||
|
|
@ -990,8 +1062,11 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
// Roots Tests
|
// Roots Tests
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testRootsSuccess() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testRootsSuccess(String clientType) {
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
List<Root> roots = List.of(Root.builder("uri1://").name("root1").build(),
|
List<Root> roots = List.of(Root.builder("uri1://").name("root1").build(),
|
||||||
Root.builder("uri2://").name("root2").build());
|
Root.builder("uri2://").name("root2").build());
|
||||||
|
|
||||||
|
|
@ -1001,7 +1076,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.rootsChangeHandler((exchange, rootsUpdate) -> rootsRef.set(rootsUpdate))
|
.rootsChangeHandler((exchange, rootsUpdate) -> rootsRef.set(rootsUpdate))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().capabilities(ClientCapabilities.builder().roots(true).build())
|
try (var mcpClient = clientBuilder.capabilities(ClientCapabilities.builder().roots(true).build())
|
||||||
.roots(roots)
|
.roots(roots)
|
||||||
.build()) {
|
.build()) {
|
||||||
|
|
||||||
|
|
@ -1036,8 +1111,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testRootsWithoutCapability() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testRootsWithoutCapability(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
McpServerFeatures.SyncToolSpecification tool = McpServerFeatures.SyncToolSpecification.builder()
|
McpServerFeatures.SyncToolSpecification tool = McpServerFeatures.SyncToolSpecification.builder()
|
||||||
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
|
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
|
||||||
.callHandler((exchange, request) -> {
|
.callHandler((exchange, request) -> {
|
||||||
|
|
@ -1054,7 +1133,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
try (
|
try (
|
||||||
// Create client without roots capability
|
// Create client without roots capability
|
||||||
// No roots capability
|
// No roots capability
|
||||||
var mcpClient = getMcpClientBuilder().capabilities(ClientCapabilities.builder().build()).build()) {
|
var mcpClient = clientBuilder.capabilities(ClientCapabilities.builder().build()).build()) {
|
||||||
|
|
||||||
assertThat(mcpClient.initialize()).isNotNull();
|
assertThat(mcpClient.initialize()).isNotNull();
|
||||||
|
|
||||||
|
|
@ -1071,15 +1150,19 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testRootsNotificationWithEmptyRootsList() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testRootsNotificationWithEmptyRootsList(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
AtomicReference<List<Root>> rootsRef = new AtomicReference<>();
|
AtomicReference<List<Root>> rootsRef = new AtomicReference<>();
|
||||||
|
|
||||||
var mcpServer = prepareSyncServerBuilder()
|
var mcpServer = prepareSyncServerBuilder()
|
||||||
.rootsChangeHandler((exchange, rootsUpdate) -> rootsRef.set(rootsUpdate))
|
.rootsChangeHandler((exchange, rootsUpdate) -> rootsRef.set(rootsUpdate))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().capabilities(ClientCapabilities.builder().roots(true).build())
|
try (var mcpClient = clientBuilder.capabilities(ClientCapabilities.builder().roots(true).build())
|
||||||
.roots(List.of()) // Empty roots list
|
.roots(List.of()) // Empty roots list
|
||||||
.build()) {
|
.build()) {
|
||||||
|
|
||||||
|
|
@ -1097,8 +1180,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testRootsWithMultipleHandlers() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testRootsWithMultipleHandlers(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
List<Root> roots = List.of(Root.builder("uri1://").name("root1").build());
|
List<Root> roots = List.of(Root.builder("uri1://").name("root1").build());
|
||||||
|
|
||||||
AtomicReference<List<Root>> rootsRef1 = new AtomicReference<>();
|
AtomicReference<List<Root>> rootsRef1 = new AtomicReference<>();
|
||||||
|
|
@ -1109,7 +1196,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.rootsChangeHandler((exchange, rootsUpdate) -> rootsRef2.set(rootsUpdate))
|
.rootsChangeHandler((exchange, rootsUpdate) -> rootsRef2.set(rootsUpdate))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().capabilities(ClientCapabilities.builder().roots(true).build())
|
try (var mcpClient = clientBuilder.capabilities(ClientCapabilities.builder().roots(true).build())
|
||||||
.roots(roots)
|
.roots(roots)
|
||||||
.build()) {
|
.build()) {
|
||||||
|
|
||||||
|
|
@ -1127,8 +1214,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testRootsServerCloseWithActiveSubscription() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testRootsServerCloseWithActiveSubscription(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
List<Root> roots = List.of(Root.builder("uri1://").name("root1").build());
|
List<Root> roots = List.of(Root.builder("uri1://").name("root1").build());
|
||||||
|
|
||||||
AtomicReference<List<Root>> rootsRef = new AtomicReference<>();
|
AtomicReference<List<Root>> rootsRef = new AtomicReference<>();
|
||||||
|
|
@ -1137,7 +1228,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.rootsChangeHandler((exchange, rootsUpdate) -> rootsRef.set(rootsUpdate))
|
.rootsChangeHandler((exchange, rootsUpdate) -> rootsRef.set(rootsUpdate))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().capabilities(ClientCapabilities.builder().roots(true).build())
|
try (var mcpClient = clientBuilder.capabilities(ClientCapabilities.builder().roots(true).build())
|
||||||
.roots(roots)
|
.roots(roots)
|
||||||
.build()) {
|
.build()) {
|
||||||
|
|
||||||
|
|
@ -1158,8 +1249,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
// Tools Tests
|
// Tools Tests
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testToolCallSuccess() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testToolCallSuccess(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
|
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
|
||||||
var callResponse = McpSchema.CallToolResult.builder()
|
var callResponse = McpSchema.CallToolResult.builder()
|
||||||
.addContent(McpSchema.TextContent.builder("CALL RESPONSE; ctx=importantValue").build())
|
.addContent(McpSchema.TextContent.builder("CALL RESPONSE; ctx=importantValue").build())
|
||||||
|
|
@ -1190,7 +1285,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool1)
|
.tools(tool1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
|
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
@ -1208,8 +1303,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testThrowingToolCallIsCaughtBeforeTimeout() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testThrowingToolCallIsCaughtBeforeTimeout(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
McpSyncServer mcpServer = prepareSyncServerBuilder()
|
McpSyncServer mcpServer = prepareSyncServerBuilder()
|
||||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||||
.tools(McpServerFeatures.SyncToolSpecification.builder()
|
.tools(McpServerFeatures.SyncToolSpecification.builder()
|
||||||
|
|
@ -1222,7 +1321,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().requestTimeout(Duration.ofMillis(6666)).build()) {
|
try (var mcpClient = clientBuilder.requestTimeout(Duration.ofMillis(6666)).build()) {
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
||||||
|
|
@ -1237,8 +1336,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testToolCallSuccessWithTranportContextExtraction() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testToolCallSuccessWithTranportContextExtraction(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
var transportContextIsNull = new AtomicBoolean(false);
|
var transportContextIsNull = new AtomicBoolean(false);
|
||||||
var transportContextIsEmpty = new AtomicBoolean(false);
|
var transportContextIsEmpty = new AtomicBoolean(false);
|
||||||
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
|
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
|
||||||
|
|
@ -1273,7 +1376,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool1)
|
.tools(tool1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
|
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
@ -1293,8 +1396,11 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testToolWithNonAsciiCharacters() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testToolWithNonAsciiCharacters(String clientType) {
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
String inputSchema = """
|
String inputSchema = """
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
@ -1319,7 +1425,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(nonAsciiTool)
|
.tools(nonAsciiTool)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
|
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
@ -1342,8 +1448,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testToolListChangeHandlingSuccess() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testToolListChangeHandlingSuccess(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
var callResponse = McpSchema.CallToolResult.builder()
|
var callResponse = McpSchema.CallToolResult.builder()
|
||||||
.addContent(McpSchema.TextContent.builder("CALL RESPONSE").build())
|
.addContent(McpSchema.TextContent.builder("CALL RESPONSE").build())
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -1375,7 +1485,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool1)
|
.tools(tool1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().toolsChangeConsumer(toolsUpdate -> {
|
try (var mcpClient = clientBuilder.toolsChangeConsumer(toolsUpdate -> {
|
||||||
// perform a blocking call to a remote service
|
// perform a blocking call to a remote service
|
||||||
try {
|
try {
|
||||||
HttpResponse<String> response = HttpClient.newHttpClient()
|
HttpResponse<String> response = HttpClient.newHttpClient()
|
||||||
|
|
@ -1430,11 +1540,15 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testInitialize() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testInitialize(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
var mcpServer = prepareSyncServerBuilder().build();
|
var mcpServer = prepareSyncServerBuilder().build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
|
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
@ -1447,13 +1561,16 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
// Logging Tests
|
// Logging Tests
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testLoggingNotification() throws InterruptedException {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testLoggingNotification(String clientType) throws InterruptedException {
|
||||||
int expectedNotificationsCount = 3;
|
int expectedNotificationsCount = 3;
|
||||||
CountDownLatch latch = new CountDownLatch(expectedNotificationsCount);
|
CountDownLatch latch = new CountDownLatch(expectedNotificationsCount);
|
||||||
// Create a list to store received logging notifications
|
// Create a list to store received logging notifications
|
||||||
List<McpSchema.LoggingMessageNotification> receivedNotifications = new CopyOnWriteArrayList<>();
|
List<McpSchema.LoggingMessageNotification> receivedNotifications = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Create server with a tool that sends logging notifications
|
// Create server with a tool that sends logging notifications
|
||||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||||
.tool(Tool.builder("logging-test", EMPTY_JSON_SCHEMA).description("Test logging notifications").build())
|
.tool(Tool.builder("logging-test", EMPTY_JSON_SCHEMA).description("Test logging notifications").build())
|
||||||
|
|
@ -1502,7 +1619,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
try (
|
try (
|
||||||
// Create client with logging notification handler
|
// Create client with logging notification handler
|
||||||
var mcpClient = getMcpClientBuilder().loggingConsumer(notification -> {
|
var mcpClient = clientBuilder.loggingConsumer(notification -> {
|
||||||
receivedNotifications.add(notification);
|
receivedNotifications.add(notification);
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}).build()) {
|
}).build()) {
|
||||||
|
|
@ -1552,14 +1669,17 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
// Progress Tests
|
// Progress Tests
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testProgressNotification() throws InterruptedException {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testProgressNotification(String clientType) throws InterruptedException {
|
||||||
int expectedNotificationsCount = 4; // 3 notifications + 1 for another progress
|
int expectedNotificationsCount = 4; // 3 notifications + 1 for another progress
|
||||||
// token
|
// token
|
||||||
CountDownLatch latch = new CountDownLatch(expectedNotificationsCount);
|
CountDownLatch latch = new CountDownLatch(expectedNotificationsCount);
|
||||||
// Create a list to store received logging notifications
|
// Create a list to store received logging notifications
|
||||||
List<McpSchema.ProgressNotification> receivedNotifications = new CopyOnWriteArrayList<>();
|
List<McpSchema.ProgressNotification> receivedNotifications = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Create server with a tool that sends logging notifications
|
// Create server with a tool that sends logging notifications
|
||||||
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
|
||||||
.tool(McpSchema.Tool.builder("progress-test", EMPTY_JSON_SCHEMA)
|
.tool(McpSchema.Tool.builder("progress-test", EMPTY_JSON_SCHEMA)
|
||||||
|
|
@ -1602,7 +1722,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
|
|
||||||
try (
|
try (
|
||||||
// Create client with progress notification handler
|
// Create client with progress notification handler
|
||||||
var mcpClient = getMcpClientBuilder().progressConsumer(notification -> {
|
var mcpClient = clientBuilder.progressConsumer(notification -> {
|
||||||
receivedNotifications.add(notification);
|
receivedNotifications.add(notification);
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}).build()) {
|
}).build()) {
|
||||||
|
|
@ -1663,8 +1783,11 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
// Completion Tests
|
// Completion Tests
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : Completion call")
|
||||||
void testCompletionShouldReturnExpectedSuggestions() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testCompletionShouldReturnExpectedSuggestions(String clientType) {
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
var expectedValues = List.of("python", "pytorch", "pyside");
|
var expectedValues = List.of("python", "pytorch", "pyside");
|
||||||
var completionResponse = new McpSchema.CompleteResult(
|
var completionResponse = new McpSchema.CompleteResult(
|
||||||
new CompleteResult.CompleteCompletion(expectedValues, 10, true));
|
new CompleteResult.CompleteCompletion(expectedValues, 10, true));
|
||||||
|
|
@ -1690,7 +1813,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
McpSchema.PromptReference.builder("code_review").title("Code review").build(), completionHandler))
|
McpSchema.PromptReference.builder("code_review").title("Code review").build(), completionHandler))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
|
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
@ -1716,8 +1839,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
// Ping Tests
|
// Ping Tests
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testPingSuccess() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testPingSuccess(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Create server with a tool that uses ping functionality
|
// Create server with a tool that uses ping functionality
|
||||||
AtomicReference<String> executionOrder = new AtomicReference<>("");
|
AtomicReference<String> executionOrder = new AtomicReference<>("");
|
||||||
|
|
||||||
|
|
@ -1751,7 +1878,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool)
|
.tools(tool)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
|
|
||||||
// Initialize client
|
// Initialize client
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
|
|
@ -1775,8 +1902,11 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
// Tool Structured Output Schema Tests
|
// Tool Structured Output Schema Tests
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testStructuredOutputValidationSuccess() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testStructuredOutputValidationSuccess(String clientType) {
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Create a tool with output schema
|
// Create a tool with output schema
|
||||||
Map<String, Object> outputSchema = Map.of(
|
Map<String, Object> outputSchema = Map.of(
|
||||||
"type", "object", "properties", Map.of("result", Map.of("type", "number"), "operation",
|
"type", "object", "properties", Map.of("result", Map.of("type", "number"), "operation",
|
||||||
|
|
@ -1805,7 +1935,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool)
|
.tools(tool)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
||||||
|
|
@ -1845,8 +1975,11 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testStructuredOutputOfObjectArrayValidationSuccess() {
|
@ValueSource(strings = { "httpclient" })
|
||||||
|
void testStructuredOutputOfObjectArrayValidationSuccess(String clientType) {
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Create a tool with output schema that returns an array of objects
|
// Create a tool with output schema that returns an array of objects
|
||||||
Map<String, Object> outputSchema = Map
|
Map<String, Object> outputSchema = Map
|
||||||
.of( // @formatter:off
|
.of( // @formatter:off
|
||||||
|
|
@ -1877,7 +2010,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool)
|
.tools(tool)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
assertThat(mcpClient.initialize()).isNotNull();
|
assertThat(mcpClient.initialize()).isNotNull();
|
||||||
|
|
||||||
// Call tool with valid structured output of type array
|
// Call tool with valid structured output of type array
|
||||||
|
|
@ -1901,8 +2034,11 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testStructuredOutputWithInHandlerError() {
|
@ValueSource(strings = { "httpclient" })
|
||||||
|
void testStructuredOutputWithInHandlerError(String clientType) {
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Create a tool with output schema
|
// Create a tool with output schema
|
||||||
Map<String, Object> outputSchema = Map.of(
|
Map<String, Object> outputSchema = Map.of(
|
||||||
"type", "object", "properties", Map.of("result", Map.of("type", "number"), "operation",
|
"type", "object", "properties", Map.of("result", Map.of("type", "number"), "operation",
|
||||||
|
|
@ -1928,7 +2064,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool)
|
.tools(tool)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
||||||
|
|
@ -1954,8 +2090,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testStructuredOutputValidationFailure() {
|
@ValueSource(strings = { "httpclient" })
|
||||||
|
void testStructuredOutputValidationFailure(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Create a tool with output schema
|
// Create a tool with output schema
|
||||||
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
|
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
|
||||||
Map.of("result", Map.of("type", "number"), "operation", Map.of("type", "string")), "required",
|
Map.of("result", Map.of("type", "number"), "operation", Map.of("type", "string")), "required",
|
||||||
|
|
@ -1983,7 +2123,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool)
|
.tools(tool)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
||||||
|
|
@ -2004,8 +2144,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testStructuredOutputMissingStructuredContent() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testStructuredOutputMissingStructuredContent(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Create a tool with output schema
|
// Create a tool with output schema
|
||||||
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
|
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
|
||||||
Map.of("result", Map.of("type", "number")), "required", List.of("result"));
|
Map.of("result", Map.of("type", "number")), "required", List.of("result"));
|
||||||
|
|
@ -2028,7 +2172,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool)
|
.tools(tool)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
||||||
|
|
@ -2050,14 +2194,18 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testStructuredOutputRuntimeToolAddition() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testStructuredOutputRuntimeToolAddition(String clientType) {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
// Start server without tools
|
// Start server without tools
|
||||||
var mcpServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
|
var mcpServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
|
||||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
InitializeResult initResult = mcpClient.initialize();
|
InitializeResult initResult = mcpClient.initialize();
|
||||||
assertThat(initResult).isNotNull();
|
assertThat(initResult).isNotNull();
|
||||||
|
|
||||||
|
|
@ -2127,8 +2275,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
// Resource Subscription Tests
|
// Resource Subscription Tests
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testResourceSubscription() throws InterruptedException {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testResourceSubscription(String clientType) throws InterruptedException {
|
||||||
|
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
String resourceUri = "test://subscribable-resource";
|
String resourceUri = "test://subscribable-resource";
|
||||||
var receivedContents = new AtomicReference<List<McpSchema.ResourceContents>>();
|
var receivedContents = new AtomicReference<List<McpSchema.ResourceContents>>();
|
||||||
var latch = new CountDownLatch(1);
|
var latch = new CountDownLatch(1);
|
||||||
|
|
@ -2146,7 +2298,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.resources(resourceSpec)
|
.resources(resourceSpec)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().resourcesUpdateConsumer(contents -> {
|
try (var mcpClient = clientBuilder.resourcesUpdateConsumer(contents -> {
|
||||||
receivedContents.set(contents);
|
receivedContents.set(contents);
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}).build()) {
|
}).build()) {
|
||||||
|
|
@ -2167,8 +2319,11 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testResourceSubscription_afterUnsubscribe_noNotification() {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testResourceSubscription_afterUnsubscribe_noNotification(String clientType) throws InterruptedException {
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
String resourceUri = "test://subscribable-resource-unsub";
|
String resourceUri = "test://subscribable-resource-unsub";
|
||||||
var notificationCount = new java.util.concurrent.atomic.AtomicInteger(0);
|
var notificationCount = new java.util.concurrent.atomic.AtomicInteger(0);
|
||||||
|
|
||||||
|
|
@ -2183,8 +2338,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.resources(resourceSpec)
|
.resources(resourceSpec)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder()
|
try (var mcpClient = clientBuilder.resourcesUpdateConsumer(contents -> notificationCount.incrementAndGet())
|
||||||
.resourcesUpdateConsumer(contents -> notificationCount.incrementAndGet())
|
|
||||||
.build()) {
|
.build()) {
|
||||||
|
|
||||||
mcpClient.initialize();
|
mcpClient.initialize();
|
||||||
|
|
@ -2202,8 +2356,11 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bounded read
|
// Bounded read
|
||||||
@Test
|
@ParameterizedTest(name = "{0} : {displayName} ")
|
||||||
void testRejectsWhenContentLengthHeaderExceedsLimit() throws Exception {
|
@MethodSource("clientsForTesting")
|
||||||
|
void testRejectsWhenContentLengthHeaderExceedsLimit(String clientType) throws Exception {
|
||||||
|
var clientBuilder = clientBuilders.get(clientType);
|
||||||
|
|
||||||
String inputSchema = """
|
String inputSchema = """
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
@ -2227,7 +2384,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
|
||||||
.tools(tool1)
|
.tools(tool1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (var mcpClient = getMcpClientBuilder().build()) {
|
try (var mcpClient = clientBuilder.build()) {
|
||||||
String oversizedBody = "a".repeat(MAX_REQUEST_SIZE + 1);
|
String oversizedBody = "a".repeat(MAX_REQUEST_SIZE + 1);
|
||||||
|
|
||||||
mcpClient.initialize();
|
mcpClient.initialize();
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,12 @@ class HttpServletSseIntegrationTests extends AbstractMcpClientServerIntegrationT
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to start Tomcat", e);
|
throw new RuntimeException("Failed to start Tomcat", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clientBuilders
|
||||||
|
.put("httpclient",
|
||||||
|
McpClient.sync(HttpClientSseClientTransport.builder("http://localhost:" + PORT)
|
||||||
|
.sseEndpoint(CUSTOM_SSE_ENDPOINT)
|
||||||
|
.build()).requestTimeout(Duration.ofHours(10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -82,15 +88,6 @@ class HttpServletSseIntegrationTests extends AbstractMcpClientServerIntegrationT
|
||||||
return McpServer.sync(this.mcpServerTransportProvider);
|
return McpServer.sync(this.mcpServerTransportProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected McpClient.SyncSpec getMcpClientBuilder() {
|
|
||||||
return McpClient
|
|
||||||
.sync(HttpClientSseClientTransport.builder("http://localhost:" + PORT)
|
|
||||||
.sseEndpoint(CUSTOM_SSE_ENDPOINT)
|
|
||||||
.build())
|
|
||||||
.requestTimeout(Duration.ofHours(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void after() {
|
public void after() {
|
||||||
if (mcpServerTransportProvider != null) {
|
if (mcpServerTransportProvider != null) {
|
||||||
|
|
@ -107,6 +104,10 @@ class HttpServletSseIntegrationTests extends AbstractMcpClientServerIntegrationT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void prepareClients(int port, String mcpEndpoint) {
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsWhenBodyBytesExceedLimitWithoutContentLengthHeader() throws Exception {
|
void rejectsWhenBodyBytesExceedLimitWithoutContentLengthHeader() throws Exception {
|
||||||
var httpClient = HttpClient.newHttpClient();
|
var httpClient = HttpClient.newHttpClient();
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,12 @@ class HttpServletStreamableIntegrationTests extends AbstractMcpClientServerInteg
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to start Tomcat", e);
|
throw new RuntimeException("Failed to start Tomcat", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clientBuilders
|
||||||
|
.put("httpclient",
|
||||||
|
McpClient.sync(HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT)
|
||||||
|
.endpoint(MESSAGE_ENDPOINT)
|
||||||
|
.build()).requestTimeout(Duration.ofHours(10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -86,15 +92,6 @@ class HttpServletStreamableIntegrationTests extends AbstractMcpClientServerInteg
|
||||||
return McpServer.sync(this.mcpServerTransportProvider);
|
return McpServer.sync(this.mcpServerTransportProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected McpClient.SyncSpec getMcpClientBuilder() {
|
|
||||||
return McpClient
|
|
||||||
.sync(HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT)
|
|
||||||
.endpoint(MESSAGE_ENDPOINT)
|
|
||||||
.build())
|
|
||||||
.requestTimeout(Duration.ofHours(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void after() {
|
public void after() {
|
||||||
if (mcpServerTransportProvider != null) {
|
if (mcpServerTransportProvider != null) {
|
||||||
|
|
@ -149,7 +146,10 @@ class HttpServletStreamableIntegrationTests extends AbstractMcpClientServerInteg
|
||||||
finally {
|
finally {
|
||||||
mcpServer.close();
|
mcpServer.close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void prepareClients(int port, String mcpEndpoint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static McpTransportContextExtractor<HttpServletRequest> TEST_CONTEXT_EXTRACTOR = (r) -> McpTransportContext
|
static McpTransportContextExtractor<HttpServletRequest> TEST_CONTEXT_EXTRACTOR = (r) -> McpTransportContext
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue