Skip to content

Commit 67747c2

Browse files
committed
Fix error behavior on unregistered handlers in stateless server handler
Instead of throwing an McpError, it now returns with a JSON-RPC method not found error (-32601), aligned with the stateful implementation.
1 parent dea53fd commit 67747c2

3 files changed

Lines changed: 84 additions & 4 deletions

File tree

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ public Mono<McpSchema.JSONRPCResponse> handleRequest(McpTransportContext transpo
3232
McpSchema.JSONRPCRequest request) {
3333
McpStatelessRequestHandler<?> requestHandler = this.requestHandlers.get(request.method());
3434
if (requestHandler == null) {
35-
return Mono.error(McpError.builder(McpSchema.ErrorCodes.METHOD_NOT_FOUND)
36-
.message("Missing handler for request type: " + request.method())
37-
.build());
35+
return Mono.just(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null,
36+
new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND,
37+
"Method not found: " + request.method(), null)));
3838
}
3939
return requestHandler.handle(transportContext, request.params())
4040
.map(result -> McpSchema.JSONRPCResponse.result(request.id(), result))
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2026-2026 the original author or authors.
3+
*/
4+
5+
package io.modelcontextprotocol.server;
6+
7+
import io.modelcontextprotocol.common.McpTransportContext;
8+
import io.modelcontextprotocol.spec.McpSchema;
9+
import org.junit.jupiter.api.Test;
10+
import reactor.test.StepVerifier;
11+
12+
import java.util.Collections;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
16+
class DefaultMcpStatelessServerHandlerTests {
17+
18+
@Test
19+
void testHandleRequestWithUnregisteredMethod() {
20+
// no request/initialization handlers
21+
DefaultMcpStatelessServerHandler handler = new DefaultMcpStatelessServerHandler(Collections.emptyMap(),
22+
Collections.emptyMap());
23+
24+
// unregistered method
25+
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, "resources/list",
26+
"test-id-123", null);
27+
28+
StepVerifier.create(handler.handleRequest(McpTransportContext.EMPTY, request)).assertNext(response -> {
29+
assertThat(response).isNotNull();
30+
assertThat(response.jsonrpc()).isEqualTo(McpSchema.JSONRPC_VERSION);
31+
assertThat(response.id()).isEqualTo("test-id-123");
32+
assertThat(response.result()).isNull();
33+
34+
assertThat(response.error()).isNotNull();
35+
assertThat(response.error().code()).isEqualTo(McpSchema.ErrorCodes.METHOD_NOT_FOUND);
36+
assertThat(response.error().message()).isEqualTo("Method not found: resources/list");
37+
}).verifyComplete();
38+
}
39+
40+
}

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

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
package io.modelcontextprotocol.server;
66

7+
import java.net.URI;
8+
import java.net.http.HttpClient;
9+
import java.net.http.HttpRequest;
10+
import java.net.http.HttpResponse;
711
import java.time.Duration;
812
import java.util.List;
913
import java.util.Map;
@@ -448,7 +452,7 @@ void testStructuredOutputOfObjectArrayValidationSuccess() {
448452
"type", "object",
449453
"properties", Map.of(
450454
"name", Map.of("type", "string"),
451-
"age", Map.of("type", "number")),
455+
"age", Map.of("type", "number")),
452456
"required", List.of("name", "age"))); // @formatter:on
453457

454458
Tool calculatorTool = Tool.builder("getMembers")
@@ -765,6 +769,42 @@ void testThrownMcpErrorAndJsonRpcError() throws Exception {
765769
mcpServer.close();
766770
}
767771

772+
@ParameterizedTest
773+
@ValueSource(strings = { "tools/list", "resources/list", "prompts/list" })
774+
void testMissingHandlerReturnsMethodNotFoundError(String method) throws Exception {
775+
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
776+
.serverInfo("test-server", "1.0.0")
777+
.capabilities(ServerCapabilities.builder().build())
778+
.build();
779+
780+
HttpResponse<String> response;
781+
782+
try {
783+
HttpRequest request = HttpRequest.newBuilder()
784+
.uri(URI.create("http://localhost:" + PORT + CUSTOM_MESSAGE_ENDPOINT))
785+
.header("Content-Type", "application/json")
786+
.header("Accept", "application/json, text/event-stream")
787+
.POST(HttpRequest.BodyPublishers.ofString("""
788+
{
789+
"jsonrpc": "2.0",
790+
"method": "%s",
791+
"id": "test-request-123",
792+
"params": {}
793+
}
794+
""".formatted(method)))
795+
.build();
796+
797+
response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
798+
}
799+
finally {
800+
mcpServer.closeGracefully();
801+
}
802+
803+
final var responseBody = response.body();
804+
assertThatJson(responseBody).inPath("error.code").isEqualTo(McpSchema.ErrorCodes.METHOD_NOT_FOUND);
805+
assertThatJson(responseBody).inPath("error.message").isEqualTo("Method not found: " + method);
806+
}
807+
768808
private double evaluateExpression(String expression) {
769809
// Simple expression evaluator for testing
770810
return switch (expression) {

0 commit comments

Comments
 (0)