feat: add progress token support and extend Request interface (#352)
- Add PaginatedRequest and ReadResourceRequest to Request sealed interface - Add default meta() and progressToken() methods to Request interface - Add _meta parameter support to request classes: - ReadResourceRequest, GetPromptRequest, CallToolRequest - CreateMessageRequest, ElicitRequest, CompleteRequest, PaginatedRequest - Add builder methods for progress token configuration - Add message field to ProgressNotification - Maintain backward compatibility with existing constructors - Add _meta field to the InitialRequest record Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
parent
d7bcb237b8
commit
c0b398c650
2 changed files with 1321 additions and 847 deletions
|
|
@ -142,7 +142,16 @@ public final class McpSchema {
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed interface Request permits InitializeRequest, CallToolRequest, CreateMessageRequest, ElicitRequest,
|
public sealed interface Request permits InitializeRequest, CallToolRequest, CreateMessageRequest, ElicitRequest,
|
||||||
CompleteRequest, GetPromptRequest {
|
CompleteRequest, GetPromptRequest, PaginatedRequest, ReadResourceRequest {
|
||||||
|
|
||||||
|
Map<String, Object> meta();
|
||||||
|
|
||||||
|
default String progressToken() {
|
||||||
|
if (meta() != null && meta().containsKey("progressToken")) {
|
||||||
|
return meta().get("progressToken").toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,7 +246,12 @@ public final class McpSchema {
|
||||||
public record InitializeRequest( // @formatter:off
|
public record InitializeRequest( // @formatter:off
|
||||||
@JsonProperty("protocolVersion") String protocolVersion,
|
@JsonProperty("protocolVersion") String protocolVersion,
|
||||||
@JsonProperty("capabilities") ClientCapabilities capabilities,
|
@JsonProperty("capabilities") ClientCapabilities capabilities,
|
||||||
@JsonProperty("clientInfo") Implementation clientInfo) implements Request {
|
@JsonProperty("clientInfo") Implementation clientInfo,
|
||||||
|
@JsonProperty("_meta") Map<String, Object> meta) implements Request {
|
||||||
|
|
||||||
|
public InitializeRequest(String protocolVersion, ClientCapabilities capabilities, Implementation clientInfo) {
|
||||||
|
this(protocolVersion, capabilities, clientInfo, null);
|
||||||
|
}
|
||||||
} // @formatter:on
|
} // @formatter:on
|
||||||
|
|
||||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||||
|
|
@ -630,7 +644,12 @@ public final class McpSchema {
|
||||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public record ReadResourceRequest( // @formatter:off
|
public record ReadResourceRequest( // @formatter:off
|
||||||
@JsonProperty("uri") String uri){
|
@JsonProperty("uri") String uri,
|
||||||
|
@JsonProperty("_meta") Map<String, Object> meta) implements Request {
|
||||||
|
|
||||||
|
public ReadResourceRequest(String uri) {
|
||||||
|
this(uri, null);
|
||||||
|
}
|
||||||
} // @formatter:on
|
} // @formatter:on
|
||||||
|
|
||||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||||
|
|
@ -786,7 +805,12 @@ public final class McpSchema {
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public record GetPromptRequest(// @formatter:off
|
public record GetPromptRequest(// @formatter:off
|
||||||
@JsonProperty("name") String name,
|
@JsonProperty("name") String name,
|
||||||
@JsonProperty("arguments") Map<String, Object> arguments) implements Request {
|
@JsonProperty("arguments") Map<String, Object> arguments,
|
||||||
|
@JsonProperty("_meta") Map<String, Object> meta) implements Request {
|
||||||
|
|
||||||
|
public GetPromptRequest(String name, Map<String, Object> arguments) {
|
||||||
|
this(name, arguments, null);
|
||||||
|
}
|
||||||
}// @formatter:off
|
}// @formatter:off
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -894,10 +918,14 @@ public final class McpSchema {
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public record CallToolRequest(// @formatter:off
|
public record CallToolRequest(// @formatter:off
|
||||||
@JsonProperty("name") String name,
|
@JsonProperty("name") String name,
|
||||||
@JsonProperty("arguments") Map<String, Object> arguments) implements Request {
|
@JsonProperty("arguments") Map<String, Object> arguments,
|
||||||
|
@JsonProperty("_meta") Map<String, Object> meta) implements Request {
|
||||||
|
|
||||||
public CallToolRequest(String name, String jsonArguments) {
|
public CallToolRequest(String name, String jsonArguments) {
|
||||||
this(name, parseJsonArguments(jsonArguments));
|
this(name, parseJsonArguments(jsonArguments), null);
|
||||||
|
}
|
||||||
|
public CallToolRequest(String name, Map<String, Object> arguments) {
|
||||||
|
this(name, arguments, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, Object> parseJsonArguments(String jsonArguments) {
|
private static Map<String, Object> parseJsonArguments(String jsonArguments) {
|
||||||
|
|
@ -908,6 +936,49 @@ public final class McpSchema {
|
||||||
throw new IllegalArgumentException("Invalid arguments: " + jsonArguments, e);
|
throw new IllegalArgumentException("Invalid arguments: " + jsonArguments, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private String name;
|
||||||
|
private Map<String, Object> arguments;
|
||||||
|
private Map<String, Object> meta;
|
||||||
|
|
||||||
|
public Builder name(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder arguments(Map<String, Object> arguments) {
|
||||||
|
this.arguments = arguments;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder arguments(String jsonArguments) {
|
||||||
|
this.arguments = parseJsonArguments(jsonArguments);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder meta(Map<String, Object> meta) {
|
||||||
|
this.meta = meta;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder progressToken(String progressToken) {
|
||||||
|
if (this.meta == null) {
|
||||||
|
this.meta = new HashMap<>();
|
||||||
|
}
|
||||||
|
this.meta.put("progressToken", progressToken);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallToolRequest build() {
|
||||||
|
Assert.hasText(name, "name must not be empty");
|
||||||
|
return new CallToolRequest(name, arguments, meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
}// @formatter:off
|
}// @formatter:off
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1103,7 +1174,18 @@ public final class McpSchema {
|
||||||
@JsonProperty("temperature") Double temperature,
|
@JsonProperty("temperature") Double temperature,
|
||||||
@JsonProperty("maxTokens") int maxTokens,
|
@JsonProperty("maxTokens") int maxTokens,
|
||||||
@JsonProperty("stopSequences") List<String> stopSequences,
|
@JsonProperty("stopSequences") List<String> stopSequences,
|
||||||
@JsonProperty("metadata") Map<String, Object> metadata) implements Request {
|
@JsonProperty("metadata") Map<String, Object> metadata,
|
||||||
|
@JsonProperty("_meta") Map<String, Object> meta) implements Request {
|
||||||
|
|
||||||
|
|
||||||
|
// backwards compatibility constructor
|
||||||
|
public CreateMessageRequest(List<SamplingMessage> messages, ModelPreferences modelPreferences,
|
||||||
|
String systemPrompt, ContextInclusionStrategy includeContext,
|
||||||
|
Double temperature, int maxTokens, List<String> stopSequences,
|
||||||
|
Map<String, Object> metadata) {
|
||||||
|
this(messages, modelPreferences, systemPrompt, includeContext, temperature, maxTokens,
|
||||||
|
stopSequences, metadata, null);
|
||||||
|
}
|
||||||
|
|
||||||
public enum ContextInclusionStrategy {
|
public enum ContextInclusionStrategy {
|
||||||
@JsonProperty("none") NONE,
|
@JsonProperty("none") NONE,
|
||||||
|
|
@ -1124,6 +1206,7 @@ public final class McpSchema {
|
||||||
private int maxTokens;
|
private int maxTokens;
|
||||||
private List<String> stopSequences;
|
private List<String> stopSequences;
|
||||||
private Map<String, Object> metadata;
|
private Map<String, Object> metadata;
|
||||||
|
private Map<String, Object> meta;
|
||||||
|
|
||||||
public Builder messages(List<SamplingMessage> messages) {
|
public Builder messages(List<SamplingMessage> messages) {
|
||||||
this.messages = messages;
|
this.messages = messages;
|
||||||
|
|
@ -1165,9 +1248,22 @@ public final class McpSchema {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder meta(Map<String, Object> meta) {
|
||||||
|
this.meta = meta;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder progressToken(String progressToken) {
|
||||||
|
if (this.meta == null) {
|
||||||
|
this.meta = new HashMap<>();
|
||||||
|
}
|
||||||
|
this.meta.put("progressToken", progressToken);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CreateMessageRequest build() {
|
public CreateMessageRequest build() {
|
||||||
return new CreateMessageRequest(messages, modelPreferences, systemPrompt,
|
return new CreateMessageRequest(messages, modelPreferences, systemPrompt,
|
||||||
includeContext, temperature, maxTokens, stopSequences, metadata);
|
includeContext, temperature, maxTokens, stopSequences, metadata, meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}// @formatter:on
|
}// @formatter:on
|
||||||
|
|
@ -1253,7 +1349,13 @@ public final class McpSchema {
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public record ElicitRequest(// @formatter:off
|
public record ElicitRequest(// @formatter:off
|
||||||
@JsonProperty("message") String message,
|
@JsonProperty("message") String message,
|
||||||
@JsonProperty("requestedSchema") Map<String, Object> requestedSchema) implements Request {
|
@JsonProperty("requestedSchema") Map<String, Object> requestedSchema,
|
||||||
|
@JsonProperty("_meta") Map<String, Object> meta) implements Request {
|
||||||
|
|
||||||
|
// backwards compatibility constructor
|
||||||
|
public ElicitRequest(String message, Map<String, Object> requestedSchema) {
|
||||||
|
this(message, requestedSchema, null);
|
||||||
|
}
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
return new Builder();
|
return new Builder();
|
||||||
|
|
@ -1262,6 +1364,7 @@ public final class McpSchema {
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private String message;
|
private String message;
|
||||||
private Map<String, Object> requestedSchema;
|
private Map<String, Object> requestedSchema;
|
||||||
|
private Map<String, Object> meta;
|
||||||
|
|
||||||
public Builder message(String message) {
|
public Builder message(String message) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
|
@ -1273,8 +1376,21 @@ public final class McpSchema {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder meta(Map<String, Object> meta) {
|
||||||
|
this.meta = meta;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder progressToken(String progressToken) {
|
||||||
|
if (this.meta == null) {
|
||||||
|
this.meta = new HashMap<>();
|
||||||
|
}
|
||||||
|
this.meta.put("progressToken", progressToken);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ElicitRequest build() {
|
public ElicitRequest build() {
|
||||||
return new ElicitRequest(message, requestedSchema);
|
return new ElicitRequest(message, requestedSchema, meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}// @formatter:on
|
}// @formatter:on
|
||||||
|
|
@ -1320,7 +1436,20 @@ public final class McpSchema {
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public record PaginatedRequest(@JsonProperty("cursor") String cursor) {
|
public record PaginatedRequest(// @formatter:off
|
||||||
|
@JsonProperty("cursor") String cursor,
|
||||||
|
@JsonProperty("_meta") Map<String, Object> meta) implements Request { // @formatter:on
|
||||||
|
|
||||||
|
public PaginatedRequest(String cursor) {
|
||||||
|
this(cursor, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new paginated request with an empty cursor.
|
||||||
|
*/
|
||||||
|
public PaginatedRequest() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||||
|
|
@ -1331,11 +1460,24 @@ public final class McpSchema {
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// Progress and Logging
|
// Progress and Logging
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
|
/**
|
||||||
|
* The Model Context Protocol (MCP) supports optional progress tracking for
|
||||||
|
* long-running operations through notification messages. Either side can send
|
||||||
|
* progress notifications to provide updates about operation status.
|
||||||
|
*
|
||||||
|
* @param progressToken A unique token to identify the progress notification. MUST be
|
||||||
|
* unique across all active requests.
|
||||||
|
* @param progress A value indicating the current progress.
|
||||||
|
* @param total An optional total amount of work to be done, if known.
|
||||||
|
* @param message An optional message providing additional context about the progress.
|
||||||
|
*/
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_ABSENT)
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public record ProgressNotification(// @formatter:off
|
public record ProgressNotification(// @formatter:off
|
||||||
@JsonProperty("progressToken") String progressToken,
|
@JsonProperty("progressToken") String progressToken,
|
||||||
@JsonProperty("progress") double progress,
|
@JsonProperty("progress") Double progress,
|
||||||
@JsonProperty("total") Double total) {
|
@JsonProperty("total") Double total,
|
||||||
|
@JsonProperty("message") String message) {
|
||||||
}// @formatter:on
|
}// @formatter:on
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1469,7 +1611,12 @@ public final class McpSchema {
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public record CompleteRequest(// @formatter:off
|
public record CompleteRequest(// @formatter:off
|
||||||
@JsonProperty("ref") McpSchema.CompleteReference ref,
|
@JsonProperty("ref") McpSchema.CompleteReference ref,
|
||||||
@JsonProperty("argument") CompleteArgument argument) implements Request {
|
@JsonProperty("argument") CompleteArgument argument,
|
||||||
|
@JsonProperty("_meta") Map<String, Object> meta) implements Request {
|
||||||
|
|
||||||
|
public CompleteRequest(McpSchema.CompleteReference ref, CompleteArgument argument) {
|
||||||
|
this(ref, argument, null);
|
||||||
|
}
|
||||||
|
|
||||||
public record CompleteArgument(
|
public record CompleteArgument(
|
||||||
@JsonProperty("name") String name,
|
@JsonProperty("name") String name,
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,23 @@
|
||||||
*/
|
*/
|
||||||
package io.modelcontextprotocol.spec;
|
package io.modelcontextprotocol.spec;
|
||||||
|
|
||||||
|
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
|
||||||
|
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
|
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;
|
||||||
|
|
||||||
import io.modelcontextprotocol.spec.McpSchema.TextResourceContents;
|
import io.modelcontextprotocol.spec.McpSchema.TextResourceContents;
|
||||||
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
|
|
||||||
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json;
|
|
||||||
import net.javacrumbs.jsonunit.core.Option;
|
import net.javacrumbs.jsonunit.core.Option;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -106,6 +107,43 @@ public class McpSchemaTests {
|
||||||
assertThat(audioContent.mimeType()).isEqualTo("audio/wav");
|
assertThat(audioContent.mimeType()).isEqualTo("audio/wav");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
Map<String, Object> metadata = new HashMap<>();
|
||||||
|
metadata.put("session", "test-session");
|
||||||
|
|
||||||
|
Map<String, Object> meta = new HashMap<>();
|
||||||
|
meta.put("progressToken", "create-message-token-456");
|
||||||
|
|
||||||
|
McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder()
|
||||||
|
.messages(Collections.singletonList(message))
|
||||||
|
.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)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.containsEntry("_meta", Map.of("progressToken", "create-message-token-456"));
|
||||||
|
|
||||||
|
// Test Request interface methods
|
||||||
|
assertThat(request.meta()).isEqualTo(meta);
|
||||||
|
assertThat(request.progressToken()).isEqualTo("create-message-token-456");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testEmbeddedResource() throws Exception {
|
void testEmbeddedResource() throws Exception {
|
||||||
McpSchema.TextResourceContents resourceContents = new McpSchema.TextResourceContents("resource://test",
|
McpSchema.TextResourceContents resourceContents = new McpSchema.TextResourceContents("resource://test",
|
||||||
|
|
@ -444,6 +482,36 @@ public class McpSchemaTests {
|
||||||
{"uri":"resource://test"}"""));
|
{"uri":"resource://test"}"""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testReadResourceRequestWithMeta() throws Exception {
|
||||||
|
Map<String, Object> meta = new HashMap<>();
|
||||||
|
meta.put("progressToken", "read-resource-token-123");
|
||||||
|
|
||||||
|
McpSchema.ReadResourceRequest request = new McpSchema.ReadResourceRequest("resource://test", meta);
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(json("""
|
||||||
|
{"uri":"resource://test","_meta":{"progressToken":"read-resource-token-123"}}"""));
|
||||||
|
|
||||||
|
// Test Request interface methods
|
||||||
|
assertThat(request.meta()).isEqualTo(meta);
|
||||||
|
assertThat(request.progressToken()).isEqualTo("read-resource-token-123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testReadResourceRequestDeserialization() throws Exception {
|
||||||
|
McpSchema.ReadResourceRequest request = mapper.readValue("""
|
||||||
|
{"uri":"resource://test","_meta":{"progressToken":"test-token"}}""",
|
||||||
|
McpSchema.ReadResourceRequest.class);
|
||||||
|
|
||||||
|
assertThat(request.uri()).isEqualTo("resource://test");
|
||||||
|
assertThat(request.meta()).containsEntry("progressToken", "test-token");
|
||||||
|
assertThat(request.progressToken()).isEqualTo("test-token");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testReadResourceResult() throws Exception {
|
void testReadResourceResult() throws Exception {
|
||||||
McpSchema.TextResourceContents contents1 = new McpSchema.TextResourceContents("resource://test1", "text/plain",
|
McpSchema.TextResourceContents contents1 = new McpSchema.TextResourceContents("resource://test1", "text/plain",
|
||||||
|
|
@ -529,6 +597,30 @@ public class McpSchemaTests {
|
||||||
.isEqualTo(request);
|
.isEqualTo(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGetPromptRequestWithMeta() throws Exception {
|
||||||
|
Map<String, Object> arguments = new HashMap<>();
|
||||||
|
arguments.put("arg1", "value1");
|
||||||
|
arguments.put("arg2", 42);
|
||||||
|
|
||||||
|
Map<String, Object> meta = new HashMap<>();
|
||||||
|
meta.put("progressToken", "token123");
|
||||||
|
|
||||||
|
McpSchema.GetPromptRequest request = new McpSchema.GetPromptRequest("test-prompt", arguments, meta);
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(
|
||||||
|
json("""
|
||||||
|
{"name":"test-prompt","arguments":{"arg1":"value1","arg2":42},"_meta":{"progressToken":"token123"}}"""));
|
||||||
|
|
||||||
|
// Test that it implements Request interface methods
|
||||||
|
assertThat(request.meta()).isEqualTo(meta);
|
||||||
|
assertThat(request.progressToken()).isEqualTo("token123");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testGetPromptResult() throws Exception {
|
void testGetPromptResult() throws Exception {
|
||||||
McpSchema.TextContent content1 = new McpSchema.TextContent("System message");
|
McpSchema.TextContent content1 = new McpSchema.TextContent("System message");
|
||||||
|
|
@ -775,6 +867,65 @@ public class McpSchemaTests {
|
||||||
{"name":"test-tool","arguments":{"name":"test","value":42}}"""));
|
{"name":"test-tool","arguments":{"name":"test","value":42}}"""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCallToolRequestWithMeta() throws Exception {
|
||||||
|
|
||||||
|
McpSchema.CallToolRequest request = McpSchema.CallToolRequest.builder()
|
||||||
|
.name("test-tool")
|
||||||
|
.arguments(Map.of("name", "test", "value", 42))
|
||||||
|
.progressToken("tool-progress-123")
|
||||||
|
.build();
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(
|
||||||
|
json("""
|
||||||
|
{"name":"test-tool","arguments":{"name":"test","value":42},"_meta":{"progressToken":"tool-progress-123"}}"""));
|
||||||
|
|
||||||
|
// Test that it implements Request interface methods
|
||||||
|
assertThat(request.meta()).isEqualTo(Map.of("progressToken", "tool-progress-123"));
|
||||||
|
assertThat(request.progressToken()).isEqualTo("tool-progress-123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCallToolRequestBuilderWithJsonArguments() throws Exception {
|
||||||
|
Map<String, Object> meta = new HashMap<>();
|
||||||
|
meta.put("progressToken", "json-builder-789");
|
||||||
|
|
||||||
|
McpSchema.CallToolRequest request = McpSchema.CallToolRequest.builder().name("test-tool").arguments("""
|
||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"value": 42
|
||||||
|
}
|
||||||
|
""").meta(meta).build();
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(
|
||||||
|
json("""
|
||||||
|
{"name":"test-tool","arguments":{"name":"test","value":42},"_meta":{"progressToken":"json-builder-789"}}"""));
|
||||||
|
|
||||||
|
// Test that it implements Request interface methods
|
||||||
|
assertThat(request.meta()).isEqualTo(meta);
|
||||||
|
assertThat(request.progressToken()).isEqualTo("json-builder-789");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCallToolRequestBuilderNameRequired() {
|
||||||
|
Map<String, Object> arguments = new HashMap<>();
|
||||||
|
arguments.put("name", "test");
|
||||||
|
|
||||||
|
McpSchema.CallToolRequest.Builder builder = McpSchema.CallToolRequest.builder().arguments(arguments);
|
||||||
|
|
||||||
|
assertThatThrownBy(builder::build).isInstanceOf(IllegalArgumentException.class)
|
||||||
|
.hasMessageContaining("name must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCallToolResult() throws Exception {
|
void testCallToolResult() throws Exception {
|
||||||
McpSchema.TextContent content = new McpSchema.TextContent("Tool execution result");
|
McpSchema.TextContent content = new McpSchema.TextContent("Tool execution result");
|
||||||
|
|
@ -991,6 +1142,141 @@ public class McpSchemaTests {
|
||||||
{"action":"accept","content":{"foo":"bar"}}"""));
|
{"action":"accept","content":{"foo":"bar"}}"""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testElicitRequestWithMeta() throws Exception {
|
||||||
|
Map<String, Object> requestedSchema = Map.of("type", "object", "required", List.of("name"), "properties",
|
||||||
|
Map.of("name", Map.of("type", "string")));
|
||||||
|
|
||||||
|
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)
|
||||||
|
.meta(meta)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.containsEntry("_meta", Map.of("progressToken", "elicit-token-789"));
|
||||||
|
|
||||||
|
// Test Request interface methods
|
||||||
|
assertThat(request.meta()).isEqualTo(meta);
|
||||||
|
assertThat(request.progressToken()).isEqualTo("elicit-token-789");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination Tests
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPaginatedRequestNoArgs() throws Exception {
|
||||||
|
McpSchema.PaginatedRequest request = new McpSchema.PaginatedRequest();
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(json("""
|
||||||
|
{}"""));
|
||||||
|
|
||||||
|
// Test that it implements Request interface methods
|
||||||
|
assertThat(request.meta()).isNull();
|
||||||
|
assertThat(request.progressToken()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPaginatedRequestWithCursor() throws Exception {
|
||||||
|
McpSchema.PaginatedRequest request = new McpSchema.PaginatedRequest("cursor123");
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(json("""
|
||||||
|
{"cursor":"cursor123"}"""));
|
||||||
|
|
||||||
|
// Test that it implements Request interface methods
|
||||||
|
assertThat(request.meta()).isNull();
|
||||||
|
assertThat(request.progressToken()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPaginatedRequestWithMeta() throws Exception {
|
||||||
|
Map<String, Object> meta = new HashMap<>();
|
||||||
|
meta.put("progressToken", "pagination-progress-456");
|
||||||
|
|
||||||
|
McpSchema.PaginatedRequest request = new McpSchema.PaginatedRequest("cursor123", meta);
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(json("""
|
||||||
|
{"cursor":"cursor123","_meta":{"progressToken":"pagination-progress-456"}}"""));
|
||||||
|
|
||||||
|
// Test that it implements Request interface methods
|
||||||
|
assertThat(request.meta()).isEqualTo(meta);
|
||||||
|
assertThat(request.progressToken()).isEqualTo("pagination-progress-456");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPaginatedRequestDeserialization() throws Exception {
|
||||||
|
McpSchema.PaginatedRequest request = mapper.readValue("""
|
||||||
|
{"cursor":"test-cursor","_meta":{"progressToken":"test-token"}}""", McpSchema.PaginatedRequest.class);
|
||||||
|
|
||||||
|
assertThat(request.cursor()).isEqualTo("test-cursor");
|
||||||
|
assertThat(request.meta()).containsEntry("progressToken", "test-token");
|
||||||
|
assertThat(request.progressToken()).isEqualTo("test-token");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete Request Tests
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompleteRequest() throws Exception {
|
||||||
|
McpSchema.PromptReference promptRef = new McpSchema.PromptReference("test-prompt");
|
||||||
|
McpSchema.CompleteRequest.CompleteArgument argument = new McpSchema.CompleteRequest.CompleteArgument("arg1",
|
||||||
|
"partial-value");
|
||||||
|
|
||||||
|
McpSchema.CompleteRequest request = new McpSchema.CompleteRequest(promptRef, argument);
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(
|
||||||
|
json("""
|
||||||
|
{"ref":{"type":"ref/prompt","name":"test-prompt"},"argument":{"name":"arg1","value":"partial-value"}}"""));
|
||||||
|
|
||||||
|
// Test that it implements Request interface methods
|
||||||
|
assertThat(request.meta()).isNull();
|
||||||
|
assertThat(request.progressToken()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompleteRequestWithMeta() throws Exception {
|
||||||
|
McpSchema.ResourceReference resourceRef = new McpSchema.ResourceReference("file:///test.txt");
|
||||||
|
McpSchema.CompleteRequest.CompleteArgument argument = new McpSchema.CompleteRequest.CompleteArgument("path",
|
||||||
|
"/partial/path");
|
||||||
|
|
||||||
|
Map<String, Object> meta = new HashMap<>();
|
||||||
|
meta.put("progressToken", "complete-progress-789");
|
||||||
|
|
||||||
|
McpSchema.CompleteRequest request = new McpSchema.CompleteRequest(resourceRef, argument, meta);
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(request);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(
|
||||||
|
json("""
|
||||||
|
{"ref":{"type":"ref/resource","uri":"file:///test.txt"},"argument":{"name":"path","value":"/partial/path"},"_meta":{"progressToken":"complete-progress-789"}}"""));
|
||||||
|
|
||||||
|
// Test that it implements Request interface methods
|
||||||
|
assertThat(request.meta()).isEqualTo(meta);
|
||||||
|
assertThat(request.progressToken()).isEqualTo("complete-progress-789");
|
||||||
|
}
|
||||||
|
|
||||||
// Roots Tests
|
// Roots Tests
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -1024,4 +1310,45 @@ public class McpSchemaTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Progress Notification Tests
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProgressNotificationWithMessage() throws Exception {
|
||||||
|
McpSchema.ProgressNotification notification = new McpSchema.ProgressNotification("progress-token-123", 0.5, 1.0,
|
||||||
|
"Processing file 1 of 2");
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(notification);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(
|
||||||
|
json("""
|
||||||
|
{"progressToken":"progress-token-123","progress":0.5,"total":1.0,"message":"Processing file 1 of 2"}"""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProgressNotificationDeserialization() throws Exception {
|
||||||
|
McpSchema.ProgressNotification notification = mapper.readValue("""
|
||||||
|
{"progressToken":"token-456","progress":0.75,"total":1.0,"message":"Almost done"}""",
|
||||||
|
McpSchema.ProgressNotification.class);
|
||||||
|
|
||||||
|
assertThat(notification.progressToken()).isEqualTo("token-456");
|
||||||
|
assertThat(notification.progress()).isEqualTo(0.75);
|
||||||
|
assertThat(notification.total()).isEqualTo(1.0);
|
||||||
|
assertThat(notification.message()).isEqualTo("Almost done");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProgressNotificationWithoutMessage() throws Exception {
|
||||||
|
McpSchema.ProgressNotification notification = new McpSchema.ProgressNotification("progress-token-789", 0.25,
|
||||||
|
null, null);
|
||||||
|
|
||||||
|
String value = mapper.writeValueAsString(notification);
|
||||||
|
assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER)
|
||||||
|
.when(Option.IGNORING_EXTRA_ARRAY_ITEMS)
|
||||||
|
.isObject()
|
||||||
|
.isEqualTo(json("""
|
||||||
|
{"progressToken":"progress-token-789","progress":0.25}"""));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue