Skip to content

Commit 8526daa

Browse files
committed
chore: generalize annotation tests to all transports
- Moves annotation test from stdio suite to abstract suite - Adds async version of sync annotation test - Fixes wildcard import
1 parent 90eefe9 commit 8526daa

File tree

7 files changed

+249
-61
lines changed

7 files changed

+249
-61
lines changed

mcp-test/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@
5454
<artifactId>junit-jupiter-api</artifactId>
5555
<version>${junit.version}</version>
5656
</dependency>
57+
<dependency>
58+
<groupId>org.junit.jupiter</groupId>
59+
<artifactId>junit-jupiter-params</artifactId>
60+
<version>${junit.version}</version>
61+
</dependency>
5762
<dependency>
5863
<groupId>org.mockito</groupId>
5964
<artifactId>mockito-core</artifactId>

mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,17 @@
3131
import org.junit.jupiter.api.BeforeEach;
3232
import org.junit.jupiter.api.Disabled;
3333
import org.junit.jupiter.api.Test;
34+
import org.junit.jupiter.params.ParameterizedTest;
35+
import org.junit.jupiter.params.provider.ValueSource;
3436
import reactor.core.publisher.Flux;
3537
import reactor.core.publisher.Mono;
3638
import reactor.test.StepVerifier;
3739

3840
import static org.assertj.core.api.Assertions.assertThat;
3941
import static org.assertj.core.api.Assertions.assertThatCode;
4042
import static org.assertj.core.api.Assertions.assertThatThrownBy;
43+
import static org.assertj.core.api.Assertions.fail;
44+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
4145

