refactor: add notification handler to stateless servers. (#835)
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf> Co-authored-by: Daniel Garnier-Moiroux <git@garnier.wf>
This commit is contained in:
parent
0557b87603
commit
fd004989b9
2 changed files with 81 additions and 4 deletions
|
|
@ -133,10 +133,26 @@ public class McpStatelessAsyncServer {
|
|||
|
||||
this.protocolVersions = new ArrayList<>(mcpTransport.protocolVersions());
|
||||
|
||||
McpStatelessServerHandler handler = new DefaultMcpStatelessServerHandler(requestHandlers, Map.of());
|
||||
Map<String, McpStatelessNotificationHandler> notificationHandlers = prepareNotificationHandlers();
|
||||
McpStatelessServerHandler handler = new DefaultMcpStatelessServerHandler(requestHandlers, notificationHandlers);
|
||||
mcpTransport.setMcpHandler(handler);
|
||||
}
|
||||
|
||||
private Map<String, McpStatelessNotificationHandler> prepareNotificationHandlers() {
|
||||
Map<String, McpStatelessNotificationHandler> notificationHandlers = new HashMap<>();
|
||||
|
||||
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_INITIALIZED, (exchange, params) -> {
|
||||
logger.debug("Received {}", McpSchema.METHOD_NOTIFICATION_INITIALIZED);
|
||||
return Mono.empty();
|
||||
});
|
||||
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_ROOTS_LIST_CHANGED, (exchange, params) -> {
|
||||
logger.debug("Received {}", McpSchema.METHOD_NOTIFICATION_ROOTS_LIST_CHANGED);
|
||||
return Mono.empty();
|
||||
});
|
||||
|
||||
return notificationHandlers;
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// Lifecycle Management
|
||||
// ---------------------------------------
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.read.ListAppender;
|
||||
import io.modelcontextprotocol.client.McpClient;
|
||||
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
|
||||
import io.modelcontextprotocol.common.McpTransportContext;
|
||||
|
|
@ -42,13 +46,13 @@ import org.junit.jupiter.api.AfterEach;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport.APPLICATION_JSON;
|
||||
import static io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport.TEXT_EVENT_STREAM;
|
||||
import static io.modelcontextprotocol.util.McpJsonMapperUtils.JSON_MAPPER;
|
||||
|
|
@ -810,6 +814,63 @@ class HttpServletStatelessIntegrationTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInitializedNotificationDoesNotLogWarn() {
|
||||
Logger handlerLogger = (Logger) LoggerFactory.getLogger(DefaultMcpStatelessServerHandler.class);
|
||||
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
|
||||
logAppender.start();
|
||||
handlerLogger.addAppender(logAppender);
|
||||
|
||||
try {
|
||||
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().build())
|
||||
.build();
|
||||
|
||||
try (var mcpClient = clientBuilder.build()) {
|
||||
mcpClient.initialize(); // automatically sends notifications/initialized
|
||||
}
|
||||
finally {
|
||||
mcpServer.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
handlerLogger.detachAppender(logAppender);
|
||||
logAppender.stop();
|
||||
}
|
||||
|
||||
assertThat(logAppender.list).noneMatch(event -> event.getLevel() == Level.WARN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRootsListChangedNotificationDoesNotLogWarn() {
|
||||
Logger handlerLogger = (Logger) LoggerFactory.getLogger(DefaultMcpStatelessServerHandler.class);
|
||||
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
|
||||
logAppender.start();
|
||||
handlerLogger.addAppender(logAppender);
|
||||
|
||||
try {
|
||||
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
.capabilities(ServerCapabilities.builder().build())
|
||||
.build();
|
||||
|
||||
try (var mcpClient = clientBuilder.build()) {
|
||||
mcpClient.initialize();
|
||||
mcpClient.rootsListChangedNotification();
|
||||
}
|
||||
finally {
|
||||
mcpServer.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
handlerLogger.detachAppender(logAppender);
|
||||
logAppender.stop();
|
||||
}
|
||||
|
||||
assertThat(logAppender.list).noneMatch(event -> event.getLevel() == Level.WARN);
|
||||
}
|
||||
|
||||
private double evaluateExpression(String expression) {
|
||||
// Simple expression evaluator for testing
|
||||
return switch (expression) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue