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
21 changed files with 4258 additions and 1688 deletions

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

View file

@ -24,9 +24,11 @@ class ResourceSubscriptionTests {
private static final String RESOURCE_URI = "test://resource/1";
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 static McpAsyncServer buildServer(MockMcpServerTransportProvider transportProvider) {
return McpServer.async(transportProvider)
@ -36,24 +38,25 @@ class ResourceSubscriptionTests {
}
private static McpSchema.JSONRPCRequest initRequest() {
return new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_INITIALIZE,
UUID.randomUUID().toString(),
new McpSchema.InitializeRequest(ProtocolVersions.MCP_2025_11_25, null, CLIENT_INFO));
return new McpSchema.JSONRPCRequest(McpSchema.METHOD_INITIALIZE, UUID.randomUUID().toString(),
McpSchema.InitializeRequest
.builder(ProtocolVersions.MCP_2025_11_25, McpSchema.ClientCapabilities.builder().build(),
CLIENT_INFO)
.build());
}
private static McpSchema.JSONRPCNotification initializedNotification() {
return new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_NOTIFICATION_INITIALIZED,
null);
return new McpSchema.JSONRPCNotification(McpSchema.METHOD_NOTIFICATION_INITIALIZED);
}
private static McpSchema.JSONRPCRequest subscribeRequest(String uri) {
return new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_RESOURCES_SUBSCRIBE,
UUID.randomUUID().toString(), new McpSchema.SubscribeRequest(uri));
return new McpSchema.JSONRPCRequest(McpSchema.METHOD_RESOURCES_SUBSCRIBE, UUID.randomUUID().toString(),
McpSchema.SubscribeRequest.builder(uri).build());
}
private static McpSchema.JSONRPCRequest unsubscribeRequest(String uri) {
return new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, McpSchema.METHOD_RESOURCES_UNSUBSCRIBE,
UUID.randomUUID().toString(), new McpSchema.UnsubscribeRequest(uri));
return new McpSchema.JSONRPCRequest(McpSchema.METHOD_RESOURCES_UNSUBSCRIBE, UUID.randomUUID().toString(),
McpSchema.UnsubscribeRequest.builder(uri).build());
}
@Test

View file

