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:
Dariusz Jędrzejczyk 2026-04-20 17:20:38 +02:00 • committed by Christian Tzolov
commit 22a0efa577
28 changed files with 4258 additions and 1688 deletions

View file

@ -87,7 +87,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
.build();
try (
// Create client without sampling capabilities
var client = clientBuilder.clientInfo(new McpSchema.Implementation("Sample " + "client", "0.0.0"))
var client = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
.requestTimeout(Duration.ofSeconds(1000))
.build()) {
@ -109,7 +110,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var clientBuilder = clientBuilders.get(clientType);
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
return exchange.createMessage(mock(McpSchema.CreateMessageRequest.class))
.then(Mono.just(mock(CallToolResult.class)));
@ -120,13 +121,14 @@ public abstract class AbstractMcpClientServerIntegrationTests {
try (
// Create client without sampling capabilities
var client = clientBuilder.clientInfo(new McpSchema.Implementation("Sample " + "client", "0.0.0"))
var client = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
.build()) {
assertThat(client.initialize()).isNotNull();
try {
client.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
client.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
}
catch (McpError e) {
assertThat(e).isInstanceOf(McpError.class)
@ -148,23 +150,26 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(request.messages()).hasSize(1);
assertThat(request.messages().get(0).content()).isInstanceOf(McpSchema.TextContent.class);
return new CreateMessageResult(Role.USER, new McpSchema.TextContent("Test message"), "MockModelName",
CreateMessageResult.StopReason.STOP_SEQUENCE);
return CreateMessageResult
.builder(Role.USER, McpSchema.TextContent.builder("Test message").build(), "MockModelName")
.stopReason(CreateMessageResult.StopReason.STOP_SEQUENCE)
.build();
};
CallToolResult callResponse = McpSchema.CallToolResult.builder()
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
.addContent(McpSchema.TextContent.builder("CALL RESPONSE").build())
.build();
AtomicReference<CreateMessageResult> samplingResult = new AtomicReference<>();
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
var createMessageRequest = McpSchema.CreateMessageRequest.builder()
.messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
new McpSchema.TextContent("Test message"))))
var createMessageRequest = McpSchema.CreateMessageRequest
.builder(List.of(McpSchema.SamplingMessage
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Test message").build())
.build()), 1000)
.modelPreferences(ModelPreferences.builder()
.hints(List.of())
.costPriority(1.0)
@ -181,7 +186,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
.capabilities(ClientCapabilities.builder().sampling().build())
.sampling(samplingHandler)
.build()) {
@ -189,7 +195,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response).isEqualTo(callResponse);
@ -225,25 +232,28 @@ public abstract class AbstractMcpClientServerIntegrationTests {
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return new CreateMessageResult(Role.USER, new McpSchema.TextContent("Test message"), "MockModelName",
CreateMessageResult.StopReason.STOP_SEQUENCE);
return CreateMessageResult
.builder(Role.USER, McpSchema.TextContent.builder("Test message").build(), "MockModelName")
.stopReason(CreateMessageResult.StopReason.STOP_SEQUENCE)
.build();
};
// Server
CallToolResult callResponse = McpSchema.CallToolResult.builder()
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
.addContent(McpSchema.TextContent.builder("CALL RESPONSE").build())
.build();
AtomicReference<CreateMessageResult> samplingResult = new AtomicReference<>();
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
var createMessageRequest = McpSchema.CreateMessageRequest.builder()
.messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
new McpSchema.TextContent("Test message"))))
var createMessageRequest = McpSchema.CreateMessageRequest
.builder(List.of(McpSchema.SamplingMessage
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Test message").build())
.build()), 1000)
.modelPreferences(ModelPreferences.builder()
.hints(List.of())
.costPriority(1.0)
@ -262,7 +272,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
.requestTimeout(Duration.ofSeconds(4))
.tools(tool)
.build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
.capabilities(ClientCapabilities.builder().sampling().build())
.sampling(samplingHandler)
.build()) {
@ -270,7 +281,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response).isEqualTo(callResponse);
@ -304,21 +316,24 @@ public abstract class AbstractMcpClientServerIntegrationTests {
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return new CreateMessageResult(Role.USER, new McpSchema.TextContent("Test message"), "MockModelName",
CreateMessageResult.StopReason.STOP_SEQUENCE);
return CreateMessageResult
.builder(Role.USER, McpSchema.TextContent.builder("Test message").build(), "MockModelName")
.stopReason(CreateMessageResult.StopReason.STOP_SEQUENCE)
.build();
};
CallToolResult callResponse = McpSchema.CallToolResult.builder()
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
.addContent(McpSchema.TextContent.builder("CALL RESPONSE").build())
.build();
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
var createMessageRequest = McpSchema.CreateMessageRequest.builder()
.messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
new McpSchema.TextContent("Test message"))))
var createMessageRequest = McpSchema.CreateMessageRequest
.builder(List.of(McpSchema.SamplingMessage
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Test message").build())
.build()), 1000)
.modelPreferences(ModelPreferences.builder()
.hints(List.of())
.costPriority(1.0)
@ -336,7 +351,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
.tools(tool)
.build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
.capabilities(ClientCapabilities.builder().sampling().build())
.sampling(samplingHandler)
.build()) {
@ -345,7 +361,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(initResult).isNotNull();
assertThatExceptionOfType(McpError.class).isThrownBy(() -> {
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
mcpClient.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
}).withMessageContaining("1000ms");
}
finally {
@ -363,7 +379,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var clientBuilder = clientBuilders.get(clientType);
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> exchange.createElicitation(mock(ElicitRequest.class))
.then(Mono.just(mock(CallToolResult.class))))
.build();
@ -371,12 +387,13 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var server = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
// Create client without elicitation capabilities
try (var client = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0")).build()) {
try (var client = clientBuilder.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
.build()) {
assertThat(client.initialize()).isNotNull();
try {
client.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
client.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
}
catch (McpError e) {
assertThat(e).isInstanceOf(McpError.class)
@ -398,23 +415,23 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(request.message()).isNotEmpty();
assertThat(request.requestedSchema()).isNotNull();
return new McpSchema.ElicitResult(McpSchema.ElicitResult.Action.ACCEPT,
Map.of("message", request.message()));
return McpSchema.ElicitResult.builder(McpSchema.ElicitResult.Action.ACCEPT)
.content(Map.of("message", request.message()))
.build();
};
CallToolResult callResponse = McpSchema.CallToolResult.builder()
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
.addContent(McpSchema.TextContent.builder("CALL RESPONSE").build())
.build();
AtomicReference<McpSchema.ElicitResult> elicitResultRef = new AtomicReference<>();
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
var elicitationRequest = McpSchema.ElicitRequest.builder()
.message("Test message")
.requestedSchema(
var elicitationRequest = McpSchema.ElicitRequest
.builder("Test message",
Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string"))))
.build();
@ -426,7 +443,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var mcpServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").tools(tool).build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
.capabilities(ClientCapabilities.builder().elicitation().build())
.elicitation(elicitationHandler)
.build()) {
@ -434,7 +452,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response).isEqualTo(callResponse);
@ -458,22 +477,23 @@ public abstract class AbstractMcpClientServerIntegrationTests {
Function<ElicitRequest, ElicitResult> elicitationHandler = request -> {
assertThat(request.message()).isNotEmpty();
assertThat(request.requestedSchema()).isNotNull();
return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message()));
return ElicitResult.builder(ElicitResult.Action.ACCEPT)
.content(Map.of("message", request.message()))
.build();
};
CallToolResult callResponse = McpSchema.CallToolResult.builder()
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
.addContent(McpSchema.TextContent.builder("CALL RESPONSE").build())
.build();
AtomicReference<ElicitResult> resultRef = new AtomicReference<>();
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
var elicitationRequest = McpSchema.ElicitRequest.builder()
.message("Test message")
.requestedSchema(
var elicitationRequest = McpSchema.ElicitRequest
.builder("Test message",
Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string"))))
.build();
@ -488,7 +508,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
.tools(tool)
.build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
.capabilities(ClientCapabilities.builder().elicitation().build())
.elicitation(elicitationHandler)
.build()) {
@ -496,7 +517,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response).isEqualTo(callResponse);
@ -531,20 +553,23 @@ public abstract class AbstractMcpClientServerIntegrationTests {
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message()));
return ElicitResult.builder(ElicitResult.Action.ACCEPT)
.content(Map.of("message", request.message()))
.build();
};
CallToolResult callResponse = CallToolResult.builder().addContent(new TextContent("CALL RESPONSE")).build();
CallToolResult callResponse = CallToolResult.builder()
.addContent(TextContent.builder("CALL RESPONSE").build())
.build();
AtomicReference<ElicitResult> resultRef = new AtomicReference<>();
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
var elicitationRequest = ElicitRequest.builder()
.message("Test message")
.requestedSchema(
var elicitationRequest = ElicitRequest
.builder("Test message",
Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string"))))
.build();
@ -559,7 +584,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
.tools(tool)
.build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample client", "0.0.0").build())
.capabilities(ClientCapabilities.builder().elicitation().build())
.elicitation(elicitationHandler)
.build()) {
@ -568,7 +594,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(initResult).isNotNull();
assertThatExceptionOfType(McpError.class).isThrownBy(() -> {
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
mcpClient.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
}).withMessageContaining("within 1000ms");
ElicitResult elicitResult = resultRef.get();
@ -587,7 +613,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
void testRootsSuccess(String clientType) {
var clientBuilder = clientBuilders.get(clientType);
List<Root> roots = List.of(new Root("uri1://", "root1"), new Root("uri2://", "root2"));
List<Root> roots = List.of(Root.builder("uri1://").name("root1").build(),
Root.builder("uri2://").name("root2").build());
AtomicReference<List<Root>> rootsRef = new AtomicReference<>();
@ -618,7 +645,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
});
// Add a new root
var root3 = new Root("uri3://", "root3");
var root3 = Root.builder("uri3://").name("root3").build();
mcpClient.addRoot(root3);
await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
@ -637,7 +664,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var clientBuilder = clientBuilders.get(clientType);
McpServerFeatures.SyncToolSpecification tool = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
exchange.listRoots(); // try to list roots
@ -658,7 +685,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
// Attempt to list roots should fail
try {
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
mcpClient.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
}
catch (McpError e) {
assertThat(e).isInstanceOf(McpError.class).hasMessage("Roots not supported");
@ -705,7 +732,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var clientBuilder = clientBuilders.get(clientType);
List<Root> roots = List.of(new Root("uri1://", "root1"));
List<Root> roots = List.of(Root.builder("uri1://").name("root1").build());
AtomicReference<List<Root>> rootsRef1 = new AtomicReference<>();
AtomicReference<List<Root>> rootsRef2 = new AtomicReference<>();
@ -739,7 +766,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var clientBuilder = clientBuilders.get(clientType);
List<Root> roots = List.of(new Root("uri1://", "root1"));
List<Root> roots = List.of(Root.builder("uri1://").name("root1").build());
AtomicReference<List<Root>> rootsRef = new AtomicReference<>();
@ -776,10 +803,10 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
var callResponse = McpSchema.CallToolResult.builder()
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=importantValue"))
.addContent(McpSchema.TextContent.builder("CALL RESPONSE; ctx=importantValue").build())
.build();
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
try {
@ -811,7 +838,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(mcpClient.listTools().tools()).contains(tool1.tool());
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
assertThat(responseBodyIsNullOrBlank.get()).isFalse();
assertThat(response).isNotNull().isEqualTo(callResponse);
@ -830,11 +858,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
McpSyncServer mcpServer = prepareSyncServerBuilder()
.capabilities(ServerCapabilities.builder().tools(true).build())
.tools(McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder()
.name("tool1")
.description("tool1 description")
.inputSchema(EMPTY_JSON_SCHEMA)
.build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
// We trigger a timeout on blocking read, raising an exception
Mono.never().block(Duration.ofSeconds(1));
@ -849,8 +873,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
// We expect the tool call to fail immediately with the exception raised by
// the offending tool instead of getting back a timeout.
assertThatExceptionOfType(McpError.class)
.isThrownBy(() -> mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())))
assertThatExceptionOfType(McpError.class).isThrownBy(
() -> mcpClient.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build()))
.withMessageContaining("Timeout on blocking read");
}
finally {
@ -869,10 +893,10 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var responseBodyIsNullOrBlank = new AtomicBoolean(false);
var expectedCallResponse = McpSchema.CallToolResult.builder()
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=value"))
.addContent(McpSchema.TextContent.builder("CALL RESPONSE; ctx=value").build())
.build();
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
McpTransportContext transportContext = exchange.transportContext();
@ -889,7 +913,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
}
return McpSchema.CallToolResult.builder()
.addContent(new McpSchema.TextContent("CALL RESPONSE; ctx=" + ctxValue))
.addContent(McpSchema.TextContent.builder("CALL RESPONSE; ctx=" + ctxValue).build())
.build();
})
.build();
@ -905,7 +929,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(mcpClient.listTools().tools()).contains(tool1.tool());
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
assertThat(transportContextIsNull.get()).isFalse();
assertThat(transportContextIsEmpty.get()).isFalse();
@ -933,15 +958,11 @@ public abstract class AbstractMcpClientServerIntegrationTests {
""";
McpServerFeatures.SyncToolSpecification nonAsciiTool = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder()
.name("greeter")
.description("打招呼")
.inputSchema(McpJsonDefaults.getMapper(), inputSchema)
.build())
.tool(Tool.builder("greeter", McpJsonDefaults.getMapper(), inputSchema).description("打招呼").build())
.callHandler((exchange, request) -> {
String username = (String) request.arguments().get("username");
return McpSchema.CallToolResult.builder()
.addContent(new McpSchema.TextContent("Hello " + username))
.addContent(McpSchema.TextContent.builder("Hello " + username).build())
.build();
})
.build();
@ -961,7 +982,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(tools.get(0).description()).isEqualTo("打招呼");
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("greeter", Map.of("username", "测试用户")));
.callTool(McpSchema.CallToolRequest.builder("greeter").arguments(Map.of("username", "测试用户")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -980,11 +1001,11 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var clientBuilder = clientBuilders.get(clientType);
var callResponse = McpSchema.CallToolResult.builder()
.addContent(new McpSchema.TextContent("CALL RESPONSE"))
.addContent(McpSchema.TextContent.builder("CALL RESPONSE").build())
.build();
McpServerFeatures.SyncToolSpecification tool1 = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((exchange, request) -> {
// perform a blocking call to a remote service
try {
@ -1050,11 +1071,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
// Add a new tool
McpServerFeatures.SyncToolSpecification tool2 = McpServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder()
.name("tool2")
.description("tool2 description")
.inputSchema(EMPTY_JSON_SCHEMA)
.build())
.tool(Tool.builder("tool2", EMPTY_JSON_SCHEMA).description("tool2 description").build())
.callHandler((exchange, request) -> callResponse)
.build();
@ -1102,48 +1119,39 @@ public abstract class AbstractMcpClientServerIntegrationTests {
// Create server with a tool that sends logging notifications
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder()
.name("logging-test")
.description("Test logging notifications")
.inputSchema(EMPTY_JSON_SCHEMA)
.build())
.tool(Tool.builder("logging-test", EMPTY_JSON_SCHEMA).description("Test logging notifications").build())
.callHandler((exchange, request) -> {
// Create and send notifications with different levels
//@formatter:off
return exchange // This should be filtered out (DEBUG < NOTICE)
.loggingNotification(McpSchema.LoggingMessageNotification.builder()
.level(McpSchema.LoggingLevel.DEBUG)
.loggingNotification(McpSchema.LoggingMessageNotification
.builder(McpSchema.LoggingLevel.DEBUG, "Debug message")
.logger("test-logger")
.data("Debug message")
.build())
.then(exchange // This should be sent (NOTICE >= NOTICE)
.loggingNotification(McpSchema.LoggingMessageNotification.builder()
.level(McpSchema.LoggingLevel.NOTICE)
.loggingNotification(McpSchema.LoggingMessageNotification
.builder(McpSchema.LoggingLevel.NOTICE, "Notice message")
.logger("test-logger")
.data("Notice message")
.build()))
.then(exchange // This should be sent (ERROR > NOTICE)
.loggingNotification(McpSchema.LoggingMessageNotification.builder()
.level(McpSchema.LoggingLevel.ERROR)
.loggingNotification(McpSchema.LoggingMessageNotification
.builder(McpSchema.LoggingLevel.ERROR, "Error message")
.logger("test-logger")
.data("Error message")
.build()))
.then(exchange // This should be filtered out (INFO < NOTICE)
.loggingNotification(McpSchema.LoggingMessageNotification.builder()
.level(McpSchema.LoggingLevel.INFO)
.loggingNotification(McpSchema.LoggingMessageNotification
.builder(McpSchema.LoggingLevel.INFO, "Another info message")
.logger("test-logger")
.data("Another info message")
.build()))
.then(exchange // This should be sent (ERROR >= NOTICE)
.loggingNotification(McpSchema.LoggingMessageNotification.builder()
.level(McpSchema.LoggingLevel.ERROR)
.loggingNotification(McpSchema.LoggingMessageNotification
.builder(McpSchema.LoggingLevel.ERROR, "Another error message")
.logger("test-logger")
.data("Another error message")
.build()))
.thenReturn(CallToolResult.builder()
.content(List.of(new McpSchema.TextContent("Logging test completed")))
.content(List.of(McpSchema.TextContent.builder("Logging test completed").build()))
.isError(false)
.build());
//@formatter:on
@ -1170,7 +1178,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
mcpClient.setLoggingLevel(McpSchema.LoggingLevel.NOTICE);
// Call the tool that sends logging notifications
CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("logging-test", Map.of()));
CallToolResult result = mcpClient
.callTool(McpSchema.CallToolRequest.builder("logging-test").arguments(Map.of()).build());
assertThat(result).isNotNull();
assertThat(result.content().get(0)).isInstanceOf(McpSchema.TextContent.class);
assertThat(((McpSchema.TextContent) result.content().get(0)).text()).isEqualTo("Logging test completed");
@ -1219,10 +1228,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
// Create server with a tool that sends logging notifications
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(McpSchema.Tool.builder()
.name("progress-test")
.tool(McpSchema.Tool.builder("progress-test", EMPTY_JSON_SCHEMA)
.description("Test progress notifications")
.inputSchema(EMPTY_JSON_SCHEMA)
.build())
.callHandler((exchange, request) -> {
@ -1230,18 +1237,25 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var progressToken = (String) request.meta().get("progressToken");
return exchange
.progressNotification(
new McpSchema.ProgressNotification(progressToken, 0.0, 1.0, "Processing started"))
.then(exchange.progressNotification(
new McpSchema.ProgressNotification(progressToken, 0.5, 1.0, "Processing data")))
.then(// Send a progress notification with another progress value
// should
exchange.progressNotification(new McpSchema.ProgressNotification("another-progress-token",
0.0, 1.0, "Another processing started")))
.then(exchange.progressNotification(
new McpSchema.ProgressNotification(progressToken, 1.0, 1.0, "Processing completed")))
.progressNotification(McpSchema.ProgressNotification.builder(progressToken, 0.0)
.total(1.0)
.message("Processing started")
.build())
.then(exchange.progressNotification(McpSchema.ProgressNotification.builder(progressToken, 0.5)
.total(1.0)
.message("Processing data")
.build()))
.then(exchange
.progressNotification(McpSchema.ProgressNotification.builder("another-progress-token", 0.0)
.total(1.0)
.message("Another processing started")
.build()))
.then(exchange.progressNotification(McpSchema.ProgressNotification.builder(progressToken, 1.0)
.total(1.0)
.message("Processing completed")
.build()))
.thenReturn(CallToolResult.builder()
.content(List.of(new McpSchema.TextContent("Progress test completed")))
.content(List.of(McpSchema.TextContent.builder("Progress test completed").build()))
.isError(false)
.build());
})
@ -1321,9 +1335,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var clientBuilder = clientBuilders.get(clientType);
var expectedValues = List.of("python", "pytorch", "pyside");
var completionResponse = new McpSchema.CompleteResult(new CompleteResult.CompleteCompletion(expectedValues, 10, // total
true // hasMore
));
var completionResponse = new McpSchema.CompleteResult(
new CompleteResult.CompleteCompletion(expectedValues, 10, true));
AtomicReference<CompleteRequest> samplingRequest = new AtomicReference<>();
BiFunction<McpSyncServerExchange, CompleteRequest, CompleteResult> completionHandler = (mcpSyncServerExchange,
@ -1333,13 +1346,17 @@ public abstract class AbstractMcpClientServerIntegrationTests {
};
var mcpServer = prepareSyncServerBuilder().capabilities(ServerCapabilities.builder().completions().build())
.prompts(new McpServerFeatures.SyncPromptSpecification(
new Prompt("code_review", "Code review", "this is code review prompt",
List.of(new PromptArgument("language", "Language", "string", false))),
(mcpSyncServerExchange, getPromptRequest) -> null))
.prompts(new McpServerFeatures.SyncPromptSpecification(Prompt.builder("code_review")
.title("Code review")
.description("this is code review prompt")
.arguments(List.of(PromptArgument.builder("language")
.title("Language")
.description("string")
.required(false)
.build()))
.build(), (mcpSyncServerExchange, getPromptRequest) -> null))
.completions(new McpServerFeatures.SyncCompletionSpecification(
new McpSchema.PromptReference(PromptReference.TYPE, "code_review", "Code review"),
completionHandler))
McpSchema.PromptReference.builder("code_review").title("Code review").build(), completionHandler))
.build();
try (var mcpClient = clientBuilder.build()) {
@ -1347,9 +1364,10 @@ public abstract class AbstractMcpClientServerIntegrationTests {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
CompleteRequest request = new CompleteRequest(
new PromptReference(PromptReference.TYPE, "code_review", "Code review"),
new CompleteRequest.CompleteArgument("language", "py"));
CompleteRequest request = CompleteRequest
.builder(PromptReference.builder("code_review").title("Code review").build(),
new CompleteRequest.CompleteArgument("language", "py"))
.build();
CompleteResult result = mcpClient.completeCompletion(request);
@ -1377,11 +1395,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
AtomicReference<String> executionOrder = new AtomicReference<>("");
McpServerFeatures.AsyncToolSpecification tool = McpServerFeatures.AsyncToolSpecification.builder()
.tool(Tool.builder()
.name("ping-async-test")
.description("Test ping async behavior")
.inputSchema(EMPTY_JSON_SCHEMA)
.build())
.tool(Tool.builder("ping-async-test", EMPTY_JSON_SCHEMA).description("Test ping async behavior").build())
.callHandler((exchange, request) -> {
executionOrder.set(executionOrder.get() + "1");
@ -1398,7 +1412,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
}).then(Mono.fromCallable(() -> {
executionOrder.set(executionOrder.get() + "3");
return CallToolResult.builder()
.content(List.of(new McpSchema.TextContent("Async ping test completed")))
.content(List.of(McpSchema.TextContent.builder("Async ping test completed").build()))
.isError(false)
.build();
}));
@ -1417,7 +1431,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(initResult).isNotNull();
// Call the tool that tests ping async behavior
CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("ping-async-test", Map.of()));
CallToolResult result = mcpClient
.callTool(McpSchema.CallToolRequest.builder("ping-async-test").arguments(Map.of()).build());
assertThat(result).isNotNull();
assertThat(result.content().get(0)).isInstanceOf(McpSchema.TextContent.class);
assertThat(((McpSchema.TextContent) result.content().get(0)).text()).isEqualTo("Async ping test completed");
@ -1444,8 +1459,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
Map.of("type", "string"), "timestamp", Map.of("type", "string")),
"required", List.of("result", "operation"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -1478,8 +1492,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
// Note: outputSchema might be null in sync server, but validation still works
// Call tool with valid structured output
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -1523,8 +1537,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
"age", Map.of("type", "number")),
"required", List.of("name", "age"))); // @formatter:on
Tool calculatorTool = Tool.builder()
.name("getMembers")
Tool calculatorTool = Tool.builder("getMembers")
.description("Returns a list of members")
.outputSchema(outputSchema)
.build();
@ -1547,7 +1560,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(mcpClient.initialize()).isNotNull();
// Call tool with valid structured output of type array
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("getMembers", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("getMembers").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -1577,8 +1591,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
Map.of("type", "string"), "timestamp", Map.of("type", "string")),
"required", List.of("result", "operation"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -1588,7 +1601,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
.tool(calculatorTool)
.callHandler((exchange, request) -> CallToolResult.builder()
.isError(true)
.content(List.of(new TextContent("Error calling tool: Simulated in-handler error")))
.content(List.of(TextContent.builder("Error calling tool: Simulated in-handler error").build()))
.build())
.build();
@ -1608,14 +1621,14 @@ public abstract class AbstractMcpClientServerIntegrationTests {
// Note: outputSchema might be null in sync server, but validation still works
// Call tool with valid structured output
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isTrue();
assertThat(response.content()).isNotEmpty();
assertThat(response.content())
.containsExactly(new McpSchema.TextContent("Error calling tool: Simulated in-handler error"));
assertThat(response.content()).containsExactly(
McpSchema.TextContent.builder("Error calling tool: Simulated in-handler error").build());
assertThat(response.structuredContent()).isNull();
}
finally {
@ -1634,8 +1647,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
Map.of("result", Map.of("type", "number"), "operation", Map.of("type", "string")), "required",
List.of("result", "operation"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -1662,8 +1674,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(initResult).isNotNull();
// Call tool with invalid structured output
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isTrue();
@ -1688,8 +1700,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
Map.of("result", Map.of("type", "number")), "required", List.of("result"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -1712,8 +1723,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
assertThat(initResult).isNotNull();
// Call tool that should return structured content but doesn't
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isTrue();
@ -1752,8 +1763,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
Map.of("message", Map.of("type", "string"), "count", Map.of("type", "integer")), "required",
List.of("message", "count"));
Tool dynamicTool = Tool.builder()
.name("dynamic-tool")
Tool dynamicTool = Tool.builder("dynamic-tool")
.description("Dynamically added tool")
.outputSchema(outputSchema)
.build();
@ -1785,7 +1795,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
// Call dynamically added tool
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("dynamic-tool", Map.of("count", 3)));
.callTool(McpSchema.CallToolRequest.builder("dynamic-tool").arguments(Map.of("count", 3)).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -1822,13 +1832,12 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var latch = new CountDownLatch(1);
McpServerFeatures.SyncResourceSpecification resourceSpec = new McpServerFeatures.SyncResourceSpecification(
McpSchema.Resource.builder()
.uri(resourceUri)
.name("Subscribable Resource")
.mimeType("text/plain")
.build(),
(exchange, req) -> new McpSchema.ReadResourceResult(
List.of(new McpSchema.TextResourceContents(resourceUri, "text/plain", "initial content"))));
McpSchema.Resource.builder(resourceUri, "Subscribable Resource").mimeType("text/plain").build(),
(exchange, req) -> McpSchema.ReadResourceResult
.builder(List.of(McpSchema.TextResourceContents.builder(resourceUri, "initial content")
.mimeType("text/plain")
.build()))
.build());
McpSyncServer mcpServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(McpSchema.ServerCapabilities.builder().resources(true, false).build())
@ -1842,7 +1851,7 @@ public abstract class AbstractMcpClientServerIntegrationTests {
mcpClient.initialize();
mcpClient.subscribeResource(new McpSchema.SubscribeRequest(resourceUri));
mcpClient.subscribeResource(McpSchema.SubscribeRequest.builder(resourceUri).build());
mcpServer.notifyResourcesUpdated(new McpSchema.ResourcesUpdatedNotification(resourceUri));
@ -1866,13 +1875,10 @@ public abstract class AbstractMcpClientServerIntegrationTests {
var notificationCount = new java.util.concurrent.atomic.AtomicInteger(0);
McpServerFeatures.SyncResourceSpecification resourceSpec = new McpServerFeatures.SyncResourceSpecification(
McpSchema.Resource.builder()
.uri(resourceUri)
.name("Subscribable Resource")
.mimeType("text/plain")
.build(),
(exchange, req) -> new McpSchema.ReadResourceResult(
List.of(new McpSchema.TextResourceContents(resourceUri, "text/plain", "content"))));
McpSchema.Resource.builder(resourceUri, "Subscribable Resource").mimeType("text/plain").build(),
(exchange, req) -> McpSchema.ReadResourceResult.builder(List
.of(McpSchema.TextResourceContents.builder(resourceUri, "content").mimeType("text/plain").build()))
.build());
McpSyncServer mcpServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(McpSchema.ServerCapabilities.builder().resources(true, false).build())
@ -1884,8 +1890,8 @@ public abstract class AbstractMcpClientServerIntegrationTests {
mcpClient.initialize();
mcpClient.subscribeResource(new McpSchema.SubscribeRequest(resourceUri));
mcpClient.unsubscribeResource(new McpSchema.UnsubscribeRequest(resourceUri));
mcpClient.subscribeResource(McpSchema.SubscribeRequest.builder(resourceUri).build());
mcpClient.unsubscribeResource(McpSchema.UnsubscribeRequest.builder(resourceUri).build());
mcpServer.notifyResourcesUpdated(new McpSchema.ResourcesUpdatedNotification(resourceUri));

View file

@ -62,7 +62,8 @@ public abstract class AbstractStatelessIntegrationTests {
try (
// Create client without sampling capabilities
var client = clientBuilder.clientInfo(new McpSchema.Implementation("Sample " + "client", "0.0.0"))
var client = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
.requestTimeout(Duration.ofSeconds(1000))
.build()) {
@ -84,12 +85,12 @@ public abstract class AbstractStatelessIntegrationTests {
var clientBuilder = clientBuilders.get(clientType);
var callResponse = McpSchema.CallToolResult.builder()
.content(List.of(new McpSchema.TextContent("CALL RESPONSE")))
.content(List.of(McpSchema.TextContent.builder("CALL RESPONSE").build()))
.isError(false)
.build();
McpStatelessServerFeatures.SyncToolSpecification tool1 = McpStatelessServerFeatures.SyncToolSpecification
.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((ctx, request) -> {
try {
@ -121,7 +122,8 @@ public abstract class AbstractStatelessIntegrationTests {
assertThat(mcpClient.listTools().tools()).contains(tool1.tool());
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
assertThat(response).isNotNull().isEqualTo(callResponse);
}
@ -139,11 +141,7 @@ public abstract class AbstractStatelessIntegrationTests {
McpStatelessSyncServer mcpServer = prepareSyncServerBuilder()
.capabilities(ServerCapabilities.builder().tools(true).build())
.tools(McpStatelessServerFeatures.SyncToolSpecification.builder()
.tool(Tool.builder()
.name("tool1")
.description("tool1 description")
.inputSchema(EMPTY_JSON_SCHEMA)
.build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((context, request) -> {
// We trigger a timeout on blocking read, raising an exception
Mono.never().block(Duration.ofSeconds(1));
@ -159,8 +157,8 @@ public abstract class AbstractStatelessIntegrationTests {
// We expect the tool call to fail immediately with the exception raised by
// the offending tool
// instead of getting back a timeout.
assertThatExceptionOfType(McpError.class)
.isThrownBy(() -> mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of())))
assertThatExceptionOfType(McpError.class).isThrownBy(
() -> mcpClient.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build()))
.withMessageContaining("Timeout on blocking read");
}
finally {
@ -175,12 +173,12 @@ public abstract class AbstractStatelessIntegrationTests {
var clientBuilder = clientBuilders.get(clientType);
var callResponse = McpSchema.CallToolResult.builder()
.content(List.of(new McpSchema.TextContent("CALL RESPONSE")))
.content(List.of(McpSchema.TextContent.builder("CALL RESPONSE").build()))
.isError(false)
.build();
McpStatelessServerFeatures.SyncToolSpecification tool1 = McpStatelessServerFeatures.SyncToolSpecification
.builder()
.tool(Tool.builder().name("tool1").description("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build())
.tool(Tool.builder("tool1", EMPTY_JSON_SCHEMA).description("tool1 description").build())
.callHandler((ctx, request) -> {
// perform a blocking call to a remote service
try {
@ -238,11 +236,7 @@ public abstract class AbstractStatelessIntegrationTests {
// Add a new tool
McpStatelessServerFeatures.SyncToolSpecification tool2 = McpStatelessServerFeatures.SyncToolSpecification
.builder()
.tool(Tool.builder()
.name("tool2")
.description("tool2 description")
.inputSchema(EMPTY_JSON_SCHEMA)
.build())
.tool(Tool.builder("tool2", EMPTY_JSON_SCHEMA).description("tool2 description").build())
.callHandler((exchange, request) -> callResponse)
.build();
@ -285,8 +279,7 @@ public abstract class AbstractStatelessIntegrationTests {
Map.of("type", "string"), "timestamp", Map.of("type", "string")),
"required", List.of("result", "operation"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -320,8 +313,8 @@ public abstract class AbstractStatelessIntegrationTests {
// Note: outputSchema might be null in sync server, but validation still works
// Call tool with valid structured output
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -365,8 +358,7 @@ public abstract class AbstractStatelessIntegrationTests {
"age", Map.of("type", "number")),
"required", List.of("name", "age"))); // @formatter:on
Tool calculatorTool = Tool.builder()
.name("getMembers")
Tool calculatorTool = Tool.builder("getMembers")
.description("Returns a list of members")
.outputSchema(outputSchema)
.build();
@ -390,7 +382,8 @@ public abstract class AbstractStatelessIntegrationTests {
assertThat(mcpClient.initialize()).isNotNull();
// Call tool with valid structured output of type array
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("getMembers", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("getMembers").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -420,8 +413,7 @@ public abstract class AbstractStatelessIntegrationTests {
Map.of("type", "string"), "timestamp", Map.of("type", "string")),
"required", List.of("result", "operation"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -432,7 +424,7 @@ public abstract class AbstractStatelessIntegrationTests {
.tool(calculatorTool)
.callHandler((exchange, request) -> CallToolResult.builder()
.isError(true)
.content(List.of(new TextContent("Error calling tool: Simulated in-handler error")))
.content(List.of(TextContent.builder("Error calling tool: Simulated in-handler error").build()))
.build())
.build();
@ -452,14 +444,14 @@ public abstract class AbstractStatelessIntegrationTests {
// Note: outputSchema might be null in sync server, but validation still works
// Call tool with valid structured output
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isTrue();
assertThat(response.content()).isNotEmpty();
assertThat(response.content())
.containsExactly(new McpSchema.TextContent("Error calling tool: Simulated in-handler error"));
assertThat(response.content()).containsExactly(
McpSchema.TextContent.builder("Error calling tool: Simulated in-handler error").build());
assertThat(response.structuredContent()).isNull();
}
finally {
@ -478,8 +470,7 @@ public abstract class AbstractStatelessIntegrationTests {
Map.of("result", Map.of("type", "number"), "operation", Map.of("type", "string")), "required",
List.of("result", "operation"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -507,8 +498,8 @@ public abstract class AbstractStatelessIntegrationTests {
assertThat(initResult).isNotNull();
// Call tool with invalid structured output
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isTrue();
@ -533,8 +524,7 @@ public abstract class AbstractStatelessIntegrationTests {
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
Map.of("result", Map.of("type", "number")), "required", List.of("result"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -557,8 +547,8 @@ public abstract class AbstractStatelessIntegrationTests {
assertThat(initResult).isNotNull();
// Call tool that should return structured content but doesn't
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isTrue();
@ -597,8 +587,7 @@ public abstract class AbstractStatelessIntegrationTests {
Map.of("message", Map.of("type", "string"), "count", Map.of("type", "integer")), "required",
List.of("message", "count"));
Tool dynamicTool = Tool.builder()
.name("dynamic-tool")
Tool dynamicTool = Tool.builder("dynamic-tool")
.description("Dynamically added tool")
.outputSchema(outputSchema)
.build();
@ -630,7 +619,7 @@ public abstract class AbstractStatelessIntegrationTests {
// Call dynamically added tool
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("dynamic-tool", Map.of("count", 3)));
.callTool(McpSchema.CallToolRequest.builder("dynamic-tool").arguments(Map.of("count", 3)).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();

View file

@ -205,7 +205,9 @@ public abstract class AbstractMcpAsyncClientResiliencyTests {
String name = tools.get().get(0).name();
// Assuming this is the echo tool
McpSchema.CallToolRequest request = new McpSchema.CallToolRequest(name, Map.of("message", "hello"));
McpSchema.CallToolRequest request = McpSchema.CallToolRequest.builder(name)
.arguments(Map.of("message", "hello"))
.build();
StepVerifier.create(mcpAsyncClient.callTool(request)).expectError().verify();
reconnect();

View file

@ -85,8 +85,10 @@ public abstract class AbstractMcpAsyncClientTests {
McpClient.AsyncSpec builder = McpClient.async(transport)
.requestTimeout(getRequestTimeout())
.initializationTimeout(getInitializationTimeout())
.sampling(req -> Mono.just(new CreateMessageResult(McpSchema.Role.USER,
new McpSchema.TextContent("Oh, hi!"), "modelId", CreateMessageResult.StopReason.END_TURN)))
.sampling(req -> Mono.just(CreateMessageResult
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Oh, hi!").build(), "modelId")
.stopReason(CreateMessageResult.StopReason.END_TURN)
.build()))
.capabilities(ClientCapabilities.builder().roots(true).sampling().build());
builder = customizer.apply(builder);
client.set(builder.build());
@ -176,11 +178,7 @@ public abstract class AbstractMcpAsyncClientTests {
assertThat(result.tools()).isNotNull();
// Verify that the returned list is immutable
assertThatThrownBy(() -> result.tools()
.add(Tool.builder()
.name("test")
.title("test")
.inputSchema(JSON_MAPPER, "{\"type\":\"object\"}")
.build()))
.add(Tool.builder("test", JSON_MAPPER, "{\"type\":\"object\"}").title("test").build()))
.isInstanceOf(UnsupportedOperationException.class);
})
.verifyComplete();
@ -203,14 +201,18 @@ public abstract class AbstractMcpAsyncClientTests {
@Test
void testCallToolWithoutInitialization() {
CallToolRequest callToolRequest = new CallToolRequest("echo", Map.of("message", ECHO_TEST_MESSAGE));
CallToolRequest callToolRequest = CallToolRequest.builder("echo")
.arguments(Map.of("message", ECHO_TEST_MESSAGE))
.build();
verifyCallSucceedsWithImplicitInitialization(client -> client.callTool(callToolRequest), "calling tools");
}
@Test
void testCallTool() {
withClient(createMcpTransport(), mcpAsyncClient -> {
CallToolRequest callToolRequest = new CallToolRequest("echo", Map.of("message", ECHO_TEST_MESSAGE));
CallToolRequest callToolRequest = CallToolRequest.builder("echo")
.arguments(Map.of("message", ECHO_TEST_MESSAGE))
.build();
StepVerifier.create(mcpAsyncClient.initialize().then(mcpAsyncClient.callTool(callToolRequest)))
.consumeNextWith(callToolResult -> {
@ -226,8 +228,9 @@ public abstract class AbstractMcpAsyncClientTests {
@Test
void testCallToolWithInvalidTool() {
withClient(createMcpTransport(), mcpAsyncClient -> {
CallToolRequest invalidRequest = new CallToolRequest("nonexistent_tool",
Map.of("message", ECHO_TEST_MESSAGE));
CallToolRequest invalidRequest = CallToolRequest.builder("nonexistent_tool")
.arguments(Map.of("message", ECHO_TEST_MESSAGE))
.build();
StepVerifier.create(mcpAsyncClient.initialize().then(mcpAsyncClient.callTool(invalidRequest)))
.consumeErrorWith(
@ -243,8 +246,9 @@ public abstract class AbstractMcpAsyncClientTests {
withClient(transport, mcpAsyncClient -> {
StepVerifier.create(mcpAsyncClient.initialize()
.then(mcpAsyncClient.callTool(new McpSchema.CallToolRequest("annotatedMessage",
Map.of("messageType", messageType, "includeImage", true)))))
.then(mcpAsyncClient.callTool(McpSchema.CallToolRequest.builder("annotatedMessage")
.arguments(Map.of("messageType", messageType, "includeImage", true))
.build())))
.consumeNextWith(result -> {
assertThat(result).isNotNull();
assertThat(result.isError()).isNotEqualTo(true);
@ -345,8 +349,7 @@ public abstract class AbstractMcpAsyncClientTests {
.consumeNextWith(result -> {
assertThat(result.resources()).isNotNull();
// Verify that the returned list is immutable
assertThatThrownBy(
() -> result.resources().add(Resource.builder().uri("test://uri").name("test").build()))
assertThatThrownBy(() -> result.resources().add(Resource.builder("test://uri", "test").build()))
.isInstanceOf(UnsupportedOperationException.class);
})
.verifyComplete();
@ -411,7 +414,8 @@ public abstract class AbstractMcpAsyncClientTests {
.consumeNextWith(result -> {
assertThat(result.prompts()).isNotNull();
// Verify that the returned list is immutable
assertThatThrownBy(() -> result.prompts().add(new Prompt("test", "test", "test", null)))
assertThatThrownBy(() -> result.prompts()
.add(Prompt.builder("test").title("test").description("test").build()))
.isInstanceOf(UnsupportedOperationException.class);
})
.verifyComplete();
@ -420,7 +424,7 @@ public abstract class AbstractMcpAsyncClientTests {
@Test
void testGetPromptWithoutInitialization() {
GetPromptRequest request = new GetPromptRequest("simple_prompt", Map.of());
GetPromptRequest request = GetPromptRequest.builder("simple_prompt").arguments(Map.of()).build();
verifyCallSucceedsWithImplicitInitialization(client -> client.getPrompt(request), "getting " + "prompts");
}
@ -429,7 +433,8 @@ public abstract class AbstractMcpAsyncClientTests {
withClient(createMcpTransport(), mcpAsyncClient -> {
StepVerifier
.create(mcpAsyncClient.initialize()
.then(mcpAsyncClient.getPrompt(new GetPromptRequest("simple_prompt", Map.of()))))
.then(mcpAsyncClient
.getPrompt(GetPromptRequest.builder("simple_prompt").arguments(Map.of()).build())))
.consumeNextWith(prompt -> {
assertThat(prompt).isNotNull().satisfies(result -> {
assertThat(result.messages()).isNotEmpty();
@ -456,8 +461,8 @@ public abstract class AbstractMcpAsyncClientTests {
@Test
void testInitializeWithRootsListProviders() {
withClient(createMcpTransport(), builder -> builder.roots(new Root("file:///test/path", "test-root")),
client -> {
withClient(createMcpTransport(),
builder -> builder.roots(Root.builder("file:///test/path").name("test-root").build()), client -> {
StepVerifier.create(client.initialize().then(client.closeGracefully())).verifyComplete();
});
}
@ -465,7 +470,7 @@ public abstract class AbstractMcpAsyncClientTests {
@Test
void testAddRoot() {
withClient(createMcpTransport(), mcpAsyncClient -> {
Root newRoot = new Root("file:///new/test/path", "new-test-root");
Root newRoot = Root.builder("file:///new/test/path").name("new-test-root").build();
StepVerifier.create(mcpAsyncClient.addRoot(newRoot)).verifyComplete();
});
}
@ -483,7 +488,7 @@ public abstract class AbstractMcpAsyncClientTests {
@Test
void testRemoveRoot() {
withClient(createMcpTransport(), mcpAsyncClient -> {
Root root = new Root("file:///test/path/to/remove", "root-to-remove");
Root root = Root.builder("file:///test/path/to/remove").name("root-to-remove").build();
StepVerifier.create(mcpAsyncClient.addRoot(root)).verifyComplete();
StepVerifier.create(mcpAsyncClient.removeRoot(root.uri())).verifyComplete();
@ -603,7 +608,7 @@ public abstract class AbstractMcpAsyncClientTests {
assertThat(result.resourceTemplates()).isNotNull();
// Verify that the returned list is immutable
assertThatThrownBy(() -> result.resourceTemplates()
.add(new McpSchema.ResourceTemplate("test://template", "test", "test", null, null, null)))
.add(McpSchema.ResourceTemplate.builder("test://template", "test").title("test").build()))
.isInstanceOf(UnsupportedOperationException.class);
})
.verifyComplete();
@ -618,8 +623,8 @@ public abstract class AbstractMcpAsyncClientTests {
return Mono.empty();
}
Resource firstResource = resources.resources().get(0);
return mcpAsyncClient.subscribeResource(new SubscribeRequest(firstResource.uri()))
.then(mcpAsyncClient.unsubscribeResource(new UnsubscribeRequest(firstResource.uri())));
return mcpAsyncClient.subscribeResource(SubscribeRequest.builder(firstResource.uri()).build())
.then(mcpAsyncClient.unsubscribeResource(UnsubscribeRequest.builder(firstResource.uri()).build()));
})).verifyComplete();
});
}
@ -646,9 +651,8 @@ public abstract class AbstractMcpAsyncClientTests {
@Test
void testInitializeWithSamplingCapability() {
ClientCapabilities capabilities = ClientCapabilities.builder().sampling().build();
CreateMessageResult createMessageResult = CreateMessageResult.builder()
.message("test")
.model("test-model")
CreateMessageResult createMessageResult = CreateMessageResult
.builder(McpSchema.Role.ASSISTANT, "test", "test-model")
.build();
withClient(createMcpTransport(),
builder -> builder.capabilities(capabilities).sampling(request -> Mono.just(createMessageResult)),
@ -660,8 +664,7 @@ public abstract class AbstractMcpAsyncClientTests {
@Test
void testInitializeWithElicitationCapability() {
ClientCapabilities capabilities = ClientCapabilities.builder().elicitation().build();
ElicitResult elicitResult = ElicitResult.builder()
.message(ElicitResult.Action.ACCEPT)
ElicitResult elicitResult = ElicitResult.builder(ElicitResult.Action.ACCEPT)
.content(Map.of("foo", "bar"))
.build();
withClient(createMcpTransport(),
@ -680,10 +683,10 @@ public abstract class AbstractMcpAsyncClientTests {
.build();
Function<CreateMessageRequest, Mono<CreateMessageResult>> samplingHandler = request -> Mono
.just(CreateMessageResult.builder().message("test").model("test-model").build());
.just(CreateMessageResult.builder(McpSchema.Role.ASSISTANT, "test", "test-model").build());
Function<ElicitRequest, Mono<ElicitResult>> elicitationHandler = request -> Mono
.just(ElicitResult.builder().message(ElicitResult.Action.ACCEPT).content(Map.of("foo", "bar")).build());
.just(ElicitResult.builder(ElicitResult.Action.ACCEPT).content(Map.of("foo", "bar")).build());
withClient(createMcpTransport(),
builder -> builder.capabilities(capabilities).sampling(samplingHandler).elicitation(elicitationHandler),
@ -757,15 +760,16 @@ public abstract class AbstractMcpAsyncClientTests {
receivedMessage.set(messageText.text());
receivedMaxTokens.set(request.maxTokens());
return Mono
.just(new McpSchema.CreateMessageResult(McpSchema.Role.USER, new McpSchema.TextContent(response),
"modelId", McpSchema.CreateMessageResult.StopReason.END_TURN));
return Mono.just(McpSchema.CreateMessageResult
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder(response).build(), "modelId")
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
.build());
}), client -> {
StepVerifier.create(client.initialize()).expectNextMatches(Objects::nonNull).verifyComplete();
StepVerifier.create(client.callTool(
new McpSchema.CallToolRequest("sampleLLM", Map.of("prompt", message, "maxTokens", maxTokens))))
.consumeNextWith(result -> {
StepVerifier.create(client.callTool(McpSchema.CallToolRequest.builder("sampleLLM")
.arguments(Map.of("prompt", message, "maxTokens", maxTokens))
.build())).consumeNextWith(result -> {
// Verify tool response to ensure our sampling response was passed
// through
assertThat(result.content()).hasAtLeastOneElementOfType(McpSchema.TextContent.class);
@ -780,8 +784,7 @@ public abstract class AbstractMcpAsyncClientTests {
assertThat(receivedPrompt.get()).isNotEmpty();
assertThat(receivedMessage.get()).endsWith(message); // Prefixed
assertThat(receivedMaxTokens.get()).isEqualTo(maxTokens);
})
.verifyComplete();
}).verifyComplete();
});
}

View file

@ -186,14 +186,16 @@ public abstract class AbstractMcpSyncClientTests {
@Test
void testCallToolsWithoutInitialization() {
verifyCallSucceedsWithImplicitInitialization(
client -> client.callTool(new CallToolRequest("add", Map.of("a", 3, "b", 4))), "calling tools");
client -> client.callTool(CallToolRequest.builder("add").arguments(Map.of("a", 3, "b", 4)).build()),
"calling tools");
}
@Test
void testCallTools() {
withClient(createMcpTransport(), mcpSyncClient -> {
mcpSyncClient.initialize();
CallToolResult toolResult = mcpSyncClient.callTool(new CallToolRequest("add", Map.of("a", 3, "b", 4)));
CallToolResult toolResult = mcpSyncClient
.callTool(CallToolRequest.builder("add").arguments(Map.of("a", 3, "b", 4)).build());
assertThat(toolResult).isNotNull().satisfies(result -> {
@ -223,7 +225,9 @@ public abstract class AbstractMcpSyncClientTests {
@Test
void testCallToolWithoutInitialization() {
CallToolRequest callToolRequest = new CallToolRequest("echo", Map.of("message", TEST_MESSAGE));
CallToolRequest callToolRequest = CallToolRequest.builder("echo")
.arguments(Map.of("message", TEST_MESSAGE))
.build();
verifyCallSucceedsWithImplicitInitialization(client -> client.callTool(callToolRequest), "calling tools");
}
@ -231,7 +235,9 @@ public abstract class AbstractMcpSyncClientTests {
void testCallTool() {
withClient(createMcpTransport(), mcpSyncClient -> {
mcpSyncClient.initialize();
CallToolRequest callToolRequest = new CallToolRequest("echo", Map.of("message", TEST_MESSAGE));
CallToolRequest callToolRequest = CallToolRequest.builder("echo")
.arguments(Map.of("message", TEST_MESSAGE))
.build();
CallToolResult callToolResult = mcpSyncClient.callTool(callToolRequest);
@ -245,7 +251,9 @@ public abstract class AbstractMcpSyncClientTests {
@Test
void testCallToolWithInvalidTool() {
withClient(createMcpTransport(), mcpSyncClient -> {
CallToolRequest invalidRequest = new CallToolRequest("nonexistent_tool", Map.of("message", TEST_MESSAGE));
CallToolRequest invalidRequest = CallToolRequest.builder("nonexistent_tool")
.arguments(Map.of("message", TEST_MESSAGE))
.build();
assertThatThrownBy(() -> mcpSyncClient.callTool(invalidRequest)).isInstanceOf(Exception.class);
});
@ -259,8 +267,9 @@ public abstract class AbstractMcpSyncClientTests {
withClient(transport, client -> {
client.initialize();
McpSchema.CallToolResult result = client.callTool(new McpSchema.CallToolRequest("annotatedMessage",
Map.of("messageType", messageType, "includeImage", true)));
McpSchema.CallToolResult result = client.callTool(McpSchema.CallToolRequest.builder("annotatedMessage")
.arguments(Map.of("messageType", messageType, "includeImage", true))
.build());
assertThat(result).isNotNull();
assertThat(result.isError()).isNotEqualTo(true);
@ -370,7 +379,8 @@ public abstract class AbstractMcpSyncClientTests {
@Test
void testInitializeWithRootsListProviders() {
withClient(createMcpTransport(), builder -> builder.roots(new Root("file:///test/path", "test-root")),
withClient(createMcpTransport(),
builder -> builder.roots(Root.builder("file:///test/path").name("test-root").build()),
mcpSyncClient -> {
assertThatCode(() -> {
@ -383,7 +393,7 @@ public abstract class AbstractMcpSyncClientTests {
@Test
void testAddRoot() {
withClient(createMcpTransport(), mcpSyncClient -> {
Root newRoot = new Root("file:///new/test/path", "new-test-root");
Root newRoot = Root.builder("file:///new/test/path").name("new-test-root").build();
assertThatCode(() -> mcpSyncClient.addRoot(newRoot)).doesNotThrowAnyException();
});
}
@ -398,7 +408,7 @@ public abstract class AbstractMcpSyncClientTests {
@Test
void testRemoveRoot() {
withClient(createMcpTransport(), mcpSyncClient -> {
Root root = new Root("file:///test/path/to/remove", "root-to-remove");
Root root = Root.builder("file:///test/path/to/remove").name("root-to-remove").build();
assertThatCode(() -> {
mcpSyncClient.addRoot(root);
mcpSyncClient.removeRoot(root.uri());
@ -533,11 +543,13 @@ public abstract class AbstractMcpSyncClientTests {
Resource firstResource = resources.resources().get(0);
// Test subscribe
assertThatCode(() -> mcpSyncClient.subscribeResource(new SubscribeRequest(firstResource.uri())))
assertThatCode(
() -> mcpSyncClient.subscribeResource(SubscribeRequest.builder(firstResource.uri()).build()))
.doesNotThrowAnyException();
// Test unsubscribe
assertThatCode(() -> mcpSyncClient.unsubscribeResource(new UnsubscribeRequest(firstResource.uri())))
assertThatCode(() -> mcpSyncClient
.unsubscribeResource(UnsubscribeRequest.builder(firstResource.uri()).build()))
.doesNotThrowAnyException();
}
});
@ -623,13 +635,16 @@ public abstract class AbstractMcpSyncClientTests {
receivedMessage.set(messageText.text());
receivedMaxTokens.set(request.maxTokens());
return new McpSchema.CreateMessageResult(McpSchema.Role.USER, new McpSchema.TextContent(response),
"modelId", McpSchema.CreateMessageResult.StopReason.END_TURN);
return McpSchema.CreateMessageResult
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder(response).build(), "modelId")
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
.build();
}), client -> {
client.initialize();
McpSchema.CallToolResult result = client.callTool(
new McpSchema.CallToolRequest("sampleLLM", Map.of("prompt", message, "maxTokens", maxTokens)));
McpSchema.CallToolResult result = client.callTool(McpSchema.CallToolRequest.builder("sampleLLM")
.arguments(Map.of("prompt", message, "maxTokens", maxTokens))
.build());
// Verify tool response to ensure our sampling response was passed through
assertThat(result.content()).hasAtLeastOneElementOfType(McpSchema.TextContent.class);

View file

@ -99,11 +99,7 @@ public abstract class AbstractMcpAsyncServerTests {
// ---------------------------------------
@Test
void testAddToolCall() {
Tool newTool = McpSchema.Tool.builder()
.name("new-tool")
.title("New test tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool newTool = McpSchema.Tool.builder("new-tool", EMPTY_JSON_SCHEMA).title("New test tool").build();
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
@ -120,11 +116,7 @@ public abstract class AbstractMcpAsyncServerTests {
@Test
void testAddDuplicateToolCall() {
Tool duplicateTool = McpSchema.Tool.builder()
.name(TEST_TOOL_NAME)
.title("Duplicate tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool duplicateTool = McpSchema.Tool.builder(TEST_TOOL_NAME, EMPTY_JSON_SCHEMA).title("Duplicate tool").build();
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
@ -144,10 +136,8 @@ public abstract class AbstractMcpAsyncServerTests {
@Test
void testDuplicateToolCallDuringBuilding() {
Tool duplicateTool = McpSchema.Tool.builder()
.name("duplicate-build-toolcall")
Tool duplicateTool = McpSchema.Tool.builder("duplicate-build-toolcall", EMPTY_JSON_SCHEMA)
.title("Duplicate toolcall during building")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
@ -164,10 +154,8 @@ public abstract class AbstractMcpAsyncServerTests {
@Test
void testDuplicateToolsInBatchListRegistration() {
Tool duplicateTool = McpSchema.Tool.builder()
.name("batch-list-tool")
Tool duplicateTool = McpSchema.Tool.builder("batch-list-tool", EMPTY_JSON_SCHEMA)
.title("Duplicate tool in batch list")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
List<McpServerFeatures.AsyncToolSpecification> specs = List.of(
@ -192,10 +180,8 @@ public abstract class AbstractMcpAsyncServerTests {
@Test
void testDuplicateToolsInBatchVarargsRegistration() {
Tool duplicateTool = McpSchema.Tool.builder()
.name("batch-varargs-tool")
Tool duplicateTool = McpSchema.Tool.builder("batch-varargs-tool", EMPTY_JSON_SCHEMA)
.title("Duplicate tool in batch varargs")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
@ -217,11 +203,7 @@ public abstract class AbstractMcpAsyncServerTests {
@Test
void testRemoveTool() {
Tool too = McpSchema.Tool.builder()
.name(TEST_TOOL_NAME)
.title("Duplicate tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool too = McpSchema.Tool.builder(TEST_TOOL_NAME, EMPTY_JSON_SCHEMA).title("Duplicate tool").build();
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
@ -248,11 +230,7 @@ public abstract class AbstractMcpAsyncServerTests {
@Test
void testNotifyToolsListChanged() {
Tool too = McpSchema.Tool.builder()
.name(TEST_TOOL_NAME)
.title("Duplicate tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool too = McpSchema.Tool.builder(TEST_TOOL_NAME, EMPTY_JSON_SCHEMA).title("Duplicate tool").build();
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
@ -296,15 +274,13 @@ public abstract class AbstractMcpAsyncServerTests {
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
Resource resource = Resource.builder()
.uri(TEST_RESOURCE_URI)
.name("Test Resource")
Resource resource = Resource.builder(TEST_RESOURCE_URI, "Test Resource")
.title("Test Resource")
.mimeType("text/plain")
.description("Test resource description")
.build();
McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
resource, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
StepVerifier.create(mcpAsyncServer.addResource(specification)).verifyComplete();
@ -330,15 +306,13 @@ public abstract class AbstractMcpAsyncServerTests {
// Create a server without resource capabilities
McpAsyncServer serverWithoutResources = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
Resource resource = Resource.builder()
.uri(TEST_RESOURCE_URI)
.name("Test Resource")
Resource resource = Resource.builder(TEST_RESOURCE_URI, "Test Resource")
.title("Test Resource")
.mimeType("text/plain")
.description("Test resource description")
.build();
McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
resource, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
StepVerifier.create(serverWithoutResources.addResource(specification)).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(IllegalStateException.class)
@ -363,15 +337,13 @@ public abstract class AbstractMcpAsyncServerTests {
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
Resource resource = Resource.builder()
.uri(TEST_RESOURCE_URI)
.name("Test Resource")
Resource resource = Resource.builder(TEST_RESOURCE_URI, "Test Resource")
.title("Test Resource")
.mimeType("text/plain")
.description("Test resource description")
.build();
McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
resource, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
StepVerifier
.create(mcpAsyncServer.addResource(specification).then(mcpAsyncServer.listResources().collectList()))
@ -387,15 +359,13 @@ public abstract class AbstractMcpAsyncServerTests {
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
Resource resource = Resource.builder()
.uri(TEST_RESOURCE_URI)
.name("Test Resource")
Resource resource = Resource.builder(TEST_RESOURCE_URI, "Test Resource")
.title("Test Resource")
.mimeType("text/plain")
.description("Test resource description")
.build();
McpServerFeatures.AsyncResourceSpecification specification = new McpServerFeatures.AsyncResourceSpecification(
resource, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
resource, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
StepVerifier
.create(mcpAsyncServer.addResource(specification).then(mcpAsyncServer.removeResource(TEST_RESOURCE_URI)))
@ -427,15 +397,14 @@ public abstract class AbstractMcpAsyncServerTests {
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder()
.uriTemplate("test://template/{id}")
.name("test-template")
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate
.builder("test://template/{id}", "test-template")
.description("Test resource template")
.mimeType("text/plain")
.build();
McpServerFeatures.AsyncResourceTemplateSpecification specification = new McpServerFeatures.AsyncResourceTemplateSpecification(
template, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
template, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
StepVerifier.create(mcpAsyncServer.addResourceTemplate(specification)).verifyComplete();
@ -447,15 +416,14 @@ public abstract class AbstractMcpAsyncServerTests {
// Create a server without resource capabilities
McpAsyncServer serverWithoutResources = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder()
.uriTemplate("test://template/{id}")
.name("test-template")
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate
.builder("test://template/{id}", "test-template")
.description("Test resource template")
.mimeType("text/plain")
.build();
McpServerFeatures.AsyncResourceTemplateSpecification specification = new McpServerFeatures.AsyncResourceTemplateSpecification(
template, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
template, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
StepVerifier.create(serverWithoutResources.addResourceTemplate(specification)).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(IllegalStateException.class)
@ -465,15 +433,14 @@ public abstract class AbstractMcpAsyncServerTests {
@Test
void testRemoveResourceTemplate() {
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder()
.uriTemplate("test://template/{id}")
.name("test-template")
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate
.builder("test://template/{id}", "test-template")
.description("Test resource template")
.mimeType("text/plain")
.build();
McpServerFeatures.AsyncResourceTemplateSpecification specification = new McpServerFeatures.AsyncResourceTemplateSpecification(
template, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
template, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().resources(true, false).build())
@ -510,15 +477,14 @@ public abstract class AbstractMcpAsyncServerTests {
@Test
void testListResourceTemplates() {
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder()
.uriTemplate("test://template/{id}")
.name("test-template")
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate
.builder("test://template/{id}", "test-template")
.description("Test resource template")
.mimeType("text/plain")
.build();
McpServerFeatures.AsyncResourceTemplateSpecification specification = new McpServerFeatures.AsyncResourceTemplateSpecification(
template, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
template, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().resources(true, false).build())
@ -568,10 +534,22 @@ public abstract class AbstractMcpAsyncServerTests {
// Create a server without prompt capabilities
McpAsyncServer serverWithoutPrompts = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", "Test Prompt", List.of());
Prompt prompt = Prompt.builder(TEST_PROMPT_NAME)
.title("Test Prompt")
.description("Test Prompt")
.arguments(List.of())
.build();
McpServerFeatures.AsyncPromptSpecification specification = new McpServerFeatures.AsyncPromptSpecification(
prompt, (exchange, req) -> Mono.just(new GetPromptResult("Test prompt description", List
.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))));
prompt,
(exchange, req) -> Mono.just(
GetPromptResult
.builder(
List.of(PromptMessage
.builder(McpSchema.Role.ASSISTANT,
McpSchema.TextContent.builder("Test content").build())
.build()))
.description("Test prompt description")
.build()));
StepVerifier.create(serverWithoutPrompts.addPrompt(specification)).verifyErrorSatisfies(error -> {
assertThat(error).isInstanceOf(IllegalStateException.class)
@ -594,10 +572,22 @@ public abstract class AbstractMcpAsyncServerTests {
void testRemovePrompt() {
String TEST_PROMPT_NAME_TO_REMOVE = "TEST_PROMPT_NAME678";
Prompt prompt = new Prompt(TEST_PROMPT_NAME_TO_REMOVE, "Test Prompt", "Test Prompt", List.of());
Prompt prompt = Prompt.builder(TEST_PROMPT_NAME_TO_REMOVE)
.title("Test Prompt")
.description("Test Prompt")
.arguments(List.of())
.build();
McpServerFeatures.AsyncPromptSpecification specification = new McpServerFeatures.AsyncPromptSpecification(
prompt, (exchange, req) -> Mono.just(new GetPromptResult("Test prompt description", List
.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))));
prompt,
(exchange, req) -> Mono.just(
GetPromptResult
.builder(
List.of(PromptMessage
.builder(McpSchema.Role.ASSISTANT,
McpSchema.TextContent.builder("Test content").build())
.build()))
.description("Test prompt description")
.build()));
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().prompts(true).build())

View file

@ -107,11 +107,7 @@ public abstract class AbstractMcpSyncServerTests {
.capabilities(ServerCapabilities.builder().tools(true).build())
.build();
Tool newTool = McpSchema.Tool.builder()
.name("new-tool")
.title("New test tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool newTool = McpSchema.Tool.builder("new-tool", EMPTY_JSON_SCHEMA).title("New test tool").build();
assertThatCode(() -> mcpSyncServer.addTool(McpServerFeatures.SyncToolSpecification.builder()
.tool(newTool)
@ -123,11 +119,7 @@ public abstract class AbstractMcpSyncServerTests {
@Test
void testAddDuplicateToolCall() {
Tool duplicateTool = McpSchema.Tool.builder()
.name(TEST_TOOL_NAME)
.title("Duplicate tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool duplicateTool = McpSchema.Tool.builder(TEST_TOOL_NAME, EMPTY_JSON_SCHEMA).title("Duplicate tool").build();
var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
@ -145,10 +137,8 @@ public abstract class AbstractMcpSyncServerTests {
@Test
void testDuplicateToolCallDuringBuilding() {
Tool duplicateTool = McpSchema.Tool.builder()
.name("duplicate-build-toolcall")
Tool duplicateTool = McpSchema.Tool.builder("duplicate-build-toolcall", EMPTY_JSON_SCHEMA)
.title("Duplicate toolcall during building")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
assertThatThrownBy(() -> prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
@ -163,10 +153,8 @@ public abstract class AbstractMcpSyncServerTests {
@Test
void testDuplicateToolsInBatchListRegistration() {
Tool duplicateTool = McpSchema.Tool.builder()
.name("batch-list-tool")
Tool duplicateTool = McpSchema.Tool.builder("batch-list-tool", EMPTY_JSON_SCHEMA)
.title("Duplicate tool in batch list")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
List<McpServerFeatures.SyncToolSpecification> specs = List.of(
McpServerFeatures.SyncToolSpecification.builder()
@ -190,10 +178,8 @@ public abstract class AbstractMcpSyncServerTests {
@Test
void testDuplicateToolsInBatchVarargsRegistration() {
Tool duplicateTool = McpSchema.Tool.builder()
.name("batch-varargs-tool")
Tool duplicateTool = McpSchema.Tool.builder("batch-varargs-tool", EMPTY_JSON_SCHEMA)
.title("Duplicate tool in batch varargs")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
assertThatThrownBy(() -> prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
@ -214,11 +200,7 @@ public abstract class AbstractMcpSyncServerTests {
@Test
void testRemoveTool() {
Tool tool = McpSchema.Tool.builder()
.name(TEST_TOOL_NAME)
.title("Test tool")
.inputSchema(EMPTY_JSON_SCHEMA)
.build();
Tool tool = McpSchema.Tool.builder(TEST_TOOL_NAME, EMPTY_JSON_SCHEMA).title("Test tool").build();
var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().tools(true).build())
@ -280,15 +262,13 @@ public abstract class AbstractMcpSyncServerTests {
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
Resource resource = Resource.builder()
.uri(TEST_RESOURCE_URI)
.name("Test Resource")
Resource resource = Resource.builder(TEST_RESOURCE_URI, "Test Resource")
.title("Test Resource")
.mimeType("text/plain")
.description("Test resource description")
.build();
McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification(
resource, (exchange, req) -> new ReadResourceResult(List.of()));
resource, (exchange, req) -> ReadResourceResult.builder(List.of()).build());
assertThatCode(() -> mcpSyncServer.addResource(specification)).doesNotThrowAnyException();
@ -312,15 +292,13 @@ public abstract class AbstractMcpSyncServerTests {
void testAddResourceWithoutCapability() {
var serverWithoutResources = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build();
Resource resource = Resource.builder()
.uri(TEST_RESOURCE_URI)
.name("Test Resource")
Resource resource = Resource.builder(TEST_RESOURCE_URI, "Test Resource")
.title("Test Resource")
.mimeType("text/plain")
.description("Test resource description")
.build();
McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification(
resource, (exchange, req) -> new ReadResourceResult(List.of()));
resource, (exchange, req) -> ReadResourceResult.builder(List.of()).build());
assertThatThrownBy(() -> serverWithoutResources.addResource(specification))
.isInstanceOf(IllegalStateException.class)
@ -342,15 +320,13 @@ public abstract class AbstractMcpSyncServerTests {
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
Resource resource = Resource.builder()
.uri(TEST_RESOURCE_URI)
.name("Test Resource")
Resource resource = Resource.builder(TEST_RESOURCE_URI, "Test Resource")
.title("Test Resource")
.mimeType("text/plain")
.description("Test resource description")
.build();
McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification(
resource, (exchange, req) -> new ReadResourceResult(List.of()));
resource, (exchange, req) -> ReadResourceResult.builder(List.of()).build());
mcpSyncServer.addResource(specification);
List<McpSchema.Resource> resources = mcpSyncServer.listResources();
@ -367,15 +343,13 @@ public abstract class AbstractMcpSyncServerTests {
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
Resource resource = Resource.builder()
.uri(TEST_RESOURCE_URI)
.name("Test Resource")
Resource resource = Resource.builder(TEST_RESOURCE_URI, "Test Resource")
.title("Test Resource")
.mimeType("text/plain")
.description("Test resource description")
.build();
McpServerFeatures.SyncResourceSpecification specification = new McpServerFeatures.SyncResourceSpecification(
resource, (exchange, req) -> new ReadResourceResult(List.of()));
resource, (exchange, req) -> ReadResourceResult.builder(List.of()).build());
mcpSyncServer.addResource(specification);
assertThatCode(() -> mcpSyncServer.removeResource(TEST_RESOURCE_URI)).doesNotThrowAnyException();
@ -406,15 +380,14 @@ public abstract class AbstractMcpSyncServerTests {
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder()
.uriTemplate("test://template/{id}")
.name("test-template")
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate
.builder("test://template/{id}", "test-template")
.description("Test resource template")
.mimeType("text/plain")
.build();
McpServerFeatures.SyncResourceTemplateSpecification specification = new McpServerFeatures.SyncResourceTemplateSpecification(
template, (exchange, req) -> new ReadResourceResult(List.of()));
template, (exchange, req) -> ReadResourceResult.builder(List.of()).build());
assertThatCode(() -> mcpSyncServer.addResourceTemplate(specification)).doesNotThrowAnyException();
@ -426,15 +399,14 @@ public abstract class AbstractMcpSyncServerTests {
// Create a server without resource capabilities
var serverWithoutResources = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build();
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder()
.uriTemplate("test://template/{id}")
.name("test-template")
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate
.builder("test://template/{id}", "test-template")
.description("Test resource template")
.mimeType("text/plain")
.build();
McpServerFeatures.SyncResourceTemplateSpecification specification = new McpServerFeatures.SyncResourceTemplateSpecification(
template, (exchange, req) -> new ReadResourceResult(List.of()));
template, (exchange, req) -> ReadResourceResult.builder(List.of()).build());
assertThatThrownBy(() -> serverWithoutResources.addResourceTemplate(specification))
.isInstanceOf(IllegalStateException.class)
@ -443,15 +415,14 @@ public abstract class AbstractMcpSyncServerTests {
@Test
void testRemoveResourceTemplate() {
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder()
.uriTemplate("test://template/{id}")
.name("test-template")
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate
.builder("test://template/{id}", "test-template")
.description("Test resource template")
.mimeType("text/plain")
.build();
McpServerFeatures.SyncResourceTemplateSpecification specification = new McpServerFeatures.SyncResourceTemplateSpecification(
template, (exchange, req) -> new ReadResourceResult(List.of()));
template, (exchange, req) -> ReadResourceResult.builder(List.of()).build());
var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().resources(true, false).build())
@ -487,15 +458,14 @@ public abstract class AbstractMcpSyncServerTests {
@Test
void testListResourceTemplates() {
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate.builder()
.uriTemplate("test://template/{id}")
.name("test-template")
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate
.builder("test://template/{id}", "test-template")
.description("Test resource template")
.mimeType("text/plain")
.build();
McpServerFeatures.SyncResourceTemplateSpecification specification = new McpServerFeatures.SyncResourceTemplateSpecification(
template, (exchange, req) -> new ReadResourceResult(List.of()));
template, (exchange, req) -> ReadResourceResult.builder(List.of()).build());
var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().resources(true, false).build())
@ -537,10 +507,21 @@ public abstract class AbstractMcpSyncServerTests {
void testAddPromptWithoutCapability() {
var serverWithoutPrompts = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0").build();
Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", "Test Prompt", List.of());
Prompt prompt = Prompt.builder(TEST_PROMPT_NAME)
.title("Test Prompt")
.description("Test Prompt")
.arguments(List.of())
.build();
McpServerFeatures.SyncPromptSpecification specification = new McpServerFeatures.SyncPromptSpecification(prompt,
(exchange, req) -> new GetPromptResult("Test prompt description", List
.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content")))));
(exchange,
req) -> GetPromptResult
.builder(
List.of(PromptMessage
.builder(McpSchema.Role.ASSISTANT,
McpSchema.TextContent.builder("Test content").build())
.build()))
.description("Test prompt description")
.build());
assertThatThrownBy(() -> serverWithoutPrompts.addPrompt(specification))
.isInstanceOf(IllegalStateException.class)
@ -558,10 +539,21 @@ public abstract class AbstractMcpSyncServerTests {
@Test
void testRemovePrompt() {
Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", "Test Prompt", List.of());
Prompt prompt = Prompt.builder(TEST_PROMPT_NAME)
.title("Test Prompt")
.description("Test Prompt")
.arguments(List.of())
.build();
McpServerFeatures.SyncPromptSpecification specification = new McpServerFeatures.SyncPromptSpecification(prompt,
(exchange, req) -> new GetPromptResult("Test prompt description", List
.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content")))));
(exchange,
req) -> GetPromptResult
.builder(
List.of(PromptMessage
.builder(McpSchema.Role.ASSISTANT,
McpSchema.TextContent.builder("Test content").build())
.build()))
.description("Test prompt description")
.build());
var mcpSyncServer = prepareSyncServerBuilder().serverInfo("test-server", "1.0.0")
.capabilities(ServerCapabilities.builder().prompts(true).build())

View file

@ -30,7 +30,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
class McpAsyncClientResponseHandlerTests {
private static final McpSchema.Implementation SERVER_INFO = new McpSchema.Implementation("test-server", "1.0.0");
private static final McpSchema.Implementation SERVER_INFO = McpSchema.Implementation.builder("test-server", "1.0.0")
.build();
private static final McpSchema.ServerCapabilities SERVER_CAPABILITIES = McpSchema.ServerCapabilities.builder()
.tools(true)
@ -43,13 +44,14 @@ class McpAsyncClientResponseHandlerTests {
private static MockMcpClientTransport initializationEnabledTransport(
McpSchema.ServerCapabilities mockServerCapabilities, McpSchema.Implementation mockServerInfo) {
McpSchema.InitializeResult mockInitResult = new McpSchema.InitializeResult(ProtocolVersions.MCP_2025_11_25,
mockServerCapabilities, mockServerInfo, "Test instructions");
McpSchema.InitializeResult mockInitResult = McpSchema.InitializeResult
.builder(ProtocolVersions.MCP_2025_11_25, mockServerCapabilities, mockServerInfo)
.instructions("Test instructions")
.build();
return new MockMcpClientTransport((t, message) -> {
if (message instanceof McpSchema.JSONRPCRequest r && METHOD_INITIALIZE.equals(r.method())) {
McpSchema.JSONRPCResponse initResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION,
r.id(), mockInitResult, null);
McpSchema.JSONRPCResponse initResponse = McpSchema.JSONRPCResponse.result(r.id(), mockInitResult);
t.simulateIncomingMessage(initResponse);
}
}).withProtocolVersion(ProtocolVersions.MCP_2025_11_25);
@ -57,7 +59,7 @@ class McpAsyncClientResponseHandlerTests {
@Test
void testSuccessfulInitialization() {
McpSchema.Implementation serverInfo = new McpSchema.Implementation("mcp-test-server", "0.0.1");
McpSchema.Implementation serverInfo = McpSchema.Implementation.builder("mcp-test-server", "0.0.1").build();
McpSchema.ServerCapabilities serverCapabilities = McpSchema.ServerCapabilities.builder()
.tools(false)
.resources(true, true) // Enable both resources and resource templates
@ -111,37 +113,37 @@ class McpAsyncClientResponseHandlerTests {
// Create a mock tools list that the server will return
Map<String, Object> inputSchema = Map.of("type", "object", "properties", Map.of(), "required", List.of());
McpSchema.Tool mockTool = McpSchema.Tool.builder()
.name("test-tool-1")
McpSchema.Tool mockTool = McpSchema.Tool
.builder("test-tool-1", JSON_MAPPER, JSON_MAPPER.writeValueAsString(inputSchema))
.description("Test Tool 1 Description")
.inputSchema(JSON_MAPPER, JSON_MAPPER.writeValueAsString(inputSchema))
.build();
// Create page 1 response with nextPageToken
String nextPageToken = "page2Token";
McpSchema.ListToolsResult mockToolsResult1 = new McpSchema.ListToolsResult(List.of(mockTool), nextPageToken);
McpSchema.ListToolsResult mockToolsResult1 = McpSchema.ListToolsResult.builder(List.of(mockTool))
.nextCursor(nextPageToken)
.build();
// Simulate server sending tools/list_changed notification
McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_NOTIFICATION_TOOLS_LIST_CHANGED, null);
McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(
McpSchema.METHOD_NOTIFICATION_TOOLS_LIST_CHANGED);
transport.simulateIncomingMessage(notification);
// Simulate server response to first tools/list request
McpSchema.JSONRPCRequest toolsListRequest1 = transport.getLastSentMessageAsRequest();
assertThat(toolsListRequest1.method()).isEqualTo(McpSchema.METHOD_TOOLS_LIST);
McpSchema.JSONRPCResponse toolsListResponse1 = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION,
toolsListRequest1.id(), mockToolsResult1, null);
McpSchema.JSONRPCResponse toolsListResponse1 = McpSchema.JSONRPCResponse.result(toolsListRequest1.id(),
mockToolsResult1);
transport.simulateIncomingMessage(toolsListResponse1);
// Create mock tools for page 2
McpSchema.Tool mockTool2 = McpSchema.Tool.builder()
.name("test-tool-2")
McpSchema.Tool mockTool2 = McpSchema.Tool
.builder("test-tool-2", JSON_MAPPER, JSON_MAPPER.writeValueAsString(inputSchema))
.description("Test Tool 2 Description")
.inputSchema(JSON_MAPPER, JSON_MAPPER.writeValueAsString(inputSchema))
.build();
// Create page 2 response with no nextPageToken (last page)
McpSchema.ListToolsResult mockToolsResult2 = new McpSchema.ListToolsResult(List.of(mockTool2), null);
McpSchema.ListToolsResult mockToolsResult2 = McpSchema.ListToolsResult.builder(List.of(mockTool2)).build();
// Simulate server response to second tools/list request with page token
McpSchema.JSONRPCRequest toolsListRequest2 = transport.getLastSentMessageAsRequest();
@ -152,8 +154,8 @@ class McpAsyncClientResponseHandlerTests {
assertThat(params).isNotNull();
assertThat(params.cursor()).isEqualTo(nextPageToken);
McpSchema.JSONRPCResponse toolsListResponse2 = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION,
toolsListRequest2.id(), mockToolsResult2, null);
McpSchema.JSONRPCResponse toolsListResponse2 = McpSchema.JSONRPCResponse.result(toolsListRequest2.id(),
mockToolsResult2);
transport.simulateIncomingMessage(toolsListResponse2);
// Verify the consumer received all expected tools from both pages
@ -171,14 +173,13 @@ class McpAsyncClientResponseHandlerTests {
MockMcpClientTransport transport = initializationEnabledTransport();
McpAsyncClient asyncMcpClient = McpClient.async(transport)
.roots(new Root("file:///test/path", "test-root"))
.roots(Root.builder("file:///test/path").name("test-root").build())
.build();
assertThat(asyncMcpClient.initialize().block()).isNotNull();
// Simulate incoming request
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_ROOTS_LIST, "test-id", null);
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.METHOD_ROOTS_LIST, "test-id");
transport.simulateIncomingMessage(request);
// Verify response
@ -187,8 +188,9 @@ class McpAsyncClientResponseHandlerTests {
McpSchema.JSONRPCResponse response = (McpSchema.JSONRPCResponse) sentMessage;
assertThat(response.id()).isEqualTo("test-id");
assertThat(response.result())
.isEqualTo(new McpSchema.ListRootsResult(List.of(new Root("file:///test/path", "test-root"))));
assertThat(response.result()).isEqualTo(McpSchema.ListRootsResult
.builder(List.of(McpSchema.Root.builder("file:///test/path").name("test-root").build()))
.build());
assertThat(response.error()).isNull();
asyncMcpClient.closeGracefully();
@ -213,26 +215,24 @@ class McpAsyncClientResponseHandlerTests {
assertThat(asyncMcpClient.initialize().block()).isNotNull();
// Create a mock resources list that the server will return
McpSchema.Resource mockResource = McpSchema.Resource.builder()
.uri("test://resource")
.name("Test Resource")
McpSchema.Resource mockResource = McpSchema.Resource.builder("test://resource", "Test Resource")
.description("A test resource")
.mimeType("text/plain")
.build();
McpSchema.ListResourcesResult mockResourcesResult = new McpSchema.ListResourcesResult(List.of(mockResource),
null);
McpSchema.ListResourcesResult mockResourcesResult = McpSchema.ListResourcesResult.builder(List.of(mockResource))
.build();
// Simulate server sending resources/list_changed notification
McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED, null);
McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(
McpSchema.METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED);
transport.simulateIncomingMessage(notification);
// Simulate server response to resources/list request
McpSchema.JSONRPCRequest resourcesListRequest = transport.getLastSentMessageAsRequest();
assertThat(resourcesListRequest.method()).isEqualTo(McpSchema.METHOD_RESOURCES_LIST);
McpSchema.JSONRPCResponse resourcesListResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION,
resourcesListRequest.id(), mockResourcesResult, null);
McpSchema.JSONRPCResponse resourcesListResponse = McpSchema.JSONRPCResponse.result(resourcesListRequest.id(),
mockResourcesResult);
transport.simulateIncomingMessage(resourcesListResponse);
// Verify the consumer received the expected resources
@ -261,21 +261,29 @@ class McpAsyncClientResponseHandlerTests {
assertThat(asyncMcpClient.initialize().block()).isNotNull();
// Create a mock prompts list that the server will return
McpSchema.Prompt mockPrompt = new McpSchema.Prompt("test-prompt", "Test Prompt", "Test Prompt Description",
List.of(new McpSchema.PromptArgument("arg1", "Test argument", "Test argument", true)));
McpSchema.ListPromptsResult mockPromptsResult = new McpSchema.ListPromptsResult(List.of(mockPrompt), null);
McpSchema.Prompt mockPrompt = McpSchema.Prompt.builder("test-prompt")
.title("Test Prompt")
.description("Test Prompt Description")
.arguments(List.of(McpSchema.PromptArgument.builder("arg1")
.title("Test argument")
.description("Test argument")
.required(true)
.build()))
.build();
McpSchema.ListPromptsResult mockPromptsResult = McpSchema.ListPromptsResult.builder(List.of(mockPrompt))
.build();
// Simulate server sending prompts/list_changed notification
McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_NOTIFICATION_PROMPTS_LIST_CHANGED, null);
McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(
McpSchema.METHOD_NOTIFICATION_PROMPTS_LIST_CHANGED);
transport.simulateIncomingMessage(notification);
// Simulate server response to prompts/list request
McpSchema.JSONRPCRequest promptsListRequest = transport.getLastSentMessageAsRequest();
assertThat(promptsListRequest.method()).isEqualTo(McpSchema.METHOD_PROMPT_LIST);
McpSchema.JSONRPCResponse promptsListResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION,
promptsListRequest.id(), mockPromptsResult, null);
McpSchema.JSONRPCResponse promptsListResponse = McpSchema.JSONRPCResponse.result(promptsListRequest.id(),
mockPromptsResult);
transport.simulateIncomingMessage(promptsListResponse);
// Verify the consumer received the expected prompts
@ -295,8 +303,9 @@ class McpAsyncClientResponseHandlerTests {
// Create a test sampling handler that echoes back the input
Function<McpSchema.CreateMessageRequest, Mono<McpSchema.CreateMessageResult>> samplingHandler = request -> {
var content = request.messages().get(0).content();
return Mono.just(new McpSchema.CreateMessageResult(McpSchema.Role.ASSISTANT, content, "test-model",
McpSchema.CreateMessageResult.StopReason.END_TURN));
return Mono.just(McpSchema.CreateMessageResult.builder(McpSchema.Role.ASSISTANT, content, "test-model")
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
.build());
};
// Create client with sampling capability and handler
@ -308,18 +317,18 @@ class McpAsyncClientResponseHandlerTests {
assertThat(asyncMcpClient.initialize().block()).isNotNull();
// Create a mock create message request
var messageRequest = new McpSchema.CreateMessageRequest(
List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message"))),
null, // modelPreferences
"Test system prompt", McpSchema.CreateMessageRequest.ContextInclusionStrategy.NONE, 0.7, // temperature
100, // maxTokens
null, // stopSequences
null // metadata
);
var messageRequest = McpSchema.CreateMessageRequest
.builder(List.of(McpSchema.SamplingMessage
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Test message").build())
.build()), 100)
.systemPrompt("Test system prompt")
.includeContext(McpSchema.CreateMessageRequest.ContextInclusionStrategy.NONE)
.temperature(0.7)
.build();
// Simulate incoming request
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, "test-id", messageRequest);
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE,
"test-id", messageRequest);
transport.simulateIncomingMessage(request);
// Verify response
@ -354,13 +363,13 @@ class McpAsyncClientResponseHandlerTests {
assertThat(asyncMcpClient.initialize().block()).isNotNull();
// Create a mock create message request
var messageRequest = new McpSchema.CreateMessageRequest(
List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, new McpSchema.TextContent("Test message"))),
null, null, null, null, 0, null, null);
var messageRequest = McpSchema.CreateMessageRequest.builder(List.of(McpSchema.SamplingMessage
.builder(McpSchema.Role.USER, McpSchema.TextContent.builder("Test message").build())
.build()), 0).build();
// Simulate incoming request
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, "test-id", messageRequest);
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE,
"test-id", messageRequest);
transport.simulateIncomingMessage(request);
// Verify error response
@ -402,8 +411,7 @@ class McpAsyncClientResponseHandlerTests {
assertThat(properties).isNotNull();
assertThat(((Map<String, Object>) properties).get("message")).isInstanceOf(Map.class);
return Mono.just(McpSchema.ElicitResult.builder()
.message(McpSchema.ElicitResult.Action.ACCEPT)
return Mono.just(McpSchema.ElicitResult.builder(McpSchema.ElicitResult.Action.ACCEPT)
.content(Map.of("message", request.message()))
.build());
};
@ -417,14 +425,14 @@ class McpAsyncClientResponseHandlerTests {
assertThat(asyncMcpClient.initialize().block()).isNotNull();
// Create a mock elicitation
var elicitRequest = McpSchema.ElicitRequest.builder()
.message("Test message")
.requestedSchema(Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string"))))
var elicitRequest = McpSchema.ElicitRequest
.builder("Test message",
Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string"))))
.build();
// Simulate incoming request
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_ELICITATION_CREATE, "test-id", elicitRequest);
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.METHOD_ELICITATION_CREATE, "test-id",
elicitRequest);
transport.simulateIncomingMessage(request);
// Verify response
@ -451,7 +459,7 @@ class McpAsyncClientResponseHandlerTests {
// Create a test elicitation handler to decline the request
Function<McpSchema.ElicitRequest, Mono<McpSchema.ElicitResult>> elicitationHandler = request -> Mono
.just(McpSchema.ElicitResult.builder().message(action).build());
.just(McpSchema.ElicitResult.builder(action).build());
// Create client with elicitation capability and handler
McpAsyncClient asyncMcpClient = McpClient.async(transport)
@ -462,14 +470,14 @@ class McpAsyncClientResponseHandlerTests {
assertThat(asyncMcpClient.initialize().block()).isNotNull();
// Create a mock elicitation
var elicitRequest = McpSchema.ElicitRequest.builder()
.message("Test message")
.requestedSchema(Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string"))))
var elicitRequest = McpSchema.ElicitRequest
.builder("Test message",
Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string"))))
.build();
// Simulate incoming request
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_ELICITATION_CREATE, "test-id", elicitRequest);
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.METHOD_ELICITATION_CREATE, "test-id",
elicitRequest);
transport.simulateIncomingMessage(request);
// Verify response
@ -502,13 +510,15 @@ class McpAsyncClientResponseHandlerTests {
assertThat(asyncMcpClient.initialize().block()).isNotNull();
// Create a mock elicitation
var elicitRequest = new McpSchema.ElicitRequest("test",
Map.of("type", "object", "properties", Map.of("test", Map.of("type", "boolean", "defaultValue", true,
"description", "test-description", "title", "test-title"))));
var elicitRequest = McpSchema.ElicitRequest
.builder("test",
Map.of("type", "object", "properties", Map.of("test", Map.of("type", "boolean", "defaultValue",
true, "description", "test-description", "title", "test-title"))))
.build();
// Simulate incoming request
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_ELICITATION_CREATE, "test-id", elicitRequest);
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.METHOD_ELICITATION_CREATE, "test-id",
elicitRequest);
transport.simulateIncomingMessage(request);
// Verify error response
@ -544,8 +554,7 @@ class McpAsyncClientResponseHandlerTests {
assertThat(asyncMcpClient.initialize().block()).isNotNull();
// Simulate incoming ping request from server
McpSchema.JSONRPCRequest pingRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_PING, "ping-id", null);
McpSchema.JSONRPCRequest pingRequest = new McpSchema.JSONRPCRequest(McpSchema.METHOD_PING, "ping-id");
transport.simulateIncomingMessage(pingRequest);
// Verify response

View file

@ -26,15 +26,18 @@ import static org.assertj.core.api.Assertions.assertThatCode;
class McpAsyncClientTests {
public static final McpSchema.Implementation MOCK_SERVER_INFO = new McpSchema.Implementation("test-server",
"1.0.0");
public static final McpSchema.Implementation MOCK_SERVER_INFO = McpSchema.Implementation
.builder("test-server", "1.0.0")
.build();
public static final McpSchema.ServerCapabilities MOCK_SERVER_CAPABILITIES = McpSchema.ServerCapabilities.builder()
.tools(true)
.build();
public static final McpSchema.InitializeResult MOCK_INIT_RESULT = new McpSchema.InitializeResult(
ProtocolVersions.MCP_2024_11_05, MOCK_SERVER_CAPABILITIES, MOCK_SERVER_INFO, "Test instructions");
public static final McpSchema.InitializeResult MOCK_INIT_RESULT = McpSchema.InitializeResult
.builder(ProtocolVersions.MCP_2024_11_05, MOCK_SERVER_CAPABILITIES, MOCK_SERVER_INFO)
.instructions("Test instructions")
.build();
private static final String CONTEXT_KEY = "context.key";
@ -44,10 +47,8 @@ class McpAsyncClientTests {
Map<String, Object> inputSchemaMap = Map.of("type", "object", "properties",
Map.of("expression", Map.of("type", "string")), "required", List.of("expression"));
McpSchema.Tool.Builder toolBuilder = McpSchema.Tool.builder()
.name("calculator")
.description("Performs mathematical calculations")
.inputSchema(inputSchemaMap);
McpSchema.Tool.Builder toolBuilder = McpSchema.Tool.builder("calculator", inputSchemaMap)
.description("Performs mathematical calculations");
if (hasOutputSchema) {
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
@ -57,7 +58,7 @@ class McpAsyncClientTests {
}
McpSchema.Tool calculatorTool = toolBuilder.build();
McpSchema.ListToolsResult mockToolsResult = new McpSchema.ListToolsResult(List.of(calculatorTool), null);
McpSchema.ListToolsResult mockToolsResult = McpSchema.ListToolsResult.builder(List.of(calculatorTool)).build();
// Create call tool result - valid or invalid based on parameter
Map<String, Object> structuredContent = invalidOutput ? Map.of("result", "5", "operation", "add")
@ -91,16 +92,13 @@ class McpAsyncClientTests {
McpSchema.JSONRPCResponse response;
if (McpSchema.METHOD_INITIALIZE.equals(request.method())) {
response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), MOCK_INIT_RESULT,
null);
response = McpSchema.JSONRPCResponse.result(request.id(), MOCK_INIT_RESULT);
}
else if (McpSchema.METHOD_TOOLS_LIST.equals(request.method())) {
response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockToolsResult,
null);
response = McpSchema.JSONRPCResponse.result(request.id(), mockToolsResult);
}
else if (McpSchema.METHOD_TOOLS_CALL.equals(request.method())) {
response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(),
mockCallToolResult, null);
response = McpSchema.JSONRPCResponse.result(request.id(), mockCallToolResult);
}
else {
return Mono.empty();
@ -155,8 +153,8 @@ class McpAsyncClientTests {
if (!(message instanceof McpSchema.JSONRPCRequest)) {
return Mono.empty();
}
McpSchema.JSONRPCResponse initResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION,
((McpSchema.JSONRPCRequest) message).id(), MOCK_INIT_RESULT, null);
McpSchema.JSONRPCResponse initResponse = McpSchema.JSONRPCResponse
.result(((McpSchema.JSONRPCRequest) message).id(), MOCK_INIT_RESULT);
return handler.apply(Mono.just(initResponse)).then();
}
@ -185,7 +183,9 @@ class McpAsyncClientTests {
StepVerifier.create(client.initialize()).expectNextMatches(Objects::nonNull).verifyComplete();
StepVerifier.create(client.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))))
StepVerifier
.create(client.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build()))
.expectNextMatches(response -> {
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -207,7 +207,9 @@ class McpAsyncClientTests {
StepVerifier.create(client.initialize()).expectNextMatches(Objects::nonNull).verifyComplete();
StepVerifier.create(client.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))))
StepVerifier
.create(client.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build()))
.expectNextMatches(response -> {
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -229,7 +231,9 @@ class McpAsyncClientTests {
StepVerifier.create(client.initialize()).expectNextMatches(Objects::nonNull).verifyComplete();
StepVerifier.create(client.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3"))))
StepVerifier
.create(client.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build()))
.expectErrorMatches(ex -> ex instanceof IllegalArgumentException
&& ex.getMessage().contains("Tool call result validation failed"))
.verify();
@ -326,50 +330,50 @@ class McpAsyncClientTests {
.tools(false)
.build();
McpSchema.InitializeResult initResult = new McpSchema.InitializeResult(ProtocolVersions.MCP_2024_11_05,
caps, MOCK_SERVER_INFO, null);
McpSchema.InitializeResult initResult = McpSchema.InitializeResult
.builder(ProtocolVersions.MCP_2024_11_05, caps, MOCK_SERVER_INFO)
.build();
response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), initResult, null);
response = McpSchema.JSONRPCResponse.result(request.id(), initResult);
}
else if (McpSchema.METHOD_PROMPT_LIST.equals(request.method())) {
capturedRequest = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class);
McpSchema.Prompt mockPrompt = new McpSchema.Prompt("test-prompt", "A test prompt", List.of());
McpSchema.ListPromptsResult mockPromptResult = new McpSchema.ListPromptsResult(List.of(mockPrompt),
null);
response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockPromptResult,
null);
McpSchema.Prompt mockPrompt = McpSchema.Prompt.builder("test-prompt")
.description("A test prompt")
.arguments(List.of())
.build();
McpSchema.ListPromptsResult mockPromptResult = McpSchema.ListPromptsResult.builder(List.of(mockPrompt))
.build();
response = McpSchema.JSONRPCResponse.result(request.id(), mockPromptResult);
}
else if (McpSchema.METHOD_RESOURCES_TEMPLATES_LIST.equals(request.method())) {
capturedRequest = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class);
McpSchema.ResourceTemplate mockTemplate = new McpSchema.ResourceTemplate("file:///{name}", "template",
null, null, null);
McpSchema.ListResourceTemplatesResult mockResourceTemplateResult = new McpSchema.ListResourceTemplatesResult(
List.of(mockTemplate), null);
response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(),
mockResourceTemplateResult, null);
McpSchema.ResourceTemplate mockTemplate = McpSchema.ResourceTemplate
.builder("file:///{name}", "template")
.build();
McpSchema.ListResourceTemplatesResult mockResourceTemplateResult = McpSchema.ListResourceTemplatesResult
.builder(List.of(mockTemplate))
.build();
response = McpSchema.JSONRPCResponse.result(request.id(), mockResourceTemplateResult);
}
else if (McpSchema.METHOD_RESOURCES_LIST.equals(request.method())) {
capturedRequest = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class);
McpSchema.Resource mockResource = McpSchema.Resource.builder()
.uri("file:///test.txt")
.name("test.txt")
McpSchema.Resource mockResource = McpSchema.Resource.builder("file:///test.txt", "test.txt").build();
McpSchema.ListResourcesResult mockResourceResult = McpSchema.ListResourcesResult
.builder(List.of(mockResource))
.build();
McpSchema.ListResourcesResult mockResourceResult = new McpSchema.ListResourcesResult(
List.of(mockResource), null);
response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockResourceResult,
null);
response = McpSchema.JSONRPCResponse.result(request.id(), mockResourceResult);
}
else if (McpSchema.METHOD_TOOLS_LIST.equals(request.method())) {
capturedRequest = JSON_MAPPER.convertValue(request.params(), McpSchema.PaginatedRequest.class);
McpSchema.Tool addTool = McpSchema.Tool.builder().name("add").description("calculate add").build();
McpSchema.ListToolsResult mockToolsResult = new McpSchema.ListToolsResult(List.of(addTool), null);
response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), mockToolsResult,
null);
McpSchema.Tool addTool = McpSchema.Tool.builder("add").description("calculate add").build();
McpSchema.ListToolsResult mockToolsResult = McpSchema.ListToolsResult.builder(List.of(addTool)).build();
response = McpSchema.JSONRPCResponse.result(request.id(), mockToolsResult);
}
else {
return Mono.empty();

View file

@ -25,7 +25,8 @@ class McpClientProtocolVersionTests {
private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(300);
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();
@Test
void shouldUseLatestVersionByDefault() {
@ -46,10 +47,11 @@ class McpClientProtocolVersionTests {
McpSchema.InitializeRequest initRequest = (McpSchema.InitializeRequest) request.params();
assertThat(initRequest.protocolVersion()).isEqualTo(transport.protocolVersions().get(0));
transport.simulateIncomingMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(),
new McpSchema.InitializeResult(protocolVersion, ServerCapabilities.builder().build(),
new McpSchema.Implementation("test-server", "1.0.0"), null),
null));
transport.simulateIncomingMessage(McpSchema.JSONRPCResponse.result(request.id(),
McpSchema.InitializeResult
.builder(protocolVersion, ServerCapabilities.builder().build(),
McpSchema.Implementation.builder("test-server", "1.0.0").build())
.build()));
}).assertNext(result -> {
assertThat(result.protocolVersion()).isEqualTo(protocolVersion);
}).verifyComplete();
@ -80,10 +82,11 @@ class McpClientProtocolVersionTests {
McpSchema.InitializeRequest initRequest = (McpSchema.InitializeRequest) request.params();
assertThat(initRequest.protocolVersion()).isIn(List.of(oldVersion, ProtocolVersions.MCP_2025_11_25));
transport.simulateIncomingMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(),
new McpSchema.InitializeResult(oldVersion, ServerCapabilities.builder().build(),
new McpSchema.Implementation("test-server", "1.0.0"), null),
null));
transport.simulateIncomingMessage(McpSchema.JSONRPCResponse.result(request.id(),
McpSchema.InitializeResult
.builder(oldVersion, ServerCapabilities.builder().build(),
McpSchema.Implementation.builder("test-server", "1.0.0").build())
.build()));
}).assertNext(result -> {
assertThat(result.protocolVersion()).isEqualTo(oldVersion);
}).verifyComplete();
@ -109,10 +112,11 @@ class McpClientProtocolVersionTests {
McpSchema.JSONRPCRequest request = transport.getLastSentMessageAsRequest();
assertThat(request.params()).isInstanceOf(McpSchema.InitializeRequest.class);
transport.simulateIncomingMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(),
new McpSchema.InitializeResult(unsupportedVersion, ServerCapabilities.builder().build(),
new McpSchema.Implementation("test-server", "1.0.0"), null),
null));
transport.simulateIncomingMessage(McpSchema.JSONRPCResponse.result(request.id(),
McpSchema.InitializeResult
.builder(unsupportedVersion, ServerCapabilities.builder().build(),
McpSchema.Implementation.builder("test-server", "1.0.0").build())
.build()));
}).expectError(RuntimeException.class).verify();
}
finally {
@ -142,10 +146,11 @@ class McpClientProtocolVersionTests {
McpSchema.InitializeRequest initRequest = (McpSchema.InitializeRequest) request.params();
assertThat(initRequest.protocolVersion()).isEqualTo(latestVersion);
transport.simulateIncomingMessage(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(),
new McpSchema.InitializeResult(latestVersion, ServerCapabilities.builder().build(),
new McpSchema.Implementation("test-server", "1.0.0"), null),
null));
transport.simulateIncomingMessage(McpSchema.JSONRPCResponse.result(request.id(),
McpSchema.InitializeResult
.builder(latestVersion, ServerCapabilities.builder().build(),
McpSchema.Implementation.builder("test-server", "1.0.0").build())
.build()));
}).assertNext(result -> {
assertThat(result.protocolVersion()).isEqualTo(latestVersion);
}).verifyComplete();

View file

@ -13,6 +13,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.modelcontextprotocol.client.transport.customizer.McpAsyncHttpClientRequestCustomizer;
import io.modelcontextprotocol.client.transport.customizer.McpSyncHttpClientRequestCustomizer;
import io.modelcontextprotocol.common.McpTransportContext;
@ -126,8 +128,7 @@ class HttpClientSseClientTransportTests {
@Test
void testErrorOnBogusMessage() {
// bogus message
JSONRPCRequest bogusMessage = new JSONRPCRequest(null, null, "test-id", Map.of("key", "value"));
var bogusMessage = new BogusJsonRpcMessage("test-id", Map.of("key", "value"));
StepVerifier.create(transport.sendMessage(bogusMessage))
.verifyErrorMessage(
@ -137,8 +138,7 @@ class HttpClientSseClientTransportTests {
@Test
void testMessageProcessing() {
// Create a test message
JSONRPCRequest testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id",
Map.of("key", "value"));
JSONRPCRequest testMessage = new McpSchema.JSONRPCRequest("test-method", "test-id", Map.of("key", "value"));
// Simulate receiving the message
transport.simulateMessageEvent("""
@ -168,8 +168,7 @@ class HttpClientSseClientTransportTests {
""");
// Create and send a request message
JSONRPCRequest testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id",
Map.of("key", "value"));
JSONRPCRequest testMessage = new McpSchema.JSONRPCRequest("test-method", "test-id", Map.of("key", "value"));
// Verify message handling
StepVerifier.create(transport.sendMessage(testMessage)).verifyComplete();
@ -192,8 +191,7 @@ class HttpClientSseClientTransportTests {
""");
// Create and send a request message
JSONRPCRequest testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id",
Map.of("key", "value"));
JSONRPCRequest testMessage = new McpSchema.JSONRPCRequest("test-method", "test-id", Map.of("key", "value"));
// Verify message handling
StepVerifier.create(transport.sendMessage(testMessage)).verifyComplete();
@ -222,8 +220,7 @@ class HttpClientSseClientTransportTests {
StepVerifier.create(transport.closeGracefully()).verifyComplete();
// Create a test message
JSONRPCRequest testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id",
Map.of("key", "value"));
JSONRPCRequest testMessage = new McpSchema.JSONRPCRequest("test-method", "test-id", Map.of("key", "value"));
// Verify message is not processed after shutdown
StepVerifier.create(transport.sendMessage(testMessage)).verifyComplete();
@ -267,11 +264,9 @@ class HttpClientSseClientTransportTests {
""");
// Create and send corresponding messages
JSONRPCRequest message1 = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "method1", "id1",
Map.of("key", "value1"));
JSONRPCRequest message1 = new McpSchema.JSONRPCRequest("method1", "id1", Map.of("key", "value1"));
JSONRPCRequest message2 = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "method2", "id2",
Map.of("key", "value2"));
JSONRPCRequest message2 = new McpSchema.JSONRPCRequest("method2", "id2", Map.of("key", "value2"));
// Verify both messages are processed
StepVerifier.create(transport.sendMessage(message1).then(transport.sendMessage(message2))).verifyComplete();
@ -355,8 +350,7 @@ class HttpClientSseClientTransportTests {
clearInvocations(mockCustomizer);
// Send test message
var testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id",
Map.of("key", "value"));
var testMessage = new McpSchema.JSONRPCRequest("test-method", "test-id", Map.of("key", "value"));
// Subscribe to messages and verify
StepVerifier
@ -398,8 +392,7 @@ class HttpClientSseClientTransportTests {
clearInvocations(mockCustomizer);
// Send test message
var testMessage = new JSONRPCRequest(McpSchema.JSONRPC_VERSION, "test-method", "test-id",
Map.of("key", "value"));
var testMessage = new McpSchema.JSONRPCRequest("test-method", "test-id", Map.of("key", "value"));
// Subscribe to messages and verify
StepVerifier
@ -458,4 +451,30 @@ class HttpClientSseClientTransportTests {
return false;
}
/**
* A minimal {@link McpSchema.JSONRPCMessage} that serializes only the supplied
* fields, intentionally omitting {@code jsonrpc} and {@code method} to produce a
* bogus wire payload for error-handling tests.
*/
private static class BogusJsonRpcMessage implements McpSchema.JSONRPCMessage {
@JsonProperty("id")
private final String id;
@JsonProperty("params")
private final Map<String, Object> params;
BogusJsonRpcMessage(String id, Map<String, Object> params) {
this.id = id;
this.params = params;
}
@Override
@JsonIgnore
public String jsonrpc() {
return null;
}
}
}

View file

@ -76,11 +76,11 @@ public class HttpClientStreamableHttpTransportEmptyJsonResponseTest {
.httpRequestCustomizer(mockRequestCustomizer)
.build();
var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_03_26,
McpSchema.ClientCapabilities.builder().roots(true).build(),
new McpSchema.Implementation("MCP Client", "0.3.1"));
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
"test-id", initializeRequest);
var initializeRequest = McpSchema.InitializeRequest
.builder(ProtocolVersions.MCP_2025_03_26, McpSchema.ClientCapabilities.builder().roots(true).build(),
McpSchema.Implementation.builder("MCP Client", "0.3.1").build())
.build();
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.METHOD_INITIALIZE, "test-id", initializeRequest);
StepVerifier.create(transport.sendMessage(testMessage)).verifyComplete();

View file

@ -770,11 +770,11 @@ public class HttpClientStreamableHttpTransportErrorHandlingTest {
}
private McpSchema.JSONRPCRequest createTestRequestMessage() {
var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_03_26,
McpSchema.ClientCapabilities.builder().roots(true).build(),
new McpSchema.Implementation("Test Client", "1.0.0"));
return new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, "test-id",
initializeRequest);
var initializeRequest = McpSchema.InitializeRequest
.builder(ProtocolVersions.MCP_2025_03_26, McpSchema.ClientCapabilities.builder().roots(true).build(),
McpSchema.Implementation.builder("Test Client", "1.0.0").build())
.build();
return new McpSchema.JSONRPCRequest(McpSchema.METHOD_INITIALIZE, "test-id", initializeRequest);
}
}

View file

@ -79,11 +79,11 @@ class HttpClientStreamableHttpTransportTest {
withTransport(transport, (t) -> {
// Send test message
var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_11_25,
McpSchema.ClientCapabilities.builder().roots(true).build(),
new McpSchema.Implementation("MCP Client", "0.3.1"));
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
"test-id", initializeRequest);
var initializeRequest = McpSchema.InitializeRequest
.builder(ProtocolVersions.MCP_2025_11_25, McpSchema.ClientCapabilities.builder().roots(true).build(),
McpSchema.Implementation.builder("MCP Client", "0.3.1").build())
.build();
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.METHOD_INITIALIZE, "test-id", initializeRequest);
StepVerifier
.create(t.sendMessage(testMessage).contextWrite(ctx -> ctx.put(McpTransportContext.KEY, context)))
@ -109,11 +109,11 @@ class HttpClientStreamableHttpTransportTest {
withTransport(transport, (t) -> {
// Send test message
var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_11_25,
McpSchema.ClientCapabilities.builder().roots(true).build(),
new McpSchema.Implementation("MCP Client", "0.3.1"));
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
"test-id", initializeRequest);
var initializeRequest = McpSchema.InitializeRequest
.builder(ProtocolVersions.MCP_2025_11_25, McpSchema.ClientCapabilities.builder().roots(true).build(),
McpSchema.Implementation.builder("MCP Client", "0.3.1").build())
.build();
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.METHOD_INITIALIZE, "test-id", initializeRequest);
StepVerifier
.create(t.sendMessage(testMessage).contextWrite(ctx -> ctx.put(McpTransportContext.KEY, context)))
@ -132,11 +132,11 @@ class HttpClientStreamableHttpTransportTest {
StepVerifier.create(transport.closeGracefully()).verifyComplete();
var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_11_25,
McpSchema.ClientCapabilities.builder().roots(true).build(),
new McpSchema.Implementation("MCP Client", "0.3.1"));
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
"test-id", initializeRequest);
var initializeRequest = McpSchema.InitializeRequest
.builder(ProtocolVersions.MCP_2025_11_25, McpSchema.ClientCapabilities.builder().roots(true).build(),
McpSchema.Implementation.builder("MCP Client", "0.3.1").build())
.build();
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.METHOD_INITIALIZE, "test-id", initializeRequest);
StepVerifier.create(transport.sendMessage(testMessage))
.expectErrorMessage("MCP session has been closed")
@ -147,11 +147,11 @@ class HttpClientStreamableHttpTransportTest {
void testCloseInitialized() {
var transport = HttpClientStreamableHttpTransport.builder(host).build();
var initializeRequest = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_11_25,
McpSchema.ClientCapabilities.builder().roots(true).build(),
new McpSchema.Implementation("MCP Client", "0.3.1"));
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
"test-id", initializeRequest);
var initializeRequest = McpSchema.InitializeRequest
.builder(ProtocolVersions.MCP_2025_11_25, McpSchema.ClientCapabilities.builder().roots(true).build(),
McpSchema.Implementation.builder("MCP Client", "0.3.1").build())
.build();
var testMessage = new McpSchema.JSONRPCRequest(McpSchema.METHOD_INITIALIZE, "test-id", initializeRequest);
StepVerifier.create(transport.sendMessage(testMessage)).verifyComplete();
StepVerifier.create(transport.closeGracefully()).verifyComplete();

View file

@ -125,8 +125,7 @@ public class AsyncServerMcpTransportContextIntegrationTests {
.build())
.build();
private final McpSchema.Tool tool = McpSchema.Tool.builder()
.name("test-tool")
private final McpSchema.Tool tool = McpSchema.Tool.builder("test-tool")
.description("return the value of the x-test header from call tool request")
.build();
@ -178,7 +177,8 @@ public class AsyncServerMcpTransportContextIntegrationTests {
// Test tool call with context
StepVerifier
.create(asyncStreamableClient.callTool(new McpSchema.CallToolRequest("test-tool", Map.of()))
.create(asyncStreamableClient
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build())
.contextWrite(ctx -> ctx.put(McpTransportContext.KEY,
McpTransportContext.create(Map.of("client-side-header-value", "some important value")))))
.assertNext(response -> {
@ -212,7 +212,8 @@ public class AsyncServerMcpTransportContextIntegrationTests {
// Test tool call with context
StepVerifier
.create(asyncStreamableClient.callTool(new McpSchema.CallToolRequest("test-tool", Map.of()))
.create(asyncStreamableClient
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build())
.contextWrite(ctx -> ctx.put(McpTransportContext.KEY,
McpTransportContext.create(Map.of("client-side-header-value", "some important value")))))
.assertNext(response -> {
@ -246,7 +247,7 @@ public class AsyncServerMcpTransportContextIntegrationTests {
// Test tool call with context
StepVerifier
.create(asyncSseClient.callTool(new McpSchema.CallToolRequest("test-tool", Map.of()))
.create(asyncSseClient.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build())
.contextWrite(ctx -> ctx.put(McpTransportContext.KEY,
McpTransportContext.create(Map.of("client-side-header-value", "some important value")))))
.assertNext(response -> {

View file

@ -41,8 +41,7 @@ class HttpClientStreamableHttpVersionNegotiationIntegrationTests {
req -> McpTransportContext.create(Map.of("protocol-version", req.getHeader("MCP-protocol-version"))))
.build();
private final McpSchema.Tool toolSpec = McpSchema.Tool.builder()
.name("test-tool")
private final McpSchema.Tool toolSpec = McpSchema.Tool.builder("test-tool")
.description("return the protocol version used")
.build();
@ -70,7 +69,8 @@ class HttpClientStreamableHttpVersionNegotiationIntegrationTests {
.build();
client.initialize();
McpSchema.CallToolResult response = client.callTool(new McpSchema.CallToolRequest("test-tool", Map.of()));
McpSchema.CallToolResult response = client
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build());
var calls = requestRecordingFilter.getCalls();
@ -100,7 +100,8 @@ class HttpClientStreamableHttpVersionNegotiationIntegrationTests {
var client = McpClient.sync(transport).build();
client.initialize();
McpSchema.CallToolResult response = client.callTool(new McpSchema.CallToolRequest("test-tool", Map.of()));
McpSchema.CallToolResult response = client
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build());
var calls = requestRecordingFilter.getCalls();
// Initialize tells the server the Client's latest supported version

View file

@ -116,8 +116,7 @@ public class SyncServerMcpTransportContextIntegrationTests {
.transportContextProvider(clientContextProvider)
.build();
private final McpSchema.Tool tool = McpSchema.Tool.builder()
.name("test-tool")
private final McpSchema.Tool tool = McpSchema.Tool.builder("test-tool")
.description("return the value of the x-test header from call tool request")
.build();
@ -156,7 +155,7 @@ public class SyncServerMcpTransportContextIntegrationTests {
CLIENT_SIDE_HEADER_VALUE_HOLDER.set("some important value");
McpSchema.CallToolResult response = streamableClient
.callTool(new McpSchema.CallToolRequest("test-tool", Map.of()));
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response.content()).hasSize(1)
@ -182,7 +181,7 @@ public class SyncServerMcpTransportContextIntegrationTests {
CLIENT_SIDE_HEADER_VALUE_HOLDER.set("some important value");
McpSchema.CallToolResult response = streamableClient
.callTool(new McpSchema.CallToolRequest("test-tool", Map.of()));
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response.content()).hasSize(1)
@ -207,7 +206,8 @@ public class SyncServerMcpTransportContextIntegrationTests {
assertThat(initResult).isNotNull();
CLIENT_SIDE_HEADER_VALUE_HOLDER.set("some important value");
McpSchema.CallToolResult response = sseClient.callTool(new McpSchema.CallToolRequest("test-tool", Map.of()));
McpSchema.CallToolResult response = sseClient
.callTool(McpSchema.CallToolRequest.builder("test-tool").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response.content()).hasSize(1)

View file

@ -116,11 +116,11 @@ class HttpServletStatelessIntegrationTests {
var clientBuilder = clientBuilders.get(clientType);
var callResponse = CallToolResult.builder()
.content(List.of(new McpSchema.TextContent("CALL RESPONSE")))
.content(List.of(McpSchema.TextContent.builder("CALL RESPONSE").build()))
.isError(false)
.build();
McpStatelessServerFeatures.SyncToolSpecification tool1 = new McpStatelessServerFeatures.SyncToolSpecification(
Tool.builder().name("tool1").title("tool1 description").inputSchema(EMPTY_JSON_SCHEMA).build(),
Tool.builder("tool1", EMPTY_JSON_SCHEMA).title("tool1 description").build(),
(transportContext, request) -> {
// perform a blocking call to a remote service
String response = RestClient.create()
@ -144,7 +144,8 @@ class HttpServletStatelessIntegrationTests {
assertThat(mcpClient.listTools().tools()).contains(tool1.tool());
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("tool1").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response).isEqualTo(callResponse);
@ -193,12 +194,17 @@ class HttpServletStatelessIntegrationTests {
var mcpServer = McpServer.sync(mcpStatelessServerTransport)
.capabilities(ServerCapabilities.builder().completions().build())
.prompts(new McpStatelessServerFeatures.SyncPromptSpecification(
new Prompt("code_review", "Code review", "this is code review prompt",
List.of(new PromptArgument("language", "Language", "string", false))),
(transportContext, getPromptRequest) -> null))
.prompts(new McpStatelessServerFeatures.SyncPromptSpecification(Prompt.builder("code_review")
.title("Code review")
.description("this is code review prompt")
.arguments(List.of(PromptArgument.builder("language")
.title("Language")
.description("string")
.required(false)
.build()))
.build(), (transportContext, getPromptRequest) -> null))
.completions(new McpStatelessServerFeatures.SyncCompletionSpecification(
new PromptReference(PromptReference.TYPE, "code_review", "Code review"), completionHandler))
PromptReference.builder("code_review").title("Code review").build(), completionHandler))
.build();
try (var mcpClient = clientBuilder.build()) {
@ -206,9 +212,10 @@ class HttpServletStatelessIntegrationTests {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
CompleteRequest request = new CompleteRequest(
new PromptReference(PromptReference.TYPE, "code_review", "Code review"),
new CompleteRequest.CompleteArgument("language", "py"));
CompleteRequest request = CompleteRequest
.builder(PromptReference.builder("code_review").title("Code review").build(),
new CompleteRequest.CompleteArgument("language", "py"))
.build();
CompleteResult result = mcpClient.completeCompletion(request);
@ -237,8 +244,7 @@ class HttpServletStatelessIntegrationTests {
Map.of("type", "string"), "timestamp", Map.of("type", "string")),
"required", List.of("result", "operation"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -270,8 +276,8 @@ class HttpServletStatelessIntegrationTests {
// Note: outputSchema might be null in sync server, but validation still works
// Call tool with valid structured output
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -312,8 +318,7 @@ class HttpServletStatelessIntegrationTests {
"age", Map.of("type", "number")),
"required", List.of("name", "age"))); // @formatter:on
Tool calculatorTool = Tool.builder()
.name("getMembers")
Tool calculatorTool = Tool.builder("getMembers")
.description("Returns a list of members")
.outputSchema(outputSchema)
.build();
@ -338,7 +343,8 @@ class HttpServletStatelessIntegrationTests {
assertThat(mcpClient.initialize()).isNotNull();
// Call tool with valid structured output of type array
CallToolResult response = mcpClient.callTool(new McpSchema.CallToolRequest("getMembers", Map.of()));
CallToolResult response = mcpClient
.callTool(McpSchema.CallToolRequest.builder("getMembers").arguments(Map.of()).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -368,8 +374,7 @@ class HttpServletStatelessIntegrationTests {
Map.of("type", "string"), "timestamp", Map.of("type", "string")),
"required", List.of("result", "operation"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -380,7 +385,7 @@ class HttpServletStatelessIntegrationTests {
.tool(calculatorTool)
.callHandler((exchange, request) -> CallToolResult.builder()
.isError(true)
.content(List.of(new TextContent("Error calling tool: Simulated in-handler error")))
.content(List.of(TextContent.builder("Error calling tool: Simulated in-handler error").build()))
.build())
.build();
@ -401,14 +406,14 @@ class HttpServletStatelessIntegrationTests {
// Note: outputSchema might be null in sync server, but validation still works
// Call tool with valid structured output
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isTrue();
assertThat(response.content()).isNotEmpty();
assertThat(response.content())
.containsExactly(new McpSchema.TextContent("Error calling tool: Simulated in-handler error"));
assertThat(response.content()).containsExactly(
McpSchema.TextContent.builder("Error calling tool: Simulated in-handler error").build());
assertThat(response.structuredContent()).isNull();
}
finally {
@ -426,8 +431,7 @@ class HttpServletStatelessIntegrationTests {
Map.of("result", Map.of("type", "number"), "operation", Map.of("type", "string")), "required",
List.of("result", "operation"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -453,8 +457,8 @@ class HttpServletStatelessIntegrationTests {
assertThat(initResult).isNotNull();
// Call tool with invalid structured output
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isTrue();
@ -478,8 +482,7 @@ class HttpServletStatelessIntegrationTests {
Map<String, Object> outputSchema = Map.of("type", "object", "properties",
Map.of("result", Map.of("type", "number")), "required", List.of("result"));
Tool calculatorTool = Tool.builder()
.name("calculator")
Tool calculatorTool = Tool.builder("calculator")
.description("Performs mathematical calculations")
.outputSchema(outputSchema)
.build();
@ -502,8 +505,8 @@ class HttpServletStatelessIntegrationTests {
assertThat(initResult).isNotNull();
// Call tool that should return structured content but doesn't
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("calculator", Map.of("expression", "2 + 3")));
CallToolResult response = mcpClient.callTool(
McpSchema.CallToolRequest.builder("calculator").arguments(Map.of("expression", "2 + 3")).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isTrue();
@ -542,8 +545,7 @@ class HttpServletStatelessIntegrationTests {
Map.of("message", Map.of("type", "string"), "count", Map.of("type", "integer")), "required",
List.of("message", "count"));
Tool dynamicTool = Tool.builder()
.name("dynamic-tool")
Tool dynamicTool = Tool.builder("dynamic-tool")
.description("Dynamically added tool")
.outputSchema(outputSchema)
.build();
@ -573,7 +575,7 @@ class HttpServletStatelessIntegrationTests {
// Call dynamically added tool
CallToolResult response = mcpClient
.callTool(new McpSchema.CallToolRequest("dynamic-tool", Map.of("count", 3)));
.callTool(McpSchema.CallToolRequest.builder("dynamic-tool").arguments(Map.of("count", 3)).build());
assertThat(response).isNotNull();
assertThat(response.isError()).isFalse();
@ -601,7 +603,7 @@ class HttpServletStatelessIntegrationTests {
.capabilities(ServerCapabilities.builder().tools(true).build())
.build();
Tool testTool = Tool.builder().name("test").description("test").build();
Tool testTool = Tool.builder("test").description("test").build();
McpStatelessServerFeatures.SyncToolSpecification toolSpec = new McpStatelessServerFeatures.SyncToolSpecification(
testTool, (transportContext, request) -> {
@ -610,9 +612,11 @@ class HttpServletStatelessIntegrationTests {
mcpServer.addTool(toolSpec);
McpSchema.CallToolRequest callToolRequest = new McpSchema.CallToolRequest("test", Map.of());
McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION,
McpSchema.METHOD_TOOLS_CALL, "test", callToolRequest);
McpSchema.CallToolRequest callToolRequest = McpSchema.CallToolRequest.builder("test")
.arguments(Map.of())
.build();
McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.METHOD_TOOLS_CALL, "test",
callToolRequest);
MockHttpServletRequest request = new MockHttpServletRequest("POST", CUSTOM_MESSAGE_ENDPOINT);
MockHttpServletResponse response = new MockHttpServletResponse();

View file

@ -98,11 +98,9 @@ class McpCompletionTests {
return new CompleteResult(new CompleteResult.CompleteCompletion(List.of("test-completion"), 1, false));
};
ResourceReference resourceRef = new ResourceReference(ResourceReference.TYPE, "test://resource/{param}");
ResourceReference resourceRef = new ResourceReference("test://resource/{param}");
var resource = Resource.builder()
.uri("test://resource/{param}")
.name("Test Resource")
var resource = Resource.builder("test://resource/{param}", "Test Resource")
.description("A resource for testing")
.mimeType("text/plain")
.size(123L)
@ -111,19 +109,21 @@ class McpCompletionTests {
var mcpServer = McpServer.sync(mcpServerTransportProvider)
.capabilities(ServerCapabilities.builder().completions().build())
.resources(new McpServerFeatures.SyncResourceSpecification(resource,
(exchange, req) -> new ReadResourceResult(List.of())))
(exchange, req) -> ReadResourceResult.builder(List.of()).build()))
.completions(new McpServerFeatures.SyncCompletionSpecification(resourceRef, completionHandler))
.build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample " + "client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
.build();) {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
// Test with context
CompleteRequest request = new CompleteRequest(resourceRef,
new CompleteRequest.CompleteArgument("param", "test"), null,
new CompleteRequest.CompleteContext(Map.of("previous", "value")));
CompleteRequest request = CompleteRequest
.builder(resourceRef, new CompleteRequest.CompleteArgument("param", "test"))
.context(new CompleteRequest.CompleteContext(Map.of("previous", "value")))
.build();
CompleteResult result = mcpClient.completeCompletion(request);
@ -145,25 +145,29 @@ class McpCompletionTests {
new CompleteResult.CompleteCompletion(List.of("no-context-completion"), 1, false));
};
McpSchema.Prompt prompt = new Prompt("test-prompt", "this is a test prompt",
List.of(new PromptArgument("arg", "string", false)));
McpSchema.Prompt prompt = Prompt.builder("test-prompt")
.description("this is a test prompt")
.arguments(List.of(PromptArgument.builder("arg").description("string").required(false).build()))
.build();
var mcpServer = McpServer.sync(mcpServerTransportProvider)
.capabilities(ServerCapabilities.builder().completions().build())
.prompts(new McpServerFeatures.SyncPromptSpecification(prompt,
(mcpSyncServerExchange, getPromptRequest) -> null))
.completions(new McpServerFeatures.SyncCompletionSpecification(
new PromptReference(PromptReference.TYPE, "test-prompt"), completionHandler))
.completions(new McpServerFeatures.SyncCompletionSpecification(new PromptReference("test-prompt"),
completionHandler))
.build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample " + "client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
.build();) {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
// Test without context
CompleteRequest request = new CompleteRequest(new PromptReference(PromptReference.TYPE, "test-prompt"),
new CompleteRequest.CompleteArgument("arg", "val"));
CompleteRequest request = CompleteRequest
.builder(new PromptReference("test-prompt"), new CompleteRequest.CompleteArgument("arg", "val"))
.build();
CompleteResult result = mcpClient.completeCompletion(request);
@ -205,9 +209,7 @@ class McpCompletionTests {
return new CompleteResult(new CompleteResult.CompleteCompletion(List.of(), 0, false));
};
McpSchema.Resource resource = Resource.builder()
.uri("db://{database}/{table}")
.name("Database Table")
McpSchema.Resource resource = Resource.builder("db://{database}/{table}", "Database Table")
.description("Resource representing a table in a database")
.mimeType("application/json")
.size(456L)
@ -216,38 +218,42 @@ class McpCompletionTests {
var mcpServer = McpServer.sync(mcpServerTransportProvider)
.capabilities(ServerCapabilities.builder().completions().build())
.resources(new McpServerFeatures.SyncResourceSpecification(resource,
(exchange, req) -> new ReadResourceResult(List.of())))
(exchange, req) -> ReadResourceResult.builder(List.of()).build()))
.completions(new McpServerFeatures.SyncCompletionSpecification(
new ResourceReference(ResourceReference.TYPE, "db://{database}/{table}"), completionHandler))
new ResourceReference("db://{database}/{table}"), completionHandler))
.build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample " + "client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
.build();) {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
// First, complete database
CompleteRequest dbRequest = new CompleteRequest(
new ResourceReference(ResourceReference.TYPE, "db://{database}/{table}"),
new CompleteRequest.CompleteArgument("database", ""));
CompleteRequest dbRequest = CompleteRequest
.builder(new ResourceReference("db://{database}/{table}"),
new CompleteRequest.CompleteArgument("database", ""))
.build();
CompleteResult dbResult = mcpClient.completeCompletion(dbRequest);
assertThat(dbResult.completion().values()).contains("users_db", "products_db");
// Then complete table with database context
CompleteRequest tableRequest = new CompleteRequest(
new ResourceReference(ResourceReference.TYPE, "db://{database}/{table}"),
new CompleteRequest.CompleteArgument("table", ""),
new CompleteRequest.CompleteContext(Map.of("database", "users_db")));
CompleteRequest tableRequest = CompleteRequest
.builder(new ResourceReference("db://{database}/{table}"),
new CompleteRequest.CompleteArgument("table", ""))
.context(new CompleteRequest.CompleteContext(Map.of("database", "users_db")))
.build();
CompleteResult tableResult = mcpClient.completeCompletion(tableRequest);
assertThat(tableResult.completion().values()).containsExactly("users", "sessions", "permissions");
// Different database gives different tables
CompleteRequest tableRequest2 = new CompleteRequest(
new ResourceReference(ResourceReference.TYPE, "db://{database}/{table}"),
new CompleteRequest.CompleteArgument("table", ""),
new CompleteRequest.CompleteContext(Map.of("database", "products_db")));
CompleteRequest tableRequest2 = CompleteRequest
.builder(new ResourceReference("db://{database}/{table}"),
new CompleteRequest.CompleteArgument("table", ""))
.context(new CompleteRequest.CompleteContext(Map.of("database", "products_db")))
.build();
CompleteResult tableResult2 = mcpClient.completeCompletion(tableRequest2);
assertThat(tableResult2.completion().values()).containsExactly("products", "categories", "inventory");
@ -282,9 +288,7 @@ class McpCompletionTests {
return new CompleteResult(new CompleteResult.CompleteCompletion(List.of(), 0, false));
};
McpSchema.Resource resource = Resource.builder()
.uri("db://{database}/{table}")
.name("Database Table")
McpSchema.Resource resource = Resource.builder("db://{database}/{table}", "Database Table")
.description("Resource representing a table in a database")
.mimeType("application/json")
.size(456L)
@ -293,30 +297,33 @@ class McpCompletionTests {
var mcpServer = McpServer.sync(mcpServerTransportProvider)
.capabilities(ServerCapabilities.builder().completions().build())
.resources(new McpServerFeatures.SyncResourceSpecification(resource,
(exchange, req) -> new ReadResourceResult(List.of())))
(exchange, req) -> ReadResourceResult.builder(List.of()).build()))
.completions(new McpServerFeatures.SyncCompletionSpecification(
new ResourceReference(ResourceReference.TYPE, "db://{database}/{table}"), completionHandler))
new ResourceReference("db://{database}/{table}"), completionHandler))
.build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample" + "client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample" + "client", "0.0.0").build())
.build();) {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
// Try to complete table without database context - should raise error
CompleteRequest requestWithoutContext = new CompleteRequest(
new ResourceReference(ResourceReference.TYPE, "db://{database}/{table}"),
new CompleteRequest.CompleteArgument("table", ""));
CompleteRequest requestWithoutContext = CompleteRequest
.builder(new ResourceReference("db://{database}/{table}"),
new CompleteRequest.CompleteArgument("table", ""))
.build();
assertThatExceptionOfType(McpError.class)
.isThrownBy(() -> mcpClient.completeCompletion(requestWithoutContext))
.withMessageContaining("Please select a database first");
// Now complete with proper context - should work normally
CompleteRequest requestWithContext = new CompleteRequest(
new ResourceReference(ResourceReference.TYPE, "db://{database}/{table}"),
new CompleteRequest.CompleteArgument("table", ""),
new CompleteRequest.CompleteContext(Map.of("database", "test_db")));
CompleteRequest requestWithContext = CompleteRequest
.builder(new ResourceReference("db://{database}/{table}"),
new CompleteRequest.CompleteArgument("table", ""))
.context(new CompleteRequest.CompleteContext(Map.of("database", "test_db")))
.build();
CompleteResult resultWithContext = mcpClient.completeCompletion(requestWithContext);
assertThat(resultWithContext.completion().values()).containsExactly("users", "orders", "products");
@ -330,24 +337,26 @@ class McpCompletionTests {
BiFunction<McpSyncServerExchange, CompleteRequest, CompleteResult> completionHandler = (exchange,
request) -> new CompleteResult(new CompleteResult.CompleteCompletion(List.of("test"), 1, false));
McpSchema.Prompt prompt = new Prompt("test-prompt", "this is a test prompt", null);
McpSchema.Prompt prompt = Prompt.builder("test-prompt").description("this is a test prompt").build();
var mcpServer = McpServer.sync(mcpServerTransportProvider)
.capabilities(ServerCapabilities.builder().completions().build())
.prompts(new McpServerFeatures.SyncPromptSpecification(prompt,
(mcpSyncServerExchange, getPromptRequest) -> null))
.completions(new McpServerFeatures.SyncCompletionSpecification(
new PromptReference(PromptReference.TYPE, "test-prompt"), completionHandler))
.completions(new McpServerFeatures.SyncCompletionSpecification(new PromptReference("test-prompt"),
completionHandler))
.build();
try (var mcpClient = clientBuilder.clientInfo(new McpSchema.Implementation("Sample " + "client", "0.0.0"))
try (var mcpClient = clientBuilder
.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build())
.build()) {
InitializeResult initResult = mcpClient.initialize();
assertThat(initResult).isNotNull();
// try completing an argument knowing that the prompt is not parameterized
CompleteRequest request = new CompleteRequest(new PromptReference(PromptReference.TYPE, "test-prompt"),
new CompleteRequest.CompleteArgument("arg", "val"));
CompleteRequest request = CompleteRequest
.builder(new PromptReference("test-prompt"), new CompleteRequest.CompleteArgument("arg", "val"))
.build();
CompleteResult completeResult = mcpClient.completeCompletion(request);
assertThat(completeResult.completion().values()).isEmpty();

View file

@ -20,13 +20,17 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
class McpServerProtocolVersionTests {
private static final McpSchema.Implementation SERVER_INFO = new McpSchema.Implementation("test-server", "1.0.0");
private static final McpSchema.Implementation SERVER_INFO = McpSchema.Implementation.builder("test-server", "1.0.0")
.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 McpSchema.JSONRPCRequest jsonRpcInitializeRequest(String requestId, String protocolVersion) {
return new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE, requestId,
new McpSchema.InitializeRequest(protocolVersion, null, CLIENT_INFO));
return new McpSchema.JSONRPCRequest(McpSchema.METHOD_INITIALIZE, requestId,
McpSchema.InitializeRequest
.builder(protocolVersion, McpSchema.ClientCapabilities.builder().build(), CLIENT_INFO)
.build());
}
@Test

Some files were not shown because too many files have changed in this diff Show more