|
6 | 6 |
|
7 | 7 | import java.time.Duration; |
8 | 8 | import java.util.Map; |
| 9 | +import java.util.concurrent.atomic.AtomicReference; |
| 10 | +import java.util.function.Function; |
9 | 11 | import java.util.stream.Stream; |
10 | 12 |
|
11 | 13 | import io.modelcontextprotocol.AbstractMcpClientServerIntegrationTests; |
|
16 | 18 | import io.modelcontextprotocol.server.McpServer.SyncSpecification; |
17 | 19 | import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; |
18 | 20 | import io.modelcontextprotocol.server.transport.TomcatTestUtil; |
| 21 | +import io.modelcontextprotocol.spec.McpSchema; |
19 | 22 | import jakarta.servlet.http.HttpServletRequest; |
20 | 23 | import org.apache.catalina.LifecycleException; |
21 | 24 | import org.apache.catalina.LifecycleState; |
22 | 25 | import org.apache.catalina.startup.Tomcat; |
| 26 | +import org.awaitility.Awaitility; |
23 | 27 | import org.junit.jupiter.api.AfterEach; |
24 | 28 | import org.junit.jupiter.api.BeforeEach; |
| 29 | +import org.junit.jupiter.api.Test; |
25 | 30 | import org.junit.jupiter.api.Timeout; |
26 | 31 | import org.junit.jupiter.params.provider.Arguments; |
| 32 | +import reactor.core.publisher.Mono; |
| 33 | +import reactor.test.StepVerifier; |
27 | 34 |
|
28 | 35 | import static org.assertj.core.api.Assertions.assertThat; |
29 | 36 |
|
@@ -96,6 +103,47 @@ public void after() { |
96 | 103 | } |
97 | 104 | } |
98 | 105 |
|
| 106 | + @Test |
| 107 | + void testMissingHandlerReturnsMethodNotFoundError() { |
| 108 | + var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0") |
| 109 | + .capabilities(McpSchema.ServerCapabilities.builder().tools(true).build()) |
| 110 | + .build(); |
| 111 | + var clientTransport = HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT) |
| 112 | + .endpoint(MESSAGE_ENDPOINT) |
| 113 | + .build(); |
| 114 | + |
| 115 | + try (var mcpClient = McpClient.sync(clientTransport).build()) { |
| 116 | + // Create a session using an MCP client |
| 117 | + McpSchema.InitializeResult initResult = mcpClient.initialize(); |
| 118 | + assertThat(initResult).isNotNull(); |
| 119 | + |
| 120 | + // Override the response handler in the client to capture responses |
| 121 | + AtomicReference<McpSchema.JSONRPCResponse> response = new AtomicReference<>(); |
| 122 | + var handler = (Function<Mono<McpSchema.JSONRPCMessage>, Mono<McpSchema.JSONRPCMessage>>) ( |
| 123 | + message) -> message.doOnNext(r -> { |
| 124 | + if (r instanceof McpSchema.JSONRPCResponse resp) { |
| 125 | + response.set(resp); |
| 126 | + } |
| 127 | + }); |
| 128 | + StepVerifier.create(clientTransport.connect(handler)).verifyComplete(); |
| 129 | + |
| 130 | + // Send an incorrect request through the transport |
| 131 | + StepVerifier |
| 132 | + .create(clientTransport.sendMessage(new McpSchema.JSONRPCRequest("foo/bar", "test-request-123"))) |
| 133 | + .verifyComplete(); |
| 134 | + |
| 135 | + // Wait until we've received the response |
| 136 | + Awaitility.await().atMost(Duration.ofSeconds(1)).until(() -> response.get() != null); |
| 137 | + |
| 138 | + assertThat(response.get().error().code()).isEqualTo(McpSchema.ErrorCodes.METHOD_NOT_FOUND); |
| 139 | + assertThat(response.get().error().message()).isEqualTo("Method not found: foo/bar"); |
| 140 | + } |
| 141 | + finally { |
| 142 | + mcpServer.close(); |
| 143 | + } |
| 144 | + |
| 145 | + } |
| 146 | + |
99 | 147 | static McpTransportContextExtractor<HttpServletRequest> TEST_CONTEXT_EXTRACTOR = (r) -> McpTransportContext |
100 | 148 | .create(Map.of("important", "value")); |
101 | 149 |
|
|
0 commit comments