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:
parent
7827cdc113
commit
25f3bad68d
16 changed files with 203 additions and 7660 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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))));
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue