feat!: enforce required MCP spec fields in McpSchema; lenient wire deserialization (#928)
Every wire-serialized record in McpSchema now validates spec-required
fields at construction time. Wire deserialization is intentionally
lenient: a missing required field is replaced with a documented default
and a WARN is logged, instead of failing the parse. A required-first
builder convention is introduced across the schema so it is no longer
possible to obtain a builder that is missing required state.
Required-field guards
---------------------
Every wire record's compact constructor asserts non-null (and non-empty
for String identifiers like name, uri, uriTemplate, version) on its
spec-required components. Passing null now throws
IllegalArgumentException at construction time instead of silently
producing invalid JSON via @JsonInclude(NON_ABSENT). This applies to
the JSON-RPC envelopes, lifecycle types, resource/prompt/tool requests
and results, sampling and elicitation, content records, root, complete,
logging and progress notifications, and the two CompleteReference
implementations. See MIGRATION-2.0.md for the full list.
Lenient wire deserialization
----------------------------
For each of those records (except JSONRPCResponse.JSONRPCError, which
still fails fast) a @JsonCreator static `fromJson` factory substitutes
a documented default for any absent required field — "" for strings,
[] for collections, {} for maps, 0 / 0.0 for numerics, INFO for
LoggingLevel, USER for SamplingMessage.role, ASSISTANT for
CreateMessageResult.role, CANCEL for ElicitResult.action, {values=[]}
for CompleteResult.completion — and logs a WARN naming the field and
the value used. Application code can still observe a malformed message
and react, but the SDK no longer halts the conversation.
Builder convention
------------------
Records that have a builder gain a required-first factory method
`builder(req1, req2, …)`; setters for required fields are removed from
the builder so it cannot be left in an invalid state. Existing no-arg
`builder()` factories and required-field setters are kept where source
compatibility demands it but are marked @Deprecated. New builders are
also added for several records that previously had none (ProgressNotification,
JSONRPCError, CompleteRequest, list/result types, content records, ...).
JSON-RPC envelope ergonomics
----------------------------
Previously every JSON-RPC envelope had to be constructed via the
canonical record constructor and the literal "2.0" string had to be
threaded through every call site, e.g.
new JSONRPCRequest("2.0", "tools/call", id, params)
new JSONRPCResponse("2.0", id, result, null)
new JSONRPCResponse("2.0", id, null, new JSONRPCError(code, message, null))
Now:
new JSONRPCRequest("tools/call", id, params) // jsonrpc defaulted
new JSONRPCNotification("notifications/initialized") // params optional
JSONRPCResponse.result(id, result) // factory
JSONRPCResponse.error(id, new JSONRPCError(code, message)) // 2-arg error
JSONRPCResponse's compact constructor additionally enforces the
JSON-RPC invariant that exactly one of `result` / `error` is set —
previously the SDK could build envelopes that violated the protocol.
CompleteReference changes
-------------------------
- PromptReference.equals/hashCode now key on `name` only (previously
derived from identifier()+type()). Two refs with the same name but
different titles now compare equal — code using PromptReference as a
map/set key should be audited.
- PromptReference's compact constructor pins `type` to "ref/prompt"
and logs a WARN if the caller supplies a different non-null value.
The legacy two-arg `PromptReference(String type, String name)`
constructor remains @Deprecated.
- ResourceReference's record components are reduced from (type, uri)
to (uri) — positional construction breaks. The legacy
`ResourceReference(String type, String uri)` constructor stays
@Deprecated and ignores `type`.
- CompleteReference.identifier() is @Deprecated and now returns null
via a default method on the interface.
Tests / refactor
----------------
Conformance harness, integration tests, sample apps, and internal
callers were migrated to the new builder factories and convenience
constructors. No semantic changes to client/server runtime behaviour
beyond the McpSchema changes above.
Docs
----
- MIGRATION-2.0.md: required-field section now covers the broader
record set and the lenient-deserialization behaviour; PromptReference
WARN behaviour and ResourceReference component reduction are
documented; the JSON-RPC envelope section is rewritten to compare the
pre-2.0 surface with the new one.
- CONTRIBUTING.md: the "Evolving wire-serialized records" recipe is
split into two cases — adding a new optional field (existing rules)
and adding/maintaining a spec-required field (new rules covering
Assert in compact constructor, @JsonCreator fromJson with defaults
and WARN, required-first builder factory).
Signed-off-by: Dariusz Jędrzejczyk <2554306+chemicL@users.noreply.github.com>
This commit is contained in:
parent
599b43cac4
commit
22a0efa577
12 changed files with 4258 additions and 1688 deletions
|
|
@ -43,13 +43,16 @@ class LifecycleInitializerPostInitializationHookTests {
|
|||
private static final McpSchema.ClientCapabilities CLIENT_CAPABILITIES = McpSchema.ClientCapabilities.builder()
|
||||
.build();
|
||||
|
||||
private static final McpSchema.Implementation CLIENT_INFO = new McpSchema.Implementation("test-client", "1.0.0");
|
||||
private static final McpSchema.Implementation CLIENT_INFO = McpSchema.Implementation.builder("test-client", "1.0.0")
|
||||
.build();
|
||||
|
||||
private static final List<String> PROTOCOL_VERSIONS = List.of("1.0.0", "2.0.0");
|
||||
|
||||
private static final McpSchema.InitializeResult MOCK_INIT_RESULT = new McpSchema.InitializeResult("2.0.0",
|
||||
McpSchema.ServerCapabilities.builder().build(), new McpSchema.Implementation("test-server", "1.0.0"),
|
||||
"Test instructions");
|
||||
private static final McpSchema.InitializeResult MOCK_INIT_RESULT = McpSchema.InitializeResult
|
||||
.builder("2.0.0", McpSchema.ServerCapabilities.builder().build(),
|
||||
McpSchema.Implementation.builder("test-server", "1.0.0").build())
|
||||
.instructions("Test instructions")
|
||||
.build();
|
||||
|
||||
@Mock
|
||||
private McpClientSession mockClientSession;
|
||||
|
|
|
|||
|
|
@ -44,13 +44,16 @@ class LifecycleInitializerTests {
|
|||
private static final McpSchema.ClientCapabilities CLIENT_CAPABILITIES = McpSchema.ClientCapabilities.builder()
|
||||
.build();
|
||||
|
||||
private static final McpSchema.Implementation CLIENT_INFO = new McpSchema.Implementation("test-client", "1.0.0");
|
||||
private static final McpSchema.Implementation CLIENT_INFO = McpSchema.Implementation.builder("test-client", "1.0.0")
|
||||
.build();
|
||||
|
||||
private static final List<String> PROTOCOL_VERSIONS = List.of("1.0.0", "2.0.0");
|
||||
|
||||
private static final McpSchema.InitializeResult MOCK_INIT_RESULT = new McpSchema.InitializeResult("2.0.0",
|
||||
McpSchema.ServerCapabilities.builder().build(), new McpSchema.Implementation("test-server", "1.0.0"),
|
||||
"Test instructions");
|
||||
private static final McpSchema.InitializeResult MOCK_INIT_RESULT = McpSchema.InitializeResult
|
||||
.builder("2.0.0", McpSchema.ServerCapabilities.builder().build(),
|
||||
McpSchema.Implementation.builder("test-server", "1.0.0").build())
|
||||
.instructions("Test instructions")
|
||||
.build();
|
||||
|
||||
@Mock
|
||||
private McpClientSession mockClientSession;
|
||||
|
|
@ -148,10 +151,12 @@ class LifecycleInitializerTests {
|
|||
|
||||
@Test
|
||||
void shouldFailForUnsupportedProtocolVersion() {
|
||||
McpSchema.InitializeResult unsupportedResult = new McpSchema.InitializeResult("999.0.0", // Unsupported
|
||||
// version
|
||||
McpSchema.ServerCapabilities.builder().build(), new McpSchema.Implementation("test-server", "1.0.0"),
|
||||
"Test instructions");
|
||||
McpSchema.InitializeResult unsupportedResult = McpSchema.InitializeResult.builder("999.0.0", // Unsupported
|
||||
// version
|
||||
McpSchema.ServerCapabilities.builder().build(),
|
||||
McpSchema.Implementation.builder("test-server", "1.0.0").build())
|
||||
.instructions("Test instructions")
|
||||
.build();
|
||||
|
||||
when(mockClientSession.sendRequest(eq(McpSchema.METHOD_INITIALIZE), any(), any()))
|
||||
.thenReturn(Mono.just(unsupportedResult));
|
||||
|
|
@ -342,8 +347,11 @@ class LifecycleInitializerTests {
|
|||
|
||||
when(mockClientSession.sendRequest(eq(McpSchema.METHOD_INITIALIZE), any(), any())).thenAnswer(invocation -> {
|
||||
capturedRequest.set((McpSchema.InitializeRequest) invocation.getArgument(1));
|
||||
return Mono.just(new McpSchema.InitializeResult("4.0.0", McpSchema.ServerCapabilities.builder().build(),
|
||||
new McpSchema.Implementation("test-server", "1.0.0"), "Test instructions"));
|
||||
return Mono.just(McpSchema.InitializeResult
|
||||
.builder("4.0.0", McpSchema.ServerCapabilities.builder().build(),
|
||||
McpSchema.Implementation.builder("test-server", "1.0.0").build())
|
||||
.instructions("Test instructions")
|
||||
.build());
|
||||
});
|
||||
|
||||
StepVerifier.create(initializer.withInitialization("test", init -> Mono.just(init.initializeResult())))
|
||||
|
|
|
|||
|
|
@ -42,16 +42,15 @@ class AsyncToolSpecificationBuilderTest {
|
|||
@Test
|
||||
void builderShouldCreateValidAsyncToolSpecification() {
|
||||
|
||||
Tool tool = McpSchema.Tool.builder()
|
||||
.name("test-tool")
|
||||
.title("A test tool")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
Tool tool = McpSchema.Tool.builder("test-tool", EMPTY_JSON_SCHEMA).title("A test tool").build();
|
||||
|
||||
McpServerFeatures.AsyncToolSpecification specification = McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(tool)
|
||||
.callHandler((exchange, request) -> Mono
|
||||
.just(CallToolResult.builder().content(List.of(new TextContent("Test result"))).isError(false).build()))
|
||||
.callHandler((exchange,
|
||||
request) -> Mono.just(CallToolResult.builder()
|
||||
.content(List.of(TextContent.builder("Test result").build()))
|
||||
.isError(false)
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
assertThat(specification).isNotNull();
|
||||
|
|
@ -69,11 +68,7 @@ class AsyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void builderShouldThrowExceptionWhenCallToolIsNull() {
|
||||
Tool tool = McpSchema.Tool.builder()
|
||||
.name("test-tool")
|
||||
.title("A test tool")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
Tool tool = McpSchema.Tool.builder("test-tool", EMPTY_JSON_SCHEMA).title("A test tool").build();
|
||||
|
||||
assertThatThrownBy(() -> McpServerFeatures.AsyncToolSpecification.builder().tool(tool).build())
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
|
|
@ -82,11 +77,7 @@ class AsyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void builderShouldAllowMethodChaining() {
|
||||
Tool tool = McpSchema.Tool.builder()
|
||||
.name("test-tool")
|
||||
.title("A test tool")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
Tool tool = McpSchema.Tool.builder("test-tool", EMPTY_JSON_SCHEMA).title("A test tool").build();
|
||||
McpServerFeatures.AsyncToolSpecification.Builder builder = McpServerFeatures.AsyncToolSpecification.builder();
|
||||
|
||||
// Then - verify method chaining returns the same builder instance
|
||||
|
|
@ -98,20 +89,19 @@ class AsyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void builtSpecificationShouldExecuteCallToolCorrectly() {
|
||||
Tool tool = McpSchema.Tool.builder()
|
||||
.name("calculator")
|
||||
.title("Simple calculator")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
Tool tool = McpSchema.Tool.builder("calculator", EMPTY_JSON_SCHEMA).title("Simple calculator").build();
|
||||
String expectedResult = "42";
|
||||
|
||||
McpServerFeatures.AsyncToolSpecification specification = McpServerFeatures.AsyncToolSpecification.builder()
|
||||
.tool(tool)
|
||||
.callHandler((exchange, request) -> Mono.just(
|
||||
CallToolResult.builder().content(List.of(new TextContent(expectedResult))).isError(false).build()))
|
||||
.callHandler((exchange,
|
||||
request) -> Mono.just(CallToolResult.builder()
|
||||
.content(List.of(TextContent.builder(expectedResult).build()))
|
||||
.isError(false)
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
CallToolRequest request = new CallToolRequest("calculator", Map.of());
|
||||
CallToolRequest request = CallToolRequest.builder("calculator").build();
|
||||
Mono<CallToolResult> resultMono = specification.callHandler().apply(null, request);
|
||||
|
||||
StepVerifier.create(resultMono).assertNext(result -> {
|
||||
|
|
@ -125,18 +115,14 @@ class AsyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void fromSyncShouldConvertSyncToolSpecificationCorrectly() {
|
||||
Tool tool = McpSchema.Tool.builder()
|
||||
.name("sync-tool")
|
||||
.title("A sync tool")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
Tool tool = McpSchema.Tool.builder("sync-tool", EMPTY_JSON_SCHEMA).title("A sync tool").build();
|
||||
String expectedResult = "sync result";
|
||||
|
||||
// Create a sync tool specification
|
||||
McpServerFeatures.SyncToolSpecification syncSpec = McpServerFeatures.SyncToolSpecification.builder()
|
||||
.tool(tool)
|
||||
.callHandler((exchange, request) -> CallToolResult.builder()
|
||||
.content(List.of(new TextContent(expectedResult)))
|
||||
.content(List.of(TextContent.builder(expectedResult).build()))
|
||||
.isError(false)
|
||||
.build())
|
||||
.build();
|
||||
|
|
@ -150,7 +136,7 @@ class AsyncToolSpecificationBuilderTest {
|
|||
assertThat(asyncSpec.callHandler()).isNotNull();
|
||||
|
||||
// Test that the converted async specification works correctly
|
||||
CallToolRequest request = new CallToolRequest("sync-tool", Map.of("param", "value"));
|
||||
CallToolRequest request = CallToolRequest.builder("sync-tool").arguments(Map.of("param", "value")).build();
|
||||
Mono<CallToolResult> resultMono = asyncSpec.callHandler().apply(null, request);
|
||||
|
||||
StepVerifier.create(resultMono).assertNext(result -> {
|
||||
|
|
@ -193,7 +179,7 @@ class AsyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void defaultShouldThrowOnInvalidName() {
|
||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||
Tool invalidTool = Tool.builder("invalid tool name", EMPTY_JSON_SCHEMA).build();
|
||||
|
||||
assertThatThrownBy(
|
||||
() -> McpServer.async(transportProvider).toolCall(invalidTool, (exchange, request) -> null))
|
||||
|
|
@ -204,7 +190,7 @@ class AsyncToolSpecificationBuilderTest {
|
|||
@Test
|
||||
void lenientDefaultShouldLogOnInvalidName() {
|
||||
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||
Tool invalidTool = Tool.builder("invalid tool name", EMPTY_JSON_SCHEMA).build();
|
||||
|
||||
assertThatCode(() -> McpServer.async(transportProvider).toolCall(invalidTool, (exchange, request) -> null))
|
||||
.doesNotThrowAnyException();
|
||||
|
|
@ -213,7 +199,7 @@ class AsyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void lenientConfigurationShouldLogOnInvalidName() {
|
||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||
Tool invalidTool = Tool.builder("invalid tool name", EMPTY_JSON_SCHEMA).build();
|
||||
|
||||
assertThatCode(() -> McpServer.async(transportProvider)
|
||||
.strictToolNameValidation(false)
|
||||
|
|
@ -224,7 +210,7 @@ class AsyncToolSpecificationBuilderTest {
|
|||
@Test
|
||||
void serverConfigurationShouldOverrideDefault() {
|
||||
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||
Tool invalidTool = Tool.builder("invalid tool name", EMPTY_JSON_SCHEMA).build();
|
||||
|
||||
assertThatThrownBy(() -> McpServer.async(transportProvider)
|
||||
.strictToolNameValidation(true)
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class McpAsyncServerExchangeTests {
|
|||
|
||||
clientCapabilities = McpSchema.ClientCapabilities.builder().roots(true).build();
|
||||
|
||||
clientInfo = new McpSchema.Implementation("test-client", "1.0.0");
|
||||
clientInfo = McpSchema.Implementation.builder("test-client", "1.0.0").build();
|
||||
|
||||
exchange = new McpAsyncServerExchange("testSessionId", mockSession, clientCapabilities, clientInfo,
|
||||
McpTransportContext.EMPTY);
|
||||
|
|
@ -61,9 +61,10 @@ class McpAsyncServerExchangeTests {
|
|||
@Test
|
||||
void testListRootsWithSinglePage() {
|
||||
|
||||
List<McpSchema.Root> roots = Arrays.asList(new McpSchema.Root("file:///home/user/project1", "Project 1"),
|
||||
new McpSchema.Root("file:///home/user/project2", "Project 2"));
|
||||
McpSchema.ListRootsResult singlePageResult = new McpSchema.ListRootsResult(roots, null);
|
||||
List<McpSchema.Root> roots = Arrays.asList(
|
||||
McpSchema.Root.builder("file:///home/user/project1").name("Project 1").build(),
|
||||
McpSchema.Root.builder("file:///home/user/project2").name("Project 2").build());
|
||||
McpSchema.ListRootsResult singlePageResult = McpSchema.ListRootsResult.builder(roots).build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), any(McpSchema.PaginatedRequest.class),
|
||||
any(TypeRef.class)))
|
||||
|
|
@ -78,7 +79,7 @@ class McpAsyncServerExchangeTests {
|
|||
assertThat(result.nextCursor()).isNull();
|
||||
|
||||
// Verify that the returned list is unmodifiable
|
||||
assertThatThrownBy(() -> result.roots().add(new McpSchema.Root("file:///test", "Test")))
|
||||
assertThatThrownBy(() -> result.roots().add(McpSchema.Root.builder("file:///test").name("Test").build()))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
|
@ -86,14 +87,18 @@ class McpAsyncServerExchangeTests {
|
|||
@Test
|
||||
void testListRootsWithMultiplePages() {
|
||||
|
||||
List<McpSchema.Root> page1Roots = Arrays.asList(new McpSchema.Root("file:///home/user/project1", "Project 1"),
|
||||
new McpSchema.Root("file:///home/user/project2", "Project 2"));
|
||||
List<McpSchema.Root> page2Roots = Arrays.asList(new McpSchema.Root("file:///home/user/project3", "Project 3"));
|
||||
List<McpSchema.Root> page1Roots = Arrays.asList(
|
||||
McpSchema.Root.builder("file:///home/user/project1").name("Project 1").build(),
|
||||
McpSchema.Root.builder("file:///home/user/project2").name("Project 2").build());
|
||||
List<McpSchema.Root> page2Roots = Arrays
|
||||
.asList(McpSchema.Root.builder("file:///home/user/project3").name("Project 3").build());
|
||||
|
||||
McpSchema.ListRootsResult page1Result = new McpSchema.ListRootsResult(page1Roots, "cursor1");
|
||||
McpSchema.ListRootsResult page2Result = new McpSchema.ListRootsResult(page2Roots, null);
|
||||
McpSchema.ListRootsResult page1Result = McpSchema.ListRootsResult.builder(page1Roots)
|
||||
.nextCursor("cursor1")
|
||||
.build();
|
||||
McpSchema.ListRootsResult page2Result = McpSchema.ListRootsResult.builder(page2Roots).build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), eq(new McpSchema.PaginatedRequest(null)),
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), eq(new McpSchema.PaginatedRequest()),
|
||||
any(TypeRef.class)))
|
||||
.thenReturn(Mono.just(page1Result));
|
||||
|
||||
|
|
@ -109,7 +114,7 @@ class McpAsyncServerExchangeTests {
|
|||
assertThat(result.nextCursor()).isNull();
|
||||
|
||||
// Verify that the returned list is unmodifiable
|
||||
assertThatThrownBy(() -> result.roots().add(new McpSchema.Root("file:///test", "Test")))
|
||||
assertThatThrownBy(() -> result.roots().add(McpSchema.Root.builder("file:///test").name("Test").build()))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
|
@ -117,7 +122,7 @@ class McpAsyncServerExchangeTests {
|
|||
@Test
|
||||
void testListRootsWithEmptyResult() {
|
||||
|
||||
McpSchema.ListRootsResult emptyResult = new McpSchema.ListRootsResult(new ArrayList<>(), null);
|
||||
McpSchema.ListRootsResult emptyResult = McpSchema.ListRootsResult.builder(new ArrayList<>()).build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), any(McpSchema.PaginatedRequest.class),
|
||||
any(TypeRef.class)))
|
||||
|
|
@ -128,7 +133,7 @@ class McpAsyncServerExchangeTests {
|
|||
assertThat(result.nextCursor()).isNull();
|
||||
|
||||
// Verify that the returned list is unmodifiable
|
||||
assertThatThrownBy(() -> result.roots().add(new McpSchema.Root("file:///test", "Test")))
|
||||
assertThatThrownBy(() -> result.roots().add(McpSchema.Root.builder("file:///test").name("Test").build()))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
|
@ -136,8 +141,9 @@ class McpAsyncServerExchangeTests {
|
|||
@Test
|
||||
void testListRootsWithSpecificCursor() {
|
||||
|
||||
List<McpSchema.Root> roots = Arrays.asList(new McpSchema.Root("file:///home/user/project3", "Project 3"));
|
||||
McpSchema.ListRootsResult result = new McpSchema.ListRootsResult(roots, "nextCursor");
|
||||
List<McpSchema.Root> roots = Arrays
|
||||
.asList(McpSchema.Root.builder("file:///home/user/project3").name("Project 3").build());
|
||||
McpSchema.ListRootsResult result = McpSchema.ListRootsResult.builder(roots).nextCursor("nextCursor").build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), eq(new McpSchema.PaginatedRequest("someCursor")),
|
||||
any(TypeRef.class)))
|
||||
|
|
@ -167,12 +173,14 @@ class McpAsyncServerExchangeTests {
|
|||
void testListRootsUnmodifiabilityAfterAccumulation() {
|
||||
|
||||
List<McpSchema.Root> page1Roots = new ArrayList<>(
|
||||
Arrays.asList(new McpSchema.Root("file:///home/user/project1", "Project 1")));
|
||||
Arrays.asList(McpSchema.Root.builder("file:///home/user/project1").name("Project 1").build()));
|
||||
List<McpSchema.Root> page2Roots = new ArrayList<>(
|
||||
Arrays.asList(new McpSchema.Root("file:///home/user/project2", "Project 2")));
|
||||
Arrays.asList(McpSchema.Root.builder("file:///home/user/project2").name("Project 2").build()));
|
||||
|
||||
McpSchema.ListRootsResult page1Result = new McpSchema.ListRootsResult(page1Roots, "cursor1");
|
||||
McpSchema.ListRootsResult page2Result = new McpSchema.ListRootsResult(page2Roots, null);
|
||||
McpSchema.ListRootsResult page1Result = McpSchema.ListRootsResult.builder(page1Roots)
|
||||
.nextCursor("cursor1")
|
||||
.build();
|
||||
McpSchema.ListRootsResult page2Result = McpSchema.ListRootsResult.builder(page2Roots).build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), eq(new McpSchema.PaginatedRequest(null)),
|
||||
any(TypeRef.class)))
|
||||
|
|
@ -187,7 +195,7 @@ class McpAsyncServerExchangeTests {
|
|||
assertThat(result.roots()).hasSize(2);
|
||||
|
||||
// Verify that the returned list is unmodifiable
|
||||
assertThatThrownBy(() -> result.roots().add(new McpSchema.Root("file:///test", "Test")))
|
||||
assertThatThrownBy(() -> result.roots().add(McpSchema.Root.builder("file:///test").name("Test").build()))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
|
||||
// Verify that clear() also throws UnsupportedOperationException
|
||||
|
|
@ -227,10 +235,9 @@ class McpAsyncServerExchangeTests {
|
|||
|
||||
@Test
|
||||
void testLoggingNotificationWithAllowedLevel() {
|
||||
McpSchema.LoggingMessageNotification notification = McpSchema.LoggingMessageNotification.builder()
|
||||
.level(McpSchema.LoggingLevel.ERROR)
|
||||
McpSchema.LoggingMessageNotification notification = McpSchema.LoggingMessageNotification
|
||||
.builder(McpSchema.LoggingLevel.ERROR, "Test error message")
|
||||
.logger("test-logger")
|
||||
.data("Test error message")
|
||||
.build();
|
||||
|
||||
when(mockSession.isNotificationForLevelAllowed(any())).thenReturn(Boolean.TRUE);
|
||||
|
|
@ -248,10 +255,9 @@ class McpAsyncServerExchangeTests {
|
|||
exchange.setMinLoggingLevel(McpSchema.LoggingLevel.DEBUG);
|
||||
verify(mockSession, times(1)).setMinLoggingLevel(eq(McpSchema.LoggingLevel.DEBUG));
|
||||
|
||||
McpSchema.LoggingMessageNotification debugNotification = McpSchema.LoggingMessageNotification.builder()
|
||||
.level(McpSchema.LoggingLevel.DEBUG)
|
||||
McpSchema.LoggingMessageNotification debugNotification = McpSchema.LoggingMessageNotification
|
||||
.builder(McpSchema.LoggingLevel.DEBUG, "Debug message that should be filtered")
|
||||
.logger("test-logger")
|
||||
.data("Debug message that should be filtered")
|
||||
.build();
|
||||
|
||||
when(mockSession.isNotificationForLevelAllowed(eq(McpSchema.LoggingLevel.DEBUG))).thenReturn(Boolean.TRUE);
|
||||
|
|
@ -264,10 +270,9 @@ class McpAsyncServerExchangeTests {
|
|||
verify(mockSession, times(1)).sendNotification(eq(McpSchema.METHOD_NOTIFICATION_MESSAGE),
|
||||
eq(debugNotification));
|
||||
|
||||
McpSchema.LoggingMessageNotification warningNotification = McpSchema.LoggingMessageNotification.builder()
|
||||
.level(McpSchema.LoggingLevel.WARNING)
|
||||
McpSchema.LoggingMessageNotification warningNotification = McpSchema.LoggingMessageNotification
|
||||
.builder(McpSchema.LoggingLevel.WARNING, "Debug message that should be filtered")
|
||||
.logger("test-logger")
|
||||
.data("Debug message that should be filtered")
|
||||
.build();
|
||||
|
||||
StepVerifier.create(exchange.loggingNotification(warningNotification)).verifyComplete();
|
||||
|
|
@ -279,10 +284,9 @@ class McpAsyncServerExchangeTests {
|
|||
|
||||
@Test
|
||||
void testLoggingNotificationWithSessionError() {
|
||||
McpSchema.LoggingMessageNotification notification = McpSchema.LoggingMessageNotification.builder()
|
||||
.level(McpSchema.LoggingLevel.ERROR)
|
||||
McpSchema.LoggingMessageNotification notification = McpSchema.LoggingMessageNotification
|
||||
.builder(McpSchema.LoggingLevel.ERROR, "Test error message")
|
||||
.logger("test-logger")
|
||||
.data("Test error message")
|
||||
.build();
|
||||
|
||||
when(mockSession.isNotificationForLevelAllowed(any())).thenReturn(Boolean.TRUE);
|
||||
|
|
@ -304,8 +308,8 @@ class McpAsyncServerExchangeTests {
|
|||
McpAsyncServerExchange exchangeWithNullCapabilities = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||
null, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide your name")
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide your name", Map.of("type", "object"))
|
||||
.build();
|
||||
|
||||
StepVerifier.create(exchangeWithNullCapabilities.createElicitation(elicitRequest))
|
||||
|
|
@ -328,8 +332,8 @@ class McpAsyncServerExchangeTests {
|
|||
McpAsyncServerExchange exchangeWithoutElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||
capabilitiesWithoutElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide your name")
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide your name", Map.of("type", "object"))
|
||||
.build();
|
||||
|
||||
StepVerifier.create(exchangeWithoutElicitation.createElicitation(elicitRequest)).verifyErrorSatisfies(error -> {
|
||||
|
|
@ -359,17 +363,15 @@ class McpAsyncServerExchangeTests {
|
|||
java.util.Map.of("type", "number")));
|
||||
requestedSchema.put("required", java.util.List.of("name"));
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide your personal information")
|
||||
.requestedSchema(requestedSchema)
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide your personal information", requestedSchema)
|
||||
.build();
|
||||
|
||||
java.util.Map<String, Object> responseContent = new java.util.HashMap<>();
|
||||
responseContent.put("name", "John Doe");
|
||||
responseContent.put("age", 30);
|
||||
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder()
|
||||
.message(McpSchema.ElicitResult.Action.ACCEPT)
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder(McpSchema.ElicitResult.Action.ACCEPT)
|
||||
.content(responseContent)
|
||||
.build();
|
||||
|
||||
|
|
@ -395,12 +397,11 @@ class McpAsyncServerExchangeTests {
|
|||
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide sensitive information")
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide sensitive information", Map.of("type", "object"))
|
||||
.build();
|
||||
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder()
|
||||
.message(McpSchema.ElicitResult.Action.DECLINE)
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder(McpSchema.ElicitResult.Action.DECLINE)
|
||||
.build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class)))
|
||||
|
|
@ -422,12 +423,11 @@ class McpAsyncServerExchangeTests {
|
|||
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide your information")
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide your information", Map.of("type", "object"))
|
||||
.build();
|
||||
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder()
|
||||
.message(McpSchema.ElicitResult.Action.CANCEL)
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder(McpSchema.ElicitResult.Action.CANCEL)
|
||||
.build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class)))
|
||||
|
|
@ -449,8 +449,8 @@ class McpAsyncServerExchangeTests {
|
|||
McpAsyncServerExchange exchangeWithElicitation = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide your name")
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide your name", Map.of("type", "object"))
|
||||
.build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class)))
|
||||
|
|
@ -471,9 +471,10 @@ class McpAsyncServerExchangeTests {
|
|||
McpAsyncServerExchange exchangeWithNullCapabilities = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||
null, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays
|
||||
.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest
|
||||
.builder(Arrays.asList(McpSchema.SamplingMessage
|
||||
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Hello, world!").build())
|
||||
.build()), 1000)
|
||||
.build();
|
||||
|
||||
StepVerifier.create(exchangeWithNullCapabilities.createMessage(createMessageRequest))
|
||||
|
|
@ -497,9 +498,10 @@ class McpAsyncServerExchangeTests {
|
|||
McpAsyncServerExchange exchangeWithoutSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||
capabilitiesWithoutSampling, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays
|
||||
.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest
|
||||
.builder(Arrays.asList(McpSchema.SamplingMessage
|
||||
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Hello, world!").build())
|
||||
.build()), 1000)
|
||||
.build();
|
||||
|
||||
StepVerifier.create(exchangeWithoutSampling.createMessage(createMessageRequest)).verifyErrorSatisfies(error -> {
|
||||
|
|
@ -522,15 +524,15 @@ class McpAsyncServerExchangeTests {
|
|||
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays
|
||||
.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest
|
||||
.builder(Arrays.asList(McpSchema.SamplingMessage
|
||||
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Hello, world!").build())
|
||||
.build()), 1000)
|
||||
.build();
|
||||
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder()
|
||||
.role(McpSchema.Role.ASSISTANT)
|
||||
.content(new McpSchema.TextContent("Hello! How can I help you today?"))
|
||||
.model("gpt-4")
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult
|
||||
.builder(McpSchema.Role.ASSISTANT,
|
||||
McpSchema.TextContent.builder("Hello! How can I help you today?").build(), "gpt-4")
|
||||
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
|
||||
.build();
|
||||
|
||||
|
|
@ -559,16 +561,20 @@ class McpAsyncServerExchangeTests {
|
|||
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
// Create request with image content
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER,
|
||||
new McpSchema.ImageContent(null, "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD...",
|
||||
"image/jpeg"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder(Arrays.asList(
|
||||
McpSchema.SamplingMessage
|
||||
.builder(McpSchema.Role.USER,
|
||||
McpSchema.ImageContent
|
||||
.builder("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD...", "image/jpeg")
|
||||
.build())
|
||||
.build()),
|
||||
1000)
|
||||
.build();
|
||||
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder()
|
||||
.role(McpSchema.Role.ASSISTANT)
|
||||
.content(new McpSchema.TextContent("I can see an image. It appears to be a photograph."))
|
||||
.model("gpt-4-vision")
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult
|
||||
.builder(McpSchema.Role.ASSISTANT,
|
||||
McpSchema.TextContent.builder("I can see an image. It appears to be a photograph.").build(),
|
||||
"gpt-4-vision")
|
||||
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
|
||||
.build();
|
||||
|
||||
|
|
@ -593,9 +599,10 @@ class McpAsyncServerExchangeTests {
|
|||
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays
|
||||
.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder(Arrays.asList(
|
||||
McpSchema.SamplingMessage.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Hello").build())
|
||||
.build()),
|
||||
1000)
|
||||
.build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE), eq(createMessageRequest),
|
||||
|
|
@ -617,16 +624,17 @@ class McpAsyncServerExchangeTests {
|
|||
McpAsyncServerExchange exchangeWithSampling = new McpAsyncServerExchange("testSessionId", mockSession,
|
||||
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER,
|
||||
new McpSchema.TextContent("What files are available?"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest
|
||||
.builder(Arrays.asList(McpSchema.SamplingMessage
|
||||
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("What files are available?").build())
|
||||
.build()), 1000)
|
||||
.includeContext(McpSchema.CreateMessageRequest.ContextInclusionStrategy.ALL_SERVERS)
|
||||
.build();
|
||||
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder()
|
||||
.role(McpSchema.Role.ASSISTANT)
|
||||
.content(new McpSchema.TextContent("Based on the available context, I can see several files..."))
|
||||
.model("gpt-4")
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult
|
||||
.builder(McpSchema.Role.ASSISTANT,
|
||||
McpSchema.TextContent.builder("Based on the available context, I can see several files...").build(),
|
||||
"gpt-4")
|
||||
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
|
||||
.build();
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class McpSyncServerExchangeTests {
|
|||
|
||||
clientCapabilities = McpSchema.ClientCapabilities.builder().roots(true).build();
|
||||
|
||||
clientInfo = new McpSchema.Implementation("test-client", "1.0.0");
|
||||
clientInfo = McpSchema.Implementation.builder("test-client", "1.0.0").build();
|
||||
|
||||
asyncExchange = new McpAsyncServerExchange("testSessionId", mockSession, clientCapabilities, clientInfo,
|
||||
McpTransportContext.EMPTY);
|
||||
|
|
@ -63,9 +63,10 @@ class McpSyncServerExchangeTests {
|
|||
@Test
|
||||
void testListRootsWithSinglePage() {
|
||||
|
||||
List<McpSchema.Root> roots = Arrays.asList(new McpSchema.Root("file:///home/user/project1", "Project 1"),
|
||||
new McpSchema.Root("file:///home/user/project2", "Project 2"));
|
||||
McpSchema.ListRootsResult singlePageResult = new McpSchema.ListRootsResult(roots, null);
|
||||
List<McpSchema.Root> roots = Arrays.asList(
|
||||
McpSchema.Root.builder("file:///home/user/project1").name("Project 1").build(),
|
||||
McpSchema.Root.builder("file:///home/user/project2").name("Project 2").build());
|
||||
McpSchema.ListRootsResult singlePageResult = McpSchema.ListRootsResult.builder(roots).build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), any(McpSchema.PaginatedRequest.class),
|
||||
any(TypeRef.class)))
|
||||
|
|
@ -81,19 +82,23 @@ class McpSyncServerExchangeTests {
|
|||
assertThat(result.nextCursor()).isNull();
|
||||
|
||||
// Verify that the returned list is unmodifiable
|
||||
assertThatThrownBy(() -> result.roots().add(new McpSchema.Root("file:///test", "Test")))
|
||||
assertThatThrownBy(() -> result.roots().add(McpSchema.Root.builder("file:///test").name("Test").build()))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testListRootsWithMultiplePages() {
|
||||
|
||||
List<McpSchema.Root> page1Roots = Arrays.asList(new McpSchema.Root("file:///home/user/project1", "Project 1"),
|
||||
new McpSchema.Root("file:///home/user/project2", "Project 2"));
|
||||
List<McpSchema.Root> page2Roots = Arrays.asList(new McpSchema.Root("file:///home/user/project3", "Project 3"));
|
||||
List<McpSchema.Root> page1Roots = Arrays.asList(
|
||||
McpSchema.Root.builder("file:///home/user/project1").name("Project 1").build(),
|
||||
McpSchema.Root.builder("file:///home/user/project2").name("Project 2").build());
|
||||
List<McpSchema.Root> page2Roots = Arrays
|
||||
.asList(McpSchema.Root.builder("file:///home/user/project3").name("Project 3").build());
|
||||
|
||||
McpSchema.ListRootsResult page1Result = new McpSchema.ListRootsResult(page1Roots, "cursor1");
|
||||
McpSchema.ListRootsResult page2Result = new McpSchema.ListRootsResult(page2Roots, null);
|
||||
McpSchema.ListRootsResult page1Result = McpSchema.ListRootsResult.builder(page1Roots)
|
||||
.nextCursor("cursor1")
|
||||
.build();
|
||||
McpSchema.ListRootsResult page2Result = McpSchema.ListRootsResult.builder(page2Roots).build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), eq(new McpSchema.PaginatedRequest(null)),
|
||||
any(TypeRef.class)))
|
||||
|
|
@ -112,14 +117,14 @@ class McpSyncServerExchangeTests {
|
|||
assertThat(result.nextCursor()).isNull();
|
||||
|
||||
// Verify that the returned list is unmodifiable
|
||||
assertThatThrownBy(() -> result.roots().add(new McpSchema.Root("file:///test", "Test")))
|
||||
assertThatThrownBy(() -> result.roots().add(McpSchema.Root.builder("file:///test").name("Test").build()))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testListRootsWithEmptyResult() {
|
||||
|
||||
McpSchema.ListRootsResult emptyResult = new McpSchema.ListRootsResult(new ArrayList<>(), null);
|
||||
McpSchema.ListRootsResult emptyResult = McpSchema.ListRootsResult.builder(new ArrayList<>()).build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), any(McpSchema.PaginatedRequest.class),
|
||||
any(TypeRef.class)))
|
||||
|
|
@ -131,15 +136,16 @@ class McpSyncServerExchangeTests {
|
|||
assertThat(result.nextCursor()).isNull();
|
||||
|
||||
// Verify that the returned list is unmodifiable
|
||||
assertThatThrownBy(() -> result.roots().add(new McpSchema.Root("file:///test", "Test")))
|
||||
assertThatThrownBy(() -> result.roots().add(McpSchema.Root.builder("file:///test").name("Test").build()))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testListRootsWithSpecificCursor() {
|
||||
|
||||
List<McpSchema.Root> roots = Arrays.asList(new McpSchema.Root("file:///home/user/project3", "Project 3"));
|
||||
McpSchema.ListRootsResult result = new McpSchema.ListRootsResult(roots, "nextCursor");
|
||||
List<McpSchema.Root> roots = Arrays
|
||||
.asList(McpSchema.Root.builder("file:///home/user/project3").name("Project 3").build());
|
||||
McpSchema.ListRootsResult result = McpSchema.ListRootsResult.builder(roots).nextCursor("nextCursor").build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), eq(new McpSchema.PaginatedRequest("someCursor")),
|
||||
any(TypeRef.class)))
|
||||
|
|
@ -167,12 +173,14 @@ class McpSyncServerExchangeTests {
|
|||
void testListRootsUnmodifiabilityAfterAccumulation() {
|
||||
|
||||
List<McpSchema.Root> page1Roots = new ArrayList<>(
|
||||
Arrays.asList(new McpSchema.Root("file:///home/user/project1", "Project 1")));
|
||||
Arrays.asList(McpSchema.Root.builder("file:///home/user/project1").name("Project 1").build()));
|
||||
List<McpSchema.Root> page2Roots = new ArrayList<>(
|
||||
Arrays.asList(new McpSchema.Root("file:///home/user/project2", "Project 2")));
|
||||
Arrays.asList(McpSchema.Root.builder("file:///home/user/project2").name("Project 2").build()));
|
||||
|
||||
McpSchema.ListRootsResult page1Result = new McpSchema.ListRootsResult(page1Roots, "cursor1");
|
||||
McpSchema.ListRootsResult page2Result = new McpSchema.ListRootsResult(page2Roots, null);
|
||||
McpSchema.ListRootsResult page1Result = McpSchema.ListRootsResult.builder(page1Roots)
|
||||
.nextCursor("cursor1")
|
||||
.build();
|
||||
McpSchema.ListRootsResult page2Result = McpSchema.ListRootsResult.builder(page2Roots).build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ROOTS_LIST), eq(new McpSchema.PaginatedRequest(null)),
|
||||
any(TypeRef.class)))
|
||||
|
|
@ -188,7 +196,7 @@ class McpSyncServerExchangeTests {
|
|||
assertThat(result.roots()).hasSize(2);
|
||||
|
||||
// Verify that the returned list is unmodifiable
|
||||
assertThatThrownBy(() -> result.roots().add(new McpSchema.Root("file:///test", "Test")))
|
||||
assertThatThrownBy(() -> result.roots().add(McpSchema.Root.builder("file:///test").name("Test").build()))
|
||||
.isInstanceOf(UnsupportedOperationException.class);
|
||||
|
||||
// Verify that clear() also throws UnsupportedOperationException
|
||||
|
|
@ -221,10 +229,9 @@ class McpSyncServerExchangeTests {
|
|||
@Test
|
||||
void testLoggingNotificationWithAllowedLevel() {
|
||||
|
||||
McpSchema.LoggingMessageNotification notification = McpSchema.LoggingMessageNotification.builder()
|
||||
.level(McpSchema.LoggingLevel.ERROR)
|
||||
McpSchema.LoggingMessageNotification notification = McpSchema.LoggingMessageNotification
|
||||
.builder(McpSchema.LoggingLevel.ERROR, "Test error message")
|
||||
.logger("test-logger")
|
||||
.data("Test error message")
|
||||
.build();
|
||||
|
||||
when(mockSession.isNotificationForLevelAllowed(any())).thenReturn(Boolean.TRUE);
|
||||
|
|
@ -243,10 +250,9 @@ class McpSyncServerExchangeTests {
|
|||
asyncExchange.setMinLoggingLevel(McpSchema.LoggingLevel.DEBUG);
|
||||
verify(mockSession, times(1)).setMinLoggingLevel(McpSchema.LoggingLevel.DEBUG);
|
||||
|
||||
McpSchema.LoggingMessageNotification debugNotification = McpSchema.LoggingMessageNotification.builder()
|
||||
.level(McpSchema.LoggingLevel.DEBUG)
|
||||
McpSchema.LoggingMessageNotification debugNotification = McpSchema.LoggingMessageNotification
|
||||
.builder(McpSchema.LoggingLevel.DEBUG, "Debug message that should be filtered")
|
||||
.logger("test-logger")
|
||||
.data("Debug message that should be filtered")
|
||||
.build();
|
||||
|
||||
when(mockSession.isNotificationForLevelAllowed(eq(McpSchema.LoggingLevel.DEBUG))).thenReturn(Boolean.TRUE);
|
||||
|
|
@ -259,10 +265,9 @@ class McpSyncServerExchangeTests {
|
|||
verify(mockSession, times(1)).sendNotification(eq(McpSchema.METHOD_NOTIFICATION_MESSAGE),
|
||||
eq(debugNotification));
|
||||
|
||||
McpSchema.LoggingMessageNotification warningNotification = McpSchema.LoggingMessageNotification.builder()
|
||||
.level(McpSchema.LoggingLevel.WARNING)
|
||||
McpSchema.LoggingMessageNotification warningNotification = McpSchema.LoggingMessageNotification
|
||||
.builder(McpSchema.LoggingLevel.WARNING, "Debug message that should be filtered")
|
||||
.logger("test-logger")
|
||||
.data("Debug message that should be filtered")
|
||||
.build();
|
||||
|
||||
exchange.loggingNotification(warningNotification);
|
||||
|
|
@ -275,10 +280,9 @@ class McpSyncServerExchangeTests {
|
|||
@Test
|
||||
void testLoggingNotificationWithSessionError() {
|
||||
|
||||
McpSchema.LoggingMessageNotification notification = McpSchema.LoggingMessageNotification.builder()
|
||||
.level(McpSchema.LoggingLevel.ERROR)
|
||||
McpSchema.LoggingMessageNotification notification = McpSchema.LoggingMessageNotification
|
||||
.builder(McpSchema.LoggingLevel.ERROR, "Test error message")
|
||||
.logger("test-logger")
|
||||
.data("Test error message")
|
||||
.build();
|
||||
|
||||
when(mockSession.isNotificationForLevelAllowed(any())).thenReturn(Boolean.TRUE);
|
||||
|
|
@ -301,8 +305,8 @@ class McpSyncServerExchangeTests {
|
|||
McpSyncServerExchange exchangeWithNullCapabilities = new McpSyncServerExchange(
|
||||
asyncExchangeWithNullCapabilities);
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide your name")
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide your name", Map.of("type", "object"))
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> exchangeWithNullCapabilities.createElicitation(elicitRequest))
|
||||
|
|
@ -324,8 +328,8 @@ class McpSyncServerExchangeTests {
|
|||
mockSession, capabilitiesWithoutElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||
McpSyncServerExchange exchangeWithoutElicitation = new McpSyncServerExchange(asyncExchangeWithoutElicitation);
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide your name")
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide your name", Map.of("type", "object"))
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> exchangeWithoutElicitation.createElicitation(elicitRequest))
|
||||
|
|
@ -355,17 +359,15 @@ class McpSyncServerExchangeTests {
|
|||
java.util.Map.of("type", "number")));
|
||||
requestedSchema.put("required", java.util.List.of("name"));
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide your personal information")
|
||||
.requestedSchema(requestedSchema)
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide your personal information", requestedSchema)
|
||||
.build();
|
||||
|
||||
java.util.Map<String, Object> responseContent = new java.util.HashMap<>();
|
||||
responseContent.put("name", "John Doe");
|
||||
responseContent.put("age", 30);
|
||||
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder()
|
||||
.message(McpSchema.ElicitResult.Action.ACCEPT)
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder(McpSchema.ElicitResult.Action.ACCEPT)
|
||||
.content(responseContent)
|
||||
.build();
|
||||
|
||||
|
|
@ -392,12 +394,11 @@ class McpSyncServerExchangeTests {
|
|||
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide sensitive information")
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide sensitive information", Map.of("type", "object"))
|
||||
.build();
|
||||
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder()
|
||||
.message(McpSchema.ElicitResult.Action.DECLINE)
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder(McpSchema.ElicitResult.Action.DECLINE)
|
||||
.build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class)))
|
||||
|
|
@ -420,12 +421,11 @@ class McpSyncServerExchangeTests {
|
|||
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide your information")
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide your information", Map.of("type", "object"))
|
||||
.build();
|
||||
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder()
|
||||
.message(McpSchema.ElicitResult.Action.CANCEL)
|
||||
McpSchema.ElicitResult expectedResult = McpSchema.ElicitResult.builder(McpSchema.ElicitResult.Action.CANCEL)
|
||||
.build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class)))
|
||||
|
|
@ -448,8 +448,8 @@ class McpSyncServerExchangeTests {
|
|||
capabilitiesWithElicitation, clientInfo, McpTransportContext.EMPTY);
|
||||
McpSyncServerExchange exchangeWithElicitation = new McpSyncServerExchange(asyncExchangeWithElicitation);
|
||||
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest.builder()
|
||||
.message("Please provide your name")
|
||||
McpSchema.ElicitRequest elicitRequest = McpSchema.ElicitRequest
|
||||
.builder("Please provide your name", Map.of("type", "object"))
|
||||
.build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_ELICITATION_CREATE), eq(elicitRequest), any(TypeRef.class)))
|
||||
|
|
@ -472,9 +472,10 @@ class McpSyncServerExchangeTests {
|
|||
McpSyncServerExchange exchangeWithNullCapabilities = new McpSyncServerExchange(
|
||||
asyncExchangeWithNullCapabilities);
|
||||
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays
|
||||
.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest
|
||||
.builder(Arrays.asList(McpSchema.SamplingMessage
|
||||
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Hello, world!").build())
|
||||
.build()), 1000)
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> exchangeWithNullCapabilities.createMessage(createMessageRequest))
|
||||
|
|
@ -497,9 +498,10 @@ class McpSyncServerExchangeTests {
|
|||
capabilitiesWithoutSampling, clientInfo, McpTransportContext.EMPTY);
|
||||
McpSyncServerExchange exchangeWithoutSampling = new McpSyncServerExchange(asyncExchangeWithoutSampling);
|
||||
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays
|
||||
.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest
|
||||
.builder(Arrays.asList(McpSchema.SamplingMessage
|
||||
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Hello, world!").build())
|
||||
.build()), 1000)
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> exchangeWithoutSampling.createMessage(createMessageRequest))
|
||||
|
|
@ -522,15 +524,15 @@ class McpSyncServerExchangeTests {
|
|||
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
||||
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays
|
||||
.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello, world!"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest
|
||||
.builder(Arrays.asList(McpSchema.SamplingMessage
|
||||
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Hello, world!").build())
|
||||
.build()), 1000)
|
||||
.build();
|
||||
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder()
|
||||
.role(McpSchema.Role.ASSISTANT)
|
||||
.content(new McpSchema.TextContent("Hello! How can I help you today?"))
|
||||
.model("gpt-4")
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult
|
||||
.builder(McpSchema.Role.ASSISTANT,
|
||||
McpSchema.TextContent.builder("Hello! How can I help you today?").build(), "gpt-4")
|
||||
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
|
||||
.build();
|
||||
|
||||
|
|
@ -560,16 +562,20 @@ class McpSyncServerExchangeTests {
|
|||
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
||||
|
||||
// Create request with image content
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER,
|
||||
new McpSchema.ImageContent(null, "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD...",
|
||||
"image/jpeg"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder(Arrays.asList(
|
||||
McpSchema.SamplingMessage
|
||||
.builder(McpSchema.Role.USER,
|
||||
McpSchema.ImageContent
|
||||
.builder("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD...", "image/jpeg")
|
||||
.build())
|
||||
.build()),
|
||||
1000)
|
||||
.build();
|
||||
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder()
|
||||
.role(McpSchema.Role.ASSISTANT)
|
||||
.content(new McpSchema.TextContent("I can see an image. It appears to be a photograph."))
|
||||
.model("gpt-4-vision")
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult
|
||||
.builder(McpSchema.Role.ASSISTANT,
|
||||
McpSchema.TextContent.builder("I can see an image. It appears to be a photograph.").build(),
|
||||
"gpt-4-vision")
|
||||
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
|
||||
.build();
|
||||
|
||||
|
|
@ -595,9 +601,10 @@ class McpSyncServerExchangeTests {
|
|||
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
||||
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays
|
||||
.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Hello"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder(Arrays.asList(
|
||||
McpSchema.SamplingMessage.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Hello").build())
|
||||
.build()),
|
||||
1000)
|
||||
.build();
|
||||
|
||||
when(mockSession.sendRequest(eq(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE), eq(createMessageRequest),
|
||||
|
|
@ -620,16 +627,17 @@ class McpSyncServerExchangeTests {
|
|||
capabilitiesWithSampling, clientInfo, McpTransportContext.EMPTY);
|
||||
McpSyncServerExchange exchangeWithSampling = new McpSyncServerExchange(asyncExchangeWithSampling);
|
||||
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest.builder()
|
||||
.messages(Arrays.asList(new McpSchema.SamplingMessage(McpSchema.Role.USER,
|
||||
new McpSchema.TextContent("What files are available?"))))
|
||||
McpSchema.CreateMessageRequest createMessageRequest = McpSchema.CreateMessageRequest
|
||||
.builder(Arrays.asList(McpSchema.SamplingMessage
|
||||
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("What files are available?").build())
|
||||
.build()), 1000)
|
||||
.includeContext(McpSchema.CreateMessageRequest.ContextInclusionStrategy.ALL_SERVERS)
|
||||
.build();
|
||||
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult.builder()
|
||||
.role(McpSchema.Role.ASSISTANT)
|
||||
.content(new McpSchema.TextContent("Based on the available context, I can see several files..."))
|
||||
.model("gpt-4")
|
||||
McpSchema.CreateMessageResult expectedResult = McpSchema.CreateMessageResult
|
||||
.builder(McpSchema.Role.ASSISTANT,
|
||||
McpSchema.TextContent.builder("Based on the available context, I can see several files...").build(),
|
||||
"gpt-4")
|
||||
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
|
||||
.build();
|
||||
|
||||
|
|
|
|||
|
|
@ -41,24 +41,12 @@ public class ResourceTemplateListingTest {
|
|||
void testResourceListingWithMixedResources() {
|
||||
// Create resource list with both regular and template resources
|
||||
List<McpSchema.Resource> allResources = List.of(
|
||||
McpSchema.Resource.builder()
|
||||
.uri("file:///test/doc1.txt")
|
||||
.name("Document 1")
|
||||
McpSchema.Resource.builder("file:///test/doc1.txt", "Document 1").mimeType("text/plain").build(),
|
||||
McpSchema.Resource.builder("file:///test/doc2.txt", "Document 2").mimeType("text/plain").build(),
|
||||
McpSchema.Resource.builder("file:///test/{type}/document.txt", "Typed Document")
|
||||
.mimeType("text/plain")
|
||||
.build(),
|
||||
McpSchema.Resource.builder()
|
||||
.uri("file:///test/doc2.txt")
|
||||
.name("Document 2")
|
||||
.mimeType("text/plain")
|
||||
.build(),
|
||||
McpSchema.Resource.builder()
|
||||
.uri("file:///test/{type}/document.txt")
|
||||
.name("Typed Document")
|
||||
.mimeType("text/plain")
|
||||
.build(),
|
||||
McpSchema.Resource.builder()
|
||||
.uri("file:///users/{userId}/files/{fileId}")
|
||||
.name("User File")
|
||||
McpSchema.Resource.builder("file:///users/{userId}/files/{fileId}", "User File")
|
||||
.mimeType("text/plain")
|
||||
.build());
|
||||
|
||||
|
|
@ -77,20 +65,18 @@ public class ResourceTemplateListingTest {
|
|||
void testResourceTemplatesListedSeparately() {
|
||||
// Create mixed resources
|
||||
List<McpSchema.Resource> resources = List.of(
|
||||
McpSchema.Resource.builder()
|
||||
.uri("file:///test/regular.txt")
|
||||
.name("Regular Resource")
|
||||
McpSchema.Resource.builder("file:///test/regular.txt", "Regular Resource")
|
||||
.mimeType("text/plain")
|
||||
.build(),
|
||||
McpSchema.Resource.builder()
|
||||
.uri("file:///test/user/{userId}/profile.txt")
|
||||
.name("User Profile")
|
||||
McpSchema.Resource.builder("file:///test/user/{userId}/profile.txt", "User Profile")
|
||||
.mimeType("text/plain")
|
||||
.build());
|
||||
|
||||
// Create explicit resource template
|
||||
McpSchema.ResourceTemplate explicitTemplate = new McpSchema.ResourceTemplate(
|
||||
"file:///test/document/{docId}/content.txt", "Document Template", null, "text/plain", null);
|
||||
McpSchema.ResourceTemplate explicitTemplate = McpSchema.ResourceTemplate
|
||||
.builder("file:///test/document/{docId}/content.txt", "Document Template")
|
||||
.mimeType("text/plain")
|
||||
.build();
|
||||
|
||||
// Filter regular resources (those without template parameters)
|
||||
List<McpSchema.Resource> regularResources = resources.stream()
|
||||
|
|
@ -100,8 +86,11 @@ public class ResourceTemplateListingTest {
|
|||
// Extract template resources (those with template parameters)
|
||||
List<McpSchema.ResourceTemplate> templateResources = resources.stream()
|
||||
.filter(resource -> resource.uri().contains("{"))
|
||||
.map(resource -> new McpSchema.ResourceTemplate(resource.uri(), resource.name(), resource.description(),
|
||||
resource.mimeType(), resource.annotations()))
|
||||
.map(resource -> McpSchema.ResourceTemplate.builder(resource.uri(), resource.name())
|
||||
.description(resource.description())
|
||||
.mimeType(resource.mimeType())
|
||||
.annotations(resource.annotations())
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Verify regular resources list
|
||||
|
|
|
|||
|
|
@ -39,12 +39,12 @@ class SyncToolSpecificationBuilderTest {
|
|||
@Test
|
||||
void builderShouldCreateValidSyncToolSpecification() {
|
||||
|
||||
Tool tool = Tool.builder().name("test-tool").title("A test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
|
||||
Tool tool = Tool.builder("test-tool", EMPTY_JSON_SCHEMA).title("A test tool").build();
|
||||
|
||||
McpServerFeatures.SyncToolSpecification specification = McpServerFeatures.SyncToolSpecification.builder()
|
||||
.tool(tool)
|
||||
.callHandler((exchange, request) -> CallToolResult.builder()
|
||||
.content(List.of(new TextContent("Test result")))
|
||||
.content(List.of(TextContent.builder("Test result").build()))
|
||||
.isError(false)
|
||||
.build())
|
||||
.build();
|
||||
|
|
@ -63,7 +63,7 @@ class SyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void builderShouldThrowExceptionWhenCallToolIsNull() {
|
||||
Tool tool = Tool.builder().name("test-tool").description("A test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
|
||||
Tool tool = Tool.builder("test-tool", EMPTY_JSON_SCHEMA).description("A test tool").build();
|
||||
|
||||
assertThatThrownBy(() -> McpServerFeatures.SyncToolSpecification.builder().tool(tool).build())
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
|
|
@ -72,7 +72,7 @@ class SyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void builderShouldAllowMethodChaining() {
|
||||
Tool tool = Tool.builder().name("test-tool").description("A test tool").inputSchema(EMPTY_JSON_SCHEMA).build();
|
||||
Tool tool = Tool.builder("test-tool", EMPTY_JSON_SCHEMA).description("A test tool").build();
|
||||
McpServerFeatures.SyncToolSpecification.Builder builder = McpServerFeatures.SyncToolSpecification.builder();
|
||||
|
||||
// Then - verify method chaining returns the same builder instance
|
||||
|
|
@ -84,11 +84,7 @@ class SyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void builtSpecificationShouldExecuteCallToolCorrectly() {
|
||||
Tool tool = Tool.builder()
|
||||
.name("calculator")
|
||||
.description("Simple calculator")
|
||||
.inputSchema(EMPTY_JSON_SCHEMA)
|
||||
.build();
|
||||
Tool tool = Tool.builder("calculator", EMPTY_JSON_SCHEMA).description("Simple calculator").build();
|
||||
String expectedResult = "42";
|
||||
|
||||
McpServerFeatures.SyncToolSpecification specification = McpServerFeatures.SyncToolSpecification.builder()
|
||||
|
|
@ -96,13 +92,13 @@ class SyncToolSpecificationBuilderTest {
|
|||
.callHandler((exchange, request) -> {
|
||||
// Simple test implementation
|
||||
return CallToolResult.builder()
|
||||
.content(List.of(new TextContent(expectedResult)))
|
||||
.content(List.of(TextContent.builder(expectedResult).build()))
|
||||
.isError(false)
|
||||
.build();
|
||||
})
|
||||
.build();
|
||||
|
||||
CallToolRequest request = new CallToolRequest("calculator", Map.of());
|
||||
CallToolRequest request = CallToolRequest.builder("calculator").build();
|
||||
CallToolResult result = specification.callHandler().apply(null, request);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
|
|
@ -138,7 +134,7 @@ class SyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void defaultShouldThrowOnInvalidName() {
|
||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||
Tool invalidTool = Tool.builder("invalid tool name", EMPTY_JSON_SCHEMA).build();
|
||||
|
||||
assertThatThrownBy(
|
||||
() -> McpServer.sync(transportProvider).toolCall(invalidTool, (exchange, request) -> null))
|
||||
|
|
@ -149,7 +145,7 @@ class SyncToolSpecificationBuilderTest {
|
|||
@Test
|
||||
void lenientDefaultShouldLogOnInvalidName() {
|
||||
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||
Tool invalidTool = Tool.builder("invalid tool name", EMPTY_JSON_SCHEMA).build();
|
||||
|
||||
assertThatCode(() -> McpServer.sync(transportProvider).toolCall(invalidTool, (exchange, request) -> null))
|
||||
.doesNotThrowAnyException();
|
||||
|
|
@ -158,7 +154,7 @@ class SyncToolSpecificationBuilderTest {
|
|||
|
||||
@Test
|
||||
void lenientConfigurationShouldLogOnInvalidName() {
|
||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||
Tool invalidTool = Tool.builder("invalid tool name", EMPTY_JSON_SCHEMA).build();
|
||||
|
||||
assertThatCode(() -> McpServer.sync(transportProvider)
|
||||
.strictToolNameValidation(false)
|
||||
|
|
@ -169,7 +165,7 @@ class SyncToolSpecificationBuilderTest {
|
|||
@Test
|
||||
void serverConfigurationShouldOverrideDefault() {
|
||||
System.setProperty(ToolNameValidator.STRICT_VALIDATION_PROPERTY, "false");
|
||||
Tool invalidTool = Tool.builder().name("invalid tool name").build();
|
||||
Tool invalidTool = Tool.builder("invalid tool name", EMPTY_JSON_SCHEMA).build();
|
||||
|
||||
assertThatThrownBy(() -> McpServer.sync(transportProvider)
|
||||
.strictToolNameValidation(true)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public class JSONRPCRequestMcpValidationTest {
|
|||
@Test
|
||||
public void testValidStringId() {
|
||||
assertDoesNotThrow(() -> {
|
||||
var request = new McpSchema.JSONRPCRequest("2.0", "test/method", "string-id", null);
|
||||
var request = new McpSchema.JSONRPCRequest("test/method", "string-id");
|
||||
assertEquals("string-id", request.id());
|
||||
});
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ public class JSONRPCRequestMcpValidationTest {
|
|||
@Test
|
||||
public void testValidIntegerId() {
|
||||
assertDoesNotThrow(() -> {
|
||||
var request = new McpSchema.JSONRPCRequest("2.0", "test/method", 123, null);
|
||||
var request = new McpSchema.JSONRPCRequest("test/method", 123);
|
||||
assertEquals(123, request.id());
|
||||
});
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ public class JSONRPCRequestMcpValidationTest {
|
|||
@Test
|
||||
public void testValidLongId() {
|
||||
assertDoesNotThrow(() -> {
|
||||
var request = new McpSchema.JSONRPCRequest("2.0", "test/method", 123L, null);
|
||||
var request = new McpSchema.JSONRPCRequest("test/method", 123L);
|
||||
assertEquals(123L, request.id());
|
||||
});
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ public class JSONRPCRequestMcpValidationTest {
|
|||
@Test
|
||||
public void testNullIdThrowsException() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new McpSchema.JSONRPCRequest("2.0", "test/method", null, null);
|
||||
new McpSchema.JSONRPCRequest("test/method", null);
|
||||
});
|
||||
|
||||
assertTrue(exception.getMessage().contains("MCP requests MUST include an ID"));
|
||||
|
|
@ -54,7 +54,7 @@ public class JSONRPCRequestMcpValidationTest {
|
|||
@Test
|
||||
public void testDoubleIdTypeThrowsException() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new McpSchema.JSONRPCRequest("2.0", "test/method", 123.45, null);
|
||||
new McpSchema.JSONRPCRequest("test/method", 123.45);
|
||||
});
|
||||
|
||||
assertTrue(exception.getMessage().contains("MCP requests MUST have an ID that is either a string or integer"));
|
||||
|
|
@ -63,7 +63,7 @@ public class JSONRPCRequestMcpValidationTest {
|
|||
@Test
|
||||
public void testBooleanIdThrowsException() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new McpSchema.JSONRPCRequest("2.0", "test/method", true, null);
|
||||
new McpSchema.JSONRPCRequest("test/method", true);
|
||||
});
|
||||
|
||||
assertTrue(exception.getMessage().contains("MCP requests MUST have an ID that is either a string or integer"));
|
||||
|
|
@ -72,7 +72,7 @@ public class JSONRPCRequestMcpValidationTest {
|
|||
@Test
|
||||
public void testArrayIdThrowsException() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new McpSchema.JSONRPCRequest("2.0", "test/method", new String[] { "array" }, null);
|
||||
new McpSchema.JSONRPCRequest("test/method", new String[] { "array" });
|
||||
});
|
||||
|
||||
assertTrue(exception.getMessage().contains("MCP requests MUST have an ID that is either a string or integer"));
|
||||
|
|
@ -81,7 +81,7 @@ public class JSONRPCRequestMcpValidationTest {
|
|||
@Test
|
||||
public void testObjectIdThrowsException() {
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new McpSchema.JSONRPCRequest("2.0", "test/method", new Object(), null);
|
||||
new McpSchema.JSONRPCRequest("test/method", new Object());
|
||||
});
|
||||
|
||||
assertTrue(exception.getMessage().contains("MCP requests MUST have an ID that is either a string or integer"));
|
||||
|
|
|
|||
|
|
@ -55,8 +55,7 @@ class McpClientSessionTests {
|
|||
// Verify response handling
|
||||
StepVerifier.create(responseMono).then(() -> {
|
||||
McpSchema.JSONRPCRequest request = transport.getLastSentMessageAsRequest();
|
||||
transport.simulateIncomingMessage(
|
||||
new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), responseData, null));
|
||||
transport.simulateIncomingMessage(McpSchema.JSONRPCResponse.result(request.id(), responseData));
|
||||
}).consumeNextWith(response -> {
|
||||
// Verify the request was sent
|
||||
McpSchema.JSONRPCMessage sentMessage = transport.getLastSentMessageAsRequest();
|
||||
|
|
@ -84,9 +83,8 @@ class McpClientSessionTests {
|
|||
McpSchema.JSONRPCRequest request = transport.getLastSentMessageAsRequest();
|
||||
// Simulate error response
|
||||
McpSchema.JSONRPCResponse.JSONRPCError error = new McpSchema.JSONRPCResponse.JSONRPCError(
|
||||
McpSchema.ErrorCodes.METHOD_NOT_FOUND, "Method not found", null);
|
||||
transport.simulateIncomingMessage(
|
||||
new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null, error));
|
||||
McpSchema.ErrorCodes.METHOD_NOT_FOUND, "Method not found");
|
||||
transport.simulateIncomingMessage(McpSchema.JSONRPCResponse.error(request.id(), error));
|
||||
}).expectError(McpError.class).verify();
|
||||
|
||||
session.close();
|
||||
|
|
@ -140,8 +138,7 @@ class McpClientSessionTests {
|
|||
var session = new McpClientSession(TIMEOUT, transport, requestHandlers, Map.of(), Function.identity());
|
||||
|
||||
// Simulate incoming request
|
||||
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, ECHO_METHOD,
|
||||
"test-id", echoMessage);
|
||||
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(ECHO_METHOD, "test-id", echoMessage);
|
||||
transport.simulateIncomingMessage(request);
|
||||
|
||||
// Verify response
|
||||
|
|
@ -166,8 +163,8 @@ class McpClientSessionTests {
|
|||
// Simulate incoming notification from the server
|
||||
Map<String, Object> notificationParams = Map.of("status", "ready");
|
||||
|
||||
McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION,
|
||||
TEST_NOTIFICATION, notificationParams);
|
||||
McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(TEST_NOTIFICATION,
|
||||
notificationParams);
|
||||
|
||||
transport.simulateIncomingMessage(notification);
|
||||
|
||||
|
|
@ -186,8 +183,7 @@ class McpClientSessionTests {
|
|||
Function.identity());
|
||||
|
||||
// Simulate incoming request for unknown method
|
||||
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, "unknown.method",
|
||||
"test-id", null);
|
||||
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest("unknown.method", "test-id");
|
||||
transport.simulateIncomingMessage(request);
|
||||
|
||||
// Verify error response
|
||||
|
|
@ -214,8 +210,7 @@ class McpClientSessionTests {
|
|||
Function.identity());
|
||||
|
||||
// Simulate incoming request that will trigger the error
|
||||
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, testMethod,
|
||||
"test-id", null);
|
||||
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(testMethod, "test-id");
|
||||
transport.simulateIncomingMessage(request);
|
||||
|
||||
// Verify: The response should contain the custom error from McpError
|
||||
|
|
@ -242,8 +237,7 @@ class McpClientSessionTests {
|
|||
Function.identity());
|
||||
|
||||
// Simulate incoming request that will trigger the error
|
||||
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, testMethod,
|
||||
"test-id", null);
|
||||
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(testMethod, "test-id");
|
||||
transport.simulateIncomingMessage(request);
|
||||
|
||||
// Verify: The response should contain INTERNAL_ERROR with aggregated exception
|
||||
|
|
@ -276,8 +270,7 @@ class McpClientSessionTests {
|
|||
Function.identity());
|
||||
|
||||
// Simulate incoming request that will trigger the error
|
||||
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, testMethod,
|
||||
"test-id", null);
|
||||
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(testMethod, "test-id");
|
||||
transport.simulateIncomingMessage(request);
|
||||
|
||||
// Verify: The response should contain INTERNAL_ERROR with full exception chain
|
||||
|
|
|
|||
|
|
@ -18,10 +18,8 @@ class PromptReferenceEqualsTest {
|
|||
|
||||
@Test
|
||||
void testEqualsWithSameIdentifierAndType() {
|
||||
McpSchema.PromptReference ref1 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt",
|
||||
"Test Title");
|
||||
McpSchema.PromptReference ref2 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt",
|
||||
"Different Title");
|
||||
McpSchema.PromptReference ref1 = PromptReference.builder("test-prompt").title("Test Title").build();
|
||||
McpSchema.PromptReference ref2 = PromptReference.builder("test-prompt").title("Different Title").build();
|
||||
|
||||
assertTrue(ref1.equals(ref2), "PromptReferences with same identifier and type should be equal");
|
||||
assertEquals(ref1.hashCode(), ref2.hashCode(), "Equal objects should have same hash code");
|
||||
|
|
@ -29,35 +27,22 @@ class PromptReferenceEqualsTest {
|
|||
|
||||
@Test
|
||||
void testEqualsWithDifferentIdentifier() {
|
||||
McpSchema.PromptReference ref1 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt-1",
|
||||
"Test Title");
|
||||
McpSchema.PromptReference ref2 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt-2",
|
||||
"Test Title");
|
||||
McpSchema.PromptReference ref1 = PromptReference.builder("test-prompt-1").title("Test Title").build();
|
||||
McpSchema.PromptReference ref2 = PromptReference.builder("test-prompt-2").title("Test Title").build();
|
||||
|
||||
assertFalse(ref1.equals(ref2), "PromptReferences with different identifiers should not be equal");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsWithDifferentType() {
|
||||
McpSchema.PromptReference ref1 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt",
|
||||
"Test Title");
|
||||
McpSchema.PromptReference ref2 = new McpSchema.PromptReference("ref/other", "test-prompt", "Test Title");
|
||||
|
||||
assertFalse(ref1.equals(ref2), "PromptReferences with different types should not be equal");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsWithNull() {
|
||||
McpSchema.PromptReference ref1 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt",
|
||||
"Test Title");
|
||||
McpSchema.PromptReference ref1 = PromptReference.builder("test-prompt").title("Test Title").build();
|
||||
|
||||
assertFalse(ref1.equals(null), "PromptReference should not be equal to null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsWithDifferentClass() {
|
||||
McpSchema.PromptReference ref1 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt",
|
||||
"Test Title");
|
||||
McpSchema.PromptReference ref1 = PromptReference.builder("test-prompt").title("Test Title").build();
|
||||
String other = "not a PromptReference";
|
||||
|
||||
assertFalse(ref1.equals(other), "PromptReference should not be equal to different class");
|
||||
|
|
@ -65,17 +50,16 @@ class PromptReferenceEqualsTest {
|
|||
|
||||
@Test
|
||||
void testEqualsWithSameInstance() {
|
||||
McpSchema.PromptReference ref1 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt",
|
||||
"Test Title");
|
||||
McpSchema.PromptReference ref1 = PromptReference.builder("test-prompt").title("Test Title").build();
|
||||
|
||||
assertTrue(ref1.equals(ref1), "PromptReference should be equal to itself");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsIgnoresTitle() {
|
||||
McpSchema.PromptReference ref1 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt", "Title 1");
|
||||
McpSchema.PromptReference ref2 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt", "Title 2");
|
||||
McpSchema.PromptReference ref3 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt", null);
|
||||
McpSchema.PromptReference ref1 = PromptReference.builder("test-prompt").title("Title 1").build();
|
||||
McpSchema.PromptReference ref2 = PromptReference.builder("test-prompt").title("Title 2").build();
|
||||
McpSchema.PromptReference ref3 = new PromptReference("test-prompt");
|
||||
|
||||
assertTrue(ref1.equals(ref2), "PromptReferences should be equal regardless of title");
|
||||
assertTrue(ref1.equals(ref3), "PromptReferences should be equal even when one has null title");
|
||||
|
|
@ -84,14 +68,11 @@ class PromptReferenceEqualsTest {
|
|||
|
||||
@Test
|
||||
void testHashCodeConsistency() {
|
||||
McpSchema.PromptReference ref1 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt",
|
||||
"Test Title");
|
||||
McpSchema.PromptReference ref2 = new McpSchema.PromptReference(PromptReference.TYPE, "test-prompt",
|
||||
"Different Title");
|
||||
McpSchema.PromptReference ref1 = PromptReference.builder("test-prompt").title("Test Title").build();
|
||||
McpSchema.PromptReference ref2 = PromptReference.builder("test-prompt").title("Different Title").build();
|
||||
|
||||
assertEquals(ref1.hashCode(), ref2.hashCode(), "Objects that are equal should have the same hash code");
|
||||
|
||||
// Call hashCode multiple times to ensure consistency
|
||||
int hashCode1 = ref1.hashCode();
|
||||
int hashCode2 = ref1.hashCode();
|
||||
assertEquals(hashCode1, hashCode2, "Hash code should be consistent across multiple calls");
|
||||
|
|
|
|||
|
|
@ -105,13 +105,13 @@ class GsonMcpJsonMapperTests {
|
|||
var gsonMapper = new GsonMcpJsonMapper();
|
||||
|
||||
// Tool builder parsing of input/output schema strings
|
||||
var tool = McpSchema.Tool.builder().name("echo").description("Echo tool").inputSchema(gsonMapper, """
|
||||
var tool = McpSchema.Tool.builder("echo", gsonMapper, """
|
||||
{
|
||||
"type": "object",
|
||||
"properties": { "x": { "type": "integer" } },
|
||||
"required": ["x"]
|
||||
}
|
||||
""").outputSchema(gsonMapper, """
|
||||
""").description("Echo tool").outputSchema(gsonMapper, """
|
||||
{
|
||||
"type": "object",
|
||||
"properties": { "y": { "type": "string" } }
|
||||
|
|
|
|||
|
|
@ -16,10 +16,7 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link ToolInputValidator}.
|
||||
|
|
@ -33,13 +30,9 @@ class ToolInputValidatorTests {
|
|||
private final Map<String, Object> inputSchema = Map.of("type", "object", "properties",
|
||||
Map.of("name", Map.of("type", "string")), "required", List.of("name"));
|
||||
|
||||
private final Tool toolWithSchema = Tool.builder()
|
||||
.name("test-tool")
|
||||
.description("Test tool")
|
||||
.inputSchema(inputSchema)
|
||||
.build();
|
||||
private final Tool toolWithSchema = Tool.builder("test-tool", inputSchema).description("Test tool").build();
|
||||
|
||||
private final Tool toolWithoutSchema = Tool.builder().name("test-tool").description("Test tool").build();
|
||||
private final Tool toolWithoutSchema = Tool.builder("test-tool").description("Test tool").build();
|
||||
|
||||
@Test
|
||||
void validate_whenDisabled_returnsNull() {
|
||||
|
|
@ -51,10 +44,12 @@ class ToolInputValidatorTests {
|
|||
|
||||
@Test
|
||||
void validate_whenNoSchema_returnsNull() {
|
||||
when(validator.validate(any(), any())).thenReturn(ValidationResponse.asValid(null));
|
||||
|
||||
CallToolResult result = ToolInputValidator.validate(toolWithoutSchema, Map.of("name", "test"), true, validator);
|
||||
|
||||
assertThat(result).isNull();
|
||||
verify(validator, never()).validate(any(), any());
|
||||
verify(validator).validate(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Reference in a new issue