diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClientFeatures.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClientFeatures.java index 127d53337..696456b9b 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClientFeatures.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClientFeatures.java @@ -41,7 +41,7 @@ * through the {@link Async#fromSync} method, which ensures proper handling of blocking * operations in non-blocking contexts by scheduling them on a bounded elastic scheduler. * - * @author Dariusz Jędrzejczyk + * @author Dariusz Jędrzejczyk, Pin He * @see McpClient * @see McpSchema.Implementation * @see McpSchema.ClientCapabilities @@ -56,6 +56,7 @@ class McpClientFeatures { * @param clientCapabilities the client capabilities. * @param roots the roots. * @param toolsChangeConsumers the tools change consumers. + * @param taskStatusConsumers the task status consumers. * @param resourcesChangeConsumers the resources change consumers. * @param promptsChangeConsumers the prompts change consumers. * @param loggingConsumers the logging consumers. @@ -64,22 +65,27 @@ class McpClientFeatures { * @param elicitationHandler the elicitation handler. * @param enableCallToolSchemaCaching whether to enable call tool schema caching. */ - record Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, List, Mono>> toolsChangeConsumers, - List, Mono>> resourcesChangeConsumers, - List, Mono>> resourcesUpdateConsumers, - List, Mono>> promptsChangeConsumers, - List>> loggingConsumers, - List>> progressConsumers, - Function> samplingHandler, - Function> elicitationHandler, - boolean enableCallToolSchemaCaching) { + record Async(// @formatter:off + McpSchema.Implementation clientInfo, + McpSchema.ClientCapabilities clientCapabilities, + Map roots, + List, Mono>> toolsChangeConsumers, + List>> taskStatusConsumers, + List, Mono>> resourcesChangeConsumers, + List, Mono>> resourcesUpdateConsumers, + List, Mono>> promptsChangeConsumers, + List>> loggingConsumers, + List>> progressConsumers, + Function> samplingHandler, + Function> elicitationHandler, + boolean enableCallToolSchemaCaching) { // @formatter:on /** * Create an instance and validate the arguments. * @param clientCapabilities the client capabilities. * @param roots the roots. * @param toolsChangeConsumers the tools change consumers. + * @param taskStatusConsumers the task status consumers. * @param resourcesChangeConsumers the resources change consumers. * @param promptsChangeConsumers the prompts change consumers. * @param loggingConsumers the logging consumers. @@ -88,17 +94,20 @@ record Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities c * @param elicitationHandler the elicitation handler. * @param enableCallToolSchemaCaching whether to enable call tool schema caching. */ - public Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, - List, Mono>> toolsChangeConsumers, - List, Mono>> resourcesChangeConsumers, - List, Mono>> resourcesUpdateConsumers, - List, Mono>> promptsChangeConsumers, - List>> loggingConsumers, - List>> progressConsumers, - Function> samplingHandler, - Function> elicitationHandler, - boolean enableCallToolSchemaCaching) { + public Async( // @formatter:off + McpSchema.Implementation clientInfo, + McpSchema.ClientCapabilities clientCapabilities, + Map roots, + List, Mono>> toolsChangeConsumers, + List>> taskStatusConsumers, + List, Mono>> resourcesChangeConsumers, + List, Mono>> resourcesUpdateConsumers, + List, Mono>> promptsChangeConsumers, + List>> loggingConsumers, + List>> progressConsumers, + Function> samplingHandler, + Function> elicitationHandler, + boolean enableCallToolSchemaCaching) { // @formatter:on Assert.notNull(clientInfo, "Client info must not be null"); this.clientInfo = clientInfo; @@ -106,11 +115,17 @@ public Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities c : new McpSchema.ClientCapabilities(null, !Utils.isEmpty(roots) ? new McpSchema.ClientCapabilities.RootCapabilities(false) : null, samplingHandler != null ? new McpSchema.ClientCapabilities.Sampling() : null, - elicitationHandler != null ? new McpSchema.ClientCapabilities.Elicitation() : null); + elicitationHandler != null ? new McpSchema.ClientCapabilities.Elicitation() : null, null // TODO + // task + // management + // capabilities + ); this.roots = roots != null ? new ConcurrentHashMap<>(roots) : new ConcurrentHashMap<>(); this.toolsChangeConsumers = toolsChangeConsumers != null ? toolsChangeConsumers : List.of(); - this.resourcesChangeConsumers = resourcesChangeConsumers != null ? resourcesChangeConsumers : List.of(); + this.taskStatusConsumers = taskStatusConsumers != null ? taskStatusConsumers : List.of(); + this.resourcesChangeConsumers = resourcesChangeConsumers != null ? resourcesChangeConsumers + : java.util.List.of(); this.resourcesUpdateConsumers = resourcesUpdateConsumers != null ? resourcesUpdateConsumers : List.of(); this.promptsChangeConsumers = promptsChangeConsumers != null ? promptsChangeConsumers : List.of(); this.loggingConsumers = loggingConsumers != null ? loggingConsumers : List.of(); @@ -123,15 +138,38 @@ public Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities c /** * @deprecated Only exists for backwards-compatibility purposes. */ - public Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, - List, Mono>> toolsChangeConsumers, - List, Mono>> resourcesChangeConsumers, - List, Mono>> resourcesUpdateConsumers, - List, Mono>> promptsChangeConsumers, - List>> loggingConsumers, - Function> samplingHandler, - Function> elicitationHandler) { + public Async( // @formatter:off + McpSchema.Implementation clientInfo, + McpSchema.ClientCapabilities clientCapabilities, + Map roots, + List, Mono>> toolsChangeConsumers, + List, Mono>> resourcesChangeConsumers, + List, Mono>> resourcesUpdateConsumers, + List, Mono>> promptsChangeConsumers, + List>> loggingConsumers, + List>> progressConsumers, + Function> samplingHandler, + Function> elicitationHandler, + boolean enableCallToolSchemaCaching) { // @formatter:on + this(clientInfo, clientCapabilities, roots, toolsChangeConsumers, List.of(), resourcesChangeConsumers, + resourcesUpdateConsumers, promptsChangeConsumers, loggingConsumers, progressConsumers, + samplingHandler, elicitationHandler, enableCallToolSchemaCaching); + } + + /** + * @deprecated Only exists for backwards-compatibility purposes. + */ + public Async( // @formatter:off + McpSchema.Implementation clientInfo, + McpSchema.ClientCapabilities clientCapabilities, + Map roots, + List, Mono>> toolsChangeConsumers, + List, Mono>> resourcesChangeConsumers, + List, Mono>> resourcesUpdateConsumers, + List, Mono>> promptsChangeConsumers, + List>> loggingConsumers, + Function> samplingHandler, + Function> elicitationHandler) { // @formatter:on this(clientInfo, clientCapabilities, roots, toolsChangeConsumers, resourcesChangeConsumers, resourcesUpdateConsumers, promptsChangeConsumers, loggingConsumers, List.of(), samplingHandler, elicitationHandler, false); @@ -152,6 +190,12 @@ public static Async fromSync(Sync syncSpec) { .subscribeOn(Schedulers.boundedElastic())); } + List>> taskStatusConsumers = new ArrayList<>(); + for (Function> consumer : syncSpec.taskStatusConsumers()) { + taskStatusConsumers.add(t -> Mono.fromRunnable(() -> consumer.apply(t).block()) + .subscribeOn(Schedulers.boundedElastic())); + } + List, Mono>> resourcesChangeConsumers = new ArrayList<>(); for (Consumer> consumer : syncSpec.resourcesChangeConsumers()) { resourcesChangeConsumers.add(r -> Mono.fromRunnable(() -> consumer.accept(r)) @@ -191,8 +235,8 @@ public static Async fromSync(Sync syncSpec) { .subscribeOn(Schedulers.boundedElastic()); return new Async(syncSpec.clientInfo(), syncSpec.clientCapabilities(), syncSpec.roots(), - toolsChangeConsumers, resourcesChangeConsumers, resourcesUpdateConsumers, promptsChangeConsumers, - loggingConsumers, progressConsumers, samplingHandler, elicitationHandler, + toolsChangeConsumers, taskStatusConsumers, resourcesChangeConsumers, resourcesUpdateConsumers, + promptsChangeConsumers, loggingConsumers, progressConsumers, samplingHandler, elicitationHandler, syncSpec.enableCallToolSchemaCaching); } } @@ -205,6 +249,7 @@ public static Async fromSync(Sync syncSpec) { * @param clientCapabilities the client capabilities. * @param roots the roots. * @param toolsChangeConsumers the tools change consumers. + * @param taskStatusConsumers the task status consumers. * @param resourcesChangeConsumers the resources change consumers. * @param promptsChangeConsumers the prompts change consumers. * @param loggingConsumers the logging consumers. @@ -213,16 +258,19 @@ public static Async fromSync(Sync syncSpec) { * @param elicitationHandler the elicitation handler. * @param enableCallToolSchemaCaching whether to enable call tool schema caching. */ - public record Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, List>> toolsChangeConsumers, - List>> resourcesChangeConsumers, - List>> resourcesUpdateConsumers, - List>> promptsChangeConsumers, - List> loggingConsumers, - List> progressConsumers, - Function samplingHandler, - Function elicitationHandler, - boolean enableCallToolSchemaCaching) { + public record Sync( // @formatter:off + McpSchema.Implementation clientInfo, + McpSchema.ClientCapabilities clientCapabilities, + Map roots, List>> toolsChangeConsumers, + List>> taskStatusConsumers, + List>> resourcesChangeConsumers, + List>> resourcesUpdateConsumers, + List>> promptsChangeConsumers, + List> loggingConsumers, + List> progressConsumers, + Function samplingHandler, + Function elicitationHandler, + boolean enableCallToolSchemaCaching) { // @formatter:on /** * Create an instance and validate the arguments. @@ -230,6 +278,7 @@ public record Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabili * @param clientCapabilities the client capabilities. * @param roots the roots. * @param toolsChangeConsumers the tools change consumers. + * @param taskStatusConsumers the task status consumers. * @param resourcesChangeConsumers the resources change consumers. * @param resourcesUpdateConsumers the resource update consumers. * @param promptsChangeConsumers the prompts change consumers. @@ -239,16 +288,19 @@ public record Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabili * @param elicitationHandler the elicitation handler. * @param enableCallToolSchemaCaching whether to enable call tool schema caching. */ - public Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, List>> toolsChangeConsumers, - List>> resourcesChangeConsumers, - List>> resourcesUpdateConsumers, - List>> promptsChangeConsumers, - List> loggingConsumers, - List> progressConsumers, - Function samplingHandler, - Function elicitationHandler, - boolean enableCallToolSchemaCaching) { + public Sync( // @formatter:off + McpSchema.Implementation clientInfo, + McpSchema.ClientCapabilities clientCapabilities, + Map roots, List>> toolsChangeConsumers, + List>> taskStatusConsumers, + List>> resourcesChangeConsumers, + List>> resourcesUpdateConsumers, + List>> promptsChangeConsumers, + List> loggingConsumers, + List> progressConsumers, + Function samplingHandler, + Function elicitationHandler, + boolean enableCallToolSchemaCaching) { // @formatter:on Assert.notNull(clientInfo, "Client info must not be null"); this.clientInfo = clientInfo; @@ -256,10 +308,16 @@ public Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities cl : new McpSchema.ClientCapabilities(null, !Utils.isEmpty(roots) ? new McpSchema.ClientCapabilities.RootCapabilities(false) : null, samplingHandler != null ? new McpSchema.ClientCapabilities.Sampling() : null, - elicitationHandler != null ? new McpSchema.ClientCapabilities.Elicitation() : null); + elicitationHandler != null ? new McpSchema.ClientCapabilities.Elicitation() : null, null + // TODO + // task + // management + // capabilities + ); this.roots = roots != null ? new HashMap<>(roots) : new HashMap<>(); this.toolsChangeConsumers = toolsChangeConsumers != null ? toolsChangeConsumers : List.of(); + this.taskStatusConsumers = taskStatusConsumers != null ? taskStatusConsumers : List.of(); this.resourcesChangeConsumers = resourcesChangeConsumers != null ? resourcesChangeConsumers : List.of(); this.resourcesUpdateConsumers = resourcesUpdateConsumers != null ? resourcesUpdateConsumers : List.of(); this.promptsChangeConsumers = promptsChangeConsumers != null ? promptsChangeConsumers : List.of(); @@ -273,15 +331,37 @@ public Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities cl /** * @deprecated Only exists for backwards-compatibility purposes. */ - public Sync(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities clientCapabilities, - Map roots, List>> toolsChangeConsumers, - List>> resourcesChangeConsumers, - List>> resourcesUpdateConsumers, - List>> promptsChangeConsumers, - List> loggingConsumers, - Function samplingHandler, - Function elicitationHandler) { - this(clientInfo, clientCapabilities, roots, toolsChangeConsumers, resourcesChangeConsumers, + public Sync( // @formatter:off + McpSchema.Implementation clientInfo, + McpSchema.ClientCapabilities clientCapabilities, + Map roots, List>> toolsChangeConsumers, + List>> resourcesChangeConsumers, + List>> resourcesUpdateConsumers, + List>> promptsChangeConsumers, + List> loggingConsumers, + List> progressConsumers, + Function samplingHandler, + Function elicitationHandler, + boolean enableCallToolSchemaCaching) { // @formatter:on + this(clientInfo, clientCapabilities, roots, toolsChangeConsumers, List.of(), resourcesChangeConsumers, + resourcesUpdateConsumers, promptsChangeConsumers, loggingConsumers, progressConsumers, + samplingHandler, elicitationHandler, enableCallToolSchemaCaching); + } + + /** + * @deprecated Only exists for backwards-compatibility purposes. + */ + public Sync( // @formatter:off + McpSchema.Implementation clientInfo, + McpSchema.ClientCapabilities clientCapabilities, + Map roots, List>> toolsChangeConsumers, + List>> resourcesChangeConsumers, + List>> resourcesUpdateConsumers, + List>> promptsChangeConsumers, + List> loggingConsumers, + Function samplingHandler, + Function elicitationHandler) { // @formatter:on + this(clientInfo, clientCapabilities, roots, toolsChangeConsumers, List.of(), resourcesChangeConsumers, resourcesUpdateConsumers, promptsChangeConsumers, loggingConsumers, List.of(), samplingHandler, elicitationHandler, false); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java index fe0608b1c..56963605d 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpServerFeatures.java @@ -80,7 +80,9 @@ record Async(McpSchema.Implementation serverInfo, McpSchema.ServerCapabilities s !Utils.isEmpty(prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null, !Utils.isEmpty(resources) ? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false) : null, - !Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null); + !Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null, + null // TODO Task management + ); this.tools = (tools != null) ? tools : List.of(); this.resources = (resources != null) ? resources : Map.of(); @@ -195,7 +197,9 @@ record Sync(McpSchema.Implementation serverInfo, McpSchema.ServerCapabilities se !Utils.isEmpty(prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null, !Utils.isEmpty(resources) ? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false) : null, - !Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null); + !Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null, + null // TODO Task management + ); this.tools = (tools != null) ? tools : new ArrayList<>(); this.resources = (resources != null) ? resources : new HashMap<>(); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerFeatures.java b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerFeatures.java index a15681ba5..c1aeb1aa1 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerFeatures.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessServerFeatures.java @@ -74,7 +74,9 @@ record Async(McpSchema.Implementation serverInfo, McpSchema.ServerCapabilities s !Utils.isEmpty(prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null, !Utils.isEmpty(resources) ? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false) : null, - !Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null); + !Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null, + null // TODO task management + ); this.tools = (tools != null) ? tools : List.of(); this.resources = (resources != null) ? resources : Map.of(); @@ -175,7 +177,9 @@ record Sync(McpSchema.Implementation serverInfo, McpSchema.ServerCapabilities se !Utils.isEmpty(prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null, !Utils.isEmpty(resources) ? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false) : null, - !Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null); + !Utils.isEmpty(tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null, + null // TODO task management + ); this.tools = (tools != null) ? tools : new ArrayList<>(); this.resources = (resources != null) ? resources : new HashMap<>(); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpMessage.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpMessage.java new file mode 100644 index 000000000..48d05c754 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpMessage.java @@ -0,0 +1,40 @@ +package io.modelcontextprotocol.spec; + +import java.util.Map; +import java.util.Optional; + +/** + * An MCP message base class for message driven architecture. + * + */ +public sealed interface McpMessage { + + /** + * @return additional metadata related to this resource. + * @see Specification + * for notes on _meta usage + */ + Map meta(); + + sealed interface McpEvent extends McpMessage { + + record TaskCreated(McpSchema.Task task, Map meta) implements McpEvent { + } + + record TaskStatusUpdated(McpSchema.Task task, Map meta) implements McpEvent { + } + + record TaskProgressUpdated(String taskId, Object progressToken, Map meta) implements McpEvent { + } + + record TaskResultRetrieved(Optional taskId, McpSchema.CallToolResult result, + Map meta) implements McpEvent { + } + + record TaskFailed(Optional taskId, McpError error, Map meta) implements McpEvent { + } + + } + +} diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index 734cff237..8d390b6cd 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -33,6 +33,7 @@ * @author Luca Chang * @author Surbhi Bansal * @author Anurag Pant + * @author Pin He */ public final class McpSchema { @@ -68,6 +69,17 @@ private McpSchema() { public static final String METHOD_NOTIFICATION_TOOLS_LIST_CHANGED = "notifications/tools/list_changed"; + // Task Methods + public static final String METHOD_TASKS_GET = "tasks/get"; + + public static final String METHOD_TASKS_LIST = "tasks/list"; + + public static final String METHOD_TASKS_RESULT = "tasks/result"; + + public static final String METHOD_TASKS_CANCEL = "tasks/cancel"; + + public static final String METHOD_NOTIFICATION_TASKS_STATUS = "notifications/tasks/status"; + // Resources Methods public static final String METHOD_RESOURCES_LIST = "resources/list"; @@ -111,6 +123,7 @@ private McpSchema() { // --------------------------- // JSON-RPC Error Codes // --------------------------- + /** * Standard error codes used in MCP JSON-RPC responses. */ @@ -163,9 +176,26 @@ public interface Meta { } - public sealed interface Request extends Meta - permits InitializeRequest, CallToolRequest, CreateMessageRequest, ElicitRequest, CompleteRequest, - GetPromptRequest, ReadResourceRequest, SubscribeRequest, UnsubscribeRequest, PaginatedRequest { + public sealed interface TaskAugmentedRequest extends Request + permits CallToolRequest, CreateMessageRequest, ElicitRequest { + + /** + * If specified, the caller is requesting task-augmented execution for this + * request. The request will return a CreateTaskResult immediately, and the actual + * result can be retrieved later via tasks/result. + *

+ * Task augmentation is subject to capability negotiation - receivers MUST declare + * support for task augmentation of specific request types in their capabilities. + */ + default TaskMetaData task() { + return null; + } + + } + + public sealed interface Request extends Meta permits CancelTaskRequest, CompleteRequest, GetPromptRequest, + GetTaskRequest, InitializeRequest, PaginatedRequest, ReadResourceRequest, SubscribeRequest, + TaskAugmentedRequest, UnsubscribeRequest, getTaskPayloadRequest { default Object progressToken() { if (meta() != null && meta().containsKey("progressToken")) { @@ -176,14 +206,15 @@ default Object progressToken() { } - public sealed interface Result extends Meta permits InitializeResult, ListResourcesResult, - ListResourceTemplatesResult, ReadResourceResult, ListPromptsResult, GetPromptResult, ListToolsResult, - CallToolResult, CreateMessageResult, ElicitResult, CompleteResult, ListRootsResult { + public sealed interface Result extends Meta permits CallToolAuxResult, CallToolResult, CancelTaskResult, + CompleteResult, CreateMessageResult, CreateTaskResult, ElicitResult, GetPromptResult, GetTaskResult, + InitializeResult, ListPromptsResult, ListResourceTemplatesResult, ListResourcesResult, ListRootsResult, + ListTasksResult, ListToolsResult, ReadResourceResult { } - public sealed interface Notification extends Meta - permits ProgressNotification, LoggingMessageNotification, ResourcesUpdatedNotification { + public sealed interface Notification extends Meta permits LoggingMessageNotification, ProgressNotification, + ResourcesUpdatedNotification, TaskStatusNotification { } @@ -328,7 +359,7 @@ public record JSONRPCError( // @formatter:off @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record InitializeRequest( // @formatter:off - @JsonProperty("protocolVersion") String protocolVersion, + @JsonProperty("protocolVersion") String protocolVersion, @JsonProperty("capabilities") ClientCapabilities capabilities, @JsonProperty("clientInfo") Implementation clientInfo, @JsonProperty("_meta") Map meta) implements Request { // @formatter:on @@ -378,6 +409,7 @@ public InitializeResult(String protocolVersion, ServerCapabilities capabilities, * @param roots Present if the client supports listing roots * @param sampling Present if the client supports sampling from an LLM * @param elicitation Present if the client supports elicitation from the server + * @param tasks Present if the client supports tasks operations */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -385,7 +417,8 @@ public record ClientCapabilities( // @formatter:off @JsonProperty("experimental") Map experimental, @JsonProperty("roots") RootCapabilities roots, @JsonProperty("sampling") Sampling sampling, - @JsonProperty("elicitation") Elicitation elicitation) { // @formatter:on + @JsonProperty("elicitation") Elicitation elicitation, + @JsonProperty("tasks") ClientCapabilities.TaskCapabilities tasks) { // @formatter:on /** * Present if the client supports listing roots. @@ -421,6 +454,57 @@ public record Sampling() { public record Elicitation() { } + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record TaskCapabilities( // @formatter:off + @JsonProperty("list") TaskCapabilities.List list, + @JsonProperty("cancel") TaskCapabilities.Cancel cancel, + @JsonProperty("requests") TaskCapabilities.Requests requests) {// @formatter:on + + /** + * Client supports the tasks/list operation + */ + public record List() { + } + + /** + * Client supports the tasks/cancel operation + */ + public record Cancel() { + } + + /** + * Client supports task-augmented requests + */ + public record Requests( // @formatter:off + @JsonProperty("sampling") Requests.Sampling sampling, + @JsonProperty("elicitation") Requests.Elicitation elicitation) { // @formatter:on + + /** + * Client supports task-augmented sampling requests + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record Sampling(@JsonProperty("createMessage") Sampling.CreateMessage createMessage) { + /** + * Client supports task-augmented sampling/createMessage requests + */ + public record CreateMessage() { + } + } + + /** + * Client supports task-augmented elicitation requests + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record Elicitation(@JsonProperty("elicitation") Elicitation.Create create) { + /** + * Client supports task-augmented elicitation/create requests + */ + public record Create() { + } + } + } + } + public static Builder builder() { return new Builder(); } @@ -435,6 +519,8 @@ public static class Builder { private Elicitation elicitation; + private ClientCapabilities.TaskCapabilities tasks; + public Builder experimental(Map experimental) { this.experimental = experimental; return this; @@ -455,8 +541,13 @@ public Builder elicitation() { return this; } + public Builder tasks(ClientCapabilities.TaskCapabilities tasks) { + this.tasks = tasks; + return this; + } + public ClientCapabilities build() { - return new ClientCapabilities(experimental, roots, sampling, elicitation); + return new ClientCapabilities(experimental, roots, sampling, elicitation, tasks); } } @@ -475,6 +566,7 @@ public ClientCapabilities build() { * @param prompts Present if the server offers any prompt templates * @param resources Present if the server offers any resources to read * @param tools Present if the server offers any tools to call + * @param tasks Present if the server supports tasks operations */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -484,7 +576,8 @@ public record ServerCapabilities( // @formatter:off @JsonProperty("logging") LoggingCapabilities logging, @JsonProperty("prompts") PromptCapabilities prompts, @JsonProperty("resources") ResourceCapabilities resources, - @JsonProperty("tools") ToolCapabilities tools) { // @formatter:on + @JsonProperty("tools") ToolCapabilities tools, + @JsonProperty("tasks") ServerCapabilities.TaskCapabilities tasks) { // @formatter:on /** * Present if the server supports argument autocompletion suggestions. @@ -532,6 +625,47 @@ public record ResourceCapabilities(@JsonProperty("subscribe") Boolean subscribe, public record ToolCapabilities(@JsonProperty("listChanged") Boolean listChanged) { } + /** + * Present if the server supports task management operations. + * + * @param list Server supports the tasks/list operation + * @param cancel Server supports the tasks/cancel operation + * @param requests supports task-augmented tools/call requests + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record TaskCapabilities( // @formatter:off + @JsonProperty("list") TaskCapabilities.List list, + @JsonProperty("cancel") TaskCapabilities.Cancel cancel, + @JsonProperty("requests") TaskCapabilities.Requests requests) { // @formatter:on + + /** + * Server supports the tasks/list operation + **/ + public record List() { + } + + /** + * Server supports the tasks/cancel operation + */ + public record Cancel() { + } + + /** + * Server supports task-augmented requests + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record Requests(@JsonProperty("tools") TaskCapabilities.Requests.Tools tools) { + /** + * Present if the server supports task-augmented tools/call requests + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record Tools(@JsonProperty("call") Tools.Call call) { + public record Call() { + } + } + } + } + /** * Create a mutated copy of this object with the specified changes. * @return A new Builder instance with the same values as this object. @@ -565,6 +699,8 @@ public static class Builder { private ToolCapabilities tools; + private TaskCapabilities tasks; + public Builder completions() { this.completions = new CompletionCapabilities(); return this; @@ -595,8 +731,13 @@ public Builder tools(Boolean listChanged) { return this; } + public Builder tasks(ServerCapabilities.TaskCapabilities tasks) { + this.tasks = tasks; + return this; + } + public ServerCapabilities build() { - return new ServerCapabilities(completions, experimental, logging, prompts, resources, tools); + return new ServerCapabilities(completions, experimental, logging, prompts, resources, tools, tasks); } } @@ -616,7 +757,7 @@ public ServerCapabilities build() { public record Implementation( // @formatter:off @JsonProperty("name") String name, @JsonProperty("title") String title, - @JsonProperty("version") String version) implements Identifier { // @formatter:on + @JsonProperty("version") String version) implements Identifier { // @formatter:on public Implementation(String name, String version) { this(name, null, version); @@ -1296,13 +1437,62 @@ public record JsonSchema( // @formatter:off @JsonProperty("definitions") Map definitions) { // @formatter:on } + /** + * Execution behavior for a tool. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record ToolExecution(TaskSupport taskSupport) { + } + + /** + * Indicates whether this tool supports task-augmented execution. This allows clients + * to handle long-running operations through polling the task system. + *

+ * - "forbidden": Tool does not support task-augmented execution (default when absent) + *

+ * - "optional": Tool may support task-augmented execution + *

+ * - "required": Tool requires task-augmented execution + *

+ * Default: "forbidden" + */ + public enum TaskSupport { + + // @formatter:off + @JsonProperty("forbidden") FORBIDDEN("forbidden"), + @JsonProperty("optional") OPTIONAL("optional"), + @JsonProperty("required") REQUIRED("required"); + // @formatter:on + + private final String value; + + TaskSupport(final String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static TaskSupport fromValue(String value) { + for (TaskSupport hint : TaskSupport.values()) { + if (hint.value.equalsIgnoreCase(value)) { + return hint; + } + } + throw new IllegalArgumentException("Unknown task support value: " + value); + } + + } + /** * Additional properties describing a Tool to clients. - * + *

* NOTE: all properties in ToolAnnotations are **hints**. They are not guaranteed to * provide a faithful description of tool behavior (including descriptive properties * like `title`). - * + *

* Clients should never make tool use decisions based on ToolAnnotations received from * untrusted servers. */ @@ -1315,6 +1505,61 @@ public record ToolAnnotations( // @formatter:off @JsonProperty("idempotentHint") Boolean idempotentHint, @JsonProperty("openWorldHint") Boolean openWorldHint, @JsonProperty("returnDirect") Boolean returnDirect) { // @formatter:on + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String title; + + private boolean readOnlyHint; + + private boolean destructiveHint; + + private boolean idempotentHint; + + private boolean openWorldHint; + + private boolean returnDirect; + + public Builder title(String title) { + this.title = title; + return this; + } + + public Builder readOnlyHint(boolean readOnlyHint) { + this.readOnlyHint = readOnlyHint; + return this; + } + + public Builder destructiveHint(boolean destructiveHint) { + this.destructiveHint = destructiveHint; + return this; + } + + public Builder idempotentHint(boolean idempotentHint) { + this.idempotentHint = idempotentHint; + return this; + } + + public Builder openWorldHint(boolean openWorldHint) { + this.openWorldHint = openWorldHint; + return this; + } + + public Builder returnDirect(boolean returnDirect) { + this.returnDirect = returnDirect; + return this; + } + + public ToolAnnotations build() { + return new ToolAnnotations(title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint, + returnDirect); + } + + } } /** @@ -1329,6 +1574,7 @@ public record ToolAnnotations( // @formatter:off * used by clients to improve the LLM's understanding of available tools. * @param inputSchema A JSON Schema object that describes the expected structure of * the arguments when calling this tool. This allows clients to validate tool + * @param execution The execution behavior for the tool. * @param outputSchema An optional JSON Schema object defining the structure of the * tool's output returned in the structuredContent field of a CallToolResult. * @param annotations Optional additional tool information. @@ -1342,9 +1588,24 @@ public record Tool( // @formatter:off @JsonProperty("description") String description, @JsonProperty("inputSchema") JsonSchema inputSchema, @JsonProperty("outputSchema") Map outputSchema, + @JsonProperty("execution") ToolExecution execution, @JsonProperty("annotations") ToolAnnotations annotations, @JsonProperty("_meta") Map meta) { // @formatter:on + /** + * @deprecated keep for backwards compatibility + */ + public Tool( // @formatter:off + String name, + String title, + String description, + JsonSchema inputSchema, + Map outputSchema, + ToolAnnotations annotations, + Map meta) { // @formatter:on + this(name, title, description, inputSchema, outputSchema, null, annotations, meta); + } + public static Builder builder() { return new Builder(); } @@ -1361,6 +1622,8 @@ public static class Builder { private Map outputSchema; + private ToolExecution execution; + private ToolAnnotations annotations; private Map meta; @@ -1400,6 +1663,11 @@ public Builder outputSchema(McpJsonMapper jsonMapper, String outputSchema) { return this; } + public Builder execution(ToolExecution execution) { + this.execution = execution; + return this; + } + public Builder annotations(ToolAnnotations annotations) { this.annotations = annotations; return this; @@ -1412,7 +1680,7 @@ public Builder meta(Map meta) { public Tool build() { Assert.hasText(name, "name must not be empty"); - return new Tool(name, title, description, inputSchema, outputSchema, annotations, meta); + return new Tool(name, title, description, inputSchema, outputSchema, execution, annotations, meta); } } @@ -1436,6 +1704,276 @@ private static JsonSchema parseSchema(McpJsonMapper jsonMapper, String schema) { } } + /** + * Metadata about a task. + * + * @param ttl Optional time to live for the task, in milliseconds. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record TaskMetaData(@JsonProperty("ttl") Long ttl) { + } + + /** + * Related task metadata. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record RelatedTaskMetaData(@JsonProperty("taskId") String taskId) { + } + + /** + * Task information interface. + */ + public interface TaskInfo { + + /** + * The task identifier. + */ + String taskId(); + + /** + * Current task state. + */ + TaskStatus status(); + + /** + * Optional human-readable message describing the current task state. This can + * provide context for any status, including: + *

+ * - Reasons for "cancelled" status + *

+ * - Summaries for "completed" status + *

+ * - Diagnostic information for "failed" status (e.g., error details, what went + * wrong) + */ + String statusMessage(); + + /** + * ISO 8601 timestamp when the task was created. + */ + String createdAt(); + + /** + * Actual retention duration from creation in milliseconds, null for unlimited. + */ + Long ttl(); + + /** + * Suggested polling interval in milliseconds, null if no suggestion. + */ + Long pollInterval(); + + } + + /** + * The server's response to a tools/call request from the client when invoked as a + * task. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record Task( //@formatter:off + @JsonProperty("taskId") String taskId, + @JsonProperty("status") TaskStatus status, + @JsonProperty("statusMessage") String statusMessage, + @JsonProperty("createdAt") String createdAt, + @JsonProperty("ttl") Long ttl, + @JsonProperty("pollInterval") Long pollInterval) implements TaskInfo { // @formatter:on + + public Task(String taskId, TaskStatus status, String statusMessage, String createdAt) { + this(taskId, status, statusMessage, createdAt, null, null); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String taskId; + + private TaskStatus status; + + private String statusMessage; + + private String createdAt; + + private long ttl; + + private long pollInterval; + + public Builder taskId(String taskId) { + this.taskId = taskId; + return this; + } + + public Builder status(TaskStatus status) { + this.status = status; + return this; + } + + public Builder statusMessage(String statusMessage) { + this.statusMessage = statusMessage; + return this; + } + + public Builder createdAt(String createdAt) { + this.createdAt = createdAt; + return this; + } + + public Builder ttl(long ttl) { + this.ttl = ttl; + return this; + } + + public Builder pollInterval(long pollInterval) { + this.pollInterval = pollInterval; + return this; + } + + public Task build() { + return new Task(taskId, status, statusMessage, createdAt, ttl, pollInterval); + } + + } + } + + /** + * A response to a task-augmented request. + * + * @param task task info + * @param meta Optional metadata about the request. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record CreateTaskResult( // @formatter:off + @JsonProperty("task") Task task, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + } + + /** + * A request to get task status. + * + * @param taskId The ID of the task to retrieve. + * @param meta Optional metadata about the request. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record GetTaskRequest( // @formatter:off + @JsonProperty("taskId") String taskId, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + } + + /** + * The response to a get task status request. + * + * @param taskId task ID + * @param status task status + * @param statusMessage task status message + * @param createdAt task creation time + * @param ttl optional task time to live + * @param pollInterval optional recommended poll interval + * @param meta Optional metadata about the request. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record GetTaskResult( // @formatter:off + @JsonProperty("taskId") String taskId, + @JsonProperty("status") TaskStatus status, + @JsonProperty("statusMessage") String statusMessage, + @JsonProperty("createdAt") String createdAt, + @JsonProperty("ttl") Long ttl, + @JsonProperty("pollInterval") Long pollInterval, + @JsonProperty("_meta") Map meta) implements TaskInfo, Result { // @formatter:on + } + + /** + * A request to get task payload. + * + * @param taskId task ID + * @param meta Optional metadata about the request. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record getTaskPayloadRequest( // @formatter:off + @JsonProperty("taskId") String taskId, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + } + + /** + * A request to cancel a task. + * + * @param taskId task ID + * @param meta Optional metadata about the request. + */ + public record CancelTaskRequest( // @formatter:off + @JsonProperty("taskId") String taskId, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + } + + /** + * The response to a cancel task request. + * + * @param taskId task ID + * @param status task status + * @param statusMessage task status message + * @param createdAt task creation time + * @param ttl optional task time to live + * @param pollInterval optional recommended poll interval + * @param meta Optional metadata about the request. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record CancelTaskResult( // @formatter:off + @JsonProperty("taskId") String taskId, + @JsonProperty("status") TaskStatus status, + @JsonProperty("statusMessage") String statusMessage, + @JsonProperty("createdAt") String createdAt, + @JsonProperty("ttl") Long ttl, + @JsonProperty("pollInterval") Long pollInterval, + @JsonProperty("_meta") Map meta) implements TaskInfo, Result { // @formatter:on + } + + /** + * An opaque token representing the pagination position after the last returned + * result. If present, there may be more results available. + * + * @param nextCursor An opaque token representing the pagination position after the + * last returned result. If present, there may be more results available + * @param tasks A list of tasks. + * @param meta Optional metadata about the request. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record ListTasksResult( // @formatter:off + @JsonProperty("tasks") List tasks, + @JsonProperty("nextCursor") String nextCursor, + @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + } + + /** + * The notification for task status updates. + * + * @param taskId task ID + * @param status task status + * @param statusMessage task status message + * @param createdAt task creation time + * @param ttl optional task time to live + * @param pollInterval optional recommended poll interval + * @param meta Optional metadata about the request. + */ + public record TaskStatusNotification( // @formatter:off + @JsonProperty("taskId") String taskId, + @JsonProperty("status") TaskStatus status, + @JsonProperty("statusMessage") String statusMessage, + @JsonProperty("createdAt") String createdAt, + @JsonProperty("ttl") Long ttl, + @JsonProperty("pollInterval") Long pollInterval, + @JsonProperty("_meta") Map meta) implements TaskInfo, Notification { // @formatter:on + } + /** * Used by the client to call a tool provided by the server. * @@ -1451,14 +1989,23 @@ private static JsonSchema parseSchema(McpJsonMapper jsonMapper, String schema) { public record CallToolRequest( // @formatter:off @JsonProperty("name") String name, @JsonProperty("arguments") Map arguments, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + @JsonProperty("task") TaskMetaData task, + @JsonProperty("_meta") Map meta) implements TaskAugmentedRequest { // @formatter:on public CallToolRequest(McpJsonMapper jsonMapper, String name, String jsonArguments) { - this(name, parseJsonArguments(jsonMapper, jsonArguments), null); + this(name, parseJsonArguments(jsonMapper, jsonArguments), null, null); + } + + public CallToolRequest(McpJsonMapper jsonMapper, String name, String jsonArguments, Map meta) { + this(name, parseJsonArguments(jsonMapper, jsonArguments), null, meta); } public CallToolRequest(String name, Map arguments) { - this(name, arguments, null); + this(name, arguments, null, null); + } + + public CallToolRequest(String name, Map arguments, Map meta) { + this(name, arguments, null, meta); } private static Map parseJsonArguments(McpJsonMapper jsonMapper, String jsonArguments) { @@ -1480,6 +2027,8 @@ public static class Builder { private Map arguments; + private TaskMetaData task; + private Map meta; public Builder name(String name) { @@ -1510,14 +2059,85 @@ public Builder progressToken(Object progressToken) { return this; } + public Builder task(TaskMetaData task) { + this.task = task; + return this; + } + public CallToolRequest build() { Assert.hasText(name, "name must not be empty"); - return new CallToolRequest(name, arguments, meta); + return new CallToolRequest(name, arguments, task, meta); } } } + /** + * Task status. + */ + public enum TaskStatus { + + // @formatter:off + @JsonProperty("working") WORKING("working"), + @JsonProperty("input_required") INPUT_REQUIRED("input_required"), + @JsonProperty("completed") COMPLETED("completed"), + @JsonProperty("failed") FAILED("failed"), + @JsonProperty("cancelled") CANCELLED("cancelled"); + // @formatter:on + + private final String value; + + TaskStatus(final String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static TaskStatus fromValue(String value) { + for (TaskStatus status : TaskStatus.values()) { + if (status.value.equalsIgnoreCase(value)) { + return status; + } + } + throw new IllegalArgumentException("Unknown TaskStatus value: " + value); + } + + } + + /** + * The server's response to a tools/call request from the client, which can be + * `CallToolResult` or `CreateTaskResult`. + * + * @param content A list of content items representing the tool's output. Each item + * can be text, an image, or an embedded resource. + * @param isError If true, indicates that the tool execution failed and the content + * contains error information. If false or absent, indicates successful execution. + * @param structuredContent An optional JSON object that represents the structured + * result of the tool call. + * @param task Task information when the tool is invoked as a task. + * @param meta See specification for notes on _meta usage + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record CallToolAuxResult( // @formatter:off + @JsonProperty("content") List content, + @JsonProperty("isError") Boolean isError, + @JsonProperty("structuredContent") Object structuredContent, + @JsonProperty("task") Task task, + @JsonProperty("_meta") Map meta) implements Result { + + public CreateTaskResult toCreateTaskResult() { + return new CreateTaskResult(task, meta); + } + + public CallToolResult toCallToolResult() { + return new CallToolResult(content, isError, structuredContent, meta); + } + + } // @formatter:on + /** * The server's response to a tools/call request from the client. * @@ -1811,6 +2431,7 @@ public record SamplingMessage( // @formatter:off * @param maxTokens The maximum number of tokens to sample, as requested by the * server. The client MAY choose to sample fewer tokens than requested * @param stopSequences Optional stop sequences for sampling + * @param task Optional task metadata * @param metadata Optional metadata to pass through to the LLM provider. The format * of this metadata is provider-specific * @param meta See specification for notes on _meta usage @@ -1825,17 +2446,26 @@ public record CreateMessageRequest( // @formatter:off @JsonProperty("temperature") Double temperature, @JsonProperty("maxTokens") Integer maxTokens, @JsonProperty("stopSequences") List stopSequences, + @JsonProperty("task") TaskMetaData task, @JsonProperty("metadata") Map metadata, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + @JsonProperty("_meta") Map meta) implements TaskAugmentedRequest { // @formatter:on // backwards compatibility constructor public CreateMessageRequest(List messages, ModelPreferences modelPreferences, String systemPrompt, ContextInclusionStrategy includeContext, Double temperature, Integer maxTokens, List stopSequences, Map metadata) { - this(messages, modelPreferences, systemPrompt, includeContext, temperature, maxTokens, stopSequences, + this(messages, modelPreferences, systemPrompt, includeContext, temperature, maxTokens, stopSequences, null, metadata, null); } + // backwards compatibility constructor + public CreateMessageRequest(List messages, ModelPreferences modelPreferences, + String systemPrompt, ContextInclusionStrategy includeContext, Double temperature, Integer maxTokens, + List stopSequences, Map metadata, Map meta) { + this(messages, modelPreferences, systemPrompt, includeContext, temperature, maxTokens, stopSequences, null, + metadata, meta); + } + public enum ContextInclusionStrategy { // @formatter:off @@ -1864,6 +2494,8 @@ public static class Builder { private List stopSequences; + private TaskMetaData task; + private Map metadata; private Map meta; @@ -1903,6 +2535,11 @@ public Builder stopSequences(List stopSequences) { return this; } + public Builder task(TaskMetaData task) { + this.task = task; + return this; + } + public Builder metadata(Map metadata) { this.metadata = metadata; return this; @@ -1923,7 +2560,7 @@ public Builder progressToken(Object progressToken) { public CreateMessageRequest build() { return new CreateMessageRequest(messages, modelPreferences, systemPrompt, includeContext, temperature, - maxTokens, stopSequences, metadata, meta); + maxTokens, stopSequences, task, metadata, meta); } } @@ -2047,13 +2684,19 @@ public CreateMessageResult build() { public record ElicitRequest( // @formatter:off @JsonProperty("message") String message, @JsonProperty("requestedSchema") Map requestedSchema, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + @JsonProperty("task") TaskMetaData task, + @JsonProperty("_meta") Map meta) implements TaskAugmentedRequest { // @formatter:on // backwards compatibility constructor public ElicitRequest(String message, Map requestedSchema) { this(message, requestedSchema, null); } + // backwards compatibility constructor + public ElicitRequest(String message, Map requestedSchema, Map meta) { + this(message, requestedSchema, null, meta); + } + public static Builder builder() { return new Builder(); } @@ -2064,6 +2707,8 @@ public static class Builder { private Map requestedSchema; + private TaskMetaData task; + private Map meta; public Builder message(String message) { @@ -2076,6 +2721,11 @@ public Builder requestedSchema(Map requestedSchema) { return this; } + public Builder task(TaskMetaData task) { + this.task = task; + return this; + } + public Builder meta(Map meta) { this.meta = meta; return this; @@ -2090,7 +2740,7 @@ public Builder progressToken(Object progressToken) { } public ElicitRequest build() { - return new ElicitRequest(message, requestedSchema, meta); + return new ElicitRequest(message, requestedSchema, task, meta); } } diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java index 0926eebae..4b0d47b98 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java @@ -879,8 +879,7 @@ void testToolWithAnnotations() throws Exception { "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").build(); McpSchema.Tool tool = McpSchema.Tool.builder() .name("test-tool") @@ -1014,8 +1013,12 @@ void testToolWithOutputSchemaAndAnnotations() throws Exception { } """; - 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) + .idempotentHint(true) + .returnDirect(true) + .build(); McpSchema.Tool tool = McpSchema.Tool.builder() .name("test-tool")