Skip to content

Commit 0f95783

Browse files
bperhapsKehrlann
andcommitted
refactor: add notification handler to stateless servers.
Co-authored-by: Daniel Garnier-Moiroux <git@garnier.wf> Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
1 parent 0557b87 commit 0f95783

2 files changed

Lines changed: 81 additions & 4 deletions

File tree

mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,26 @@ public class McpStatelessAsyncServer {
133133

134134
this.protocolVersions = new ArrayList<>(mcpTransport.protocolVersions());
135135

136-
McpStatelessServerHandler handler = new DefaultMcpStatelessServerHandler(requestHandlers, Map.of());
136+
Map<String, McpStatelessNotificationHandler> notificationHandlers = prepareNotificationHandlers();
137+
McpStatelessServerHandler handler = new DefaultMcpStatelessServerHandler(requestHandlers, notificationHandlers);
137138
mcpTransport.setMcpHandler(handler);
138139
}
139140

141+
private Map<String, McpStatelessNotificationHandler> prepareNotificationHandlers() {
142+
Map<String, McpStatelessNotificationHandler> notificationHandlers = new HashMap<>();
143+
144+
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_INITIALIZED, (exchange, params) -> {
145+
logger.debug("Received {}", McpSchema.METHOD_NOTIFICATION_INITIALIZED);
146+
return Mono.empty();
147+
});
148+
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_ROOTS_LIST_CHANGED, (exchange, params) -> {
149+
logger.debug("Received {}", McpSchema.METHOD_NOTIFICATION_ROOTS_LIST_CHANGED);
150+
return Mono.empty();
151+
});
152+
153+
return notificationHandlers;
154+
}
155+
140156
// ---------------------------------------
141157
// Lifecycle Management
142158
// ---------------------------------------

mcp-test/src/test/java/io/modelcontextprotocol/server/HttpServletStatelessIntegrationTests.java

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
import java.util.function.BiFunction;
1212
import java.util.function.Function;
1313

14+
import ch.qos.logback.classic.Level;
15+
import ch.qos.logback.classic.Logger;
16+
import ch.qos.logback.classic.spi.ILoggingEvent;
17+
import ch.qos.logback.core.read.ListAppender;
1418
import io.modelcontextprotocol.client.McpClient;
1519
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
1620
import io.modelcontextprotocol.common.McpTransportContext;
@@ -42,13 +46,13 @@
4246
import org.junit.jupiter.api.BeforeEach;
4347
import org.junit.jupiter.api.Test;
4448
import org.junit.jupiter.api.Timeout;
49+
import org.slf4j.LoggerFactory;
50+
import reactor.core.publisher.Mono;
51+
import reactor.test.StepVerifier;
4552

4653
import org.springframework.mock.web.MockHttpServletRequest;
4754
import org.springframework.mock.web.MockHttpServletResponse;
4855
import org.springframework.web.client.RestClient;
49-
import reactor.core.publisher.Mono;
50-
import reactor.test.StepVerifier;
51-
5256
import static io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport.APPLICATION_JSON;
5357
import static io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport.TEXT_EVENT_STREAM;
5458
import static io.modelcontextprotocol.util.McpJsonMapperUtils.JSON_MAPPER;
@@ -810,6 +814,63 @@ void testMissingHandlerReturnsMethodNotFoundError() {
810814
}
811815
}
812816

817+
@Test
818+
void testInitializedNotificationDoesNotLogWarn() {
819+
Logger handlerLogger = (Logger) LoggerFactory.getLogger(DefaultMcpStatelessServerHandler.class);
820+
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
821+
logAppender.start();
822+
handlerLogger.addAppender(logAppender);
823+
824+
try {
825+
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
826+
.serverInfo("test-server", "1.0.0")
827+
.capabilities(ServerCapabilities.builder().build())
828+
.build();
829+
830+
try (var mcpClient = clientBuilder.build()) {
831+
mcpClient.initialize(); // automatically sends notifications/initialized
832+
}
833+
finally {
834+
mcpServer.close();
835+
}
836+
}
837+
finally {
838+
handlerLogger.detachAppender(logAppender);
839+
logAppender.stop();
840+
}
841+
842+
assertThat(logAppender.list).noneMatch(event -> event.getLevel() == Level.WARN);
843+
}
844+
845+
@Test
846+
void testRootsListChangedNotificationDoesNotLogWarn() {
847+
Logger handlerLogger = (Logger) LoggerFactory.getLogger(DefaultMcpStatelessServerHandler.class);
848+
ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
849+
logAppender.start();
850+
handlerLogger.addAppender(logAppender);
851+
852+
try {
853+
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
854+
.serverInfo("test-server", "1.0.0")
855+
.capabilities(ServerCapabilities.builder().build())
856+
.build();
857+
858+
try (var mcpClient = clientBuilder.build()) {
859+
mcpClient.initialize();
860+
mcpClient.rootsListChangedNotification();
861+
}
862+
finally {
863+
mcpServer.close();
864+
}
865+
}
866+
finally {
867+
handlerLogger.detachAppender(logAppender);
868+
logAppender.stop();
869+
}
870+
871+
assertThat(logAppender.list).noneMatch(event -> event.getLevel() == Level.WARN);
872+
}
873+
813874
private double evaluateExpression(String expression) {
814875
// Simple expression evaluator for testing
815876
return switch (expression) {

0 commit comments

Comments
 (0)