refactor: remove deprecated 0.7.0 code

These changes are part of the planned deprecation cycle announced in 0.8.0,
with the deprecated classes scheduled for removal in 0.9.0

- Delete WebFluxSseServerTransport, WebMvcSseServerTransport, StdioServerTransport, and HttpServletSseServerTransport
- Remove deprecated interfaces: ServerMcpTransport, ClientMcpTransport
- Delete DefaultMcpSession implementation
- Remove all deprecated test classes for the removed implementations
- Update references to use McpServerTransport and McpClientTransport interfaces
- Split MockMcpTransport into client and server implementations
  * Rename MockMcpTransport to MockMcpClientTransport in mcp/src/test
  * Create new MockMcpServerTransport implementation
  * Add MockMcpServerTransportProvider for server tests
  * Mark MockMcpTransport in mcp-test module as deprecated
  * Update all test classes to use the new implementations

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
Christian Tzolov 2025-03-22 16:50:33 +01:00
commit 25f3bad68d
16 changed files with 203 additions and 7660 deletions

View file

@ -0,0 +1,95 @@
/*
* Copyright 2024-2024 the original author or authors.
*/
package io.modelcontextprotocol;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification;
import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
/**
* A mock implementation of the {@link McpClientTransport} interfaces.
*/
public class MockMcpClientTransport implements McpClientTransport {
private final Sinks.Many<McpSchema.JSONRPCMessage> inbound = Sinks.many().unicast().onBackpressureBuffer();
private final List<McpSchema.JSONRPCMessage> sent = new ArrayList<>();
private final BiConsumer<MockMcpClientTransport, McpSchema.JSONRPCMessage> interceptor;
public MockMcpClientTransport() {
this((t, msg) -> {
});
}
public MockMcpClientTransport(BiConsumer<MockMcpClientTransport, McpSchema.JSONRPCMessage> interceptor) {
this.interceptor = interceptor;
}
public void simulateIncomingMessage(McpSchema.JSONRPCMessage message) {
if (inbound.tryEmitNext(message).isFailure()) {
throw new RuntimeException("Failed to process incoming message " + message);
}
}
@Override
public Mono<Void> sendMessage(McpSchema.JSONRPCMessage message) {
sent.add(message);
interceptor.accept(this, message);
return Mono.empty();
}
public McpSchema.JSONRPCRequest getLastSentMessageAsRequest() {
return (JSONRPCRequest) getLastSentMessage();
}
public McpSchema.JSONRPCNotification getLastSentMessageAsNotification() {
return (JSONRPCNotification) getLastSentMessage();
}
public McpSchema.JSONRPCMessage getLastSentMessage() {
return !sent.isEmpty() ? sent.get(sent.size() - 1) : null;
}
private volatile boolean connected = false;
@Override
public Mono<Void> connect(Function<Mono<McpSchema.JSONRPCMessage>, Mono<McpSchema.JSONRPCMessage>> handler) {
if (connected) {
return Mono.error(new IllegalStateException("Already connected"));
}
connected = true;
return inbound.asFlux()
.flatMap(message -> Mono.just(message).transform(handler))
.doFinally(signal -> connected = false)
.then();
}
@Override
public Mono<Void> closeGracefully() {
return Mono.defer(() -> {
connected = false;
inbound.tryEmitComplete();
// Wait for all subscribers to complete
return Mono.empty();
});
}
@Override
public <T> T unmarshalFrom(Object data, TypeReference<T> typeRef) {
return new ObjectMapper().convertValue(data, typeRef);
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2024-2024 the original author or authors.
*/
package io.modelcontextprotocol;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.JSONRPCNotification;
import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest;
import io.modelcontextprotocol.spec.McpServerTransport;
import reactor.core.publisher.Mono;
/**
* A mock implementation of the {@link McpServerTransport} interfaces.
*/
public class MockMcpServerTransport implements McpServerTransport {
private final List<McpSchema.JSONRPCMessage> sent = new ArrayList<>();
private final BiConsumer<MockMcpServerTransport, McpSchema.JSONRPCMessage> interceptor;
public MockMcpServerTransport() {
this((t, msg) -> {
});
}
public MockMcpServerTransport(BiConsumer<MockMcpServerTransport, McpSchema.JSONRPCMessage> interceptor) {
this.interceptor = interceptor;
}
@Override
public Mono<Void> sendMessage(McpSchema.JSONRPCMessage message) {
sent.add(message);
interceptor.accept(this, message);
return Mono.empty();
}
public McpSchema.JSONRPCRequest getLastSentMessageAsRequest() {
return (JSONRPCRequest) getLastSentMessage();
}
public McpSchema.JSONRPCNotification getLastSentMessageAsNotification() {
return (JSONRPCNotification) getLastSentMessage();
}
public McpSchema.JSONRPCMessage getLastSentMessage() {
return !sent.isEmpty() ? sent.get(sent.size() - 1) : null;
}
@Override
public Mono<Void> closeGracefully() {
return Mono.empty();
}
@Override
public <T> T unmarshalFrom(Object data, TypeReference<T> typeRef) {
return new ObjectMapper().convertValue(data, typeRef);
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright 2025 - 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.modelcontextprotocol;
import java.util.Map;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpServerSession;
import io.modelcontextprotocol.spec.McpServerSession.Factory;
import io.modelcontextprotocol.spec.McpServerTransportProvider;
import reactor.core.publisher.Mono;
/**
* @author Christian Tzolov
*/
public class MockMcpServerTransportProvider implements McpServerTransportProvider {
private McpServerSession session;
private final MockMcpServerTransport transport;
public MockMcpServerTransportProvider(MockMcpServerTransport transport) {
this.transport = transport;
}
public MockMcpServerTransport getTransport() {
return transport;
}
@Override
public void setSessionFactory(Factory sessionFactory) {
session = sessionFactory.create(transport);
}
@Override
public Mono<Void> notifyClients(String method, Map<String, Object> params) {
return session.sendNotification(method, params);
}
@Override
public Mono<Void> closeGracefully() {
return session.closeGracefully();
}
public void simulateIncomingMessage(McpSchema.JSONRPCMessage message) {
session.handle(message).subscribe();
}
}

View file

@ -12,7 +12,7 @@ import java.util.function.Function;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.MockMcpTransport;
import io.modelcontextprotocol.MockMcpClientTransport;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities;
@ -34,16 +34,16 @@ class McpAsyncClientResponseHandlerTests {
.resources(true, true) // Enable both resources and resource templates
.build();
private static MockMcpTransport initializationEnabledTransport() {
private static MockMcpClientTransport initializationEnabledTransport() {
return initializationEnabledTransport(SERVER_CAPABILITIES, SERVER_INFO);
}
private static MockMcpTransport initializationEnabledTransport(McpSchema.ServerCapabilities mockServerCapabilities,
McpSchema.Implementation mockServerInfo) {
private static MockMcpClientTransport initializationEnabledTransport(
McpSchema.ServerCapabilities mockServerCapabilities, McpSchema.Implementation mockServerInfo) {
McpSchema.InitializeResult mockInitResult = new McpSchema.InitializeResult(McpSchema.LATEST_PROTOCOL_VERSION,
mockServerCapabilities, mockServerInfo, "Test instructions");
return new MockMcpTransport((t, message) -> {
return new MockMcpClientTransport((t, message) -> {
if (message instanceof McpSchema.JSONRPCRequest r && METHOD_INITIALIZE.equals(r.method())) {
McpSchema.JSONRPCResponse initResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION,
r.id(), mockInitResult, null);
@ -59,7 +59,7 @@ class McpAsyncClientResponseHandlerTests {
.tools(false)
.resources(true, true) // Enable both resources and resource templates
.build();
MockMcpTransport transport = initializationEnabledTransport(serverCapabilities, serverInfo);
MockMcpClientTransport transport = initializationEnabledTransport(serverCapabilities, serverInfo);
McpAsyncClient asyncMcpClient = McpClient.async(transport).build();
// Verify client is not initialized initially
@ -91,7 +91,7 @@ class McpAsyncClientResponseHandlerTests {
@Test
void testToolsChangeNotificationHandling() throws JsonProcessingException {
MockMcpTransport transport = initializationEnabledTransport();
MockMcpClientTransport transport = initializationEnabledTransport();
// Create a list to store received tools for verification
List<McpSchema.Tool> receivedTools = new ArrayList<>();
@ -134,7 +134,7 @@ class McpAsyncClientResponseHandlerTests {
@Test
void testRootsListRequestHandling() {
MockMcpTransport transport = initializationEnabledTransport();
MockMcpClientTransport transport = initializationEnabledTransport();
McpAsyncClient asyncMcpClient = McpClient.async(transport)
.roots(new Root("file:///test/path", "test-root"))
@ -162,7 +162,7 @@ class McpAsyncClientResponseHandlerTests {
@Test
void testResourcesChangeNotificationHandling() {
MockMcpTransport transport = initializationEnabledTransport();
MockMcpClientTransport transport = initializationEnabledTransport();
// Create a list to store received resources for verification
List<McpSchema.Resource> receivedResources = new ArrayList<>();
@ -208,7 +208,7 @@ class McpAsyncClientResponseHandlerTests {
@Test
void testPromptsChangeNotificationHandling() {
MockMcpTransport transport = initializationEnabledTransport();
MockMcpClientTransport transport = initializationEnabledTransport();
// Create a list to store received prompts for verification
List<McpSchema.Prompt> receivedPrompts = new ArrayList<>();
@ -252,7 +252,7 @@ class McpAsyncClientResponseHandlerTests {
@Test
void testSamplingCreateMessageRequestHandling() {
MockMcpTransport transport = initializationEnabledTransport();
MockMcpClientTransport transport = initializationEnabledTransport();
// Create a test sampling handler that echoes back the input
Function<McpSchema.CreateMessageRequest, Mono<McpSchema.CreateMessageResult>> samplingHandler = request -> {
@ -306,7 +306,7 @@ class McpAsyncClientResponseHandlerTests {
@Test
void testSamplingCreateMessageRequestHandlingWithoutCapability() {
MockMcpTransport transport = initializationEnabledTransport();
MockMcpClientTransport transport = initializationEnabledTransport();
// Create client without sampling capability
McpAsyncClient asyncMcpClient = McpClient.async(transport)
@ -340,7 +340,7 @@ class McpAsyncClientResponseHandlerTests {
@Test
void testSamplingCreateMessageRequestHandlingWithNullHandler() {
MockMcpTransport transport = new MockMcpTransport();
MockMcpClientTransport transport = new MockMcpClientTransport();
// Create client with sampling capability but null handler
assertThatThrownBy(

View file

@ -7,7 +7,7 @@ package io.modelcontextprotocol.client;
import java.time.Duration;
import java.util.List;
import io.modelcontextprotocol.MockMcpTransport;
import io.modelcontextprotocol.MockMcpClientTransport;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.InitializeResult;
@ -28,7 +28,7 @@ class McpClientProtocolVersionTests {
@Test
void shouldUseLatestVersionByDefault() {
MockMcpTransport transport = new MockMcpTransport();
MockMcpClientTransport transport = new MockMcpClientTransport();
McpAsyncClient client = McpClient.async(transport)
.clientInfo(CLIENT_INFO)
.requestTimeout(REQUEST_TIMEOUT)
@ -61,7 +61,7 @@ class McpClientProtocolVersionTests {
@Test
void shouldNegotiateSpecificVersion() {
String oldVersion = "0.1.0";
MockMcpTransport transport = new MockMcpTransport();
MockMcpClientTransport transport = new MockMcpClientTransport();
McpAsyncClient client = McpClient.async(transport)
.clientInfo(CLIENT_INFO)
.requestTimeout(REQUEST_TIMEOUT)
@ -94,7 +94,7 @@ class McpClientProtocolVersionTests {
@Test
void shouldFailForUnsupportedVersion() {
String unsupportedVersion = "999.999.999";
MockMcpTransport transport = new MockMcpTransport();
MockMcpClientTransport transport = new MockMcpClientTransport();
McpAsyncClient client = McpClient.async(transport)
.clientInfo(CLIENT_INFO)
.requestTimeout(REQUEST_TIMEOUT)
@ -124,7 +124,7 @@ class McpClientProtocolVersionTests {
String middleVersion = "0.2.0";
String latestVersion = McpSchema.LATEST_PROTOCOL_VERSION;
MockMcpTransport transport = new MockMcpTransport();
MockMcpClientTransport transport = new MockMcpClientTransport();
McpAsyncClient client = McpClient.async(transport)
.clientInfo(CLIENT_INFO)
.requestTimeout(REQUEST_TIMEOUT)

View file

@ -1,466 +0,0 @@
/*
* Copyright 2024-2024 the original author or authors.
*/
package io.modelcontextprotocol.server;
import java.time.Duration;
import java.util.List;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult;
import io.modelcontextprotocol.spec.McpSchema.Resource;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
import io.modelcontextprotocol.spec.McpSchema.Tool;
import io.modelcontextprotocol.spec.McpTransport;
import io.modelcontextprotocol.spec.ServerMcpTransport;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Test suite for the {@link McpAsyncServer} that can be used with different
* {@link McpTransport} implementations.
*
* @author Christian Tzolov
*/
// KEEP IN SYNC with the class in mcp-test module
@Deprecated
public abstract class AbstractMcpAsyncServerDeprecatedTests {
private static final String TEST_TOOL_NAME = "test-tool";
private static final String TEST_RESOURCE_URI = "test://resource";
private static final String TEST_PROMPT_NAME = "test-prompt";
abstract protected ServerMcpTransport createMcpTransport();
protected void onStart() {
}
protected void onClose() {
}
@BeforeEach
void setUp() {
}
@AfterEach
void tearDown() {
onClose();
}
// ---------------------------------------
// Server Lifecycle Tests
// ---------------------------------------
@Test
void testConstructorWithInvalidArguments() {
assertThatThrownBy(() -> McpServer.async((ServerMcpTransport) null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Transport must not be null");
assertThatThrownBy(() -> McpServer.async(createMcpTransport()).serverInfo((McpSchema.Implementation) null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Server info must not be null");
}
@Test
void testGracefulShutdown() {
var mcpAsyncServer = McpServer.async(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
StepVerifier.create(mcpAsyncServer.closeGracefully()).verifyComplete();
}
@Test
void testImmediateClose() {
var mcpAsyncServer = McpServer.async(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThatCode(() -> mcpAsyncServer.close()).doesNotThrowAnyException();
}
// ---------------------------------------
// Tools Tests
// ---------------------------------------
String emptyJsonSchema = """
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {}
}
""";
@Test
void testAddTool() {
Tool newTool = new McpSchema.Tool("new-tool", "New test tool", emptyJsonSchema);
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
.build();
StepVerifier.create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolRegistration(newTool,
args -> Mono.just(new CallToolResult(List.of(), false)))))
.verifyComplete();
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
}
@Test
void testAddDuplicateTool() {
Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
.tool(duplicateTool, args -> Mono.just(new CallToolResult(List.of(), false)))
.build();
StepVerifier.create(mcpAsyncServer.addTool(new McpServerFeatures.AsyncToolRegistration(duplicateTool,
args -> Mono.just(new CallToolResult(List.of(), false)))))
.verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class)
.hasMessage("Tool with name '" + TEST_TOOL_NAME + "' already exists");
});
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
}
@Test
void testRemoveTool() {
Tool too = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
.tool(too, args -> Mono.just(new CallToolResult(List.of(), false)))
.build();
StepVerifier.create(mcpAsyncServer.removeTool(TEST_TOOL_NAME)).verifyComplete();
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
}
@Test
void testRemoveNonexistentTool() {
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
.build();
StepVerifier.create(mcpAsyncServer.removeTool("nonexistent-tool")).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class).hasMessage("Tool with name 'nonexistent-tool' not found");
});
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
}
@Test
void testNotifyToolsListChanged() {
Tool too = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
.tool(too, args -> Mono.just(new CallToolResult(List.of(), false)))
.build();
StepVerifier.create(mcpAsyncServer.notifyToolsListChanged()).verifyComplete();
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
}
// ---------------------------------------
// Resources Tests
// ---------------------------------------
@Test
void testNotifyResourcesListChanged() {
var mcpAsyncServer = McpServer.async(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
StepVerifier.create(mcpAsyncServer.notifyResourcesListChanged()).verifyComplete();
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
}
@Test
void testAddResource() {
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
null);
McpServerFeatures.AsyncResourceRegistration registration = new McpServerFeatures.AsyncResourceRegistration(
resource, req -> Mono.just(new ReadResourceResult(List.of())));
StepVerifier.create(mcpAsyncServer.addResource(registration)).verifyComplete();
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
}
@Test
void testAddResourceWithNullRegistration() {
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
StepVerifier.create(mcpAsyncServer.addResource((McpServerFeatures.AsyncResourceRegistration) null))
.verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class).hasMessage("Resource must not be null");
});
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
}
@Test
void testAddResourceWithoutCapability() {
// Create a server without resource capabilities
McpAsyncServer serverWithoutResources = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.build();
Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
null);
McpServerFeatures.AsyncResourceRegistration registration = new McpServerFeatures.AsyncResourceRegistration(
resource, req -> Mono.just(new ReadResourceResult(List.of())));
StepVerifier.create(serverWithoutResources.addResource(registration)).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class)
.hasMessage("Server must be configured with resource capabilities");
});
}
@Test
void testRemoveResourceWithoutCapability() {
// Create a server without resource capabilities
McpAsyncServer serverWithoutResources = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.build();
StepVerifier.create(serverWithoutResources.removeResource(TEST_RESOURCE_URI)).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class)
.hasMessage("Server must be configured with resource capabilities");
});
}
// ---------------------------------------
// Prompts Tests
// ---------------------------------------
@Test
void testNotifyPromptsListChanged() {
var mcpAsyncServer = McpServer.async(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
StepVerifier.create(mcpAsyncServer.notifyPromptsListChanged()).verifyComplete();
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
}
@Test
void testAddPromptWithNullRegistration() {
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().prompts(false).build())
.build();
StepVerifier.create(mcpAsyncServer.addPrompt((McpServerFeatures.AsyncPromptRegistration) null))
.verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class).hasMessage("Prompt registration must not be null");
});
}
@Test
void testAddPromptWithoutCapability() {
// Create a server without prompt capabilities
McpAsyncServer serverWithoutPrompts = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.build();
Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of());
McpServerFeatures.AsyncPromptRegistration registration = new McpServerFeatures.AsyncPromptRegistration(prompt,
req -> Mono.just(new GetPromptResult("Test prompt description", List
.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))));
StepVerifier.create(serverWithoutPrompts.addPrompt(registration)).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class)
.hasMessage("Server must be configured with prompt capabilities");
});
}
@Test
void testRemovePromptWithoutCapability() {
// Create a server without prompt capabilities
McpAsyncServer serverWithoutPrompts = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.build();
StepVerifier.create(serverWithoutPrompts.removePrompt(TEST_PROMPT_NAME)).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class)
.hasMessage("Server must be configured with prompt capabilities");
});
}
@Test
void testRemovePrompt() {
String TEST_PROMPT_NAME_TO_REMOVE = "TEST_PROMPT_NAME678";
Prompt prompt = new Prompt(TEST_PROMPT_NAME_TO_REMOVE, "Test Prompt", List.of());
McpServerFeatures.AsyncPromptRegistration registration = new McpServerFeatures.AsyncPromptRegistration(prompt,
req -> Mono.just(new GetPromptResult("Test prompt description", List
.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))));
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().prompts(true).build())
.prompts(registration)
.build();
StepVerifier.create(mcpAsyncServer.removePrompt(TEST_PROMPT_NAME_TO_REMOVE)).verifyComplete();
assertThatCode(() -> mcpAsyncServer.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException();
}
@Test
void testRemoveNonexistentPrompt() {
var mcpAsyncServer2 = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().prompts(true).build())
.build();
StepVerifier.create(mcpAsyncServer2.removePrompt("nonexistent-prompt")).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class)
.hasMessage("Prompt with name 'nonexistent-prompt' not found");
});
assertThatCode(() -> mcpAsyncServer2.closeGracefully().block(Duration.ofSeconds(10)))
.doesNotThrowAnyException();
}
// ---------------------------------------
// Roots Tests
// ---------------------------------------
@Test
void testRootsChangeConsumers() {
// Test with single consumer
var rootsReceived = new McpSchema.Root[1];
var consumerCalled = new boolean[1];
var singleConsumerServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.rootsChangeConsumers(List.of(roots -> Mono.fromRunnable(() -> {
consumerCalled[0] = true;
if (!roots.isEmpty()) {
rootsReceived[0] = roots.get(0);
}
})))
.build();
assertThat(singleConsumerServer).isNotNull();
assertThatCode(() -> singleConsumerServer.closeGracefully().block(Duration.ofSeconds(10)))
.doesNotThrowAnyException();
onClose();
// Test with multiple consumers
var consumer1Called = new boolean[1];
var consumer2Called = new boolean[1];
var rootsContent = new List[1];
var multipleConsumersServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.rootsChangeConsumers(List.of(roots -> Mono.fromRunnable(() -> {
consumer1Called[0] = true;
rootsContent[0] = roots;
}), roots -> Mono.fromRunnable(() -> consumer2Called[0] = true)))
.build();
assertThat(multipleConsumersServer).isNotNull();
assertThatCode(() -> multipleConsumersServer.closeGracefully().block(Duration.ofSeconds(10)))
.doesNotThrowAnyException();
onClose();
// Test error handling
var errorHandlingServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.rootsChangeConsumers(List.of(roots -> {
throw new RuntimeException("Test error");
}))
.build();
assertThat(errorHandlingServer).isNotNull();
assertThatCode(() -> errorHandlingServer.closeGracefully().block(Duration.ofSeconds(10)))
.doesNotThrowAnyException();
onClose();
// Test without consumers
var noConsumersServer = McpServer.async(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThat(noConsumersServer).isNotNull();
assertThatCode(() -> noConsumersServer.closeGracefully().block(Duration.ofSeconds(10)))
.doesNotThrowAnyException();
}
// ---------------------------------------
// Logging Tests
// ---------------------------------------
@Test
void testLoggingLevels() {
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().logging().build())
.build();
// Test all logging levels
for (McpSchema.LoggingLevel level : McpSchema.LoggingLevel.values()) {
var notification = McpSchema.LoggingMessageNotification.builder()
.level(level)
.logger("test-logger")
.data("Test message with level " + level)
.build();
StepVerifier.create(mcpAsyncServer.loggingNotification(notification)).verifyComplete();
}
}
@Test
void testLoggingWithoutCapability() {
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().build()) // No logging capability
.build();
var notification = McpSchema.LoggingMessageNotification.builder()
.level(McpSchema.LoggingLevel.INFO)
.logger("test-logger")
.data("Test log message")
.build();
StepVerifier.create(mcpAsyncServer.loggingNotification(notification)).verifyComplete();
}
@Test
void testLoggingWithNullNotification() {
var mcpAsyncServer = McpServer.async(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().logging().build())
.build();
StepVerifier.create(mcpAsyncServer.loggingNotification(null)).verifyError(McpError.class);
}
}

View file

@ -1,433 +0,0 @@
/*
* Copyright 2024-2024 the original author or authors.
*/
package io.modelcontextprotocol.server;
import java.util.List;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult;
import io.modelcontextprotocol.spec.McpSchema.Resource;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
import io.modelcontextprotocol.spec.McpSchema.Tool;
import io.modelcontextprotocol.spec.McpTransport;
import io.modelcontextprotocol.spec.ServerMcpTransport;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Test suite for the {@link McpSyncServer} that can be used with different
* {@link McpTransport} implementations.
*
* @author Christian Tzolov
*/
// KEEP IN SYNC with the class in mcp-test module
@Deprecated
public abstract class AbstractMcpSyncServerDeprecatedTests {
private static final String TEST_TOOL_NAME = "test-tool";
private static final String TEST_RESOURCE_URI = "test://resource";
private static final String TEST_PROMPT_NAME = "test-prompt";
abstract protected ServerMcpTransport createMcpTransport();
protected void onStart() {
}
protected void onClose() {
}
@BeforeEach
void setUp() {
// onStart();
}
@AfterEach
void tearDown() {
onClose();
}
// ---------------------------------------
// Server Lifecycle Tests
// ---------------------------------------
@Test
void testConstructorWithInvalidArguments() {
assertThatThrownBy(() -> McpServer.sync((ServerMcpTransport) null)).isInstanceOf(IllegalArgumentException.class)
.hasMessage("Transport must not be null");
assertThatThrownBy(() -> McpServer.sync(createMcpTransport()).serverInfo(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Server info must not be null");
}
@Test
void testGracefulShutdown() {
var mcpSyncServer = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
@Test
void testImmediateClose() {
var mcpSyncServer = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThatCode(() -> mcpSyncServer.close()).doesNotThrowAnyException();
}
@Test
void testGetAsyncServer() {
var mcpSyncServer = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThat(mcpSyncServer.getAsyncServer()).isNotNull();
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
// ---------------------------------------
// Tools Tests
// ---------------------------------------
String emptyJsonSchema = """
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {}
}
""";
@Test
void testAddTool() {
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
.build();
Tool newTool = new McpSchema.Tool("new-tool", "New test tool", emptyJsonSchema);
assertThatCode(() -> mcpSyncServer
.addTool(new McpServerFeatures.SyncToolRegistration(newTool, args -> new CallToolResult(List.of(), false))))
.doesNotThrowAnyException();
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
@Test
void testAddDuplicateTool() {
Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
.tool(duplicateTool, args -> new CallToolResult(List.of(), false))
.build();
assertThatThrownBy(() -> mcpSyncServer.addTool(new McpServerFeatures.SyncToolRegistration(duplicateTool,
args -> new CallToolResult(List.of(), false))))
.isInstanceOf(McpError.class)
.hasMessage("Tool with name '" + TEST_TOOL_NAME + "' already exists");
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
@Test
void testRemoveTool() {
Tool tool = new McpSchema.Tool(TEST_TOOL_NAME, "Test tool", emptyJsonSchema);
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
.tool(tool, args -> new CallToolResult(List.of(), false))
.build();
assertThatCode(() -> mcpSyncServer.removeTool(TEST_TOOL_NAME)).doesNotThrowAnyException();
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
@Test
void testRemoveNonexistentTool() {
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
.build();
assertThatThrownBy(() -> mcpSyncServer.removeTool("nonexistent-tool")).isInstanceOf(McpError.class)
.hasMessage("Tool with name 'nonexistent-tool' not found");
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
@Test
void testNotifyToolsListChanged() {
var mcpSyncServer = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThatCode(() -> mcpSyncServer.notifyToolsListChanged()).doesNotThrowAnyException();
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
// ---------------------------------------
// Resources Tests
// ---------------------------------------
@Test
void testNotifyResourcesListChanged() {
var mcpSyncServer = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThatCode(() -> mcpSyncServer.notifyResourcesListChanged()).doesNotThrowAnyException();
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
@Test
void testAddResource() {
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
null);
McpServerFeatures.SyncResourceRegistration registration = new McpServerFeatures.SyncResourceRegistration(
resource, req -> new ReadResourceResult(List.of()));
assertThatCode(() -> mcpSyncServer.addResource(registration)).doesNotThrowAnyException();
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
@Test
void testAddResourceWithNullRegistration() {
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
assertThatThrownBy(() -> mcpSyncServer.addResource((McpServerFeatures.SyncResourceRegistration) null))
.isInstanceOf(McpError.class)
.hasMessage("Resource must not be null");
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
@Test
void testAddResourceWithoutCapability() {
var serverWithoutResources = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
null);
McpServerFeatures.SyncResourceRegistration registration = new McpServerFeatures.SyncResourceRegistration(
resource, req -> new ReadResourceResult(List.of()));
assertThatThrownBy(() -> serverWithoutResources.addResource(registration)).isInstanceOf(McpError.class)
.hasMessage("Server must be configured with resource capabilities");
}
@Test
void testRemoveResourceWithoutCapability() {
var serverWithoutResources = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThatThrownBy(() -> serverWithoutResources.removeResource(TEST_RESOURCE_URI)).isInstanceOf(McpError.class)
.hasMessage("Server must be configured with resource capabilities");
}
// ---------------------------------------
// Prompts Tests
// ---------------------------------------
@Test
void testNotifyPromptsListChanged() {
var mcpSyncServer = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThatCode(() -> mcpSyncServer.notifyPromptsListChanged()).doesNotThrowAnyException();
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
@Test
void testAddPromptWithNullRegistration() {
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().prompts(false).build())
.build();
assertThatThrownBy(() -> mcpSyncServer.addPrompt((McpServerFeatures.SyncPromptRegistration) null))
.isInstanceOf(McpError.class)
.hasMessage("Prompt registration must not be null");
}
@Test
void testAddPromptWithoutCapability() {
var serverWithoutPrompts = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of());
McpServerFeatures.SyncPromptRegistration registration = new McpServerFeatures.SyncPromptRegistration(prompt,
req -> new GetPromptResult("Test prompt description", List
.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content")))));
assertThatThrownBy(() -> serverWithoutPrompts.addPrompt(registration)).isInstanceOf(McpError.class)
.hasMessage("Server must be configured with prompt capabilities");
}
@Test
void testRemovePromptWithoutCapability() {
var serverWithoutPrompts = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThatThrownBy(() -> serverWithoutPrompts.removePrompt(TEST_PROMPT_NAME)).isInstanceOf(McpError.class)
.hasMessage("Server must be configured with prompt capabilities");
}
@Test
void testRemovePrompt() {
Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of());
McpServerFeatures.SyncPromptRegistration registration = new McpServerFeatures.SyncPromptRegistration(prompt,
req -> new GetPromptResult("Test prompt description", List
.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content")))));
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().prompts(true).build())
.prompts(registration)
.build();
assertThatCode(() -> mcpSyncServer.removePrompt(TEST_PROMPT_NAME)).doesNotThrowAnyException();
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
@Test
void testRemoveNonexistentPrompt() {
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().prompts(true).build())
.build();
assertThatThrownBy(() -> mcpSyncServer.removePrompt("nonexistent-prompt")).isInstanceOf(McpError.class)
.hasMessage("Prompt with name 'nonexistent-prompt' not found");
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
}
// ---------------------------------------
// Roots Tests
// ---------------------------------------
@Test
void testRootsChangeConsumers() {
// Test with single consumer
var rootsReceived = new McpSchema.Root[1];
var consumerCalled = new boolean[1];
var singleConsumerServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.rootsChangeConsumers(List.of(roots -> {
consumerCalled[0] = true;
if (!roots.isEmpty()) {
rootsReceived[0] = roots.get(0);
}
}))
.build();
assertThat(singleConsumerServer).isNotNull();
assertThatCode(() -> singleConsumerServer.closeGracefully()).doesNotThrowAnyException();
onClose();
// Test with multiple consumers
var consumer1Called = new boolean[1];
var consumer2Called = new boolean[1];
var rootsContent = new List[1];
var multipleConsumersServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.rootsChangeConsumers(List.of(roots -> {
consumer1Called[0] = true;
rootsContent[0] = roots;
}, roots -> consumer2Called[0] = true))
.build();
assertThat(multipleConsumersServer).isNotNull();
assertThatCode(() -> multipleConsumersServer.closeGracefully()).doesNotThrowAnyException();
onClose();
// Test error handling
var errorHandlingServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.rootsChangeConsumers(List.of(roots -> {
throw new RuntimeException("Test error");
}))
.build();
assertThat(errorHandlingServer).isNotNull();
assertThatCode(() -> errorHandlingServer.closeGracefully()).doesNotThrowAnyException();
onClose();
// Test without consumers
var noConsumersServer = McpServer.sync(createMcpTransport()).serverInfo("test-server", "1.0.0").build();
assertThat(noConsumersServer).isNotNull();
assertThatCode(() -> noConsumersServer.closeGracefully()).doesNotThrowAnyException();
}
// ---------------------------------------
// Logging Tests
// ---------------------------------------
@Test
void testLoggingLevels() {
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().logging().build())
.build();
// Test all logging levels
for (McpSchema.LoggingLevel level : McpSchema.LoggingLevel.values()) {
var notification = McpSchema.LoggingMessageNotification.builder()
.level(level)
.logger("test-logger")
.data("Test message with level " + level)
.build();
assertThatCode(() -> mcpSyncServer.loggingNotification(notification)).doesNotThrowAnyException();
}
}
@Test
void testLoggingWithoutCapability() {
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().build()) // No logging capability
.build();
var notification = McpSchema.LoggingMessageNotification.builder()
.level(McpSchema.LoggingLevel.INFO)
.logger("test-logger")
.data("Test log message")
.build();
assertThatCode(() -> mcpSyncServer.loggingNotification(notification)).doesNotThrowAnyException();
}
@Test
void testLoggingWithNullNotification() {
var mcpSyncServer = McpServer.sync(createMcpTransport())
.serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().logging().build())
.build();
assertThatThrownBy(() -> mcpSyncServer.loggingNotification(null)).isInstanceOf(McpError.class);
}
}

View file

@ -7,7 +7,8 @@ package io.modelcontextprotocol.server;
import java.util.List;
import java.util.UUID;
import io.modelcontextprotocol.MockMcpTransport;
import io.modelcontextprotocol.MockMcpServerTransport;
import io.modelcontextprotocol.MockMcpServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import org.junit.jupiter.api.Test;
@ -29,14 +30,16 @@ class McpServerProtocolVersionTests {
@Test
void shouldUseLatestVersionByDefault() {
MockMcpTransport transport = new MockMcpTransport();
McpAsyncServer server = McpServer.async(transport).serverInfo(SERVER_INFO).build();
MockMcpServerTransport serverTransport = new MockMcpServerTransport();
var transportProvider = new MockMcpServerTransportProvider(serverTransport);
McpAsyncServer server = McpServer.async(transportProvider).serverInfo(SERVER_INFO).build();
String requestId = UUID.randomUUID().toString();
transport.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, McpSchema.LATEST_PROTOCOL_VERSION));
transportProvider
.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, McpSchema.LATEST_PROTOCOL_VERSION));
McpSchema.JSONRPCMessage response = transport.getLastSentMessage();
McpSchema.JSONRPCMessage response = serverTransport.getLastSentMessage();
assertThat(response).isInstanceOf(McpSchema.JSONRPCResponse.class);
McpSchema.JSONRPCResponse jsonResponse = (McpSchema.JSONRPCResponse) response;
assertThat(jsonResponse.id()).isEqualTo(requestId);
@ -50,16 +53,18 @@ class McpServerProtocolVersionTests {
@Test
void shouldNegotiateSpecificVersion() {
String oldVersion = "0.1.0";
MockMcpTransport transport = new MockMcpTransport();
McpAsyncServer server = McpServer.async(transport).serverInfo(SERVER_INFO).build();
MockMcpServerTransport serverTransport = new MockMcpServerTransport();
var transportProvider = new MockMcpServerTransportProvider(serverTransport);
McpAsyncServer server = McpServer.async(transportProvider).serverInfo(SERVER_INFO).build();
server.setProtocolVersions(List.of(oldVersion, McpSchema.LATEST_PROTOCOL_VERSION));
String requestId = UUID.randomUUID().toString();
transport.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, oldVersion));
transportProvider.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, oldVersion));
McpSchema.JSONRPCMessage response = transport.getLastSentMessage();
McpSchema.JSONRPCMessage response = serverTransport.getLastSentMessage();
assertThat(response).isInstanceOf(McpSchema.JSONRPCResponse.class);
McpSchema.JSONRPCResponse jsonResponse = (McpSchema.JSONRPCResponse) response;
assertThat(jsonResponse.id()).isEqualTo(requestId);
@ -73,14 +78,16 @@ class McpServerProtocolVersionTests {
@Test
void shouldSuggestLatestVersionForUnsupportedVersion() {
String unsupportedVersion = "999.999.999";
MockMcpTransport transport = new MockMcpTransport();
McpAsyncServer server = McpServer.async(transport).serverInfo(SERVER_INFO).build();
MockMcpServerTransport serverTransport = new MockMcpServerTransport();
var transportProvider = new MockMcpServerTransportProvider(serverTransport);
McpAsyncServer server = McpServer.async(transportProvider).serverInfo(SERVER_INFO).build();
String requestId = UUID.randomUUID().toString();
transport.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, unsupportedVersion));
transportProvider.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, unsupportedVersion));
McpSchema.JSONRPCMessage response = transport.getLastSentMessage();
McpSchema.JSONRPCMessage response = serverTransport.getLastSentMessage();
assertThat(response).isInstanceOf(McpSchema.JSONRPCResponse.class);
McpSchema.JSONRPCResponse jsonResponse = (McpSchema.JSONRPCResponse) response;
assertThat(jsonResponse.id()).isEqualTo(requestId);
@ -97,15 +104,17 @@ class McpServerProtocolVersionTests {
String middleVersion = "0.2.0";
String latestVersion = McpSchema.LATEST_PROTOCOL_VERSION;
MockMcpTransport transport = new MockMcpTransport();
McpAsyncServer server = McpServer.async(transport).serverInfo(SERVER_INFO).build();
MockMcpServerTransport serverTransport = new MockMcpServerTransport();
var transportProvider = new MockMcpServerTransportProvider(serverTransport);
McpAsyncServer server = McpServer.async(transportProvider).serverInfo(SERVER_INFO).build();
server.setProtocolVersions(List.of(oldVersion, middleVersion, latestVersion));
String requestId = UUID.randomUUID().toString();
transport.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, latestVersion));
transportProvider.simulateIncomingMessage(jsonRpcInitializeRequest(requestId, latestVersion));
McpSchema.JSONRPCMessage response = transport.getLastSentMessage();
McpSchema.JSONRPCMessage response = serverTransport.getLastSentMessage();
assertThat(response).isInstanceOf(McpSchema.JSONRPCResponse.class);
McpSchema.JSONRPCResponse jsonResponse = (McpSchema.JSONRPCResponse) response;
assertThat(jsonResponse.id()).isEqualTo(requestId);

View file

@ -1,26 +0,0 @@
/*
* Copyright 2024-2024 the original author or authors.
*/
package io.modelcontextprotocol.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.transport.HttpServletSseServerTransport;
import io.modelcontextprotocol.spec.ServerMcpTransport;
import org.junit.jupiter.api.Timeout;
/**
* Tests for {@link McpAsyncServer} using {@link HttpServletSseServerTransport}.
*
* @author Christian Tzolov
*/
@Deprecated
@Timeout(15) // Giving extra time beyond the client timeout
class ServletSseMcpAsyncServerDeprecatedTests extends AbstractMcpAsyncServerDeprecatedTests {
@Override
protected ServerMcpTransport createMcpTransport() {
return new HttpServletSseServerTransport(new ObjectMapper(), "/mcp/message");
}
}

View file

@ -1,26 +0,0 @@
/*
* Copyright 2024-2024 the original author or authors.
*/
package io.modelcontextprotocol.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.transport.HttpServletSseServerTransport;
import io.modelcontextprotocol.spec.ServerMcpTransport;
import org.junit.jupiter.api.Timeout;
/**
* Tests for {@link McpSyncServer} using {@link HttpServletSseServerTransport}.
*
* @author Christian Tzolov
*/
@Deprecated
@Timeout(15) // Giving extra time beyond the client timeout
class ServletSseMcpSyncServerDeprecatedTests extends AbstractMcpSyncServerDeprecatedTests {
@Override
protected ServerMcpTransport createMcpTransport() {
return new HttpServletSseServerTransport(new ObjectMapper(), "/mcp/message");
}
}

View file

@ -1,25 +0,0 @@
/*
* Copyright 2024-2024 the original author or authors.
*/
package io.modelcontextprotocol.server;
import io.modelcontextprotocol.server.transport.StdioServerTransport;
import io.modelcontextprotocol.spec.ServerMcpTransport;
import org.junit.jupiter.api.Timeout;
/**
* Tests for {@link McpAsyncServer} using {@link StdioServerTransport}.
*
* @author Christian Tzolov
*/
@Deprecated
@Timeout(15) // Giving extra time beyond the client timeout
class StdioMcpAsyncServerDeprecatedTests extends AbstractMcpAsyncServerDeprecatedTests {
@Override
protected ServerMcpTransport createMcpTransport() {
return new StdioServerTransport();
}
}

View file

@ -4,7 +4,6 @@
package io.modelcontextprotocol.server;
import io.modelcontextprotocol.server.transport.StdioServerTransport;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpServerTransportProvider;
import org.junit.jupiter.api.Timeout;

View file

@ -1,25 +0,0 @@
/*
* Copyright 2024-2024 the original author or authors.
*/
package io.modelcontextprotocol.server;
import io.modelcontextprotocol.server.transport.StdioServerTransport;
import io.modelcontextprotocol.spec.ServerMcpTransport;
import org.junit.jupiter.api.Timeout;
/**
* Tests for {@link McpSyncServer} using {@link StdioServerTransport}.
*
* @author Christian Tzolov
*/
@Deprecated
@Timeout(15) // Giving extra time beyond the client timeout
class StdioMcpSyncServerDeprecatedTests extends AbstractMcpSyncServerDeprecatedTests {
@Override
protected ServerMcpTransport createMcpTransport() {
return new StdioServerTransport();
}
}

View file

@ -1,328 +0,0 @@
/*
* Copyright 2024 - 2024 the original author or authors.
*/
package io.modelcontextprotocol.server.transport;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.spec.McpError;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.ClientCapabilities;
import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest;
import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult;
import io.modelcontextprotocol.spec.McpSchema.InitializeResult;
import io.modelcontextprotocol.spec.McpSchema.Role;
import io.modelcontextprotocol.spec.McpSchema.Root;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
import io.modelcontextprotocol.spec.McpSchema.Tool;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.startup.Tomcat;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;
import org.springframework.web.client.RestClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
public class HttpServletSseServerTransportIntegrationTests {
private static final int PORT = 8184;
private static final String MESSAGE_ENDPOINT = "/mcp/message";
private HttpServletSseServerTransport mcpServerTransport;
McpClient.SyncSpec clientBuilder;
private Tomcat tomcat;
@BeforeEach
public void before() {
tomcat = new Tomcat();
tomcat.setPort(PORT);
String baseDir = System.getProperty("java.io.tmpdir");
tomcat.setBaseDir(baseDir);
Context context = tomcat.addContext("", baseDir);
// Create and configure the transport
mcpServerTransport = new HttpServletSseServerTransport(new ObjectMapper(), MESSAGE_ENDPOINT);
// Add transport servlet to Tomcat
org.apache.catalina.Wrapper wrapper = context.createWrapper();
wrapper.setName("mcpServlet");
wrapper.setServlet(mcpServerTransport);
wrapper.setLoadOnStartup(1);
wrapper.setAsyncSupported(true);
context.addChild(wrapper);
context.addServletMappingDecoded("/*", "mcpServlet");
try {
var connector = tomcat.getConnector();
connector.setAsyncTimeout(3000);
tomcat.start();
assertThat(tomcat.getServer().getState() == LifecycleState.STARTED);
}
catch (Exception e) {
throw new RuntimeException("Failed to start Tomcat", e);
}
this.clientBuilder = McpClient.sync(new HttpClientSseClientTransport("http://localhost:" + PORT));
}
@AfterEach
public void after() {
if (mcpServerTransport != null) {
mcpServerTransport.closeGracefully().block();
}
if (tomcat != null) {
try {
tomcat.stop();
tomcat.destroy();
}
catch (LifecycleException e) {
throw new RuntimeException("Failed to stop Tomcat", e);
}
}
}
@Test
void testCreateMessageWithoutInitialization() {
var mcpAsyncServer = McpServer.async(mcpServerTransport).serverInfo("test-server", "1.0.0").build();
var messages = List
.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message")));
var modelPrefs = new McpSchema.ModelPreferences(List.of(), 1.0, 1.0, 1.0);
var request = new McpSchema.CreateMessageRequest(messages, modelPrefs, null,
McpSchema.CreateMessageRequest.ContextInclusionStrategy.NONE, null, 100, List.of(), Map.of());
StepVerifier.create(mcpAsyncServer.createMessage(request)).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class)
.hasMessage("Client must be initialized. Call the initialize method first!");
});
}
@Test
void testCreateMessageWithoutSamplingCapabilities() {
var mcpAsyncServer = McpServer.async(mcpServerTransport).serverInfo("test-server", "1.0.0").build();
var client = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0")).build();
InitializeResult initResult = client.initialize();
assertThat(initResult).isNotNull();
var messages = List
.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message")));
var modelPrefs = new McpSchema.ModelPreferences(List.of(), 1.0, 1.0, 1.0);
var request = new McpSchema.CreateMessageRequest(messages, modelPrefs, null,
McpSchema.CreateMessageRequest.ContextInclusionStrategy.NONE, null, 100, List.of(), Map.of());
StepVerifier.create(mcpAsyncServer.createMessage(request)).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(McpError.class)
.hasMessage("Client must be configured with sampling capabilities");
});
}
@Test
void testCreateMessageSuccess() {
var mcpAsyncServer = McpServer.async(mcpServerTransport).serverInfo("test-server", "1.0.0").build();
Function<CreateMessageRequest, CreateMessageResult> samplingHandler = request -> {
assertThat(request.messages()).hasSize(1);
assertThat(request.messages().get(0).content()).isInstanceOf(McpSchema.TextContent.class);
return new CreateMessageResult(Role.USER, new McpSchema.TextContent("Test message"), "MockModelName",
CreateMessageResult.StopReason.STOP_SEQUENCE);
};
var client = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
.capabilities(ClientCapabilities.builder().sampling().build())
.sampling(samplingHandler)
.build();
InitializeResult initResult = client.initialize();
assertThat(initResult).isNotNull();
var messages = List
.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message")));
var modelPrefs = new McpSchema.ModelPreferences(List.of(), 1.0, 1.0, 1.0);
var request = new McpSchema.CreateMessageRequest(messages, modelPrefs, null,
McpSchema.CreateMessageRequest.ContextInclusionStrategy.NONE, null, 100, List.of(), Map.of());
StepVerifier.create(mcpAsyncServer.createMessage(request)).consumeNextWith(result -> {
assertThat(result).isNotNull();
assertThat(result.role()).isEqualTo(Role.USER);
assertThat(result.content()).isInstanceOf(McpSchema.TextContent.class);
assertThat(((McpSchema.TextContent) result.content()).text()).isEqualTo("Test message");
assertThat(result.model()).isEqualTo("MockModelName");
assertThat(result.stopReason()).isEqualTo(CreateMessageResult.StopReason.STOP_SEQUENCE);
}).verifyComplete();
}
@Test
void testRootsSuccess() {
List<Root> roots = List.of(new Root("uri1://", "root1"), new Root("uri2://", "root2"));
AtomicReference<List<Root>> rootsRef = new AtomicReference<>();
var mcpServer = McpServer.sync(mcpServerTransport)
.rootsChangeConsumer(rootsUpdate -> rootsRef.set(rootsUpdate))
.build();
var mcpClient = clientBuilder.capabilities(ClientCapabilities.builder().roots(true).build())
.roots(roots)
.build();
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
assertThat(rootsRef.get()).isNull();
assertThat(mcpServer.listRoots().roots()).containsAll(roots);
mcpClient.rootsListChangedNotification();
await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
assertThat(rootsRef.get()).containsAll(roots);
});
mcpClient.close();
mcpServer.close();
}
String emptyJsonSchema = """
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {}
}
""";
@Test
void testToolCallSuccess() {
var callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
McpServerFeatures.SyncToolRegistration tool1 = new McpServerFeatures.SyncToolRegistration(
new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema), request -> {
String response = RestClient.create()
.get()
.uri("https://github.com/modelcontextprotocol/specification/blob/main/README.md")
.retrieve()
.body(String.class);
assertThat(response).isNotBlank();
return callResponse;
});
var mcpServer = McpServer.sync(mcpServerTransport)
.capabilities(ServerCapabilities.builder().tools(true).build())
.tools(tool1)
.build();
var mcpClient = clientBuilder.build();
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
assertThat(mcpClient.listTools().tools()).contains(tool1.tool());
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
assertThat(response).isNotNull();
assertThat(response).isEqualTo(callResponse);
mcpClient.close();
mcpServer.close();
}
@Test
void testToolListChangeHandlingSuccess() {
var callResponse = new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("CALL RESPONSE")), null);
McpServerFeatures.SyncToolRegistration tool1 = new McpServerFeatures.SyncToolRegistration(
new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema), request -> {
String response = RestClient.create()
.get()
.uri("https://github.com/modelcontextprotocol/specification/blob/main/README.md")
.retrieve()
.body(String.class);
assertThat(response).isNotBlank();
return callResponse;
});
var mcpServer = McpServer.sync(mcpServerTransport)
.capabilities(ServerCapabilities.builder().tools(true).build())
.tools(tool1)
.build();
AtomicReference<List<Tool>> toolsRef = new AtomicReference<>();
var mcpClient = clientBuilder.toolsChangeConsumer(toolsUpdate -> {
String response = RestClient.create()
.get()
.uri("https://github.com/modelcontextprotocol/specification/blob/main/README.md")
.retrieve()
.body(String.class);
assertThat(response).isNotBlank();
toolsRef.set(toolsUpdate);
}).build();
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
assertThat(toolsRef.get()).isNull();
assertThat(mcpClient.listTools().tools()).contains(tool1.tool());
mcpServer.notifyToolsListChanged();
await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
assertThat(toolsRef.get()).containsAll(List.of(tool1.tool()));
});
mcpServer.removeTool("tool1");
await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
assertThat(toolsRef.get()).isEmpty();
});
McpServerFeatures.SyncToolRegistration tool2 = new McpServerFeatures.SyncToolRegistration(
new McpSchema.Tool("tool2", "tool2 description", emptyJsonSchema), request -> callResponse);
mcpServer.addTool(tool2);
await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
assertThat(toolsRef.get()).containsAll(List.of(tool2.tool()));
});
mcpClient.close();
mcpServer.close();
}
@Test
void testInitialize() {
var mcpServer = McpServer.sync(mcpServerTransport).build();
var mcpClient = clientBuilder.build();
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
mcpClient.close();
mcpServer.close();
}
}

