feat: Add roots change notification support to MCP server
- Implement roots change notification handler in McpAsyncServer - Add rootsChangeConsumer builder methods to support single and multiple consumers - Rename info() builder method to serverInfo() for clarity - Add comprehensive tests for roots change notification functionality - Add default server info implementation in Builder Resolves #39
This commit is contained in:
parent
0b0d5086ce
commit
0fd93a9f82
3 changed files with 266 additions and 58 deletions
|
|
@ -43,7 +43,7 @@ public class DemoServer {
|
|||
SseServerTransport transport = new SseServerTransport(new ObjectMapper(), "/mcp/message");
|
||||
|
||||
var mcpServer = McpServer.using(transport)
|
||||
.info("Weather Forecast", "1.0.0")
|
||||
.serverInfo("Weather Forecast", "1.0.0")
|
||||
.tool(new McpSchema.Tool("weather", "Weather forecast tool by location", Map.of("city", "String")),
|
||||
(arguments) -> {
|
||||
String city = (String) arguments.get("city");
|
||||
|
|
|
|||
|
|
@ -84,21 +84,21 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
assertThatThrownBy(() -> McpServer.using(null)).isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("Transport must not be null");
|
||||
|
||||
assertThatThrownBy(() -> McpServer.using(createMcpTransport()).info((McpSchema.Implementation) null))
|
||||
assertThatThrownBy(() -> McpServer.using(createMcpTransport()).serverInfo((McpSchema.Implementation) null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("Server info must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGracefulShutdown() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").async();
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").async();
|
||||
|
||||
StepVerifier.create(mcpAsyncServer.closeGracefully()).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testImmediateClose() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").async();
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").async();
|
||||
|
||||
assertThatCode(() -> mcpAsyncServer.close()).doesNotThrowAnyException();
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
void testAddTool() {
|
||||
Tool newTool = new McpSchema.Tool("new-tool", "New test tool", Map.of("input", "string"));
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.async();
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", Map.of("input", "string"));
|
||||
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.tool(duplicateTool, args -> new CallToolResult(List.of(), false))
|
||||
.async();
|
||||
|
|
@ -148,7 +148,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
Tool too = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", Map.of("input", "string"));
|
||||
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.tool(too, args -> new CallToolResult(List.of(), false))
|
||||
.async();
|
||||
|
|
@ -161,7 +161,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
@Test
|
||||
void testRemoveNonexistentTool() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.async();
|
||||
|
||||
|
|
@ -177,7 +177,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
Tool too = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", Map.of("input", "string"));
|
||||
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.tool(too, args -> new CallToolResult(List.of(), false))
|
||||
.async();
|
||||
|
|
@ -193,7 +193,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
|
||||
@Test
|
||||
void testNotifyResourcesListChanged() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").async();
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").async();
|
||||
|
||||
StepVerifier.create(mcpAsyncServer.notifyResourcesListChanged()).verifyComplete();
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
@Test
|
||||
void testAddResource() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().resources(true, false).build())
|
||||
.async();
|
||||
|
||||
|
|
@ -220,7 +220,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
@Test
|
||||
void testAddResourceWithNullRegistration() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().resources(true, false).build())
|
||||
.async();
|
||||
|
||||
|
|
@ -235,7 +235,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
void testAddResourceWithoutCapability() {
|
||||
// Create a server without resource capabilities
|
||||
McpAsyncServer serverWithoutResources = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.async();
|
||||
|
||||
Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
|
||||
|
|
@ -253,7 +253,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
void testRemoveResourceWithoutCapability() {
|
||||
// Create a server without resource capabilities
|
||||
McpAsyncServer serverWithoutResources = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.async();
|
||||
|
||||
StepVerifier.create(serverWithoutResources.removeResource(TEST_RESOURCE_URI)).verifyErrorSatisfies(error -> {
|
||||
|
|
@ -268,7 +268,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
|
||||
@Test
|
||||
void testNotifyPromptsListChanged() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").async();
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").async();
|
||||
|
||||
StepVerifier.create(mcpAsyncServer.notifyPromptsListChanged()).verifyComplete();
|
||||
|
||||
|
|
@ -278,7 +278,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
@Test
|
||||
void testAddPromptWithNullRegistration() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().prompts(false).build())
|
||||
.async();
|
||||
|
||||
|
|
@ -291,7 +291,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
void testAddPromptWithoutCapability() {
|
||||
// Create a server without prompt capabilities
|
||||
McpAsyncServer serverWithoutPrompts = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.async();
|
||||
|
||||
Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of());
|
||||
|
|
@ -309,7 +309,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
void testRemovePromptWithoutCapability() {
|
||||
// Create a server without prompt capabilities
|
||||
McpAsyncServer serverWithoutPrompts = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.async();
|
||||
|
||||
StepVerifier.create(serverWithoutPrompts.removePrompt(TEST_PROMPT_NAME)).verifyErrorSatisfies(error -> {
|
||||
|
|
@ -328,7 +328,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
List.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content")))));
|
||||
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().prompts(true).build())
|
||||
.prompts(registration)
|
||||
.async();
|
||||
|
|
@ -341,7 +341,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
@Test
|
||||
void testRemoveNonexistentPrompt() {
|
||||
var mcpAsyncServer2 = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().prompts(true).build())
|
||||
.async();
|
||||
|
||||
|
|
@ -354,6 +354,70 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// Roots Tests
|
||||
// ---------------------------------------
|
||||
|
||||
@Test
|
||||
void testRootsChangeConsumers() {
|
||||
// Test with single consumer
|
||||
var rootsReceived = new McpSchema.Root[1];
|
||||
var consumerCalled = new boolean[1];
|
||||
|
||||
var singleConsumerServer = McpServer.using(createMcpTransport())
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.rootsChangeConsumers(List.of(roots -> {
|
||||
consumerCalled[0] = true;
|
||||
if (!roots.isEmpty()) {
|
||||
rootsReceived[0] = roots.get(0);
|
||||
}
|
||||
}))
|
||||
.async();
|
||||
|
||||
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.using(createMcpTransport())
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.rootsChangeConsumers(List.of(roots -> {
|
||||
consumer1Called[0] = true;
|
||||
rootsContent[0] = roots;
|
||||
}, roots -> consumer2Called[0] = true))
|
||||
.async();
|
||||
|
||||
assertThat(multipleConsumersServer).isNotNull();
|
||||
assertThatCode(() -> multipleConsumersServer.closeGracefully().block(Duration.ofSeconds(10)))
|
||||
.doesNotThrowAnyException();
|
||||
onClose();
|
||||
|
||||
// Test error handling
|
||||
var errorHandlingServer = McpServer.using(createMcpTransport())
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.rootsChangeConsumers(List.of(roots -> {
|
||||
throw new RuntimeException("Test error");
|
||||
}))
|
||||
.async();
|
||||
|
||||
assertThat(errorHandlingServer).isNotNull();
|
||||
assertThatCode(() -> errorHandlingServer.closeGracefully().block(Duration.ofSeconds(10)))
|
||||
.doesNotThrowAnyException();
|
||||
onClose();
|
||||
|
||||
// Test without consumers
|
||||
var noConsumersServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").async();
|
||||
|
||||
assertThat(noConsumersServer).isNotNull();
|
||||
assertThatCode(() -> noConsumersServer.closeGracefully().block(Duration.ofSeconds(10)))
|
||||
.doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// Logging Tests
|
||||
// ---------------------------------------
|
||||
|
|
@ -361,7 +425,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
@Test
|
||||
void testLoggingLevels() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().logging().build())
|
||||
.async();
|
||||
|
||||
|
|
@ -380,7 +444,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
@Test
|
||||
void testLoggingWithoutCapability() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().build()) // No logging capability
|
||||
.async();
|
||||
|
||||
|
|
@ -396,7 +460,7 @@ public abstract class AbstractMcpAsyncServerTests {
|
|||
@Test
|
||||
void testLoggingWithNullNotification() {
|
||||
var mcpAsyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().logging().build())
|
||||
.async();
|
||||
|
||||
|
|
|
|||
|
|
@ -83,28 +83,28 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
assertThatThrownBy(() -> McpServer.using(null)).isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("Transport must not be null");
|
||||
|
||||
assertThatThrownBy(() -> McpServer.using(createMcpTransport()).info((McpSchema.Implementation) null))
|
||||
assertThatThrownBy(() -> McpServer.using(createMcpTransport()).serverInfo((McpSchema.Implementation) null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("Server info must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGracefulShutdown() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync();
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
assertThatCode(() -> mcpSyncServer.closeGracefully()).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testImmediateClose() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync();
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
assertThatCode(() -> mcpSyncServer.close()).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAsyncServer() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync();
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
assertThat(mcpSyncServer.getAsyncServer()).isNotNull();
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
@Test
|
||||
void testAddTool() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.sync();
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", Map.of("input", "string"));
|
||||
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.tool(duplicateTool, args -> new CallToolResult(List.of(), false))
|
||||
.sync();
|
||||
|
|
@ -153,7 +153,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
Tool tool = new McpSchema.Tool(TEST_TOOL_NAME, "Test tool", Map.of("input", "string"));
|
||||
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.tool(tool, args -> new CallToolResult(List.of(), false))
|
||||
.sync();
|
||||
|
|
@ -166,7 +166,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
@Test
|
||||
void testRemoveNonexistentTool() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().tools(true).build())
|
||||
.sync();
|
||||
|
||||
|
|
@ -178,7 +178,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testNotifyToolsListChanged() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync();
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
assertThatCode(() -> mcpSyncServer.notifyToolsListChanged()).doesNotThrowAnyException();
|
||||
|
||||
|
|
@ -191,7 +191,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testNotifyResourcesListChanged() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync();
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
assertThatCode(() -> mcpSyncServer.notifyResourcesListChanged()).doesNotThrowAnyException();
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
@Test
|
||||
void testAddResource() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().resources(true, false).build())
|
||||
.sync();
|
||||
|
||||
|
|
@ -218,7 +218,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
@Test
|
||||
void testAddResourceWithNullRegistration() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().resources(true, false).build())
|
||||
.sync();
|
||||
|
||||
|
|
@ -230,7 +230,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testAddResourceWithoutCapability() {
|
||||
var serverWithoutResources = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync();
|
||||
var serverWithoutResources = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
|
||||
null);
|
||||
|
|
@ -243,7 +243,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testRemoveResourceWithoutCapability() {
|
||||
var serverWithoutResources = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync();
|
||||
var serverWithoutResources = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
assertThatThrownBy(() -> serverWithoutResources.removeResource(TEST_RESOURCE_URI)).isInstanceOf(McpError.class)
|
||||
.hasMessage("Server must be configured with resource capabilities");
|
||||
|
|
@ -255,7 +255,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testNotifyPromptsListChanged() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync();
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
assertThatCode(() -> mcpSyncServer.notifyPromptsListChanged()).doesNotThrowAnyException();
|
||||
|
||||
|
|
@ -265,7 +265,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
@Test
|
||||
void testAddPromptWithNullRegistration() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().prompts(false).build())
|
||||
.sync();
|
||||
|
||||
|
|
@ -275,7 +275,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testAddPromptWithoutCapability() {
|
||||
var serverWithoutPrompts = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync();
|
||||
var serverWithoutPrompts = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of());
|
||||
PromptRegistration registration = new PromptRegistration(prompt, req -> new GetPromptResult(
|
||||
|
|
@ -288,7 +288,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
|
||||
@Test
|
||||
void testRemovePromptWithoutCapability() {
|
||||
var serverWithoutPrompts = McpServer.using(createMcpTransport()).info("test-server", "1.0.0").sync();
|
||||
var serverWithoutPrompts = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
assertThatThrownBy(() -> serverWithoutPrompts.removePrompt(TEST_PROMPT_NAME)).isInstanceOf(McpError.class)
|
||||
.hasMessage("Server must be configured with prompt capabilities");
|
||||
|
|
@ -302,7 +302,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
List.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content")))));
|
||||
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().prompts(true).build())
|
||||
.prompts(registration)
|
||||
.sync();
|
||||
|
|
@ -315,7 +315,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
@Test
|
||||
void testRemoveNonexistentPrompt() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().prompts(true).build())
|
||||
.sync();
|
||||
|
||||
|
|
@ -325,6 +325,66 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
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.using(createMcpTransport())
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.rootsChangeConsumers(List.of(roots -> {
|
||||
consumerCalled[0] = true;
|
||||
if (!roots.isEmpty()) {
|
||||
rootsReceived[0] = roots.get(0);
|
||||
}
|
||||
}))
|
||||
.sync();
|
||||
|
||||
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.using(createMcpTransport())
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.rootsChangeConsumers(List.of(roots -> {
|
||||
consumer1Called[0] = true;
|
||||
rootsContent[0] = roots;
|
||||
}, roots -> consumer2Called[0] = true))
|
||||
.sync();
|
||||
|
||||
assertThat(multipleConsumersServer).isNotNull();
|
||||
assertThatCode(() -> multipleConsumersServer.closeGracefully()).doesNotThrowAnyException();
|
||||
onClose();
|
||||
|
||||
// Test error handling
|
||||
var errorHandlingServer = McpServer.using(createMcpTransport())
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.rootsChangeConsumers(List.of(roots -> {
|
||||
throw new RuntimeException("Test error");
|
||||
}))
|
||||
.sync();
|
||||
|
||||
assertThat(errorHandlingServer).isNotNull();
|
||||
assertThatCode(() -> errorHandlingServer.closeGracefully()).doesNotThrowAnyException();
|
||||
onClose();
|
||||
|
||||
// Test without consumers
|
||||
var noConsumersServer = McpServer.using(createMcpTransport()).serverInfo("test-server", "1.0.0").sync();
|
||||
|
||||
assertThat(noConsumersServer).isNotNull();
|
||||
assertThatCode(() -> noConsumersServer.closeGracefully()).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// Logging Tests
|
||||
// ---------------------------------------
|
||||
|
|
@ -332,7 +392,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
@Test
|
||||
void testLoggingLevels() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().logging().build())
|
||||
.sync();
|
||||
|
||||
|
|
@ -351,7 +411,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
@Test
|
||||
void testLoggingWithoutCapability() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().build()) // No logging capability
|
||||
.sync();
|
||||
|
||||
|
|
@ -367,7 +427,7 @@ public abstract class AbstractMcpSyncServerTests {
|
|||
@Test
|
||||
void testLoggingWithNullNotification() {
|
||||
var mcpSyncServer = McpServer.using(createMcpTransport())
|
||||
.info("test-server", "1.0.0")
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().logging().build())
|
||||
.sync();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue