Skip to content

Commit 9ed2d3c

Browse files
Merge branch 'main' into feature/SEP-2243-Mcp-Name-in-header
2 parents 3003b65 + f7d9a64 commit 9ed2d3c

2 files changed

Lines changed: 140 additions & 71 deletions

File tree

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

Lines changed: 83 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@
44

55
package io.modelcontextprotocol.server;
66

7-
import static io.modelcontextprotocol.util.ToolsUtils.EMPTY_JSON_SCHEMA;
8-
97
import java.time.Duration;
108
import java.util.List;
119
import java.util.Map;
12-
import java.util.concurrent.ConcurrentHashMap;
1310
import java.util.concurrent.atomic.AtomicReference;
1411
import java.util.function.BiFunction;
1512

@@ -19,6 +16,7 @@
1916
import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport;
2017
import io.modelcontextprotocol.server.transport.TomcatTestUtil;
2118
import io.modelcontextprotocol.spec.HttpHeaders;
19+
import io.modelcontextprotocol.spec.McpError;
2220
import io.modelcontextprotocol.spec.McpSchema;
2321
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
2422
import io.modelcontextprotocol.spec.McpSchema.CompleteRequest;
@@ -43,8 +41,6 @@
4341
import org.junit.jupiter.api.BeforeEach;
4442
import org.junit.jupiter.api.Test;
4543
import org.junit.jupiter.api.Timeout;
46-
import org.junit.jupiter.params.ParameterizedTest;
47-
import org.junit.jupiter.params.provider.ValueSource;
4844

4945
import org.springframework.mock.web.MockHttpServletRequest;
5046
import org.springframework.mock.web.MockHttpServletResponse;
@@ -53,9 +49,12 @@
5349
import static io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport.APPLICATION_JSON;
5450
import static io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport.TEXT_EVENT_STREAM;
5551
import static io.modelcontextprotocol.util.McpJsonMapperUtils.JSON_MAPPER;
52+
import static io.modelcontextprotocol.util.ToolsUtils.EMPTY_JSON_SCHEMA;
5653
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
5754
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json;
5855
import static org.assertj.core.api.Assertions.assertThat;
56+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
57+
import static org.assertj.core.api.InstanceOfAssertFactories.type;
5958
import static org.awaitility.Awaitility.await;
6059

6160
@Timeout(15)
@@ -67,7 +66,12 @@ class HttpServletStatelessIntegrationTests {
6766

6867
private HttpServletStatelessServerTransport mcpStatelessServerTransport;
6968

70-
ConcurrentHashMap<String, McpClient.SyncSpec> clientBuilders = new ConcurrentHashMap<>();
69+
private final McpClient.SyncSpec clientBuilder = McpClient
70+
.sync(HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT)
71+
.endpoint(CUSTOM_MESSAGE_ENDPOINT)
72+
.build())
73+
.initializationTimeout(Duration.ofHours(10))
74+
.requestTimeout(Duration.ofHours(10));
7175

7276
private Tomcat tomcat;
7377

@@ -85,12 +89,6 @@ public void before() {
8589
catch (Exception e) {
8690
throw new RuntimeException("Failed to start Tomcat", e);
8791
}
88-
89-
clientBuilders
90-
.put("httpclient",
91-
McpClient.sync(HttpClientStreamableHttpTransport.builder("http://localhost:" + PORT)
92-
.endpoint(CUSTOM_MESSAGE_ENDPOINT)
93-
.build()).initializationTimeout(Duration.ofHours(10)).requestTimeout(Duration.ofHours(10)));
9492
}
9593

9694
@AfterEach
@@ -112,12 +110,8 @@ public void after() {
112110
// ---------------------------------------
113111
// Tools Tests
114112
// ---------------------------------------
115-
@ParameterizedTest(name = "{0} : {displayName} ")
116-
@ValueSource(strings = { "httpclient" })
117-
void testToolCallSuccess(String clientType) {
118-
119-
var clientBuilder = clientBuilders.get(clientType);
120-
113+
@Test
114+
void testToolCallSuccess() {
121115
var callResponse = CallToolResult.builder()
122116
.content(List.of(McpSchema.TextContent.builder("CALL RESPONSE").build()))
123117
.isError(false)
@@ -158,12 +152,8 @@ void testToolCallSuccess(String clientType) {
158152
}
159153
}
160154

161-
@ParameterizedTest(name = "{0} : {displayName} ")
162-
@ValueSource(strings = { "httpclient" })
163-
void testInitialize(String clientType) {
164-
165-
var clientBuilder = clientBuilders.get(clientType);
166-
155+
@Test
156+
void testInitialize() {
167157
var mcpServer = McpServer.sync(mcpStatelessServerTransport).build();
168158

169159
try (var mcpClient = clientBuilder.build()) {
@@ -178,11 +168,8 @@ void testInitialize(String clientType) {
178168
// ---------------------------------------
179169
// Completion Tests
180170
// ---------------------------------------
181-
@ParameterizedTest(name = "{0} : Completion call")
182-
@ValueSource(strings = { "httpclient" })
183-
void testCompletionShouldReturnExpectedSuggestions(String clientType) {
184-
var clientBuilder = clientBuilders.get(clientType);
185-
171+
@Test
172+
void testCompletionShouldReturnExpectedSuggestions() {
186173
var expectedValues = List.of("python", "pytorch", "pyside");
187174
var completionResponse = new CompleteResult(new CompleteResult.CompleteCompletion(expectedValues, 10, // total
188175
true // hasMore
@@ -233,11 +220,8 @@ void testCompletionShouldReturnExpectedSuggestions(String clientType) {
233220
}
234221
}
235222

236-
@ParameterizedTest(name = "{0} : Completion call without matching handler")
237-
@ValueSource(strings = { "httpclient" })
238-
void testCompletionWithoutMatchingHandlerReturnsEmptyResult(String clientType) {
239-
var clientBuilder = clientBuilders.get(clientType);
240-
223+
@Test
224+
void testCompletionWithoutMatchingHandlerReturnsEmptyResult() {
241225
BiFunction<McpTransportContext, CompleteRequest, CompleteResult> completionHandler = (transportContext,
242226
request) -> new CompleteResult(new CompleteResult.CompleteCompletion(List.of("java"), 1, false));
243227

@@ -286,11 +270,8 @@ void testCompletionWithoutMatchingHandlerReturnsEmptyResult(String clientType) {
286270
}
287271
}
288272

289-
@ParameterizedTest(name = "{0} : Resource template completion call without matching handler")
290-
@ValueSource(strings = { "httpclient" })
291-
void testResourceTemplateCompletionWithoutMatchingHandlerReturnsEmptyResult(String clientType) {
292-
var clientBuilder = clientBuilders.get(clientType);
293-
273+
@Test
274+
void testResourceTemplateCompletionWithoutMatchingHandlerReturnsEmptyResult() {
294275
BiFunction<McpTransportContext, CompleteRequest, CompleteResult> completionHandler = (transportContext,
295276
request) -> new CompleteResult(new CompleteResult.CompleteCompletion(List.of("java"), 1, false));
296277

@@ -337,14 +318,62 @@ void testResourceTemplateCompletionWithoutMatchingHandlerReturnsEmptyResult(Stri
337318
}
338319
}
339320

321+
@Test
322+
void testCompletionForNonExistentPromptReturnsInvalidParams() {
323+
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
324+
.capabilities(ServerCapabilities.builder().completions().build())
325+
.build();
326+
327+
try (var mcpClient = clientBuilder.build()) {
328+
InitializeResult initResult = mcpClient.initialize();
329+
assertThat(initResult).isNotNull();
330+
331+
CompleteRequest request = CompleteRequest
332+
.builder(new PromptReference("nonexistent-prompt"), new CompleteRequest.CompleteArgument("arg", "val"))
333+
.build();
334+
335+
assertThatThrownBy(() -> mcpClient.completeCompletion(request)).isInstanceOf(McpError.class)
336+
.asInstanceOf(type(McpError.class))
337+
.extracting(McpError::getJsonRpcError)
338+
.extracting(McpSchema.JSONRPCResponse.JSONRPCError::code)
339+
.isEqualTo(ErrorCodes.INVALID_PARAMS);
340+
}
341+
finally {
342+
mcpServer.close();
343+
}
344+
}
345+
346+
@Test
347+
void testCompletionForNonExistentResourceReturnsResourceNotFound() {
348+
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
349+
.capabilities(ServerCapabilities.builder().completions().build())
350+
.build();
351+
352+
try (var mcpClient = clientBuilder.build()) {
353+
InitializeResult initResult = mcpClient.initialize();
354+
assertThat(initResult).isNotNull();
355+
356+
CompleteRequest request = CompleteRequest
357+
.builder(new ResourceReference("test://nonexistent/{param}"),
358+
new CompleteRequest.CompleteArgument("param", "val"))
359+
.build();
360+
361+
assertThatThrownBy(() -> mcpClient.completeCompletion(request)).isInstanceOf(McpError.class)
362+
.asInstanceOf(type(McpError.class))
363+
.extracting(McpError::getJsonRpcError)
364+
.extracting(McpSchema.JSONRPCResponse.JSONRPCError::code)
365+
.isEqualTo(McpSchema.ErrorCodes.RESOURCE_NOT_FOUND);
366+
}
367+
finally {
368+
mcpServer.close();
369+
}
370+
}
371+
340372
// ---------------------------------------
341373
// Tool Structured Output Schema Tests
342374
// ---------------------------------------
343-
@ParameterizedTest(name = "{0} : {displayName} ")
344-
@ValueSource(strings = { "httpclient" })
345-
void testStructuredOutputValidationSuccess(String clientType) {
346-
var clientBuilder = clientBuilders.get(clientType);
347-
375+
@Test
376+
void testStructuredOutputValidationSuccess() {
348377
// Create a tool with output schema
349378
Map<String, Object> outputSchema = Map.of(
350379
"type", "object", "properties", Map.of("result", Map.of("type", "number"), "operation",
@@ -409,11 +438,8 @@ void testStructuredOutputValidationSuccess(String clientType) {
409438
}
410439
}
411440

412-
@ParameterizedTest(name = "{0} : {displayName} ")
413-
@ValueSource(strings = { "httpclient" })
414-
void testStructuredOutputOfObjectArrayValidationSuccess(String clientType) {
415-
var clientBuilder = clientBuilders.get(clientType);
416-
441+
@Test
442+
void testStructuredOutputOfObjectArrayValidationSuccess() {
417443
// Create a tool with output schema that returns an array of objects
418444
Map<String, Object> outputSchema = Map
419445
.of( // @formatter:off
@@ -470,11 +496,8 @@ void testStructuredOutputOfObjectArrayValidationSuccess(String clientType) {
470496
}
471497
}
472498

473-
@ParameterizedTest(name = "{0} : {displayName} ")
474-
@ValueSource(strings = { "httpclient" })
475-
void testStructuredOutputWithInHandlerError(String clientType) {
476-
var clientBuilder = clientBuilders.get(clientType);
477-
499+
@Test
500+
void testStructuredOutputWithInHandlerError() {
478501
// Create a tool with output schema
479502
Map<String, Object> outputSchema = Map.of(
480503
"type", "object", "properties", Map.of("result", Map.of("type", "number"), "operation",
@@ -528,11 +551,8 @@ void testStructuredOutputWithInHandlerError(String clientType) {
528551
}
529552
}
530553

531-
@ParameterizedTest(name = "{0} : {displayName} ")
532-
@ValueSource(strings = { "httpclient" })
533-
void testStructuredOutputValidationFailure(String clientType) {
534-
var clientBuilder = clientBuilders.get(clientType);
535-
554+
@Test
555+
void testStructuredOutputValidationFailure() {
536556
// Create a tool with output schema
537557
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
538558
Map.of("result", Map.of("type", "number"), "operation", Map.of("type", "string")), "required",
@@ -580,11 +600,8 @@ void testStructuredOutputValidationFailure(String clientType) {
580600
}
581601
}
582602

583-
@ParameterizedTest(name = "{0} : {displayName} ")
584-
@ValueSource(strings = { "httpclient" })
585-
void testStructuredOutputMissingStructuredContent(String clientType) {
586-
var clientBuilder = clientBuilders.get(clientType);
587-
603+
@Test
604+
void testStructuredOutputMissingStructuredContent() {
588605
// Create a tool with output schema
589606
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
590607
Map.of("result", Map.of("type", "number")), "required", List.of("result"));
@@ -629,11 +646,8 @@ void testStructuredOutputMissingStructuredContent(String clientType) {
629646
}
630647
}
631648

632-
@ParameterizedTest(name = "{0} : {displayName} ")
633-
@ValueSource(strings = { "httpclient" })
634-
void testStructuredOutputRuntimeToolAddition(String clientType) {
635-
var clientBuilder = clientBuilders.get(clientType);
636-
649+
@Test
650+
void testStructuredOutputRuntimeToolAddition() {
637651
// Start server without tools
638652
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
639653
.serverInfo("test-server", "1.0.0")

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

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,25 @@
2121
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
2222
import io.modelcontextprotocol.server.transport.HttpServletSseServerTransportProvider;
2323
import io.modelcontextprotocol.server.transport.TomcatTestUtil;
24+
import io.modelcontextprotocol.spec.McpError;
2425
import io.modelcontextprotocol.spec.McpSchema;
2526
import io.modelcontextprotocol.spec.McpSchema.CompleteRequest;
2627
import io.modelcontextprotocol.spec.McpSchema.CompleteResult;
2728
import io.modelcontextprotocol.spec.McpSchema.ErrorCodes;
2829
import io.modelcontextprotocol.spec.McpSchema.InitializeResult;
2930
import io.modelcontextprotocol.spec.McpSchema.Prompt;
3031
import io.modelcontextprotocol.spec.McpSchema.PromptArgument;
32+
import io.modelcontextprotocol.spec.McpSchema.PromptReference;
3133
import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult;
3234
import io.modelcontextprotocol.spec.McpSchema.Resource;
3335
import io.modelcontextprotocol.spec.McpSchema.ResourceReference;
3436
import io.modelcontextprotocol.spec.McpSchema.ResourceTemplate;
35-
import io.modelcontextprotocol.spec.McpSchema.PromptReference;
3637
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
37-
import io.modelcontextprotocol.spec.McpError;
3838

3939
import static org.assertj.core.api.Assertions.assertThat;
4040
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
41+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
42+
import static org.assertj.core.api.InstanceOfAssertFactories.type;
4143

4244
/**
4345
* Tests for completion functionality with context support.
@@ -273,6 +275,59 @@ void testResourceTemplateCompletionWithoutMatchingHandlerReturnsEmptyResult() {
273275
mcpServer.close();
274276
}
275277

278+
@Test
279+
void testCompletionForNonExistentPromptReturnsInvalidParams() {
280+
var mcpServer = McpServer.sync(mcpServerTransportProvider)
281+
.capabilities(ServerCapabilities.builder().completions().build())
282+
.build();
283+
284+
try (var mcpClient = clientBuilder
285+
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
286+
.build()) {
287+
InitializeResult initResult = mcpClient.initialize();
288+
assertThat(initResult).isNotNull();
289+
290+
CompleteRequest request = CompleteRequest
291+
.builder(new PromptReference("nonexistent-prompt"), new CompleteRequest.CompleteArgument("arg", "val"))
292+
.build();
293+
294+
assertThatThrownBy(() -> mcpClient.completeCompletion(request)).isInstanceOf(McpError.class)
295+
.asInstanceOf(type(McpError.class))
296+
.extracting(McpError::getJsonRpcError)
297+
.extracting(McpSchema.JSONRPCResponse.JSONRPCError::code)
298+
.isEqualTo(ErrorCodes.INVALID_PARAMS);
299+
}
300+
301+
mcpServer.close();
302+
}
303+
304+
@Test
305+
void testCompletionForNonExistentResourceReturnsResourceNotFound() {
306+
var mcpServer = McpServer.sync(mcpServerTransportProvider)
307+
.capabilities(ServerCapabilities.builder().completions().build())
308+
.build();
309+
310+
try (var mcpClient = clientBuilder
311+
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
312+
.build()) {
313+
InitializeResult initResult = mcpClient.initialize();
314+
assertThat(initResult).isNotNull();
315+
316+
CompleteRequest request = CompleteRequest
317+
.builder(new ResourceReference("test://nonexistent/{param}"),
318+
new CompleteRequest.CompleteArgument("param", "val"))
319+
.build();
320+
321+
assertThatThrownBy(() -> mcpClient.completeCompletion(request)).isInstanceOf(McpError.class)
322+
.asInstanceOf(type(McpError.class))
323+
.extracting(McpError::getJsonRpcError)
324+
.extracting(McpSchema.JSONRPCResponse.JSONRPCError::code)
325+
.isEqualTo(McpSchema.ErrorCodes.RESOURCE_NOT_FOUND);
326+
}
327+
328+
mcpServer.close();
329+
}
330+
276331
@Test
277332
void testDependentCompletionScenario() {
278333
BiFunction<McpSyncServerExchange, CompleteRequest, CompleteResult> completionHandler = (exchange, request) -> {

0 commit comments

Comments
 (0)