Skip to content

Commit 055bc10

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 055bc10

2 files changed

Lines changed: 80 additions & 4 deletions

File tree

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,20 @@ 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) -> Mono.empty());
145+
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_ROOTS_LIST_CHANGED, (exchange, params) -> Mono.empty());
146+
147+
return notificationHandlers;
148+
}
149+
140150
// ---------------------------------------
141151
// Lifecycle Management
142152
// ---------------------------------------

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

Lines changed: 69 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,68 @@ void testMissingHandlerReturnsMethodNotFoundError() {
810814
}
811815
}
812816

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

0 commit comments

Comments
 (0)