|
4 | 4 |
|
5 | 5 | package io.modelcontextprotocol.server; |
6 | 6 |
|
| 7 | +import java.util.List; |
| 8 | +import java.util.Map; |
7 | 9 | import java.util.UUID; |
8 | 10 |
|
9 | 11 | import io.modelcontextprotocol.MockMcpServerTransport; |
10 | 12 | import io.modelcontextprotocol.MockMcpServerTransportProvider; |
11 | 13 | import io.modelcontextprotocol.spec.McpSchema; |
12 | 14 | import io.modelcontextprotocol.spec.ProtocolVersions; |
13 | 15 | import org.junit.jupiter.api.Test; |
| 16 | +import reactor.core.publisher.Mono; |
14 | 17 | import reactor.test.StepVerifier; |
15 | 18 |
|
16 | 19 | import static org.assertj.core.api.Assertions.assertThat; |
@@ -59,6 +62,31 @@ private static McpSchema.JSONRPCRequest unsubscribeRequest(String uri) { |
59 | 62 | McpSchema.UnsubscribeRequest.builder(uri).build()); |
60 | 63 | } |
61 | 64 |
|
| 65 | + private static McpAsyncServer buildServerWithResource(MockMcpServerTransportProvider transportProvider) { |
| 66 | + McpSchema.Resource resource = McpSchema.Resource.builder(RESOURCE_URI, "Test Resource") |
| 67 | + .mimeType("text/plain") |
| 68 | + .build(); |
| 69 | + return McpServer.async(transportProvider) |
| 70 | + .serverInfo(SERVER_INFO) |
| 71 | + .capabilities(McpSchema.ServerCapabilities.builder().resources(true, false).build()) |
| 72 | + .resources(new McpServerFeatures.AsyncResourceSpecification(resource, |
| 73 | + (exchange, request) -> Mono.just(new McpSchema.ReadResourceResult(List.of())))) |
| 74 | + .build(); |
| 75 | + } |
| 76 | + |
| 77 | + private static McpAsyncServer buildServerWithResourceTemplate(MockMcpServerTransportProvider transportProvider) { |
| 78 | + McpSchema.ResourceTemplate resourceTemplate = McpSchema.ResourceTemplate |
| 79 | + .builder("test://resource/{id}", "Test Resource Template") |
| 80 | + .mimeType("text/plain") |
| 81 | + .build(); |
| 82 | + return McpServer.async(transportProvider) |
| 83 | + .serverInfo(SERVER_INFO) |
| 84 | + .capabilities(McpSchema.ServerCapabilities.builder().resources(true, false).build()) |
| 85 | + .resourceTemplates(new McpServerFeatures.AsyncResourceTemplateSpecification(resourceTemplate, |
| 86 | + (exchange, request) -> Mono.just(new McpSchema.ReadResourceResult(List.of())))) |
| 87 | + .build(); |
| 88 | + } |
| 89 | + |
62 | 90 | @Test |
63 | 91 | void notifyResourcesUpdated_noSubscribers_completesEmpty() { |
64 | 92 | MockMcpServerTransport transport = new MockMcpServerTransport(); |
@@ -166,4 +194,69 @@ void notifyResourcesUpdated_afterSessionClose_doesNotNotifySession() { |
166 | 194 | server.closeGracefully().block(); |
167 | 195 | } |
168 | 196 |
|
| 197 | + @Test |
| 198 | + void subscribeToRegisteredUri_succeeds() { |
| 199 | + MockMcpServerTransport transport = new MockMcpServerTransport(); |
| 200 | + MockMcpServerTransportProvider transportProvider = new MockMcpServerTransportProvider(transport); |
| 201 | + McpAsyncServer server = buildServerWithResource(transportProvider); |
| 202 | + |
| 203 | + transportProvider.simulateIncomingMessage(initRequest()); |
| 204 | + transportProvider.simulateIncomingMessage(initializedNotification()); |
| 205 | + transport.clearSentMessages(); |
| 206 | + transportProvider.simulateIncomingMessage(subscribeRequest(RESOURCE_URI)); |
| 207 | + |
| 208 | + McpSchema.JSONRPCMessage sent = transport.getLastSentMessage(); |
| 209 | + assertThat(sent).isInstanceOf(McpSchema.JSONRPCResponse.class); |
| 210 | + McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sent; |
| 211 | + assertThat(response.error()).as("subscribing to a registered resource must succeed").isNull(); |
| 212 | + assertThat(response.result()).isEqualTo(Map.of()); |
| 213 | + |
| 214 | + server.closeGracefully().block(); |
| 215 | + } |
| 216 | + |
| 217 | + @Test |
| 218 | + void subscribeToUnregisteredUri_returnsResourceNotFound() { |
| 219 | + MockMcpServerTransport transport = new MockMcpServerTransport(); |
| 220 | + MockMcpServerTransportProvider transportProvider = new MockMcpServerTransportProvider(transport); |
| 221 | + McpAsyncServer server = buildServerWithResource(transportProvider); |
| 222 | + |
| 223 | + transportProvider.simulateIncomingMessage(initRequest()); |
| 224 | + transportProvider.simulateIncomingMessage(initializedNotification()); |
| 225 | + transport.clearSentMessages(); |
| 226 | + transportProvider.simulateIncomingMessage(subscribeRequest("test://unknown/uri")); |
| 227 | + |
| 228 | + McpSchema.JSONRPCMessage sent = transport.getLastSentMessage(); |
| 229 | + assertThat(sent).isInstanceOf(McpSchema.JSONRPCResponse.class); |
| 230 | + McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sent; |
| 231 | + assertThat(response.error()).isNotNull(); |
| 232 | + assertThat(response.error().code()).isEqualTo(McpSchema.ErrorCodes.RESOURCE_NOT_FOUND); |
| 233 | + |
| 234 | + server.closeGracefully().block(); |
| 235 | + } |
| 236 | + |
| 237 | + @Test |
| 238 | + void subscribeBeyondPerSessionLimit_isRejected() { |
| 239 | + MockMcpServerTransport transport = new MockMcpServerTransport(); |
| 240 | + MockMcpServerTransportProvider transportProvider = new MockMcpServerTransportProvider(transport); |
| 241 | + McpAsyncServer server = buildServerWithResourceTemplate(transportProvider); |
| 242 | + |
| 243 | + transportProvider.simulateIncomingMessage(initRequest()); |
| 244 | + transportProvider.simulateIncomingMessage(initializedNotification()); |
| 245 | + transport.clearSentMessages(); |
| 246 | + |
| 247 | + for (int i = 0; i < 1024; i++) { |
| 248 | + transportProvider.simulateIncomingMessage(subscribeRequest("test://resource/" + i)); |
| 249 | + } |
| 250 | + |
| 251 | + transportProvider.simulateIncomingMessage(subscribeRequest("test://resource/1024")); |
| 252 | + |
| 253 | + McpSchema.JSONRPCMessage sent = transport.getLastSentMessage(); |
| 254 | + assertThat(sent).isInstanceOf(McpSchema.JSONRPCResponse.class); |
| 255 | + McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sent; |
| 256 | + assertThat(response.error()).isNotNull(); |
| 257 | + assertThat(response.error().code()).isEqualTo(McpSchema.ErrorCodes.INVALID_PARAMS); |
| 258 | + |
| 259 | + server.closeGracefully().block(); |
| 260 | + } |
| 261 | + |
169 | 262 | } |
0 commit comments