@ -62,15 +62,13 @@ public class ResourceTemplateManagementTests {
.capabilities(ServerCapabilities.builder().resources(true, false).build())
.build();
ResourceTemplate template = ResourceTemplate.builder()
.uriTemplate(TEST_TEMPLATE_URI)
.name(TEST_TEMPLATE_NAME)
ResourceTemplate template = ResourceTemplate.builder(TEST_TEMPLATE_URI, TEST_TEMPLATE_NAME)
.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();
}
@ -82,15 +80,13 @@ public class ResourceTemplateManagementTests {
.serverInfo("test-server", "1.0.0")
.build();
ResourceTemplate template = ResourceTemplate.builder()
.uriTemplate(TEST_TEMPLATE_URI)
.name(TEST_TEMPLATE_NAME)
ResourceTemplate template = ResourceTemplate.builder(TEST_TEMPLATE_URI, TEST_TEMPLATE_NAME)
.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)
@ -103,15 +99,13 @@ public class ResourceTemplateManagementTests {
@Test
void testRemoveResourceTemplate() {
ResourceTemplate template = ResourceTemplate.builder()
.uriTemplate(TEST_TEMPLATE_URI)
.name(TEST_TEMPLATE_NAME)
ResourceTemplate template = ResourceTemplate.builder(TEST_TEMPLATE_URI, TEST_TEMPLATE_NAME)
.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()));
mcpAsyncServer = McpServer.async(mockTransportProvider)
.serverInfo("test-server", "1.0.0")
@ -154,25 +148,21 @@ public class ResourceTemplateManagementTests {
@Test
void testReplaceExistingResourceTemplate() {
ResourceTemplate originalTemplate = ResourceTemplate.builder()
.uriTemplate(TEST_TEMPLATE_URI)
.name(TEST_TEMPLATE_NAME)
ResourceTemplate originalTemplate = ResourceTemplate.builder(TEST_TEMPLATE_URI, TEST_TEMPLATE_NAME)
.description("Original template")
.mimeType("text/plain")
.build();
ResourceTemplate updatedTemplate = ResourceTemplate.builder()
.uriTemplate(TEST_TEMPLATE_URI)
.name(TEST_TEMPLATE_NAME)
ResourceTemplate updatedTemplate = ResourceTemplate.builder(TEST_TEMPLATE_URI, TEST_TEMPLATE_NAME)
.description("Updated template")
.mimeType("application/json")
.build();
McpServerFeatures.AsyncResourceTemplateSpecification originalSpec = new McpServerFeatures.AsyncResourceTemplateSpecification(
originalTemplate, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
originalTemplate, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
McpServerFeatures.AsyncResourceTemplateSpecification updatedSpec = new McpServerFeatures.AsyncResourceTemplateSpecification(
updatedTemplate, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
updatedTemplate, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
mcpAsyncServer = McpServer.async(mockTransportProvider)
.serverInfo("test-server", "1.0.0")
@ -190,15 +180,13 @@ public class ResourceTemplateManagementTests {
@Test
void testSyncAddResourceTemplate() {
ResourceTemplate template = ResourceTemplate.builder()
.uriTemplate(TEST_TEMPLATE_URI)
.name(TEST_TEMPLATE_NAME)
ResourceTemplate template = ResourceTemplate.builder(TEST_TEMPLATE_URI, TEST_TEMPLATE_NAME)
.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 = McpServer.sync(mockTransportProvider)
.serverInfo("test-server", "1.0.0")
@ -212,15 +200,13 @@ public class ResourceTemplateManagementTests {
@Test
void testSyncRemoveResourceTemplate() {
ResourceTemplate template = ResourceTemplate.builder()
.uriTemplate(TEST_TEMPLATE_URI)
.name(TEST_TEMPLATE_NAME)
ResourceTemplate template = ResourceTemplate.builder(TEST_TEMPLATE_URI, TEST_TEMPLATE_NAME)
.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 = McpServer.sync(mockTransportProvider)
.serverInfo("test-server", "1.0.0")
@ -239,25 +225,21 @@ public class ResourceTemplateManagementTests {
@Test
void testResourceTemplateMapBasedStorage() {
ResourceTemplate template1 = ResourceTemplate.builder()
.uriTemplate("test://template1/{id}")
.name("template1")
ResourceTemplate template1 = ResourceTemplate.builder("test://template1/{id}", "template1")
.description("First template")
.mimeType("text/plain")
.build();
ResourceTemplate template2 = ResourceTemplate.builder()
.uriTemplate("test://template2/{id}")
.name("template2")
ResourceTemplate template2 = ResourceTemplate.builder("test://template2/{id}", "template2")
.description("Second template")
.mimeType("application/json")
.build();
McpServerFeatures.AsyncResourceTemplateSpecification spec1 = new McpServerFeatures.AsyncResourceTemplateSpecification(
template1, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
template1, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
McpServerFeatures.AsyncResourceTemplateSpecification spec2 = new McpServerFeatures.AsyncResourceTemplateSpecification(
template2, (exchange, req) -> Mono.just(new ReadResourceResult(List.of())));
template2, (exchange, req) -> Mono.just(ReadResourceResult.builder(List.of()).build()));
mcpAsyncServer = McpServer.async(mockTransportProvider)
.serverInfo("test-server", "1.0.0")
@ -274,15 +256,13 @@ public class ResourceTemplateManagementTests {
@Test
void testResourceTemplateBuilderWithMap() {
// Test that the new Map-based builder methods work correctly
ResourceTemplate template = ResourceTemplate.builder()
.uriTemplate(TEST_TEMPLATE_URI)
.name(TEST_TEMPLATE_NAME)
ResourceTemplate template = ResourceTemplate.builder(TEST_TEMPLATE_URI, TEST_TEMPLATE_NAME)
.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()));
// Test varargs builder method
assertThatCode(() -> {

View file

@ -49,9 +49,11 @@ class ToolInputValidationIntegrationTests {
private static final String TOOL_NAME = "test-tool";
private static final McpSchema.JsonSchema INPUT_SCHEMA = new McpSchema.JsonSchema("object",
Map.of("name", Map.of("type", "string"), "age", Map.of("type", "integer", "minimum", 0)),
List.of("name", "age"), null, null, null);
private static final McpSchema.JsonSchema INPUT_SCHEMA = McpSchema.JsonSchema.builder()
.type("object")
.properties(Map.of("name", Map.of("type", "string"), "age", Map.of("type", "integer", "minimum", 0)))
.required(List.of("name", "age"))
.build();
private static final McpTransportContextExtractor<HttpServletRequest> TEST_CONTEXT_EXTRACTOR = (
r) -> McpTransportContext.create(Map.of("important", "value"));
@ -123,34 +125,26 @@ class ToolInputValidationIntegrationTests {
}
private McpServerFeatures.SyncToolSpecification createSyncTool() {
Tool tool = Tool.builder()
.name(TOOL_NAME)
.description("Test tool with schema")
.inputSchema(INPUT_SCHEMA)
.build();
Tool tool = Tool.builder(TOOL_NAME).inputSchema(INPUT_SCHEMA).description("Test tool with schema").build();
return McpServerFeatures.SyncToolSpecification.builder().tool(tool).callHandler((exchange, request) -> {
String name = (String) request.arguments().get("name");
Integer age = ((Number) request.arguments().get("age")).intValue();
return CallToolResult.builder()
.content(List.of(new TextContent("Hello " + name + ", age " + age)))
.content(List.of(TextContent.builder("Hello " + name + ", age " + age).build()))
.isError(false)
.build();
}).build();
}
private McpServerFeatures.AsyncToolSpecification createAsyncTool() {
Tool tool = Tool.builder()
.name(TOOL_NAME)
.description("Test tool with schema")
.inputSchema(INPUT_SCHEMA)
.build();
Tool tool = Tool.builder(TOOL_NAME).inputSchema(INPUT_SCHEMA).description("Test tool with schema").build();
return McpServerFeatures.AsyncToolSpecification.builder().tool(tool).callHandler((exchange, request) -> {
String name = (String) request.arguments().get("name");
Integer age = ((Number) request.arguments().get("age")).intValue();
return Mono.just(CallToolResult.builder()
.content(List.of(new TextContent("Hello " + name + ", age " + age)))
.content(List.of(TextContent.builder("Hello " + name + ", age " + age).build()))
.isError(false)
.build());
}).build();
@ -162,9 +156,10 @@ class ToolInputValidationIntegrationTests {
String expectedOutput) {
Object server = createServer(serverType, validationEnabled);
try (var client = clientBuilder.clientInfo(new McpSchema.Implementation("test-client", "1.0.0")).build()) {
try (var client = clientBuilder.clientInfo(McpSchema.Implementation.builder("test-client", "1.0.0").build())
.build()) {
client.initialize();
CallToolResult result = client.callTool(new CallToolRequest(TOOL_NAME, input));
CallToolResult result = client.callTool(CallToolRequest.builder(TOOL_NAME).arguments(input).build());
assertThat(result.isError()).isFalse();
assertThat(((TextContent) result.content().get(0)).text()).isEqualTo(expectedOutput);
@ -180,9 +175,10 @@ class ToolInputValidationIntegrationTests {
String expectedErrorSubstring) {
Object server = createServerWithDefaultValidation(serverType);
try (var client = clientBuilder.clientInfo(new McpSchema.Implementation("test-client", "1.0.0")).build()) {
try (var client = clientBuilder.clientInfo(McpSchema.Implementation.builder("test-client", "1.0.0").build())
.build()) {
client.initialize();
CallToolResult result = client.callTool(new CallToolRequest(TOOL_NAME, input));
CallToolResult result = client.callTool(CallToolRequest.builder(TOOL_NAME).arguments(input).build());
assertThat(result.isError()).isTrue();
String errorMessage = ((TextContent) result.content().get(0)).text();
@ -199,13 +195,14 @@ class ToolInputValidationIntegrationTests {
String ignored) {
Object server = createServer(serverType, false);
try (var client = clientBuilder.clientInfo(new McpSchema.Implementation("test-client", "1.0.0")).build()) {
try (var client = clientBuilder.clientInfo(McpSchema.Implementation.builder("test-client", "1.0.0").build())
.build()) {
client.initialize();
// Invalid input should pass through when validation is disabled
// The tool handler will fail, but that's expected - we're testing validation
// is skipped
try {
client.callTool(new CallToolRequest(TOOL_NAME, input));
client.callTool(CallToolRequest.builder(TOOL_NAME).arguments(input).build());
}
catch (Exception e) {
// Expected - tool handler fails on invalid input, but validation didn't

View file

@ -78,7 +78,7 @@ class HttpServletSseServerCustomContextPathTests {
void testCustomContextPath() {
var server = McpServer.async(mcpServerTransportProvider).serverInfo("test-server", "1.0.0").build();
try (//@formatter:off
var client = clientBuilder.clientInfo(new McpSchema.Implementation("Sample " + "client", "0.0.0")) .build()) { //@formatter:on
var client = clientBuilder.clientInfo(McpSchema.Implementation.builder("Sample " + "client", "0.0.0").build()) .build()) { //@formatter:on
assertThat(client.initialize()).isNotNull();
}

View file

@ -7,6 +7,7 @@ package io.modelcontextprotocol.spec;
import static io.modelcontextprotocol.util.McpJsonMapperUtils.JSON_MAPPER;
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.io.IOException;
@ -50,7 +51,7 @@ class CompleteReferenceJsonTests {
McpSchema.CompleteRequest req = mapper.readValue(json, McpSchema.CompleteRequest.class);
assertThat(req.ref()).isInstanceOf(McpSchema.PromptReference.class);
assertThat(req.ref().identifier()).isEqualTo("my-prompt");
assertThat(((McpSchema.PromptReference) req.ref()).name()).isEqualTo("my-prompt");
assertThat(req.argument().name()).isEqualTo("lang");
assertThat(req.argument().value()).isEqualTo("java");
}
@ -64,7 +65,7 @@ class CompleteReferenceJsonTests {
McpSchema.CompleteRequest req = mapper.readValue(json, McpSchema.CompleteRequest.class);
assertThat(req.ref()).isInstanceOf(McpSchema.ResourceReference.class);
assertThat(req.ref().identifier()).isEqualTo("file:///src/Foo.java");
assertThat(((McpSchema.ResourceReference) req.ref()).uri()).isEqualTo("file:///src/Foo.java");
}
@Test
@ -79,7 +80,21 @@ class CompleteReferenceJsonTests {
});
assertThat(req.ref()).isInstanceOf(McpSchema.PromptReference.class);
assertThat(req.ref().identifier()).isEqualTo("my-prompt");
assertThat(((McpSchema.PromptReference) req.ref()).name()).isEqualTo("my-prompt");
}
@Test
void completeRequestMissingRefFailsToInstantiate() throws IOException {
String json = """
{"argument":{"name":"lang","value":"java"}}
""";
// This is the real in-process path: params arrives as a Map from JSON-RPC
Object paramsMap = mapper.readValue(json, Object.class);
assertThatThrownBy(() -> mapper.convertValue(paramsMap, new TypeRef<McpSchema.CompleteRequest>() {
})).hasMessageContaining("ref must not be null");
}
@Test

View file

@ -24,7 +24,7 @@ class ContentJsonTests {
@Test
void textContentHasExactlyOneTypeProperty() throws IOException {
McpSchema.TextContent content = new McpSchema.TextContent("hello");
McpSchema.TextContent content = McpSchema.TextContent.builder("hello").build();
String json = mapper.writeValueAsString(content);
assertExactlyOneTypeProperty(json);
@ -34,7 +34,7 @@ class ContentJsonTests {
@Test
void imageContentHasExactlyOneTypeProperty() throws IOException {
McpSchema.ImageContent content = new McpSchema.ImageContent(null, "base64data", "image/png");
McpSchema.ImageContent content = McpSchema.ImageContent.builder("base64data", "image/png").build();
String json = mapper.writeValueAsString(content);
assertExactlyOneTypeProperty(json);
@ -43,7 +43,7 @@ class ContentJsonTests {
@Test
void audioContentHasExactlyOneTypeProperty() throws IOException {
McpSchema.AudioContent content = new McpSchema.AudioContent(null, "base64data", "audio/mp3");
McpSchema.AudioContent content = McpSchema.AudioContent.builder("base64data", "audio/mp3").build();
String json = mapper.writeValueAsString(content);
assertExactlyOneTypeProperty(json);
@ -52,7 +52,7 @@ class ContentJsonTests {
@Test
void textContentRoundTrip() throws IOException {
McpSchema.TextContent original = new McpSchema.TextContent("round-trip");
McpSchema.TextContent original = McpSchema.TextContent.builder("round-trip").build();
String json = mapper.writeValueAsString(original);
McpSchema.Content decoded = mapper.readValue(json, McpSchema.Content.class);

View file

@ -34,7 +34,7 @@ public class McpSchemaTests {
@Test
void testTextContent() throws Exception {
McpSchema.TextContent test = new McpSchema.TextContent("XXX");
McpSchema.TextContent test = McpSchema.TextContent.builder("XXX").build();
String value = JSON_MAPPER.writeValueAsString(test);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -73,7 +73,7 @@ public class McpSchemaTests {
@Test
void testImageContent() throws Exception {
McpSchema.ImageContent test = new McpSchema.ImageContent(null, "base64encodeddata", "image/png");
McpSchema.ImageContent test = McpSchema.ImageContent.builder("base64encodeddata", "image/png").build();
String value = JSON_MAPPER.writeValueAsString(test);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -97,7 +97,7 @@ public class McpSchemaTests {
@Test
void testAudioContent() throws Exception {
McpSchema.AudioContent audioContent = new McpSchema.AudioContent(null, "base64encodeddata", "audio/wav");
McpSchema.AudioContent audioContent = McpSchema.AudioContent.builder("base64encodeddata", "audio/wav").build();
String value = JSON_MAPPER.writeValueAsString(audioContent);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -121,11 +121,15 @@ public class McpSchemaTests {
@Test
void testCreateMessageRequestWithMeta() throws Exception {
McpSchema.TextContent content = new McpSchema.TextContent("User message");
McpSchema.SamplingMessage message = new McpSchema.SamplingMessage(McpSchema.Role.USER, content);
McpSchema.ModelHint hint = new McpSchema.ModelHint("gpt-4");
McpSchema.ModelPreferences preferences = new McpSchema.ModelPreferences(Collections.singletonList(hint), 0.3,
0.7, 0.9);
McpSchema.TextContent content = McpSchema.TextContent.builder("User message").build();
McpSchema.SamplingMessage message = McpSchema.SamplingMessage.builder(McpSchema.Role.USER, content).build();
McpSchema.ModelHint hint = McpSchema.ModelHint.of("gpt-4");
McpSchema.ModelPreferences preferences = McpSchema.ModelPreferences.builder()
.hints(Collections.singletonList(hint))
.costPriority(0.3)
.speedPriority(0.7)
.intelligencePriority(0.9)
.build();
Map<String, Object> metadata = new HashMap<>();
metadata.put("session", "test-session");
@ -133,13 +137,12 @@ public class McpSchemaTests {
Map<String, Object> meta = new HashMap<>();
meta.put("progressToken", "create-message-token-456");
McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder()
.messages(Collections.singletonList(message))
McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest
.builder(Collections.singletonList(message), 1000)
.modelPreferences(preferences)
.systemPrompt("You are a helpful assistant")
.includeContext(McpSchema.CreateMessageRequest.ContextInclusionStrategy.THIS_SERVER)
.temperature(0.7)
.maxTokens(1000)
.stopSequences(Arrays.asList("STOP", "END"))
.metadata(metadata)
.meta(meta)
@ -158,10 +161,12 @@ public class McpSchemaTests {
@Test
void testEmbeddedResource() throws Exception {
McpSchema.TextResourceContents resourceContents = new McpSchema.TextResourceContents("resource://test",
"text/plain", "Sample resource content");
McpSchema.TextResourceContents resourceContents = McpSchema.TextResourceContents
.builder("resource://test", "Sample resource content")
.mimeType("text/plain")
.build();
McpSchema.EmbeddedResource test = new McpSchema.EmbeddedResource(null, resourceContents);
McpSchema.EmbeddedResource test = McpSchema.EmbeddedResource.builder(resourceContents).build();
String value = JSON_MAPPER.writeValueAsString(test);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -189,10 +194,12 @@ public class McpSchemaTests {
@Test
void testEmbeddedResourceWithBlobContents() throws Exception {
McpSchema.BlobResourceContents resourceContents = new McpSchema.BlobResourceContents("resource://test",
"application/octet-stream", "base64encodedblob");
McpSchema.BlobResourceContents resourceContents = McpSchema.BlobResourceContents
.builder("resource://test", "base64encodedblob")
.mimeType("application/octet-stream")
.build();
McpSchema.EmbeddedResource test = new McpSchema.EmbeddedResource(null, resourceContents);
McpSchema.EmbeddedResource test = McpSchema.EmbeddedResource.builder(resourceContents).build();
String value = JSON_MAPPER.writeValueAsString(test);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -221,9 +228,14 @@ public class McpSchemaTests {
@Test
void testResourceLink() throws Exception {
McpSchema.ResourceLink resourceLink = new McpSchema.ResourceLink("main.rs", "Main file",
"file:///project/src/main.rs", "Primary application entry point", "text/x-rust", null, null,
Map.of("metaKey", "metaValue"));
McpSchema.ResourceLink resourceLink = McpSchema.ResourceLink.builder()
.name("main.rs")
.title("Main file")
.uri("file:///project/src/main.rs")
.description("Primary application entry point")
.mimeType("text/x-rust")
.meta(Map.of("metaKey", "metaValue"))
.build();
String value = JSON_MAPPER.writeValueAsString(resourceLink);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -256,8 +268,7 @@ public class McpSchemaTests {
Map<String, Object> params = new HashMap<>();
params.put("key", "value");
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, "method_name", 1,
params);
McpSchema.JSONRPCRequest request = new McpSchema.JSONRPCRequest("method_name", 1, params);
String value = JSON_MAPPER.writeValueAsString(request);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -272,8 +283,7 @@ public class McpSchemaTests {
Map<String, Object> params = new HashMap<>();
params.put("key", "value");
McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION,
"notification_method", params);
McpSchema.JSONRPCNotification notification = new McpSchema.JSONRPCNotification("notification_method", params);
String value = JSON_MAPPER.writeValueAsString(notification);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -288,7 +298,7 @@ public class McpSchemaTests {
Map<String, Object> result = new HashMap<>();
result.put("result_key", "result_value");
McpSchema.JSONRPCResponse response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, 1, result, null);
McpSchema.JSONRPCResponse response = McpSchema.JSONRPCResponse.result(1, result);
String value = JSON_MAPPER.writeValueAsString(response);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -301,9 +311,9 @@ public class McpSchemaTests {
@Test
void testJSONRPCResponseWithError() throws Exception {
McpSchema.JSONRPCResponse.JSONRPCError error = new McpSchema.JSONRPCResponse.JSONRPCError(
McpSchema.ErrorCodes.INVALID_REQUEST, "Invalid request", null);
McpSchema.ErrorCodes.INVALID_REQUEST, "Invalid request");
McpSchema.JSONRPCResponse response = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, 1, null, error);
McpSchema.JSONRPCResponse response = McpSchema.JSONRPCResponse.error(1, error);
String value = JSON_MAPPER.writeValueAsString(response);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -322,11 +332,13 @@ public class McpSchemaTests {
.sampling()
.build();
McpSchema.Implementation clientInfo = new McpSchema.Implementation("test-client", "1.0.0");
McpSchema.Implementation clientInfo = McpSchema.Implementation.builder("test-client", "1.0.0").build();
Map<String, Object> meta = Map.of("metaKey", "metaValue");
McpSchema.InitializeRequest request = new McpSchema.InitializeRequest(ProtocolVersions.MCP_2024_11_05,
capabilities, clientInfo, meta);
McpSchema.InitializeRequest request = McpSchema.InitializeRequest
.builder(ProtocolVersions.MCP_2024_11_05, capabilities, clientInfo)
.meta(meta)
.build();
String value = JSON_MAPPER.writeValueAsString(request);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -346,10 +358,12 @@ public class McpSchemaTests {
.tools(true)
.build();
McpSchema.Implementation serverInfo = new McpSchema.Implementation("test-server", "1.0.0");
McpSchema.Implementation serverInfo = McpSchema.Implementation.builder("test-server", "1.0.0").build();
McpSchema.InitializeResult result = new McpSchema.InitializeResult(ProtocolVersions.MCP_2024_11_05,
capabilities, serverInfo, "Server initialized successfully");
McpSchema.InitializeResult result = McpSchema.InitializeResult
.builder(ProtocolVersions.MCP_2024_11_05, capabilities, serverInfo)
.instructions("Server initialized successfully")
.build();
String value = JSON_MAPPER.writeValueAsString(result);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -364,12 +378,12 @@ public class McpSchemaTests {
@Test
void testResource() throws Exception {
McpSchema.Annotations annotations = new McpSchema.Annotations(
Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8);
McpSchema.Annotations annotations = McpSchema.Annotations.builder()
.audience(Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT))
.priority(0.8)
.build();
McpSchema.Resource resource = McpSchema.Resource.builder()
.uri("resource://test")
.name("Test Resource")
McpSchema.Resource resource = McpSchema.Resource.builder("resource://test", "Test Resource")
.description("A test resource")
.mimeType("text/plain")
.annotations(annotations)
@ -386,12 +400,12 @@ public class McpSchemaTests {
@Test
void testResourceBuilder() throws Exception {
McpSchema.Annotations annotations = new McpSchema.Annotations(
Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8);
McpSchema.Annotations annotations = McpSchema.Annotations.builder()
.audience(Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT))
.priority(0.8)
.build();
McpSchema.Resource resource = McpSchema.Resource.builder()
.uri("resource://test")
.name("Test Resource")
McpSchema.Resource resource = McpSchema.Resource.builder("resource://test", "Test Resource")
.description("A test resource")
.mimeType("text/plain")
.size(256L)
@ -410,41 +424,32 @@ public class McpSchemaTests {
@Test
void testResourceBuilderUriRequired() {
McpSchema.Annotations annotations = new McpSchema.Annotations(
Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8);
McpSchema.Resource.Builder resourceBuilder = McpSchema.Resource.builder()
.name("Test Resource")
.description("A test resource")
.mimeType("text/plain")
.size(256L)
.annotations(annotations);
assertThatThrownBy(resourceBuilder::build).isInstanceOf(java.lang.IllegalArgumentException.class);
assertThatThrownBy(() -> McpSchema.Resource.builder(null, "Test Resource"))
.isInstanceOf(java.lang.IllegalArgumentException.class);
}
@Test
void testResourceBuilderNameRequired() {
McpSchema.Annotations annotations = new McpSchema.Annotations(
Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8);
McpSchema.Resource.Builder resourceBuilder = McpSchema.Resource.builder()
.uri("resource://test")
.description("A test resource")
.mimeType("text/plain")
.size(256L)
.annotations(annotations);
assertThatThrownBy(resourceBuilder::build).isInstanceOf(java.lang.IllegalArgumentException.class);
assertThatThrownBy(() -> McpSchema.Resource.builder("resource://test", null))
.isInstanceOf(java.lang.IllegalArgumentException.class);
}
@Test
void testResourceTemplate() throws Exception {
McpSchema.Annotations annotations = new McpSchema.Annotations(Arrays.asList(McpSchema.Role.USER), 0.5);
McpSchema.Annotations annotations = McpSchema.Annotations.builder()
.audience(Arrays.asList(McpSchema.Role.USER))
.priority(0.5)
.build();
Map<String, Object> meta = Map.of("metaKey", "metaValue");
McpSchema.ResourceTemplate template = new McpSchema.ResourceTemplate("resource://{param}/test", "Test Template",
"Test Template", "A test resource template", "text/plain", annotations, meta);
McpSchema.ResourceTemplate template = McpSchema.ResourceTemplate
.builder("resource://{param}/test", "Test Template")
.title("Test Template")
.description("A test resource template")
.mimeType("text/plain")
.annotations(annotations)
.meta(meta)
.build();
String value = JSON_MAPPER.writeValueAsString(template);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -457,24 +462,23 @@ public class McpSchemaTests {
@Test
void testListResourcesResult() throws Exception {
McpSchema.Resource resource1 = McpSchema.Resource.builder()
.uri("resource://test1")
.name("Test Resource 1")
McpSchema.Resource resource1 = McpSchema.Resource.builder("resource://test1", "Test Resource 1")
.description("First test resource")
.mimeType("text/plain")
.build();
McpSchema.Resource resource2 = McpSchema.Resource.builder()
.uri("resource://test2")
.name("Test Resource 2")
McpSchema.Resource resource2 = McpSchema.Resource.builder("resource://test2", "Test Resource 2")
.description("Second test resource")
.mimeType("application/json")
.build();
Map<String, Object> meta = Map.of("metaKey", "metaValue");
McpSchema.ListResourcesResult result = new McpSchema.ListResourcesResult(Arrays.asList(resource1, resource2),
"next-cursor", meta);
McpSchema.ListResourcesResult result = McpSchema.ListResourcesResult
.builder(Arrays.asList(resource1, resource2))
.nextCursor("next-cursor")
.meta(meta)
.build();
String value = JSON_MAPPER.writeValueAsString(result);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -487,14 +491,24 @@ public class McpSchemaTests {
@Test
void testListResourceTemplatesResult() throws Exception {
McpSchema.ResourceTemplate template1 = new McpSchema.ResourceTemplate("resource://{param}/test1",
"Test Template 1", "Test Template 1", "First test template", "text/plain", null);
McpSchema.ResourceTemplate template1 = McpSchema.ResourceTemplate
.builder("resource://{param}/test1", "Test Template 1")
.title("Test Template 1")
.description("First test template")
.mimeType("text/plain")
.build();
McpSchema.ResourceTemplate template2 = new McpSchema.ResourceTemplate("resource://{param}/test2",
"Test Template 2", "Test Template 2", "Second test template", "application/json", null);
McpSchema.ResourceTemplate template2 = McpSchema.ResourceTemplate
.builder("resource://{param}/test2", "Test Template 2")
.title("Test Template 2")
.description("Second test template")
.mimeType("application/json")
.build();
McpSchema.ListResourceTemplatesResult result = new McpSchema.ListResourceTemplatesResult(
Arrays.asList(template1, template2), "next-cursor");
McpSchema.ListResourceTemplatesResult result = McpSchema.ListResourceTemplatesResult
.builder(Arrays.asList(template1, template2))
.nextCursor("next-cursor")
.build();
String value = JSON_MAPPER.writeValueAsString(result);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -507,8 +521,9 @@ public class McpSchemaTests {
@Test
void testReadResourceRequest() throws Exception {
McpSchema.ReadResourceRequest request = new McpSchema.ReadResourceRequest("resource://test",
Map.of("metaKey", "metaValue"));
McpSchema.ReadResourceRequest request = McpSchema.ReadResourceRequest.builder("resource://test")
.meta(Map.of("metaKey", "metaValue"))
.build();
String value = JSON_MAPPER.writeValueAsString(request);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -523,7 +538,9 @@ public class McpSchemaTests {
Map<String, Object> meta = new HashMap<>();
meta.put("progressToken", "read-resource-token-123");
McpSchema.ReadResourceRequest request = new McpSchema.ReadResourceRequest("resource://test", meta);
McpSchema.ReadResourceRequest request = McpSchema.ReadResourceRequest.builder("resource://test")
.meta(meta)
.build();
String value = JSON_MAPPER.writeValueAsString(request);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -550,14 +567,19 @@ public class McpSchemaTests {
@Test
void testReadResourceResult() throws Exception {
McpSchema.TextResourceContents contents1 = new McpSchema.TextResourceContents("resource://test1", "text/plain",
"Sample text content");
McpSchema.TextResourceContents contents1 = McpSchema.TextResourceContents
.builder("resource://test1", "Sample text content")
.mimeType("text/plain")
.build();
McpSchema.BlobResourceContents contents2 = new McpSchema.BlobResourceContents("resource://test2",
"application/octet-stream", "base64encodedblob");
McpSchema.BlobResourceContents contents2 = McpSchema.BlobResourceContents
.builder("resource://test2", "base64encodedblob")
.mimeType("application/octet-stream")
.build();
McpSchema.ReadResourceResult result = new McpSchema.ReadResourceResult(Arrays.asList(contents1, contents2),
Map.of("metaKey", "metaValue"));
McpSchema.ReadResourceResult result = McpSchema.ReadResourceResult.builder(Arrays.asList(contents1, contents2))
.meta(Map.of("metaKey", "metaValue"))
.build();
String value = JSON_MAPPER.writeValueAsString(result);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -572,13 +594,24 @@ public class McpSchemaTests {
@Test
void testPrompt() throws Exception {
McpSchema.PromptArgument arg1 = new McpSchema.PromptArgument("arg1", "First argument", "First argument", true);
McpSchema.PromptArgument arg1 = McpSchema.PromptArgument.builder("arg1")
.title("First argument")
.description("First argument")
.required(true)
.build();
McpSchema.PromptArgument arg2 = new McpSchema.PromptArgument("arg2", "Second argument", "Second argument",
false);
McpSchema.PromptArgument arg2 = McpSchema.PromptArgument.builder("arg2")
.title("Second argument")
.description("Second argument")
.required(false)
.build();
McpSchema.Prompt prompt = new McpSchema.Prompt("test-prompt", "Test Prompt", "A test prompt",
Arrays.asList(arg1, arg2), Map.of("metaKey", "metaValue"));
McpSchema.Prompt prompt = McpSchema.Prompt.builder("test-prompt")
.title("Test Prompt")
.description("A test prompt")
.arguments(Arrays.asList(arg1, arg2))
.meta(Map.of("metaKey", "metaValue"))
.build();
String value = JSON_MAPPER.writeValueAsString(prompt);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -591,9 +624,9 @@ public class McpSchemaTests {
@Test
void testPromptMessage() throws Exception {
McpSchema.TextContent content = new McpSchema.TextContent("Hello, world!");
McpSchema.TextContent content = McpSchema.TextContent.builder("Hello, world!").build();
McpSchema.PromptMessage message = new McpSchema.PromptMessage(McpSchema.Role.USER, content);
McpSchema.PromptMessage message = McpSchema.PromptMessage.builder(McpSchema.Role.USER, content).build();
String value = JSON_MAPPER.writeValueAsString(message);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -605,16 +638,27 @@ public class McpSchemaTests {
@Test
void testListPromptsResult() throws Exception {
McpSchema.PromptArgument arg = new McpSchema.PromptArgument("arg", "Argument", "An argument", true);
McpSchema.PromptArgument arg = McpSchema.PromptArgument.builder("arg")
.title("Argument")
.description("An argument")
.required(true)
.build();
McpSchema.Prompt prompt1 = new McpSchema.Prompt("prompt1", "First prompt", "First prompt",
Collections.singletonList(arg));
McpSchema.Prompt prompt1 = McpSchema.Prompt.builder("prompt1")
.title("First prompt")
.description("First prompt")
.arguments(Collections.singletonList(arg))
.build();
McpSchema.Prompt prompt2 = new McpSchema.Prompt("prompt2", "Second prompt", "Second prompt",
Collections.emptyList());
McpSchema.Prompt prompt2 = McpSchema.Prompt.builder("prompt2")
.title("Second prompt")
.description("Second prompt")
.arguments(Collections.emptyList())
.build();
McpSchema.ListPromptsResult result = new McpSchema.ListPromptsResult(Arrays.asList(prompt1, prompt2),
"next-cursor");
McpSchema.ListPromptsResult result = McpSchema.ListPromptsResult.builder(Arrays.asList(prompt1, prompt2))
.nextCursor("next-cursor")
.build();
String value = JSON_MAPPER.writeValueAsString(result);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -631,7 +675,9 @@ public class McpSchemaTests {
arguments.put("arg1", "value1");
arguments.put("arg2", 42);
McpSchema.GetPromptRequest request = new McpSchema.GetPromptRequest("test-prompt", arguments);
McpSchema.GetPromptRequest request = McpSchema.GetPromptRequest.builder("test-prompt")
.arguments(arguments)
.build();
assertThat(JSON_MAPPER.readValue("""
{"name":"test-prompt","arguments":{"arg1":"value1","arg2":42}}""", McpSchema.GetPromptRequest.class))
@ -647,7 +693,10 @@ public class McpSchemaTests {
Map<String, Object> meta = new HashMap<>();
meta.put("progressToken", "token123");
McpSchema.GetPromptRequest request = new McpSchema.GetPromptRequest("test-prompt", arguments, meta);
McpSchema.GetPromptRequest request = McpSchema.GetPromptRequest.builder("test-prompt")
.arguments(arguments)
.meta(meta)
.build();
String value = JSON_MAPPER.writeValueAsString(request);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -664,15 +713,16 @@ public class McpSchemaTests {
@Test
void testGetPromptResult() throws Exception {
McpSchema.TextContent content1 = new McpSchema.TextContent("System message");
McpSchema.TextContent content2 = new McpSchema.TextContent("User message");
McpSchema.TextContent content1 = McpSchema.TextContent.builder("System message").build();
McpSchema.TextContent content2 = McpSchema.TextContent.builder("User message").build();
McpSchema.PromptMessage message1 = new McpSchema.PromptMessage(McpSchema.Role.ASSISTANT, content1);
McpSchema.PromptMessage message1 = McpSchema.PromptMessage.builder(McpSchema.Role.ASSISTANT, content1).build();
McpSchema.PromptMessage message2 = new McpSchema.PromptMessage(McpSchema.Role.USER, content2);
McpSchema.PromptMessage message2 = McpSchema.PromptMessage.builder(McpSchema.Role.USER, content2).build();
McpSchema.GetPromptResult result = new McpSchema.GetPromptResult("A test prompt result",
Arrays.asList(message1, message2));
McpSchema.GetPromptResult result = McpSchema.GetPromptResult.builder(Arrays.asList(message1, message2))
.description("A test prompt result")
.build();
String value = JSON_MAPPER.writeValueAsString(result);
@ -793,10 +843,8 @@ public class McpSchemaTests {
}
""";
McpSchema.Tool tool = McpSchema.Tool.builder()
.name("test-tool")
McpSchema.Tool tool = McpSchema.Tool.builder("test-tool", JSON_MAPPER, schemaJson)
.description("A test tool")
.inputSchema(JSON_MAPPER, schemaJson)
.build();
String value = JSON_MAPPER.writeValueAsString(tool);
@ -831,10 +879,8 @@ public class McpSchemaTests {
}
""";
McpSchema.Tool tool = McpSchema.Tool.builder()
.name("addressTool")
McpSchema.Tool tool = McpSchema.Tool.builder("addressTool", JSON_MAPPER, complexSchemaJson)
.title("Handles addresses")
.inputSchema(JSON_MAPPER, complexSchemaJson)
.build();
// Serialize the tool to a string
@ -877,11 +923,9 @@ public class McpSchemaTests {
Map<String, Object> inputSchema = Map.of("inputSchema", schemaJson);
Map<String, Object> meta = Map.of("metaKey", "metaValue");
McpSchema.Tool tool = McpSchema.Tool.builder()
.name("addressTool")
McpSchema.Tool tool = McpSchema.Tool.builder("addressTool", inputSchema)
.title("addressTool")
.description("Handles addresses")
.inputSchema(inputSchema)
.meta(meta)
.build();
@ -906,13 +950,17 @@ public class McpSchemaTests {
"required": ["name"]
}
""";
McpSchema.ToolAnnotations annotations = new McpSchema.ToolAnnotations("A test tool", false, false, false, false,
false);
McpSchema.ToolAnnotations annotations = McpSchema.ToolAnnotations.builder()
.title("A test tool")
.readOnlyHint(false)
.destructiveHint(false)
.idempotentHint(false)
.openWorldHint(false)
.returnDirect(false)
.build();
McpSchema.Tool tool = McpSchema.Tool.builder()
.name("test-tool")
McpSchema.Tool tool = McpSchema.Tool.builder("test-tool", JSON_MAPPER, schemaJson)
.description("A test tool")
.inputSchema(JSON_MAPPER, schemaJson)
.annotations(annotations)
.build();
@ -977,10 +1025,8 @@ public class McpSchemaTests {
}
""";
McpSchema.Tool tool = McpSchema.Tool.builder()
.name("test-tool")
McpSchema.Tool tool = McpSchema.Tool.builder("test-tool", JSON_MAPPER, inputSchemaJson)
.description("A test tool")
.inputSchema(JSON_MAPPER, inputSchemaJson)
.outputSchema(JSON_MAPPER, outputSchemaJson)
.build();
@ -1041,13 +1087,17 @@ public class McpSchemaTests {
}
""";
McpSchema.ToolAnnotations annotations = new McpSchema.ToolAnnotations("A test tool with output", true, false,
true, false, true);
McpSchema.ToolAnnotations annotations = McpSchema.ToolAnnotations.builder()
.title("A test tool with output")
.readOnlyHint(true)
.destructiveHint(false)
.idempotentHint(true)
.openWorldHint(false)
.returnDirect(true)
.build();
McpSchema.Tool tool = McpSchema.Tool.builder()
.name("test-tool")
McpSchema.Tool tool = McpSchema.Tool.builder("test-tool", JSON_MAPPER, inputSchemaJson)
.description("A test tool")
.inputSchema(JSON_MAPPER, inputSchemaJson)
.outputSchema(JSON_MAPPER, outputSchemaJson)
.annotations(annotations)
.build();
@ -1166,7 +1216,7 @@ public class McpSchemaTests {
arguments.put("name", "test");
arguments.put("value", 42);
McpSchema.CallToolRequest request = new McpSchema.CallToolRequest("test-tool", arguments);
McpSchema.CallToolRequest request = McpSchema.CallToolRequest.builder("test-tool").arguments(arguments).build();
String value = JSON_MAPPER.writeValueAsString(request);
@ -1180,12 +1230,12 @@ public class McpSchemaTests {
@Test
void testCallToolRequestJsonArguments() throws Exception {
McpSchema.CallToolRequest request = new McpSchema.CallToolRequest(JSON_MAPPER, "test-tool", """
McpSchema.CallToolRequest request = McpSchema.CallToolRequest.builder("test-tool").arguments(JSON_MAPPER, """
{
"name": "test",
"value": 42
}
""");
""").build();
String value = JSON_MAPPER.writeValueAsString(request);
@ -1261,7 +1311,7 @@ public class McpSchemaTests {
@Test
void testCallToolResult() throws Exception {
McpSchema.TextContent content = new McpSchema.TextContent("Tool execution result");
McpSchema.TextContent content = McpSchema.TextContent.builder("Tool execution result").build();
McpSchema.CallToolResult result = McpSchema.CallToolResult.builder()
.content(Collections.singletonList(content))
@ -1294,8 +1344,8 @@ public class McpSchemaTests {
@Test
void testCallToolResultBuilderWithMultipleContents() throws Exception {
McpSchema.TextContent textContent = new McpSchema.TextContent("Text result");
McpSchema.ImageContent imageContent = new McpSchema.ImageContent(null, "base64data", "image/png");
McpSchema.TextContent textContent = McpSchema.TextContent.builder("Text result").build();
McpSchema.ImageContent imageContent = McpSchema.ImageContent.builder("base64data", "image/png").build();
McpSchema.CallToolResult result = McpSchema.CallToolResult.builder()
.addContent(textContent)
@ -1315,8 +1365,8 @@ public class McpSchemaTests {
@Test
void testCallToolResultBuilderWithContentList() throws Exception {
McpSchema.TextContent textContent = new McpSchema.TextContent("Text result");
McpSchema.ImageContent imageContent = new McpSchema.ImageContent(null, "base64data", "image/png");
McpSchema.TextContent textContent = McpSchema.TextContent.builder("Text result").build();
McpSchema.ImageContent imageContent = McpSchema.ImageContent.builder("base64data", "image/png").build();
List<McpSchema.Content> contents = Arrays.asList(textContent, imageContent);
McpSchema.CallToolResult result = McpSchema.CallToolResult.builder().content(contents).isError(true).build();
@ -1347,29 +1397,42 @@ public class McpSchemaTests {
{"content":[{"type":"text","text":"Error: Operation failed"}],"isError":true}"""));
}
@Test
void testCallToolResultDeserializationWithMissingContent() throws Exception {
McpSchema.CallToolResult result = JSON_MAPPER.readValue("""
{"isError":false}""", McpSchema.CallToolResult.class);
assertThat(result).isNotNull();
assertThat(result.content()).isEmpty();
assertThat(result.isError()).isFalse();
}
// Sampling Tests
@Test
void testCreateMessageRequest() throws Exception {
McpSchema.TextContent content = new McpSchema.TextContent("User message");
McpSchema.TextContent content = McpSchema.TextContent.builder("User message").build();
McpSchema.SamplingMessage message = new McpSchema.SamplingMessage(McpSchema.Role.USER, content);
McpSchema.SamplingMessage message = McpSchema.SamplingMessage.builder(McpSchema.Role.USER, content).build();
McpSchema.ModelHint hint = new McpSchema.ModelHint("gpt-4");
McpSchema.ModelHint hint = McpSchema.ModelHint.of("gpt-4");
McpSchema.ModelPreferences preferences = new McpSchema.ModelPreferences(Collections.singletonList(hint), 0.3,
0.7, 0.9);
McpSchema.ModelPreferences preferences = McpSchema.ModelPreferences.builder()
.hints(Collections.singletonList(hint))
.costPriority(0.3)
.speedPriority(0.7)
.intelligencePriority(0.9)
.build();
Map<String, Object> metadata = new HashMap<>();
metadata.put("session", "test-session");
McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder()
.messages(Collections.singletonList(message))
McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest
.builder(Collections.singletonList(message), 1000)
.modelPreferences(preferences)
.systemPrompt("You are a helpful assistant")
.includeContext(McpSchema.CreateMessageRequest.ContextInclusionStrategy.THIS_SERVER)
.temperature(0.7)
.maxTokens(1000)
.stopSequences(Arrays.asList("STOP", "END"))
.metadata(metadata)
.build();
@ -1385,13 +1448,31 @@ public class McpSchemaTests {
}
@Test
void testCreateMessageResult() throws Exception {
McpSchema.TextContent content = new McpSchema.TextContent("Assistant response");
void testSamplingMessageDeserializationWithMissingFields() throws Exception {
McpSchema.SamplingMessage message = JSON_MAPPER.readValue("{}", McpSchema.SamplingMessage.class);
McpSchema.CreateMessageResult result = McpSchema.CreateMessageResult.builder()
.role(McpSchema.Role.ASSISTANT)
.content(content)
.model("gpt-4")
assertThat(message).isNotNull();
assertThat(message.role()).isEqualTo(McpSchema.Role.USER);
assertThat(message.content()).isInstanceOf(McpSchema.TextContent.class);
}
@Test
void testCreateMessageRequestDeserializationWithMissingRequiredFields() throws Exception {
McpSchema.CreateMessageRequest request = JSON_MAPPER.readValue("""
{"systemPrompt":"hello"}""", McpSchema.CreateMessageRequest.class);
assertThat(request).isNotNull();
assertThat(request.messages()).isEmpty();
assertThat(request.maxTokens()).isZero();
assertThat(request.systemPrompt()).isEqualTo("hello");
}
@Test
void testCreateMessageResult() throws Exception {
McpSchema.TextContent content = McpSchema.TextContent.builder("Assistant response").build();
McpSchema.CreateMessageResult result = McpSchema.CreateMessageResult
.builder(McpSchema.Role.ASSISTANT, content, "gpt-4")
.stopReason(McpSchema.CreateMessageResult.StopReason.END_TURN)
.build();
@ -1412,11 +1493,9 @@ public class McpSchemaTests {
McpSchema.CreateMessageResult value = JSON_MAPPER.readValue(input, McpSchema.CreateMessageResult.class);
McpSchema.TextContent expectedContent = new McpSchema.TextContent("Assistant response");
McpSchema.CreateMessageResult expected = McpSchema.CreateMessageResult.builder()
.role(McpSchema.Role.ASSISTANT)
.content(expectedContent)
.model("gpt-4")
McpSchema.TextContent expectedContent = McpSchema.TextContent.builder("Assistant response").build();
McpSchema.CreateMessageResult expected = McpSchema.CreateMessageResult
.builder(McpSchema.Role.ASSISTANT, expectedContent, "gpt-4")
.stopReason(McpSchema.CreateMessageResult.StopReason.UNKNOWN)
.build();
assertThat(value).isEqualTo(expected);
@ -1426,9 +1505,9 @@ public class McpSchemaTests {
@Test
void testCreateElicitationRequest() throws Exception {
McpSchema.ElicitRequest request = McpSchema.ElicitRequest.builder()
.requestedSchema(Map.of("type", "object", "required", List.of("a"), "properties",
Map.of("foo", Map.of("type", "string"))))
McpSchema.ElicitRequest request = McpSchema.ElicitRequest
.builder("Please provide additional information", Map.of("type", "object", "required", List.of("a"),
"properties", Map.of("foo", Map.of("type", "string"))))
.build();
String value = JSON_MAPPER.writeValueAsString(request);
@ -1436,15 +1515,15 @@ public class McpSchemaTests {
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
.isObject()
.isEqualTo(json("""
{"requestedSchema":{"properties":{"foo":{"type":"string"}},"required":["a"],"type":"object"}}"""));
.isEqualTo(
json("""
{"message":"Please provide additional information","requestedSchema":{"properties":{"foo":{"type":"string"}},"required":["a"],"type":"object"}}"""));
}
@Test
void testCreateElicitationResult() throws Exception {
McpSchema.ElicitResult result = McpSchema.ElicitResult.builder()
McpSchema.ElicitResult result = McpSchema.ElicitResult.builder(McpSchema.ElicitResult.Action.ACCEPT)
.content(Map.of("foo", "bar"))
.message(McpSchema.ElicitResult.Action.ACCEPT)
.build();
String value = JSON_MAPPER.writeValueAsString(result);
@ -1456,6 +1535,15 @@ public class McpSchemaTests {
{"action":"accept","content":{"foo":"bar"}}"""));
}
@Test
void testElicitRequestDeserializationWithMissingRequiredFields() throws Exception {
McpSchema.ElicitRequest request = JSON_MAPPER.readValue("{}", McpSchema.ElicitRequest.class);
assertThat(request).isNotNull();
assertThat(request.message()).isEmpty();
assertThat(request.requestedSchema()).isEmpty();
}
@Test
void testElicitRequestWithMeta() throws Exception {
Map<String, Object> requestedSchema = Map.of("type", "object", "required", List.of("name"), "properties",
@ -1464,9 +1552,7 @@ public class McpSchemaTests {
Map<String, Object> meta = new HashMap<>();
meta.put("progressToken", "elicit-token-789");
McpSchema.ElicitRequest request = McpSchema.ElicitRequest.builder()
.message("Please provide your name")
.requestedSchema(requestedSchema)
McpSchema.ElicitRequest request = McpSchema.ElicitRequest.builder("Please provide your name", requestedSchema)
.meta(meta)
.build();
@ -1552,7 +1638,7 @@ public class McpSchemaTests {
McpSchema.CompleteRequest.CompleteArgument argument = new McpSchema.CompleteRequest.CompleteArgument("arg1",
"partial-value");
McpSchema.CompleteRequest request = new McpSchema.CompleteRequest(promptRef, argument);
McpSchema.CompleteRequest request = McpSchema.CompleteRequest.builder(promptRef, argument).build();
String value = JSON_MAPPER.writeValueAsString(request);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -1576,7 +1662,7 @@ public class McpSchemaTests {
Map<String, Object> meta = new HashMap<>();
meta.put("progressToken", "complete-progress-789");
McpSchema.CompleteRequest request = new McpSchema.CompleteRequest(resourceRef, argument, meta, null);
McpSchema.CompleteRequest request = McpSchema.CompleteRequest.builder(resourceRef, argument).meta(meta).build();
String value = JSON_MAPPER.writeValueAsString(request);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -1595,7 +1681,10 @@ public class McpSchemaTests {
@Test
void testRoot() throws Exception {
McpSchema.Root root = new McpSchema.Root("file:///path/to/root", "Test Root", Map.of("metaKey", "metaValue"));
McpSchema.Root root = McpSchema.Root.builder("file:///path/to/root")
.name("Test Root")
.meta(Map.of("metaKey", "metaValue"))
.build();
String value = JSON_MAPPER.writeValueAsString(root);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -1607,11 +1696,13 @@ public class McpSchemaTests {
@Test
void testListRootsResult() throws Exception {
McpSchema.Root root1 = new McpSchema.Root("file:///path/to/root1", "First Root");
McpSchema.Root root1 = McpSchema.Root.builder("file:///path/to/root1").name("First Root").build();
McpSchema.Root root2 = new McpSchema.Root("file:///path/to/root2", "Second Root");
McpSchema.Root root2 = McpSchema.Root.builder("file:///path/to/root2").name("Second Root").build();
McpSchema.ListRootsResult result = new McpSchema.ListRootsResult(Arrays.asList(root1, root2), "next-cursor");
McpSchema.ListRootsResult result = McpSchema.ListRootsResult.builder(Arrays.asList(root1, root2))
.nextCursor("next-cursor")
.build();
String value = JSON_MAPPER.writeValueAsString(result);
@ -1729,8 +1820,11 @@ public class McpSchemaTests {
@Test
void testProgressNotificationWithMessage() throws Exception {
McpSchema.ProgressNotification notification = new McpSchema.ProgressNotification("progress-token-123", 0.5, 1.0,
"Processing file 1 of 2", Map.of("key", "value"));
McpSchema.ProgressNotification notification = McpSchema.ProgressNotification.builder("progress-token-123", 0.5)
.total(1.0)
.message("Processing file 1 of 2")
.meta(Map.of("key", "value"))
.build();
String value = JSON_MAPPER.writeValueAsString(notification);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -1755,10 +1849,21 @@ public class McpSchemaTests {
assertThat(notification.meta()).containsEntry("key", "value");
}
@Test
void testProgressNotificationDeserializationWithMissingRequiredFields() throws Exception {
McpSchema.ProgressNotification notification = JSON_MAPPER.readValue("""
{"total":1.0}""", McpSchema.ProgressNotification.class);
assertThat(notification).isNotNull();
assertThat(notification.progressToken()).isEqualTo("");
assertThat(notification.progress()).isZero();
assertThat(notification.total()).isEqualTo(1.0);
}
@Test
void testProgressNotificationWithoutMessage() throws Exception {
McpSchema.ProgressNotification notification = new McpSchema.ProgressNotification("progress-token-789", 0.25,
null, null);
McpSchema.ProgressNotification notification = McpSchema.ProgressNotification.builder("progress-token-789", 0.25)
.build();
String value = JSON_MAPPER.writeValueAsString(notification);
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
@ -1768,4 +1873,15 @@ public class McpSchemaTests {
{"progressToken":"progress-token-789","progress":0.25}"""));
}
@Test
void testLoggingMessageNotificationDeserializationWithMissingRequiredFields() throws Exception {
McpSchema.LoggingMessageNotification notification = JSON_MAPPER.readValue("""
{"logger":"my-logger"}""", McpSchema.LoggingMessageNotification.class);
assertThat(notification).isNotNull();
assertThat(notification.level()).isEqualTo(McpSchema.LoggingLevel.INFO);
assertThat(notification.logger()).isEqualTo("my-logger");
assertThat(notification.data()).isEmpty();
}
}

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