View file

@ -1,157 +0,0 @@
/*
* Copyright 2024-2024 the original author or authors.
*/
package io.modelcontextprotocol.server.transport;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link StdioServerTransport}.
*
* @author Christian Tzolov
*/
class StdioServerTransportTests {
private final InputStream originalIn = System.in;
private final PrintStream originalOut = System.out;
private final PrintStream originalErr = System.err;
private ByteArrayOutputStream testOut;
private ByteArrayOutputStream testErr;
private PrintStream testOutPrintStream;
private StdioServerTransport transport;
private ObjectMapper objectMapper;
@BeforeEach
void setUp() {
testOut = new ByteArrayOutputStream();
testErr = new ByteArrayOutputStream();
testOutPrintStream = new PrintStream(testOut, true);
System.setOut(testOutPrintStream);
System.setErr(new PrintStream(testErr));
objectMapper = new ObjectMapper();
}
@AfterEach
void tearDown() {
if (transport != null) {
transport.closeGracefully().block();
}
if (testOutPrintStream != null) {
testOutPrintStream.close();
}
System.setIn(originalIn);
System.setOut(originalOut);
System.setErr(originalErr);
}
@Test
void shouldHandleIncomingMessages() throws Exception {
// Prepare test input
String jsonMessage = "{\"jsonrpc\":\"2.0\",\"method\":\"test\",\"params\":{},\"id\":1}";
// Create transport with test streams
transport = new StdioServerTransport(objectMapper);
// Parse expected message
McpSchema.JSONRPCRequest expected = objectMapper.readValue(jsonMessage, McpSchema.JSONRPCRequest.class);
// Connect transport with message handler and verify message
StepVerifier.create(transport.connect(message -> message.doOnNext(msg -> {
McpSchema.JSONRPCRequest received = (McpSchema.JSONRPCRequest) msg;
assertThat(received.id()).isEqualTo(expected.id());
assertThat(received.method()).isEqualTo(expected.method());
}))).verifyComplete();
}
@Test
@Disabled
void shouldHandleOutgoingMessages() throws Exception {
// Create transport with test streams
transport = new StdioServerTransport(objectMapper);
// transport = new StdioServerTransport(objectMapper, new BlockingInputStream(),
// testOutPrintStream);
// Create test messages
JSONRPCRequest initMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "init", "init-id",
Map.of("init", "true"));
JSONRPCRequest testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test", "test-id",
Map.of("key", "value"));
// Connect transport, send messages, and verify output in a reactive chain
StepVerifier.create(transport.connect(message -> message)
.then(transport.sendMessage(initMessage))
// .then(Mono.fromRunnable(() -> testOut.reset())) // Clear buffer after init
// message
.then(transport.sendMessage(testMessage))
.then(Mono.fromCallable(() -> {
String output = testOut.toString(StandardCharsets.UTF_8);
assertThat(output).contains("\"jsonrpc\":\"2.0\"");
assertThat(output).contains("\"method\":\"test\"");
assertThat(output).contains("\"id\":\"test-id\"");
return null;
}))).verifyComplete();
}
@Test
void shouldWaitForProcessorsBeforeSendingMessage() {
// Create transport with test streams
transport = new StdioServerTransport(objectMapper);
// Create test message
JSONRPCRequest testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test", "test-id",
Map.of("key", "value"));
// Try to send message before connecting (before processors are ready)
StepVerifier.create(transport.sendMessage(testMessage)).verifyTimeout(java.time.Duration.ofMillis(100));
// Connect transport and verify message can be sent
StepVerifier.create(transport.connect(message -> message).then(transport.sendMessage(testMessage)))
.verifyComplete();
}
@Test
void shouldCloseGracefully() {
// Create transport with test streams
transport = new StdioServerTransport(objectMapper);
// Create test message
JSONRPCRequest initMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "init", "init-id",
Map.of("init", "true"));
// Connect transport, send message, and close gracefully in a reactive chain
StepVerifier
.create(transport.connect(message -> message)
.then(transport.sendMessage(initMessage))
.then(transport.closeGracefully()))
.verifyComplete();
// Verify error log is empty
assertThat(testErr.toString()).doesNotContain("Error");
}
}