4246
/**
4347
* Test suite for the {@link McpAsyncClient} that can be used with different
@@ -202,6 +206,64 @@ void testCallToolWithInvalidTool() {
202206
});
203207
}
204208

209+
@ParameterizedTest
210+
@ValueSource(strings = { "success", "error", "debug" })
211+
void testCallToolWithMessageAnnotations(String messageType) {
212+
McpClientTransport transport = createMcpTransport();
213+
214+
withClient(transport, mcpAsyncClient -> {
215+
StepVerifier.create(mcpAsyncClient.initialize()
216+
.then(mcpAsyncClient.callTool(new McpSchema.CallToolRequest("annotatedMessage",
217+
Map.of("messageType", messageType, "includeImage", true)))))
218+
.consumeNextWith(result -> {
219+
assertThat(result).isNotNull();
220+
assertThat(result.isError()).isNotEqualTo(true);
221+
assertThat(result.content()).isNotEmpty();
222+
assertThat(result.content()).allSatisfy(content -> {
223+
switch (content.type()) {
224+
case "text":
225+
McpSchema.TextContent textContent = assertInstanceOf(McpSchema.TextContent.class,
226+
content);
227+
assertThat(textContent.text()).isNotEmpty();
228+
assertThat(textContent.annotations()).isNotNull();
229+
230+
switch (messageType) {
231+
case "error":
232+
assertThat(textContent.annotations().priority()).isEqualTo(1.0);
233+
assertThat(textContent.annotations().audience())
234+
.containsOnly(McpSchema.Role.USER, McpSchema.Role.ASSISTANT);
235+
break;
236+
case "success":
237+
assertThat(textContent.annotations().priority()).isEqualTo(0.7);
238+
assertThat(textContent.annotations().audience())
239+
.containsExactly(McpSchema.Role.USER);
240+
break;
241+
case "debug":
242+
assertThat(textContent.annotations().priority()).isEqualTo(0.3);
243+
assertThat(textContent.annotations().audience())
244+
.containsExactly(McpSchema.Role.ASSISTANT);
245+
break;
246+
default:
247+
throw new IllegalStateException("Unexpected value: " + content.type());
248+
}
249+
break;
250+
case "image":
251+
McpSchema.ImageContent imageContent = assertInstanceOf(McpSchema.ImageContent.class,
252+
content);
253+
assertThat(imageContent.data()).isNotEmpty();
254+
assertThat(imageContent.annotations()).isNotNull();
255+
assertThat(imageContent.annotations().priority()).isEqualTo(0.5);
256+
assertThat(imageContent.annotations().audience()).containsExactly(McpSchema.Role.USER);
257+
break;
258+
default:
259+
fail("Unexpected content type: " + content.type());
260+
}
261+
});
262+
})
263+
.verifyComplete();
264+
});
265+
}
266+
205267
@Test
206268
void testListResourcesWithoutInitialization() {
207269
verifyCallSucceedsWithImplicitInitialization(client -> client.listResources(null), "listing resources");

mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import org.junit.jupiter.api.AfterEach;
3131
import org.junit.jupiter.api.BeforeEach;
3232
import org.junit.jupiter.api.Test;
33+
import org.junit.jupiter.params.ParameterizedTest;
34+
import org.junit.jupiter.params.provider.ValueSource;
3335
import reactor.core.publisher.Mono;
3436
import reactor.core.scheduler.Scheduler;
3537
import reactor.core.scheduler.Schedulers;
@@ -38,6 +40,8 @@
3840
import static org.assertj.core.api.Assertions.assertThat;
3941
import static org.assertj.core.api.Assertions.assertThatCode;
4042
import static org.assertj.core.api.Assertions.assertThatThrownBy;
43+
import static org.assertj.core.api.Assertions.fail;
44+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
4145

4246
/**
4347
* Unit tests for MCP Client Session functionality.
@@ -183,6 +187,60 @@ void testCallTools() {
183187
});
184188
}
185189

190+
@ParameterizedTest
191+
@ValueSource(strings = { "success", "error", "debug" })
192+
void testCallToolWithMessageAnnotations(String messageType) {
193+
McpClientTransport transport = createMcpTransport();
194+
195+
withClient(transport, client -> {
196+
client.initialize();
197+
198+
McpSchema.CallToolResult result = client.callTool(new McpSchema.CallToolRequest("annotatedMessage",
199+
Map.of("messageType", messageType, "includeImage", true)));
200+
201+
assertThat(result).isNotNull();
202+
assertThat(result.isError()).isNotEqualTo(true);
203+
assertThat(result.content()).isNotEmpty();
204+
assertThat(result.content()).allSatisfy(content -> {
205+
switch (content.type()) {
206+
case "text":
207+
McpSchema.TextContent textContent = assertInstanceOf(McpSchema.TextContent.class, content);
208+
assertThat(textContent.text()).isNotEmpty();
209+
assertThat(textContent.annotations()).isNotNull();
210+
211+
switch (messageType) {
212+
case "error":
213+
assertThat(textContent.annotations().priority()).isEqualTo(1.0);
214+
assertThat(textContent.annotations().audience()).containsOnly(McpSchema.Role.USER,
215+
McpSchema.Role.ASSISTANT);
216+
break;
217+
case "success":
218+
assertThat(textContent.annotations().priority()).isEqualTo(0.7);
219+
assertThat(textContent.annotations().audience()).containsExactly(McpSchema.Role.USER);
220+
break;
221+
case "debug":
222+
assertThat(textContent.annotations().priority()).isEqualTo(0.3);
223+
assertThat(textContent.annotations().audience())
224+
.containsExactly(McpSchema.Role.ASSISTANT);
225+
break;
226+
default:
227+
throw new IllegalStateException("Unexpected value: " + content.type());
228+
}
229+
break;
230+
case "image":
231+
McpSchema.ImageContent imageContent = assertInstanceOf(McpSchema.ImageContent.class, content);
232+
assertThat(imageContent.data()).isNotEmpty();
233+
assertThat(imageContent.annotations()).isNotNull();
234+
assertThat(imageContent.annotations().priority()).isEqualTo(0.5);
235+
assertThat(imageContent.annotations().audience()).containsExactly(McpSchema.Role.USER);
236+
break;
237+
default:
238+
fail("Unexpected content type: " + content.type());
239+
}
240+
});
241+
});
242+
}
243+
186244
@Test
187245
void testPingWithoutInitialization() {
188246
verifyCallSucceedsWithImplicitInitialization(client -> client.ping(), "pinging the server");

mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
package io.modelcontextprotocol.spec;
66

77
import java.io.IOException;
8-
import java.util.*;
8+
import java.util.ArrayList;
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
912

1013
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
1114
import com.fasterxml.jackson.annotation.JsonInclude;

mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@
3333
import org.junit.jupiter.api.BeforeEach;
3434
import org.junit.jupiter.api.Disabled;
3535
import org.junit.jupiter.api.Test;
36+
import org.junit.jupiter.params.ParameterizedTest;
37+
import org.junit.jupiter.params.provider.ValueSource;
3638
import reactor.core.publisher.Flux;
3739
import reactor.core.publisher.Mono;
3840
import reactor.test.StepVerifier;
3941

4042
import static org.assertj.core.api.Assertions.assertThat;
4143
import static org.assertj.core.api.Assertions.assertThatCode;
4244
import static org.assertj.core.api.Assertions.assertThatThrownBy;
45+
import static org.assertj.core.api.Assertions.fail;
46+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
4347

4448
/**
4549
* Test suite for the {@link McpAsyncClient} that can be used with different
@@ -205,6 +209,64 @@ void testCallToolWithInvalidTool() {
205209
});
206210
}
207211

212+
@ParameterizedTest
213+
@ValueSource(strings = { "success", "error", "debug" })
214+
void testCallToolWithMessageAnnotations(String messageType) {
215+
McpClientTransport transport = createMcpTransport();
216+
217+
withClient(transport, mcpAsyncClient -> {
218+
StepVerifier.create(mcpAsyncClient.initialize()
219+
.then(mcpAsyncClient.callTool(new McpSchema.CallToolRequest("annotatedMessage",
220+
Map.of("messageType", messageType, "includeImage", true)))))
221+
.consumeNextWith(result -> {
222+
assertThat(result).isNotNull();
223+
assertThat(result.isError()).isNotEqualTo(true);
224+
assertThat(result.content()).isNotEmpty();
225+
assertThat(result.content()).allSatisfy(content -> {
226+
switch (content.type()) {
227+
case "text":
228+
McpSchema.TextContent textContent = assertInstanceOf(McpSchema.TextContent.class,
229+
content);
230+
assertThat(textContent.text()).isNotEmpty();
231+
assertThat(textContent.annotations()).isNotNull();
232+
233+
switch (messageType) {
234+
case "error":
235+
assertThat(textContent.annotations().priority()).isEqualTo(1.0);
236+
assertThat(textContent.annotations().audience())
237+
.containsOnly(McpSchema.Role.USER, McpSchema.Role.ASSISTANT);
238+
break;
239+
case "success":
240+
assertThat(textContent.annotations().priority()).isEqualTo(0.7);
241+
assertThat(textContent.annotations().audience())
242+
.containsExactly(McpSchema.Role.USER);
243+
break;
244+
case "debug":
245+
assertThat(textContent.annotations().priority()).isEqualTo(0.3);
246+
assertThat(textContent.annotations().audience())
247+
.containsExactly(McpSchema.Role.ASSISTANT);
248+
break;
249+
default:
250+
throw new IllegalStateException("Unexpected value: " + content.type());
251+
}
252+
break;
253+
case "image":
254+
McpSchema.ImageContent imageContent = assertInstanceOf(McpSchema.ImageContent.class,
255+
content);
256+
assertThat(imageContent.data()).isNotEmpty();
257+
assertThat(imageContent.annotations()).isNotNull();
258+
assertThat(imageContent.annotations().priority()).isEqualTo(0.5);
259+
assertThat(imageContent.annotations().audience()).containsExactly(McpSchema.Role.USER);
260+
break;
261+
default:
262+
fail("Unexpected content type: " + content.type());
263+
}
264+
});
265+
})
266+
.verifyComplete();
267+
});
268+
}
269+
208270
@Test
209271
void testListResourcesWithoutInitialization() {
210272
verifyCallSucceedsWithImplicitInitialization(client -> client.listResources(null), "listing resources");

mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import org.junit.jupiter.api.AfterEach;
3131
import org.junit.jupiter.api.BeforeEach;
3232
import org.junit.jupiter.api.Test;
33+
import org.junit.jupiter.params.ParameterizedTest;
34+
import org.junit.jupiter.params.provider.ValueSource;
3335
import reactor.core.publisher.Mono;
3436
import reactor.core.scheduler.Scheduler;
3537
import reactor.core.scheduler.Schedulers;
@@ -38,6 +40,8 @@
3840
import static org.assertj.core.api.Assertions.assertThat;
3941
import static org.assertj.core.api.Assertions.assertThatCode;
4042
import static org.assertj.core.api.Assertions.assertThatThrownBy;
43+
import static org.assertj.core.api.Assertions.fail;
44+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
4145

4246
/**
4347
* Unit tests for MCP Client Session functionality.
@@ -227,6 +231,60 @@ void testCallToolWithInvalidTool() {
227231
});
228232
}
229233

234+
@ParameterizedTest
235+
@ValueSource(strings = { "success", "error", "debug" })
236+
void testCallToolWithMessageAnnotations(String messageType) {
237+
McpClientTransport transport = createMcpTransport();
238+
239+
withClient(transport, client -> {
240+
client.initialize();
241+
242+
McpSchema.CallToolResult result = client.callTool(new McpSchema.CallToolRequest("annotatedMessage",
243+
Map.of("messageType", messageType, "includeImage", true)));
244+
245+
assertThat(result).isNotNull();
246+
assertThat(result.isError()).isNotEqualTo(true);
247+
assertThat(result.content()).isNotEmpty();
248+
assertThat(result.content()).allSatisfy(content -> {
249+
switch (content.type()) {
250+
case "text":
251+
McpSchema.TextContent textContent = assertInstanceOf(McpSchema.TextContent.class, content);
252+
assertThat(textContent.text()).isNotEmpty();
253+
assertThat(textContent.annotations()).isNotNull();
254+
255+
switch (messageType) {
256+
case "error":
257+
assertThat(textContent.annotations().priority()).isEqualTo(1.0);
258+
assertThat(textContent.annotations().audience()).containsOnly(McpSchema.Role.USER,
259+
McpSchema.Role.ASSISTANT);
260+
break;
261+
case "success":
262+
assertThat(textContent.annotations().priority()).isEqualTo(0.7);
263+
assertThat(textContent.annotations().audience()).containsExactly(McpSchema.Role.USER);
264+
break;
265+
case "debug":
266+
assertThat(textContent.annotations().priority()).isEqualTo(0.3);
267+
assertThat(textContent.annotations().audience())
268+
.containsExactly(McpSchema.Role.ASSISTANT);
269+
break;
270+
default:
271+
throw new IllegalStateException("Unexpected value: " + content.type());
272+
}
273+
break;
274+
case "image":
275+
McpSchema.ImageContent imageContent = assertInstanceOf(McpSchema.ImageContent.class, content);
276+
assertThat(imageContent.data()).isNotEmpty();
277+
assertThat(imageContent.annotations()).isNotNull();
278+
assertThat(imageContent.annotations().priority()).isEqualTo(0.5);
279+
assertThat(imageContent.annotations().audience()).containsExactly(McpSchema.Role.USER);
280+
break;
281+
default:
282+
fail("Unexpected content type: " + content.type());
283+
}
284+
});
285+
});
286+
}
287+
230288
@Test
231289
void testRootsListChangedWithoutInitialization() {
232290
verifyNotificationSucceedsWithImplicitInitialization(client -> client.rootsListChangedNotification(),

0 commit comments

Comments
 (0)