From 0fdf016d53f3c40d680b9358989985bdaefb329a Mon Sep 17 00:00:00 2001 From: He-Pin Date: Tue, 18 Nov 2025 14:32:50 +0800 Subject: [PATCH 01/13] feat: Add task related things to schema Signed-off-by: He-Pin --- .../client/McpClientFeatures.java | 12 +- .../server/McpServerFeatures.java | 8 +- .../server/McpStatelessServerFeatures.java | 8 +- .../modelcontextprotocol/spec/McpSchema.java | 635 +++++++++++++++++- .../spec/McpSchemaTests.java | 21 +- 5 files changed, 643 insertions(+), 41 deletions(-) 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..e0c6bf809 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClientFeatures.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClientFeatures.java @@ -106,7 +106,11 @@ 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(); @@ -256,7 +260,11 @@ 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(); 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/McpSchema.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index 734cff237..cedff9aad 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 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); @@ -1297,12 +1438,55 @@ public record JsonSchema( // @formatter:off } /** - * Additional properties describing a Tool to clients. + * The task hint. + *

+ * If taskHint is {@code always}, clients SHOULD invoke the tool as a task. Servers + * MAY return a -32601 (Method not found) error if a client does not attempt to do so. + *

+ * If taskHint is {@code optional}, clients MAY invoke the tool as a task or as a + * normal request. + *

+ * If taskHint is not present or {@code never}, clients MUST NOT attempt to invoke the + * tool as a task. Servers SHOULD return a -32601 (Method not found) error if a client + * attempts to do so. This is the default behavior. * + */ + public enum TaskHint { + + // @formatter:off + @JsonProperty("always") ALWAYS("always"), + @JsonProperty("optional") OPTIONAL("optional"), + @JsonProperty("never") NEVER("never"); + // @formatter:on + + private final String value; + + TaskHint(final String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static TaskHint fromValue(String value) { + for (TaskHint hint : TaskHint.values()) { + if (hint.value.equalsIgnoreCase(value)) { + return hint; + } + } + throw new IllegalArgumentException("Unknown TaskHint 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. */ @@ -1314,7 +1498,70 @@ public record ToolAnnotations( // @formatter:off @JsonProperty("destructiveHint") Boolean destructiveHint, @JsonProperty("idempotentHint") Boolean idempotentHint, @JsonProperty("openWorldHint") Boolean openWorldHint, - @JsonProperty("returnDirect") Boolean returnDirect) { // @formatter:on + @JsonProperty("returnDirect") Boolean returnDirect, + @JsonProperty("taskHint") TaskHint taskHint) { // @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; + + private TaskHint taskHint; + + 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 Builder taskHint(TaskHint taskHint) { + this.taskHint = taskHint; + return this; + } + + public ToolAnnotations build() { + return new ToolAnnotations(title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint, + returnDirect, taskHint); + } + + } } /** @@ -1436,6 +1683,231 @@ 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) { + } + + /** + * 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) { // @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 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 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 Notification { // @formatter:on + } + /** * Used by the client to call a tool provided by the server. * @@ -1451,14 +1923,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 +1961,8 @@ public static class Builder { private Map arguments; + private TaskMetaData task; + private Map meta; public Builder name(String name) { @@ -1510,14 +1993,53 @@ 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. * @@ -1535,6 +2057,7 @@ public record CallToolResult( // @formatter:off @JsonProperty("content") List content, @JsonProperty("isError") Boolean isError, @JsonProperty("structuredContent") Object structuredContent, + @JsonProperty("task") Task task, @JsonProperty("_meta") Map meta) implements Result { // @formatter:on /** @@ -1542,7 +2065,7 @@ public record CallToolResult( // @formatter:off */ @Deprecated public CallToolResult(List content, Boolean isError) { - this(content, isError, (Object) null, null); + this(content, isError, (Object) null, null, null); } /** @@ -1550,7 +2073,7 @@ public CallToolResult(List content, Boolean isError) { */ @Deprecated public CallToolResult(List content, Boolean isError, Map structuredContent) { - this(content, isError, structuredContent, null); + this(content, isError, structuredContent, null, null); } /** @@ -1567,6 +2090,23 @@ public CallToolResult(String content, Boolean isError) { this(List.of(new TextContent(content)), isError, null); } + /** + * Create a new instance of {@link CallToolResult} with a task. + * @param task The task metadata. + */ + public CallToolResult(Task task) { + this(null, false, null, task, null); + } + + /** + * Create a new instance of {@link CallToolResult} with a task. + * @param task The task metadata. + * @param meta Optional metadata about the result. + */ + public CallToolResult(Task task, Map meta) { + this(null, false, null, task, meta); + } + /** * Creates a builder for {@link CallToolResult}. * @return a new builder instance @@ -1586,6 +2126,8 @@ public static class Builder { private Object structuredContent; + private Task task; + private Map meta; /** @@ -1672,12 +2214,17 @@ public Builder meta(Map meta) { return this; } + public Builder task(Task task) { + this.task = task; + return this; + } + /** * Builds a new {@link CallToolResult} instance. * @return a new CallToolResult instance */ public CallToolResult build() { - return new CallToolResult(content, isError, structuredContent, meta); + return new CallToolResult(content, isError, structuredContent, task, meta); } } @@ -1811,6 +2358,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 +2373,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 +2421,8 @@ public static class Builder { private List stopSequences; + private TaskMetaData task; + private Map metadata; private Map meta; @@ -1903,6 +2462,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 +2487,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 +2611,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 +2634,8 @@ public static class Builder { private Map requestedSchema; + private TaskMetaData task; + private Map meta; public Builder message(String message) { @@ -2076,6 +2648,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 +2667,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..43c98c75b 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,10 @@ 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") + .taskHint(McpSchema.TaskHint.OPTIONAL) + .build(); McpSchema.Tool tool = McpSchema.Tool.builder() .name("test-tool") @@ -911,7 +913,8 @@ void testToolWithAnnotations() throws Exception { "destructiveHint":false, "idempotentHint":false, "openWorldHint":false, - "returnDirect":false + "returnDirect":false, + "taskHint":"optional" } } """)); @@ -1014,8 +1017,13 @@ 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) + .taskHint(McpSchema.TaskHint.OPTIONAL) + .build(); McpSchema.Tool tool = McpSchema.Tool.builder() .name("test-tool") @@ -1053,7 +1061,8 @@ void testToolWithOutputSchemaAndAnnotations() throws Exception { "destructiveHint":false, "idempotentHint":true, "openWorldHint":false, - "returnDirect":true + "returnDirect":true, + "taskHint":"optional" } }""")); } From b3d1f5e9f929ceb6aba353c32a0211ae84c062b6 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Fri, 21 Nov 2025 01:03:15 +0800 Subject: [PATCH 02/13] chore: With taskInfo Signed-off-by: He-Pin --- .../modelcontextprotocol/spec/McpSchema.java | 53 +++++++++++++++++-- 1 file changed, 49 insertions(+), 4 deletions(-) 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 cedff9aad..503cfacb6 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -1701,6 +1701,51 @@ public record TaskMetaData(@JsonProperty("ttl") Long ttl) { 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. @@ -1713,7 +1758,7 @@ public record Task( //@formatter:off @JsonProperty("statusMessage") String statusMessage, @JsonProperty("createdAt") String createdAt, @JsonProperty("ttl") Long ttl, - @JsonProperty("pollInterval") Long pollInterval) { // @formatter:on + @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); @@ -1820,7 +1865,7 @@ public record GetTaskResult( // @formatter:off @JsonProperty("createdAt") String createdAt, @JsonProperty("ttl") Long ttl, @JsonProperty("pollInterval") Long pollInterval, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + @JsonProperty("_meta") Map meta) implements TaskInfo, Result { // @formatter:on } /** @@ -1867,7 +1912,7 @@ public record CancelTaskResult( // @formatter:off @JsonProperty("createdAt") String createdAt, @JsonProperty("ttl") Long ttl, @JsonProperty("pollInterval") Long pollInterval, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + @JsonProperty("_meta") Map meta) implements TaskInfo, Result { // @formatter:on } /** @@ -1905,7 +1950,7 @@ public record TaskStatusNotification( // @formatter:off @JsonProperty("createdAt") String createdAt, @JsonProperty("ttl") Long ttl, @JsonProperty("pollInterval") Long pollInterval, - @JsonProperty("_meta") Map meta) implements Notification { // @formatter:on + @JsonProperty("_meta") Map meta) implements TaskInfo, Notification { // @formatter:on } /** From 22a32ee8232a3e2019107d1bdb268d1c01858e75 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Fri, 21 Nov 2025 02:55:46 +0800 Subject: [PATCH 03/13] chore: with task status consumers Signed-off-by: He-Pin --- .../client/McpClientFeatures.java | 206 ++++++++++++------ 1 file changed, 139 insertions(+), 67 deletions(-) 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 e0c6bf809..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; @@ -114,7 +123,9 @@ public Async(McpSchema.Implementation clientInfo, McpSchema.ClientCapabilities c 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(); @@ -127,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); @@ -156,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)) @@ -195,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); } } @@ -209,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. @@ -217,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. @@ -234,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. @@ -243,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; @@ -260,14 +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, null // TODO - // task - // management - // capabilities + 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(); @@ -281,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); } From 7195cc3786fc4e06141b6c1e814c61b354de5cc5 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Fri, 21 Nov 2025 16:05:00 +0800 Subject: [PATCH 04/13] chore: rename taskHint to TaskSupport Signed-off-by: He-Pin --- .../modelcontextprotocol/spec/McpSchema.java | 67 +++++++++++++------ .../spec/McpSchemaTests.java | 12 +--- 2 files changed, 48 insertions(+), 31 deletions(-) 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 503cfacb6..221b24c99 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -1438,20 +1438,28 @@ public record JsonSchema( // @formatter:off } /** - * The task hint. + * Execution behavior for a tool. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record ToolExecution(TaskSupport task) { + } + + /** + * The task augmented support mode for a tool. *

- * If taskHint is {@code always}, clients SHOULD invoke the tool as a task. Servers - * MAY return a -32601 (Method not found) error if a client does not attempt to do so. + * If mode is {@code always}, clients SHOULD invoke the tool as a task. Servers MAY + * return a -32601 (Method not found) error if a client does not attempt to do so. *

- * If taskHint is {@code optional}, clients MAY invoke the tool as a task or as a - * normal request. + * If mode is {@code optional}, clients MAY invoke the tool as a task or as a normal + * request. *

- * If taskHint is not present or {@code never}, clients MUST NOT attempt to invoke the + * If mode is not present or {@code never}, clients MUST NOT attempt to invoke the * tool as a task. Servers SHOULD return a -32601 (Method not found) error if a client * attempts to do so. This is the default behavior. * */ - public enum TaskHint { + public enum TaskSupport { // @formatter:off @JsonProperty("always") ALWAYS("always"), @@ -1461,7 +1469,7 @@ public enum TaskHint { private final String value; - TaskHint(final String value) { + TaskSupport(final String value) { this.value = value; } @@ -1469,13 +1477,13 @@ public String getValue() { return value; } - public static TaskHint fromValue(String value) { - for (TaskHint hint : TaskHint.values()) { + public static TaskSupport fromValue(String value) { + for (TaskSupport hint : TaskSupport.values()) { if (hint.value.equalsIgnoreCase(value)) { return hint; } } - throw new IllegalArgumentException("Unknown TaskHint value: " + value); + throw new IllegalArgumentException("Unknown task support value: " + value); } } @@ -1498,8 +1506,7 @@ public record ToolAnnotations( // @formatter:off @JsonProperty("destructiveHint") Boolean destructiveHint, @JsonProperty("idempotentHint") Boolean idempotentHint, @JsonProperty("openWorldHint") Boolean openWorldHint, - @JsonProperty("returnDirect") Boolean returnDirect, - @JsonProperty("taskHint") TaskHint taskHint) { // @formatter:on + @JsonProperty("returnDirect") Boolean returnDirect) { // @formatter:on public static Builder builder() { return new Builder(); @@ -1519,8 +1526,6 @@ public static class Builder { private boolean returnDirect; - private TaskHint taskHint; - public Builder title(String title) { this.title = title; return this; @@ -1551,14 +1556,9 @@ public Builder returnDirect(boolean returnDirect) { return this; } - public Builder taskHint(TaskHint taskHint) { - this.taskHint = taskHint; - return this; - } - public ToolAnnotations build() { return new ToolAnnotations(title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint, - returnDirect, taskHint); + returnDirect); } } @@ -1576,6 +1576,7 @@ public ToolAnnotations build() { * 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. @@ -1589,9 +1590,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(); } @@ -1608,6 +1624,8 @@ public static class Builder { private Map outputSchema; + private ToolExecution execution; + private ToolAnnotations annotations; private Map meta; @@ -1647,6 +1665,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; @@ -1659,7 +1682,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); } } 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 43c98c75b..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,10 +879,7 @@ void testToolWithAnnotations() throws Exception { "required": ["name"] } """; - McpSchema.ToolAnnotations annotations = McpSchema.ToolAnnotations.builder() - .title("A test tool") - .taskHint(McpSchema.TaskHint.OPTIONAL) - .build(); + McpSchema.ToolAnnotations annotations = McpSchema.ToolAnnotations.builder().title("A test tool").build(); McpSchema.Tool tool = McpSchema.Tool.builder() .name("test-tool") @@ -913,8 +910,7 @@ void testToolWithAnnotations() throws Exception { "destructiveHint":false, "idempotentHint":false, "openWorldHint":false, - "returnDirect":false, - "taskHint":"optional" + "returnDirect":false } } """)); @@ -1022,7 +1018,6 @@ void testToolWithOutputSchemaAndAnnotations() throws Exception { .readOnlyHint(true) .idempotentHint(true) .returnDirect(true) - .taskHint(McpSchema.TaskHint.OPTIONAL) .build(); McpSchema.Tool tool = McpSchema.Tool.builder() @@ -1061,8 +1056,7 @@ void testToolWithOutputSchemaAndAnnotations() throws Exception { "destructiveHint":false, "idempotentHint":true, "openWorldHint":false, - "returnDirect":true, - "taskHint":"optional" + "returnDirect":true } }""")); } From 360c5361a9d9c3f1a42124aea3205429f13fc8b9 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Sat, 22 Nov 2025 13:51:52 +0800 Subject: [PATCH 05/13] chore: rename task to taskSupport Signed-off-by: He-Pin --- .../modelcontextprotocol/spec/McpSchema.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) 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 221b24c99..2a4c19b61 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -1442,29 +1442,27 @@ public record JsonSchema( // @formatter:off */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) - public record ToolExecution(TaskSupport task) { + public record ToolExecution(TaskSupport taskSupport) { } /** - * The task augmented support mode for a tool. + * Indicates whether this tool supports task-augmented execution. This allows clients + * to handle long-running operations through polling the task system. *

- * If mode is {@code always}, clients SHOULD invoke the tool as a task. Servers MAY - * return a -32601 (Method not found) error if a client does not attempt to do so. + * - "forbidden": Tool does not support task-augmented execution (default when absent) *

- * If mode is {@code optional}, clients MAY invoke the tool as a task or as a normal - * request. + * - "optional": Tool may support task-augmented execution *

- * If mode is not present or {@code never}, clients MUST NOT attempt to invoke the - * tool as a task. Servers SHOULD return a -32601 (Method not found) error if a client - * attempts to do so. This is the default behavior. - * + * - "required": Tool requires task-augmented execution + *

+ * Default: "forbidden" */ public enum TaskSupport { // @formatter:off - @JsonProperty("always") ALWAYS("always"), + @JsonProperty("forbidden") FORBIDDEN("forbidden"), @JsonProperty("optional") OPTIONAL("optional"), - @JsonProperty("never") NEVER("never"); + @JsonProperty("required") REQUIRED("required"); // @formatter:on private final String value; From 7ee2cca77903dbdcbca13cd7f22939bea9a6bfd4 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Sat, 22 Nov 2025 14:04:48 +0800 Subject: [PATCH 06/13] chore: Remove task from CreateTaskResult Signed-off-by: He-Pin --- .../modelcontextprotocol/spec/McpSchema.java | 31 ++----------------- 1 file changed, 3 insertions(+), 28 deletions(-) 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 2a4c19b61..95f2d86e6 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -2123,7 +2123,6 @@ public record CallToolResult( // @formatter:off @JsonProperty("content") List content, @JsonProperty("isError") Boolean isError, @JsonProperty("structuredContent") Object structuredContent, - @JsonProperty("task") Task task, @JsonProperty("_meta") Map meta) implements Result { // @formatter:on /** @@ -2131,7 +2130,7 @@ public record CallToolResult( // @formatter:off */ @Deprecated public CallToolResult(List content, Boolean isError) { - this(content, isError, (Object) null, null, null); + this(content, isError, (Object) null, null); } /** @@ -2139,7 +2138,7 @@ public CallToolResult(List content, Boolean isError) { */ @Deprecated public CallToolResult(List content, Boolean isError, Map structuredContent) { - this(content, isError, structuredContent, null, null); + this(content, isError, structuredContent, null); } /** @@ -2156,23 +2155,6 @@ public CallToolResult(String content, Boolean isError) { this(List.of(new TextContent(content)), isError, null); } - /** - * Create a new instance of {@link CallToolResult} with a task. - * @param task The task metadata. - */ - public CallToolResult(Task task) { - this(null, false, null, task, null); - } - - /** - * Create a new instance of {@link CallToolResult} with a task. - * @param task The task metadata. - * @param meta Optional metadata about the result. - */ - public CallToolResult(Task task, Map meta) { - this(null, false, null, task, meta); - } - /** * Creates a builder for {@link CallToolResult}. * @return a new builder instance @@ -2192,8 +2174,6 @@ public static class Builder { private Object structuredContent; - private Task task; - private Map meta; /** @@ -2280,17 +2260,12 @@ public Builder meta(Map meta) { return this; } - public Builder task(Task task) { - this.task = task; - return this; - } - /** * Builds a new {@link CallToolResult} instance. * @return a new CallToolResult instance */ public CallToolResult build() { - return new CallToolResult(content, isError, structuredContent, task, meta); + return new CallToolResult(content, isError, structuredContent, meta); } } From 9744c0614d77bacdb7e1b3ba03002d9dee039389 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Sat, 22 Nov 2025 14:29:31 +0800 Subject: [PATCH 07/13] chore: add mcp Message Signed-off-by: He-Pin --- .../modelcontextprotocol/spec/McpMessage.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/spec/McpMessage.java 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 { + } + + } + +} From 923b524ae2944145f93a4c349060cca0dc3d8839 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Sat, 22 Nov 2025 14:48:41 +0800 Subject: [PATCH 08/13] chore: add call tool result aux Signed-off-by: He-Pin --- .../modelcontextprotocol/spec/McpSchema.java | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) 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 95f2d86e6..92ac54ff8 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -206,10 +206,10 @@ default Object progressToken() { } - public sealed interface Result extends Meta permits CallToolResult, CancelTaskResult, CompleteResult, - CreateMessageResult, CreateTaskResult, ElicitResult, GetPromptResult, GetTaskResult, InitializeResult, - ListPromptsResult, ListResourceTemplatesResult, ListResourcesResult, ListRootsResult, ListTasksResult, - ListToolsResult, ReadResourceResult { + public sealed interface Result extends Meta permits CallToolAuxResult, CallToolResult, CancelTaskResult, + CompleteResult, CreateMessageResult, CreateTaskResult, ElicitResult, GetPromptResult, GetTaskResult, + InitializeResult, ListPromptsResult, ListResourceTemplatesResult, ListResourcesResult, ListRootsResult, + ListTasksResult, ListToolsResult, ReadResourceResult { } @@ -2106,6 +2106,28 @@ public static TaskStatus fromValue(String 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 {} // @formatter:on + /** * The server's response to a tools/call request from the client. * From 12ff269c6522a7e4bb7ae65c990902a858342fbf Mon Sep 17 00:00:00 2001 From: He-Pin Date: Sat, 22 Nov 2025 14:55:43 +0800 Subject: [PATCH 09/13] chore: add lastUpdatedAt Signed-off-by: He-Pin --- .../modelcontextprotocol/spec/McpSchema.java | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) 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 92ac54ff8..113d0e976 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -1755,6 +1755,11 @@ public interface TaskInfo { */ String createdAt(); + /** + * ISO 8601 timestamp when the task status was last updated + */ + String lastUpdatedAt(); + /** * Actual retention duration from creation in milliseconds, null for unlimited. */ @@ -1778,11 +1783,15 @@ public record Task( //@formatter:off @JsonProperty("status") TaskStatus status, @JsonProperty("statusMessage") String statusMessage, @JsonProperty("createdAt") String createdAt, + @JsonProperty("lastUpdatedAt") String lastUpdatedAt, @JsonProperty("ttl") Long ttl, @JsonProperty("pollInterval") Long pollInterval) implements TaskInfo { // @formatter:on + /** + * Binary compatibility constructor + */ public Task(String taskId, TaskStatus status, String statusMessage, String createdAt) { - this(taskId, status, statusMessage, createdAt, null, null); + this(taskId, status, statusMessage, createdAt, null, null, null); } public static Builder builder() { @@ -1799,6 +1808,8 @@ public static class Builder { private String createdAt; + private String lastUpdatedAt; + private long ttl; private long pollInterval; @@ -1823,6 +1834,11 @@ public Builder createdAt(String createdAt) { return this; } + public Builder lastUpdatedAt(String lastUpdatedAt) { + this.lastUpdatedAt = lastUpdatedAt; + return this; + } + public Builder ttl(long ttl) { this.ttl = ttl; return this; @@ -1834,7 +1850,7 @@ public Builder pollInterval(long pollInterval) { } public Task build() { - return new Task(taskId, status, statusMessage, createdAt, ttl, pollInterval); + return new Task(taskId, status, statusMessage, createdAt, lastUpdatedAt, ttl, pollInterval); } } @@ -1873,6 +1889,7 @@ public record GetTaskRequest( // @formatter:off * @param status task status * @param statusMessage task status message * @param createdAt task creation time + * @param lastUpdatedAt task last updated time * @param ttl optional task time to live * @param pollInterval optional recommended poll interval * @param meta Optional metadata about the request. @@ -1884,6 +1901,7 @@ public record GetTaskResult( // @formatter:off @JsonProperty("status") TaskStatus status, @JsonProperty("statusMessage") String statusMessage, @JsonProperty("createdAt") String createdAt, + @JsonProperty("lastUpdatedAt") String lastUpdatedAt, @JsonProperty("ttl") Long ttl, @JsonProperty("pollInterval") Long pollInterval, @JsonProperty("_meta") Map meta) implements TaskInfo, Result { // @formatter:on @@ -1920,6 +1938,7 @@ public record CancelTaskRequest( // @formatter:off * @param status task status * @param statusMessage task status message * @param createdAt task creation time + * @param lastUpdatedAt task last updated time * @param ttl optional task time to live * @param pollInterval optional recommended poll interval * @param meta Optional metadata about the request. @@ -1931,6 +1950,7 @@ public record CancelTaskResult( // @formatter:off @JsonProperty("status") TaskStatus status, @JsonProperty("statusMessage") String statusMessage, @JsonProperty("createdAt") String createdAt, + @JsonProperty("lastUpdatedAt") String lastUpdatedAt, @JsonProperty("ttl") Long ttl, @JsonProperty("pollInterval") Long pollInterval, @JsonProperty("_meta") Map meta) implements TaskInfo, Result { // @formatter:on @@ -1960,6 +1980,7 @@ public record ListTasksResult( // @formatter:off * @param status task status * @param statusMessage task status message * @param createdAt task creation time + * @param lastUpdatedAt task last updated time * @param ttl optional task time to live * @param pollInterval optional recommended poll interval * @param meta Optional metadata about the request. @@ -1969,6 +1990,7 @@ public record TaskStatusNotification( // @formatter:off @JsonProperty("status") TaskStatus status, @JsonProperty("statusMessage") String statusMessage, @JsonProperty("createdAt") String createdAt, + @JsonProperty("lastUpdatedAt") String lastUpdatedAt, @JsonProperty("ttl") Long ttl, @JsonProperty("pollInterval") Long pollInterval, @JsonProperty("_meta") Map meta) implements TaskInfo, Notification { // @formatter:on @@ -2126,7 +2148,17 @@ public record CallToolAuxResult( // @formatter:off @JsonProperty("isError") Boolean isError, @JsonProperty("structuredContent") Object structuredContent, @JsonProperty("task") Task task, - @JsonProperty("_meta") Map meta) implements Result {} // @formatter:on + @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. From 5d3852f65741dc98d10a63a9f23042588aa9445a Mon Sep 17 00:00:00 2001 From: He-Pin Date: Tue, 25 Nov 2025 15:48:12 +0800 Subject: [PATCH 10/13] chore: Make PaginatedResult a interface Signed-off-by: He-Pin --- .../modelcontextprotocol/spec/McpSchema.java | 134 +++++++++--------- 1 file changed, 70 insertions(+), 64 deletions(-) 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 113d0e976..59e3744e5 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -206,10 +206,9 @@ default Object progressToken() { } - 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 Result extends Meta permits CallToolResult, CancelTaskResult, CompleteResult, + CreateMessageResult, CreateTaskResult, ElicitResult, GetPromptResult, GetTaskPayloadResult, GetTaskResult, + InitializeResult, PaginatedResult, ReadResourceResult { } @@ -1102,7 +1101,7 @@ public ResourceTemplate build() { public record ListResourcesResult( // @formatter:off @JsonProperty("resources") List resources, @JsonProperty("nextCursor") String nextCursor, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + @JsonProperty("_meta") Map meta) implements PaginatedResult { // @formatter:on public ListResourcesResult(List resources, String nextCursor) { this(resources, nextCursor, null); @@ -1122,7 +1121,7 @@ public ListResourcesResult(List resources, String nextCursor) { public record ListResourceTemplatesResult( // @formatter:off @JsonProperty("resourceTemplates") List resourceTemplates, @JsonProperty("nextCursor") String nextCursor, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + @JsonProperty("_meta") Map meta) implements PaginatedResult { // @formatter:on public ListResourceTemplatesResult(List resourceTemplates, String nextCursor) { this(resourceTemplates, nextCursor, null); @@ -1348,7 +1347,7 @@ public record PromptMessage( // @formatter:off public record ListPromptsResult( // @formatter:off @JsonProperty("prompts") List prompts, @JsonProperty("nextCursor") String nextCursor, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + @JsonProperty("_meta") Map meta) implements PaginatedResult { // @formatter:on public ListPromptsResult(List prompts, String nextCursor) { this(prompts, nextCursor, null); @@ -1409,7 +1408,7 @@ public GetPromptResult(String description, List messages) { public record ListToolsResult( // @formatter:off @JsonProperty("tools") List tools, @JsonProperty("nextCursor") String nextCursor, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + @JsonProperty("_meta") Map meta) implements PaginatedResult { // @formatter:on public ListToolsResult(List tools, String nextCursor) { this(tools, nextCursor, null); @@ -1438,7 +1437,7 @@ public record JsonSchema( // @formatter:off } /** - * Execution behavior for a tool. + * Execution-related properties for a tool. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -1705,9 +1704,10 @@ private static JsonSchema parseSchema(McpJsonMapper jsonMapper, String schema) { } /** - * Metadata about a task. + * Metadata for augmenting a request with task execution. Include this in the + * {@code task} field of the request parameters. * - * @param ttl Optional time to live for the task, in milliseconds. + * @param ttl Optional duration in milliseconds to retain task from creation. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -1715,7 +1715,10 @@ public record TaskMetaData(@JsonProperty("ttl") Long ttl) { } /** - * Related task metadata. + * Metadata for associating messages with a task. Include this in the {@code _meta} + * field under the key {@code io.modelcontextprotocol/related-task}. + * + * @param taskId The task identifier this message is associated with. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -1723,7 +1726,7 @@ public record RelatedTaskMetaData(@JsonProperty("taskId") String taskId) { } /** - * Task information interface. + * Data associated with a task. */ public interface TaskInfo { @@ -1870,9 +1873,9 @@ public record CreateTaskResult( // @formatter:off } /** - * A request to get task status. + * A request to retrieve the state of a task. * - * @param taskId The ID of the task to retrieve. + * @param taskId The task identifier to retrieve. * @param meta Optional metadata about the request. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @@ -1908,9 +1911,11 @@ public record GetTaskResult( // @formatter:off } /** - * A request to get task payload. + * The response to a tasks/result request. The structure matches the result type of + * the original request. For example, a tools/call task would return the + * CallToolResult structure. * - * @param taskId task ID + * @param taskId The task identifier to retrieve results for. * @param meta Optional metadata about the request. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @@ -1920,6 +1925,15 @@ public record getTaskPayloadRequest( // @formatter:off @JsonProperty("_meta") Map meta) implements Request { // @formatter:on } + /** + * The response to a tasks/result request. The structure matches the result type of + * the original request. For example, a tools/call task would return the + * CallToolResult structure. + */ + sealed interface GetTaskPayloadResult extends Result { + + } + /** * A request to cancel a task. * @@ -1932,7 +1946,7 @@ public record CancelTaskRequest( // @formatter:off } /** - * The response to a cancel task request. + * The response to a tasks/cancel request. * * @param taskId task ID * @param status task status @@ -1970,7 +1984,7 @@ public record CancelTaskResult( // @formatter:off public record ListTasksResult( // @formatter:off @JsonProperty("tasks") List tasks, @JsonProperty("nextCursor") String nextCursor, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + @JsonProperty("_meta") Map meta) implements PaginatedResult { // @formatter:on } /** @@ -2128,38 +2142,6 @@ public static TaskStatus fromValue(String 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. * @@ -2169,6 +2151,7 @@ public CallToolResult toCallToolResult() { * 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) @@ -2177,14 +2160,15 @@ public record CallToolResult( // @formatter:off @JsonProperty("content") List content, @JsonProperty("isError") Boolean isError, @JsonProperty("structuredContent") Object structuredContent, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + @JsonProperty("task") Task task, + @JsonProperty("_meta") Map meta) implements Result, GetTaskPayloadResult { // @formatter:on /** * @deprecated use the builder instead. */ @Deprecated public CallToolResult(List content, Boolean isError) { - this(content, isError, (Object) null, null); + this(content, isError, (Object) null, null, null); } /** @@ -2192,7 +2176,15 @@ public CallToolResult(List content, Boolean isError) { */ @Deprecated public CallToolResult(List content, Boolean isError, Map structuredContent) { - this(content, isError, structuredContent, null); + this(content, isError, structuredContent, null, null); + } + + /** + * Binary compatibility constructor + */ + public CallToolResult(List content, Boolean isError, Map structuredContent, + Map meta) { + this(content, isError, structuredContent, null, meta); } /** @@ -2228,6 +2220,8 @@ public static class Builder { private Object structuredContent; + private Task task; + private Map meta; /** @@ -2314,12 +2308,22 @@ public Builder meta(Map meta) { return this; } + /** + * Sets the task information for the tool result. + * @param task task information + * @return this builder + */ + public Builder task(Task task) { + this.task = task; + return this; + } + /** * Builds a new {@link CallToolResult} instance. * @return a new CallToolResult instance */ public CallToolResult build() { - return new CallToolResult(content, isError, structuredContent, meta); + return new CallToolResult(content, isError, structuredContent, task, meta); } } @@ -2861,15 +2865,17 @@ public PaginatedRequest() { } /** - * 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 + * Pagination result interface. */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record PaginatedResult(@JsonProperty("nextCursor") String nextCursor) { + sealed interface PaginatedResult extends Result permits ListResourceTemplatesResult, ListResourcesResult, + ListRootsResult, ListPromptsResult, ListToolsResult, ListTasksResult { + + /** + * An opaque token representing the pagination position after the ast returned + * result. If present, there may be more results available + */ + String nextCursor(); + } // --------------------------- @@ -3534,7 +3540,7 @@ public Root(String uri, String name) { public record ListRootsResult( // @formatter:off @JsonProperty("roots") List roots, @JsonProperty("nextCursor") String nextCursor, - @JsonProperty("_meta") Map meta) implements Result { // @formatter:on + @JsonProperty("_meta") Map meta) implements PaginatedResult { // @formatter:on public ListRootsResult(List roots) { this(roots, null); From ec21bbb391a205ccf6360f49ba4842c9def60766 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Tue, 25 Nov 2025 16:03:26 +0800 Subject: [PATCH 11/13] chore: add MCP_2025_11_25 Signed-off-by: He-Pin --- .../java/io/modelcontextprotocol/spec/ProtocolVersions.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/ProtocolVersions.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/ProtocolVersions.java index d8cb913a5..fbea49ac4 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/ProtocolVersions.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/ProtocolVersions.java @@ -20,4 +20,9 @@ public interface ProtocolVersions { */ String MCP_2025_06_18 = "2025-06-18"; + /** + * TODO update to the reference link + */ + String MCP_2025_11_25 = "2025-11-25"; + } From 55a6d0c9854089b0173f4e663c25bf1437f72b2b Mon Sep 17 00:00:00 2001 From: He-Pin Date: Fri, 28 Nov 2025 01:00:08 +0800 Subject: [PATCH 12/13] chore: binary compatibility Signed-off-by: He-Pin --- .../modelcontextprotocol/spec/McpSchema.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) 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 59e3744e5..d3fd0b3fe 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -419,6 +419,17 @@ public record ClientCapabilities( // @formatter:off @JsonProperty("elicitation") Elicitation elicitation, @JsonProperty("tasks") ClientCapabilities.TaskCapabilities tasks) { // @formatter:on + /** + * Keep for backward compatibility + */ + public ClientCapabilities( // @formatter:off + Map experimental, + RootCapabilities roots, + Sampling sampling, + Elicitation elicitation) { // @formatter:on + this(experimental, roots, sampling, elicitation, null); + } + /** * Present if the client supports listing roots. * @@ -578,6 +589,19 @@ public record ServerCapabilities( // @formatter:off @JsonProperty("tools") ToolCapabilities tools, @JsonProperty("tasks") ServerCapabilities.TaskCapabilities tasks) { // @formatter:on + /** + * Keep for backward compatibility + */ + public ServerCapabilities( // @formatter:off + CompletionCapabilities completions, + Map experimental, + LoggingCapabilities logging, + PromptCapabilities prompts, + ResourceCapabilities resources, + ToolCapabilities tools) { // @formatter:on + this(completions, experimental, logging, prompts, resources, tools, null); + } + /** * Present if the server supports argument autocompletion suggestions. */ From b5d240d1d9409d4025bee9f1e970b45399e2a29e Mon Sep 17 00:00:00 2001 From: He-Pin Date: Mon, 1 Dec 2025 11:53:18 +0800 Subject: [PATCH 13/13] . Signed-off-by: He-Pin --- .../modelcontextprotocol/spec/McpMessage.java | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/spec/McpMessage.java diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpMessage.java b/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpMessage.java deleted file mode 100644 index 48d05c754..000000000 --- a/mcp-core/src/main/java/io/modelcontextprotocol/spec/McpMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -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 { - } - - } - -}