View file

@ -8,7 +8,7 @@ import java.time.Duration;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import io.modelcontextprotocol.MockMcpTransport;
import io.modelcontextprotocol.MockMcpClientTransport;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -41,11 +41,11 @@ class McpClientSessionTests {
private McpClientSession session;
private MockMcpTransport transport;
private MockMcpClientTransport transport;
@BeforeEach
void setUp() {
transport = new MockMcpTransport();
transport = new MockMcpClientTransport();
session = new McpClientSession(TIMEOUT, transport, Map.of(),
Map.of(TEST_NOTIFICATION, params -> Mono.fromRunnable(() -> logger.info("Status update: " + params))));
}
@ -139,7 +139,7 @@ class McpClientSessionTests {
String echoMessage = "Hello MCP!";
Map<String, McpClientSession.RequestHandler<?>> requestHandlers = Map.of(ECHO_METHOD,
params -> Mono.just(params));
transport = new MockMcpTransport();
transport = new MockMcpClientTransport();
session = new McpClientSession(TIMEOUT, transport, requestHandlers, Map.of());
// Simulate incoming request
@ -159,7 +159,7 @@ class McpClientSessionTests {
void testNotificationHandling() {
Sinks.One<Object> receivedParams = Sinks.one();
transport = new MockMcpTransport();
transport = new MockMcpClientTransport();
session = new McpClientSession(TIMEOUT, transport, Map.of(),
Map.of(TEST_NOTIFICATION, params -> Mono.fromRunnable(() -> receivedParams.tryEmitValue(params))));