From 9977a064d51f4e26f95a3bdc715c616b4a37dc64 Mon Sep 17 00:00:00 2001 From: bzsurbhi Date: Thu, 3 Jul 2025 11:43:28 -0700 Subject: [PATCH] feat: Add title field support to MCP schema classes (#372) - Add BaseMetadata interface with name() and title() methods for consistent metadata handling - The Prompt, PromptArgument, Resource, ResourceTemplate, ResuorceLink, PromptReference, Tool, and Implementation classes implement the BaseMetadata interface, adding a new, optional title field - Update constructors to support backward compatibility while allowing title specification - Modify ResourceContent interface to extend BaseMetadata - Update all test cases to accommodate new title parameter The title field provides human-readable display names optimized for UI contexts, while name remains for programmatic/logical use. This change maintains backward compatibility. Signed-off-by: Christian Tzolov Co-authored-by: Christian Tzolov --- .../WebFluxSseIntegrationTests.java | 9 +- .../client/AbstractMcpAsyncClientTests.java | 4 +- .../server/AbstractMcpAsyncServerTests.java | 4 +- .../server/AbstractMcpSyncServerTests.java | 4 +- .../server/McpAsyncServer.java | 7 +- .../modelcontextprotocol/spec/McpSchema.java | 1694 +++++++++-------- .../client/AbstractMcpAsyncClientTests.java | 4 +- .../McpAsyncClientResponseHandlerTests.java | 4 +- .../server/AbstractMcpAsyncServerTests.java | 4 +- .../server/AbstractMcpSyncServerTests.java | 4 +- .../spec/McpSchemaTests.java | 36 +- 11 files changed, 949 insertions(+), 825 deletions(-) diff --git a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/WebFluxSseIntegrationTests.java b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/WebFluxSseIntegrationTests.java index 73aff46..c2ad1d6 100644 --- a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/WebFluxSseIntegrationTests.java +++ b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/WebFluxSseIntegrationTests.java @@ -996,11 +996,11 @@ class WebFluxSseIntegrationTests { var mcpServer = McpServer.sync(mcpServerTransportProvider) .capabilities(ServerCapabilities.builder().completions().build()) .prompts(new McpServerFeatures.SyncPromptSpecification( - new Prompt("code_review", "this is code review prompt", - List.of(new PromptArgument("language", "string", false))), + new Prompt("code_review", "Code review", "this is code review prompt", + List.of(new PromptArgument("language", "Language", "string", false))), (mcpSyncServerExchange, getPromptRequest) -> null)) .completions(new McpServerFeatures.SyncCompletionSpecification( - new McpSchema.PromptReference("ref/prompt", "code_review"), completionHandler)) + new McpSchema.PromptReference("ref/prompt", "code_review", "Code review"), completionHandler)) .build(); try (var mcpClient = clientBuilder.build()) { @@ -1008,7 +1008,8 @@ class WebFluxSseIntegrationTests { InitializeResult initResult = mcpClient.initialize(); assertThat(initResult).isNotNull(); - CompleteRequest request = new CompleteRequest(new PromptReference("ref/prompt", "code_review"), + CompleteRequest request = new CompleteRequest( + new PromptReference("ref/prompt", "code_review", "Code review"), new CompleteRequest.CompleteArgument("language", "py")); CompleteResult result = mcpClient.completeCompletion(request); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java index 9cccd39..659a322 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java @@ -420,7 +420,7 @@ public abstract class AbstractMcpAsyncClientTests { .consumeNextWith(result -> { assertThat(result.prompts()).isNotNull(); // Verify that the returned list is immutable - assertThatThrownBy(() -> result.prompts().add(new Prompt("test", "test", null))) + assertThatThrownBy(() -> result.prompts().add(new Prompt("test", "Test", "test", null))) .isInstanceOf(UnsupportedOperationException.class); }) .verifyComplete(); @@ -604,7 +604,7 @@ public abstract class AbstractMcpAsyncClientTests { assertThat(result.resourceTemplates()).isNotNull(); // Verify that the returned list is immutable assertThatThrownBy(() -> result.resourceTemplates() - .add(new McpSchema.ResourceTemplate("test://template", "test", null, null, null))) + .add(new McpSchema.ResourceTemplate("test://template", "test", "test", null, null, null))) .isInstanceOf(UnsupportedOperationException.class); }) .verifyComplete(); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java index 12827f4..1f67306 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java @@ -302,7 +302,7 @@ public abstract class AbstractMcpAsyncServerTests { .serverInfo("test-server", "1.0.0") .build(); - Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of()); + Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", "Test Prompt", List.of()); McpServerFeatures.AsyncPromptSpecification specification = new McpServerFeatures.AsyncPromptSpecification( prompt, (exchange, req) -> Mono.just(new GetPromptResult("Test prompt description", List .of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content")))))); @@ -330,7 +330,7 @@ public abstract class AbstractMcpAsyncServerTests { void testRemovePrompt() { String TEST_PROMPT_NAME_TO_REMOVE = "TEST_PROMPT_NAME678"; - Prompt prompt = new Prompt(TEST_PROMPT_NAME_TO_REMOVE, "Test Prompt", List.of()); + Prompt prompt = new Prompt(TEST_PROMPT_NAME_TO_REMOVE, "Test Prompt", "Test Prompt", List.of()); McpServerFeatures.AsyncPromptSpecification specification = new McpServerFeatures.AsyncPromptSpecification( prompt, (exchange, req) -> Mono.just(new GetPromptResult("Test prompt description", List .of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content")))))); diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java index eefcdf9..5dac6f6 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java @@ -289,7 +289,7 @@ public abstract class AbstractMcpSyncServerTests { .serverInfo("test-server", "1.0.0") .build(); - Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of()); + Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", "Test Prompt", List.of()); McpServerFeatures.SyncPromptSpecification specification = new McpServerFeatures.SyncPromptSpecification(prompt, (exchange, req) -> new GetPromptResult("Test prompt description", List .of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))); @@ -310,7 +310,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testRemovePrompt() { - Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of()); + Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", "Test Prompt", List.of()); McpServerFeatures.SyncPromptSpecification specification = new McpServerFeatures.SyncPromptSpecification(prompt, (exchange, req) -> new GetPromptResult("Test prompt description", List .of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))); diff --git a/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java b/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java index 02ad955..6efe58d 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java +++ b/mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java @@ -462,8 +462,8 @@ public class McpAsyncServer { .filter(uri -> uri.contains("{")) .map(uri -> { var resource = this.resources.get(uri).resource(); - var template = new McpSchema.ResourceTemplate(resource.uri(), resource.name(), resource.description(), - resource.mimeType(), resource.annotations()); + var template = new McpSchema.ResourceTemplate(resource.uri(), resource.name(), resource.title(), + resource.description(), resource.mimeType(), resource.annotations()); return template; }) .toList(); @@ -725,7 +725,8 @@ public class McpAsyncServer { String refType = (String) refMap.get("type"); McpSchema.CompleteReference ref = switch (refType) { - case "ref/prompt" -> new McpSchema.PromptReference(refType, (String) refMap.get("name")); + case "ref/prompt" -> new McpSchema.PromptReference(refType, (String) refMap.get("name"), + refMap.get("title") != null ? (String) refMap.get("title") : null); case "ref/resource" -> new McpSchema.ResourceReference(refType, (String) refMap.get("uri")); default -> throw new IllegalArgumentException("Invalid ref type: " + refType); }; diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index 9be585c..79534c8 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory; * * @author Christian Tzolov * @author Luca Chang + * @author Surbhi Bansal */ public final class McpSchema { @@ -203,40 +204,40 @@ public final class McpSchema { // TODO: batching support // @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) public record JSONRPCRequest( // @formatter:off - @JsonProperty("jsonrpc") String jsonrpc, - @JsonProperty("method") String method, - @JsonProperty("id") Object id, - @JsonProperty("params") Object params) implements JSONRPCMessage { - } // @formatter:on + @JsonProperty("jsonrpc") String jsonrpc, + @JsonProperty("method") String method, + @JsonProperty("id") Object id, + @JsonProperty("params") Object params) implements JSONRPCMessage { + } // @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) // TODO: batching support // @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) public record JSONRPCNotification( // @formatter:off - @JsonProperty("jsonrpc") String jsonrpc, - @JsonProperty("method") String method, - @JsonProperty("params") Object params) implements JSONRPCMessage { - } // @formatter:on + @JsonProperty("jsonrpc") String jsonrpc, + @JsonProperty("method") String method, + @JsonProperty("params") Object params) implements JSONRPCMessage { + } // @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) // TODO: batching support // @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) public record JSONRPCResponse( // @formatter:off - @JsonProperty("jsonrpc") String jsonrpc, - @JsonProperty("id") Object id, - @JsonProperty("result") Object result, - @JsonProperty("error") JSONRPCError error) implements JSONRPCMessage { + @JsonProperty("jsonrpc") String jsonrpc, + @JsonProperty("id") Object id, + @JsonProperty("result") Object result, + @JsonProperty("error") JSONRPCError error) implements JSONRPCMessage { - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record JSONRPCError( - @JsonProperty("code") int code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { - } - }// @formatter:on + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record JSONRPCError( + @JsonProperty("code") int code, + @JsonProperty("message") String message, + @JsonProperty("data") Object data) { + } + }// @formatter:on // --------------------------- // Initialization @@ -244,24 +245,24 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record InitializeRequest( // @formatter:off - @JsonProperty("protocolVersion") String protocolVersion, - @JsonProperty("capabilities") ClientCapabilities capabilities, - @JsonProperty("clientInfo") Implementation clientInfo, + @JsonProperty("protocolVersion") String protocolVersion, + @JsonProperty("capabilities") ClientCapabilities capabilities, + @JsonProperty("clientInfo") Implementation clientInfo, @JsonProperty("_meta") Map meta) implements Request { public InitializeRequest(String protocolVersion, ClientCapabilities capabilities, Implementation clientInfo) { this(protocolVersion, capabilities, clientInfo, null); } - } // @formatter:on + } // @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record InitializeResult( // @formatter:off - @JsonProperty("protocolVersion") String protocolVersion, - @JsonProperty("capabilities") ServerCapabilities capabilities, - @JsonProperty("serverInfo") Implementation serverInfo, - @JsonProperty("instructions") String instructions) { - } // @formatter:on + @JsonProperty("protocolVersion") String protocolVersion, + @JsonProperty("capabilities") ServerCapabilities capabilities, + @JsonProperty("serverInfo") Implementation serverInfo, + @JsonProperty("instructions") String instructions) { + } // @formatter:on /** * Clients can implement additional features to enrich connected MCP servers with @@ -282,184 +283,198 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ClientCapabilities( // @formatter:off - @JsonProperty("experimental") Map experimental, - @JsonProperty("roots") RootCapabilities roots, - @JsonProperty("sampling") Sampling sampling, - @JsonProperty("elicitation") Elicitation elicitation) { + @JsonProperty("experimental") Map experimental, + @JsonProperty("roots") RootCapabilities roots, + @JsonProperty("sampling") Sampling sampling, + @JsonProperty("elicitation") Elicitation elicitation) { - /** - * Roots define the boundaries of where servers can operate within the filesystem, - * allowing them to understand which directories and files they have access to. - * Servers can request the list of roots from supporting clients and - * receive notifications when that list changes. - * - * @param listChanged Whether the client would send notification about roots - * has changed since the last time the server checked. - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record RootCapabilities( - @JsonProperty("listChanged") Boolean listChanged) { - } + /** + * Roots define the boundaries of where servers can operate within the filesystem, + * allowing them to understand which directories and files they have access to. + * Servers can request the list of roots from supporting clients and + * receive notifications when that list changes. + * + * @param listChanged Whether the client would send notification about roots + * has changed since the last time the server checked. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record RootCapabilities( + @JsonProperty("listChanged") Boolean listChanged) { + } - /** - * Provides a standardized way for servers to request LLM - * sampling ("completions" or "generations") from language - * models via clients. This flow allows clients to maintain - * control over model access, selection, and permissions - * while enabling servers to leverage AI capabilities—with - * no server API keys necessary. Servers can request text or - * image-based interactions and optionally include context - * from MCP servers in their prompts. - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - public record Sampling() { - } + /** + * Provides a standardized way for servers to request LLM + * sampling ("completions" or "generations") from language + * models via clients. This flow allows clients to maintain + * control over model access, selection, and permissions + * while enabling servers to leverage AI capabilities—with + * no server API keys necessary. Servers can request text or + * image-based interactions and optionally include context + * from MCP servers in their prompts. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record Sampling() { + } - /** - * Provides a standardized way for servers to request additional - * information from users through the client during interactions. - * This flow allows clients to maintain control over user - * interactions and data sharing while enabling servers to gather - * necessary information dynamically. Servers can request structured - * data from users with optional JSON schemas to validate responses. - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - public record Elicitation() { - } + /** + * Provides a standardized way for servers to request additional + * information from users through the client during interactions. + * This flow allows clients to maintain control over user + * interactions and data sharing while enabling servers to gather + * necessary information dynamically. Servers can request structured + * data from users with optional JSON schemas to validate responses. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record Elicitation() { + } - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private Map experimental; - private RootCapabilities roots; - private Sampling sampling; - private Elicitation elicitation; + public static class Builder { + private Map experimental; + private RootCapabilities roots; + private Sampling sampling; + private Elicitation elicitation; - public Builder experimental(Map experimental) { - this.experimental = experimental; - return this; - } + public Builder experimental(Map experimental) { + this.experimental = experimental; + return this; + } - public Builder roots(Boolean listChanged) { - this.roots = new RootCapabilities(listChanged); - return this; - } + public Builder roots(Boolean listChanged) { + this.roots = new RootCapabilities(listChanged); + return this; + } - public Builder sampling() { - this.sampling = new Sampling(); - return this; - } + public Builder sampling() { + this.sampling = new Sampling(); + return this; + } - public Builder elicitation() { - this.elicitation = new Elicitation(); - return this; - } + public Builder elicitation() { + this.elicitation = new Elicitation(); + return this; + } - public ClientCapabilities build() { - return new ClientCapabilities(experimental, roots, sampling, elicitation); - } - } - }// @formatter:on + public ClientCapabilities build() { + return new ClientCapabilities(experimental, roots, sampling, elicitation); + } + } + }// @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ServerCapabilities( // @formatter:off - @JsonProperty("completions") CompletionCapabilities completions, - @JsonProperty("experimental") Map experimental, - @JsonProperty("logging") LoggingCapabilities logging, - @JsonProperty("prompts") PromptCapabilities prompts, - @JsonProperty("resources") ResourceCapabilities resources, - @JsonProperty("tools") ToolCapabilities tools) { + @JsonProperty("completions") CompletionCapabilities completions, + @JsonProperty("experimental") Map experimental, + @JsonProperty("logging") LoggingCapabilities logging, + @JsonProperty("prompts") PromptCapabilities prompts, + @JsonProperty("resources") ResourceCapabilities resources, + @JsonProperty("tools") ToolCapabilities tools) { - @JsonInclude(JsonInclude.Include.NON_ABSENT) - public record CompletionCapabilities() { - } + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record CompletionCapabilities() { + } - @JsonInclude(JsonInclude.Include.NON_ABSENT) - public record LoggingCapabilities() { - } + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record LoggingCapabilities() { + } - @JsonInclude(JsonInclude.Include.NON_ABSENT) - public record PromptCapabilities( - @JsonProperty("listChanged") Boolean listChanged) { - } + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record PromptCapabilities( + @JsonProperty("listChanged") Boolean listChanged) { + } - @JsonInclude(JsonInclude.Include.NON_ABSENT) - public record ResourceCapabilities( - @JsonProperty("subscribe") Boolean subscribe, - @JsonProperty("listChanged") Boolean listChanged) { - } + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record ResourceCapabilities( + @JsonProperty("subscribe") Boolean subscribe, + @JsonProperty("listChanged") Boolean listChanged) { + } - @JsonInclude(JsonInclude.Include.NON_ABSENT) - public record ToolCapabilities( - @JsonProperty("listChanged") Boolean listChanged) { - } + @JsonInclude(JsonInclude.Include.NON_ABSENT) + public record ToolCapabilities( + @JsonProperty("listChanged") Boolean listChanged) { + } - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { + public static class Builder { - private CompletionCapabilities completions; - private Map experimental; - private LoggingCapabilities logging = new LoggingCapabilities(); - private PromptCapabilities prompts; - private ResourceCapabilities resources; - private ToolCapabilities tools; + private CompletionCapabilities completions; + private Map experimental; + private LoggingCapabilities logging = new LoggingCapabilities(); + private PromptCapabilities prompts; + private ResourceCapabilities resources; + private ToolCapabilities tools; - public Builder completions() { - this.completions = new CompletionCapabilities(); - return this; - } + public Builder completions() { + this.completions = new CompletionCapabilities(); + return this; + } - public Builder experimental(Map experimental) { - this.experimental = experimental; - return this; - } + public Builder experimental(Map experimental) { + this.experimental = experimental; + return this; + } - public Builder logging() { - this.logging = new LoggingCapabilities(); - return this; - } + public Builder logging() { + this.logging = new LoggingCapabilities(); + return this; + } - public Builder prompts(Boolean listChanged) { - this.prompts = new PromptCapabilities(listChanged); - return this; - } + public Builder prompts(Boolean listChanged) { + this.prompts = new PromptCapabilities(listChanged); + return this; + } - public Builder resources(Boolean subscribe, Boolean listChanged) { - this.resources = new ResourceCapabilities(subscribe, listChanged); - return this; - } + public Builder resources(Boolean subscribe, Boolean listChanged) { + this.resources = new ResourceCapabilities(subscribe, listChanged); + return this; + } - public Builder tools(Boolean listChanged) { - this.tools = new ToolCapabilities(listChanged); - return this; - } + public Builder tools(Boolean listChanged) { + this.tools = new ToolCapabilities(listChanged); + return this; + } - public ServerCapabilities build() { - return new ServerCapabilities(completions, experimental, logging, prompts, resources, tools); - } - } - } // @formatter:on + public ServerCapabilities build() { + return new ServerCapabilities(completions, experimental, logging, prompts, resources, tools); + } + } + } // @formatter:on + /** + * Describes the name and version of an MCP implementation, with an optional title for + * UI representation. + * + * @param name Intended for programmatic or logical use, but used as a display name in + * past specs or fallback (if title isn't present). + * @param title Intended for UI and end-user contexts + * @param version The version of the implementation. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record Implementation(// @formatter:off - @JsonProperty("name") String name, - @JsonProperty("version") String version) { - } // @formatter:on + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("version") String version) implements BaseMetadata {// @formatter:on + + public Implementation(String name, String version) { + this(name, null, version); + } + } // Existing Enums and Base Types (from previous implementation) public enum Role {// @formatter:off - @JsonProperty("user") USER, - @JsonProperty("assistant") ASSISTANT - }// @formatter:on + @JsonProperty("user") USER, + @JsonProperty("assistant") ASSISTANT + }// @formatter:on // --------------------------- // Resource Interfaces @@ -489,9 +504,9 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record Annotations( // @formatter:off - @JsonProperty("audience") List audience, - @JsonProperty("priority") Double priority) { - } // @formatter:on + @JsonProperty("audience") List audience, + @JsonProperty("priority") Double priority) { + } // @formatter:on /** * A common interface for resource content, which includes metadata about the resource @@ -499,12 +514,10 @@ public final class McpSchema { * interface is implemented by both {@link Resource} and {@link ResourceLink} to * provide a consistent way to access resource metadata. */ - public interface ResourceContent { + public interface ResourceContent extends BaseMetadata { String uri(); - String name(); - String description(); String mimeType(); @@ -515,12 +528,35 @@ public final class McpSchema { } + /** + * Base interface for metadata with name (identifier) and title (display name) + * properties. + */ + public interface BaseMetadata { + + /** + * Intended for programmatic or logical use, but used as a display name in past + * specs or fallback (if title isn't present). + */ + String name(); + + /** + * Intended for UI and end-user contexts — optimized to be human-readable and + * easily understood, even by those unfamiliar with domain-specific terminology. + * + * If not provided, the name should be used for display. + */ + String title(); + + } + /** * A known resource that the server is capable of reading. * * @param uri the URI of the resource. * @param name A human-readable name for this resource. This can be used by clients to * populate UI elements. + * @param title An optional title for this resource. * @param description A description of what this resource represents. This can be used * by clients to improve the LLM's understanding of available resources. It can be * thought of like a "hint" to the model. @@ -534,72 +570,87 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record Resource( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("name") String name, - @JsonProperty("description") String description, - @JsonProperty("mimeType") String mimeType, - @JsonProperty("size") Long size, - @JsonProperty("annotations") Annotations annotations) implements Annotated, ResourceContent { + @JsonProperty("uri") String uri, + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("mimeType") String mimeType, + @JsonProperty("size") Long size, + @JsonProperty("annotations") Annotations annotations) implements Annotated, ResourceContent {// @formatter:on - /** - * @deprecated Only exists for backwards-compatibility purposes. Use - * {@link Resource#builder()} instead. - */ - @Deprecated - public Resource(String uri, String name, String description, String mimeType, Annotations annotations) { - this(uri, name, description, mimeType, null, annotations); - } + /** + * @deprecated Only exists for backwards-compatibility purposes. Use + * {@link Resource#builder()} instead. + */ + @Deprecated + public Resource(String uri, String name, String description, String mimeType, Annotations annotations) { + this(uri, name, null, description, mimeType, null, annotations); + } - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private String uri; - private String name; - private String description; - private String mimeType; - private Long size; - private Annotations annotations; + public static class Builder { - public Builder uri(String uri) { - this.uri = uri; - return this; - } + private String uri; - public Builder name(String name) { - this.name = name; - return this; - } + private String name; - public Builder description(String description) { - this.description = description; - return this; - } + private String title; - public Builder mimeType(String mimeType) { - this.mimeType = mimeType; - return this; - } + private String description; - public Builder size(Long size) { - this.size = size; - return this; - } + private String mimeType; - public Builder annotations(Annotations annotations) { - this.annotations = annotations; - return this; - } + private Long size; - public Resource build() { - Assert.hasText(uri, "uri must not be empty"); - Assert.hasText(name, "name must not be empty"); + private Annotations annotations; - return new Resource(uri, name, description, mimeType, size, annotations); - } - } - } // @formatter:on + public Builder uri(String uri) { + this.uri = uri; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder title(String title) { + this.title = title; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder mimeType(String mimeType) { + this.mimeType = mimeType; + return this; + } + + public Builder size(Long size) { + this.size = size; + return this; + } + + public Builder annotations(Annotations annotations) { + this.annotations = annotations; + return this; + } + + public Resource build() { + Assert.hasText(uri, "uri must not be empty"); + Assert.hasText(name, "name must not be empty"); + + return new Resource(uri, name, title, description, mimeType, size, annotations); + } + + } + } /** * Resource templates allow servers to expose parameterized resources using URI @@ -609,6 +660,7 @@ public final class McpSchema { * resource. * @param name A human-readable name for this resource. This can be used by clients to * populate UI elements. + * @param title An optional title for this resource. * @param description A description of what this resource represents. This can be used * by clients to improve the LLM's understanding of available resources. It can be * thought of like a "hint" to the model. @@ -620,43 +672,44 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ResourceTemplate( // @formatter:off - @JsonProperty("uriTemplate") String uriTemplate, - @JsonProperty("name") String name, - @JsonProperty("description") String description, - @JsonProperty("mimeType") String mimeType, - @JsonProperty("annotations") Annotations annotations) implements Annotated { - } // @formatter:on + @JsonProperty("uriTemplate") String uriTemplate, + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("mimeType") String mimeType, + @JsonProperty("annotations") Annotations annotations) implements Annotated, BaseMetadata { + } // @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ListResourcesResult( // @formatter:off - @JsonProperty("resources") List resources, - @JsonProperty("nextCursor") String nextCursor) { - } // @formatter:on + @JsonProperty("resources") List resources, + @JsonProperty("nextCursor") String nextCursor) { + } // @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ListResourceTemplatesResult( // @formatter:off - @JsonProperty("resourceTemplates") List resourceTemplates, - @JsonProperty("nextCursor") String nextCursor) { - } // @formatter:on + @JsonProperty("resourceTemplates") List resourceTemplates, + @JsonProperty("nextCursor") String nextCursor) { + } // @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ReadResourceRequest( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("_meta") Map meta) implements Request { + @JsonProperty("uri") String uri, + @JsonProperty("_meta") Map meta) implements Request {// @formatter:on - public ReadResourceRequest(String uri) { - this(uri, null); - } - } // @formatter:on + public ReadResourceRequest(String uri) { + this(uri, null); + } + } @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ReadResourceResult( // @formatter:off - @JsonProperty("contents") List contents){ - } // @formatter:on + @JsonProperty("contents") List contents){ + } // @formatter:on /** * Sent from the client to request resources/updated notifications from the server @@ -668,14 +721,14 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record SubscribeRequest( // @formatter:off - @JsonProperty("uri") String uri){ - } // @formatter:on + @JsonProperty("uri") String uri){ + } // @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record UnsubscribeRequest( // @formatter:off - @JsonProperty("uri") String uri){ - } // @formatter:on + @JsonProperty("uri") String uri){ + } // @formatter:on /** * The contents of a specific resource or sub-resource. @@ -710,10 +763,10 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record TextResourceContents( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("mimeType") String mimeType, - @JsonProperty("text") String text) implements ResourceContents { - } // @formatter:on + @JsonProperty("uri") String uri, + @JsonProperty("mimeType") String mimeType, + @JsonProperty("text") String text) implements ResourceContents { + } // @formatter:on /** * Binary contents of a resource. @@ -727,10 +780,10 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record BlobResourceContents( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("mimeType") String mimeType, - @JsonProperty("blob") String blob) implements ResourceContents { - } // @formatter:on + @JsonProperty("uri") String uri, + @JsonProperty("mimeType") String mimeType, + @JsonProperty("blob") String blob) implements ResourceContents { + } // @formatter:on // --------------------------- // Prompt Interfaces @@ -739,31 +792,35 @@ public final class McpSchema { * A prompt or prompt template that the server offers. * * @param name The name of the prompt or prompt template. + * @param title An optional title for the prompt. * @param description An optional description of what this prompt provides. * @param arguments A list of arguments to use for templating the prompt. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record Prompt( // @formatter:off - @JsonProperty("name") String name, - @JsonProperty("description") String description, - @JsonProperty("arguments") List arguments) { - } // @formatter:on + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("arguments") List arguments) implements BaseMetadata { + } // @formatter:on /** * Describes an argument that a prompt can accept. * * @param name The name of the argument. + * @param title An optional title for the argument, which can be used in UI * @param description A human-readable description of the argument. * @param required Whether this argument must be provided. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record PromptArgument( // @formatter:off - @JsonProperty("name") String name, - @JsonProperty("description") String description, - @JsonProperty("required") Boolean required) { - }// @formatter:on + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("required") Boolean required) implements BaseMetadata { + }// @formatter:on /** * Describes a message returned as part of a prompt. @@ -777,9 +834,9 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record PromptMessage( // @formatter:off - @JsonProperty("role") Role role, - @JsonProperty("content") Content content) { - } // @formatter:on + @JsonProperty("role") Role role, + @JsonProperty("content") Content content) { + } // @formatter:on /** * The server's response to a prompts/list request from the client. @@ -791,9 +848,9 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ListPromptsResult( // @formatter:off - @JsonProperty("prompts") List prompts, - @JsonProperty("nextCursor") String nextCursor) { - }// @formatter:on + @JsonProperty("prompts") List prompts, + @JsonProperty("nextCursor") String nextCursor) { + }// @formatter:on /** * Used by the client to get a prompt provided by the server. @@ -804,27 +861,27 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record GetPromptRequest(// @formatter:off - @JsonProperty("name") String name, - @JsonProperty("arguments") Map arguments, - @JsonProperty("_meta") Map meta) implements Request { + @JsonProperty("name") String name, + @JsonProperty("arguments") Map arguments, + @JsonProperty("_meta") Map meta) implements Request { - public GetPromptRequest(String name, Map arguments) { - this(name, arguments, null); - } - }// @formatter:off + public GetPromptRequest(String name, Map arguments) { + this(name, arguments, null); + } + }// @formatter:off - /** - * The server's response to a prompts/get request from the client. - * - * @param description An optional description for the prompt. - * @param messages A list of messages to display as part of the prompt. - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record GetPromptResult( // @formatter:off - @JsonProperty("description") String description, - @JsonProperty("messages") List messages) { - } // @formatter:on + /** + * The server's response to a prompts/get request from the client. + * + * @param description An optional description for the prompt. + * @param messages A list of messages to display as part of the prompt. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record GetPromptResult( // @formatter:off + @JsonProperty("description") String description, + @JsonProperty("messages") List messages) { + } // @formatter:on // --------------------------- // Tool Interfaces @@ -839,20 +896,20 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ListToolsResult( // @formatter:off - @JsonProperty("tools") List tools, - @JsonProperty("nextCursor") String nextCursor) { - }// @formatter:on + @JsonProperty("tools") List tools, + @JsonProperty("nextCursor") String nextCursor) { + }// @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record JsonSchema( // @formatter:off - @JsonProperty("type") String type, - @JsonProperty("properties") Map properties, - @JsonProperty("required") List required, - @JsonProperty("additionalProperties") Boolean additionalProperties, - @JsonProperty("$defs") Map defs, - @JsonProperty("definitions") Map definitions) { - } // @formatter:on + @JsonProperty("type") String type, + @JsonProperty("properties") Map properties, + @JsonProperty("required") List required, + @JsonProperty("additionalProperties") Boolean additionalProperties, + @JsonProperty("$defs") Map defs, + @JsonProperty("definitions") Map definitions) { + } // @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -863,7 +920,7 @@ public final class McpSchema { @JsonProperty("idempotentHint") Boolean idempotentHint, @JsonProperty("openWorldHint") Boolean openWorldHint, @JsonProperty("returnDirect") Boolean returnDirect) { - } // @formatter:on + } // @formatter:on /** * Represents a tool that the server provides. Tools enable servers to expose @@ -872,6 +929,7 @@ public final class McpSchema { * * @param name A unique identifier for the tool. This name is used when calling the * tool. + * @param title A human-readable title for the tool. * @param description A human-readable description of what the tool does. This can be * used by clients to improve the LLM's understanding of available tools. * @param inputSchema A JSON Schema object that describes the expected structure of @@ -883,19 +941,69 @@ public final class McpSchema { @JsonIgnoreProperties(ignoreUnknown = true) public record Tool( // @formatter:off @JsonProperty("name") String name, + @JsonProperty("title") String title, @JsonProperty("description") String description, @JsonProperty("inputSchema") JsonSchema inputSchema, - @JsonProperty("annotations") ToolAnnotations annotations) { + @JsonProperty("annotations") ToolAnnotations annotations) implements BaseMetadata { // @formatter:on - public Tool(String name, String description, String schema) { - this(name, description, parseSchema(schema), null); - } + public Tool(String name, String description, String schema) { + this(name, null, description, parseSchema(schema), null); + } - public Tool(String name, String description, String schema, ToolAnnotations annotations) { - this(name, description, parseSchema(schema), annotations); - } + public Tool(String name, String description, String schema, ToolAnnotations annotations) { + this(name, null, description, parseSchema(schema), annotations); + } - } // @formatter:on + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String name; + + private String title; + + private String description; + + private JsonSchema inputSchema; + + private ToolAnnotations annotations; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder title(String title) { + this.title = title; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder inputSchema(JsonSchema inputSchema) { + this.inputSchema = inputSchema; + return this; + } + + public Builder annotations(ToolAnnotations annotations) { + this.annotations = annotations; + return this; + } + + public Tool build() { + Assert.hasText(name, "name must not be empty"); + + return new Tool(name, title, description, inputSchema, annotations); + } + + } + + } private static JsonSchema parseSchema(String schema) { try { @@ -917,181 +1025,181 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record CallToolRequest(// @formatter:off - @JsonProperty("name") String name, - @JsonProperty("arguments") Map arguments, - @JsonProperty("_meta") Map meta) implements Request { + @JsonProperty("name") String name, + @JsonProperty("arguments") Map arguments, + @JsonProperty("_meta") Map meta) implements Request { - public CallToolRequest(String name, String jsonArguments) { - this(name, parseJsonArguments(jsonArguments), null); - } - public CallToolRequest(String name, Map arguments) { - this(name, arguments, null); - } + public CallToolRequest(String name, String jsonArguments) { + this(name, parseJsonArguments(jsonArguments), null); + } + public CallToolRequest(String name, Map arguments) { + this(name, arguments, null); + } - private static Map parseJsonArguments(String jsonArguments) { - try { - return OBJECT_MAPPER.readValue(jsonArguments, MAP_TYPE_REF); - } - catch (IOException e) { - throw new IllegalArgumentException("Invalid arguments: " + jsonArguments, e); - } - } + private static Map parseJsonArguments(String jsonArguments) { + try { + return OBJECT_MAPPER.readValue(jsonArguments, MAP_TYPE_REF); + } + catch (IOException e) { + throw new IllegalArgumentException("Invalid arguments: " + jsonArguments, e); + } + } - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private String name; - private Map arguments; - private Map meta; + public static class Builder { + private String name; + private Map arguments; + private Map meta; - public Builder name(String name) { - this.name = name; - return this; - } + public Builder name(String name) { + this.name = name; + return this; + } - public Builder arguments(Map arguments) { - this.arguments = arguments; - return this; - } + public Builder arguments(Map arguments) { + this.arguments = arguments; + return this; + } - public Builder arguments(String jsonArguments) { - this.arguments = parseJsonArguments(jsonArguments); - return this; - } + public Builder arguments(String jsonArguments) { + this.arguments = parseJsonArguments(jsonArguments); + return this; + } - public Builder meta(Map meta) { - this.meta = meta; - return this; - } + public Builder meta(Map meta) { + this.meta = meta; + return this; + } - public Builder progressToken(String progressToken) { - if (this.meta == null) { - this.meta = new HashMap<>(); - } - this.meta.put("progressToken", progressToken); - return this; - } + public Builder progressToken(String progressToken) { + if (this.meta == null) { + this.meta = new HashMap<>(); + } + this.meta.put("progressToken", progressToken); + return this; + } - public CallToolRequest build() { - Assert.hasText(name, "name must not be empty"); - return new CallToolRequest(name, arguments, meta); - } - } - }// @formatter:off + public CallToolRequest build() { + Assert.hasText(name, "name must not be empty"); + return new CallToolRequest(name, arguments, meta); + } + } + }// @formatter:off - /** - * The server's response to a tools/call request from the client. - * - * @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. - */ - @JsonInclude(JsonInclude.Include.NON_ABSENT) - @JsonIgnoreProperties(ignoreUnknown = true) - public record CallToolResult( // @formatter:off - @JsonProperty("content") List content, - @JsonProperty("isError") Boolean isError) { + /** + * The server's response to a tools/call request from the client. + * + * @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. + */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) + @JsonIgnoreProperties(ignoreUnknown = true) + public record CallToolResult( // @formatter:off + @JsonProperty("content") List content, + @JsonProperty("isError") Boolean isError) { - /** - * Creates a new instance of {@link CallToolResult} with a string containing the - * tool result. - * - * @param content The content of the tool result. This will be mapped to a one-sized list - * with a {@link TextContent} element. - * @param isError If true, indicates that the tool execution failed and the content contains error information. - * If false or absent, indicates successful execution. - */ - public CallToolResult(String content, Boolean isError) { - this(List.of(new TextContent(content)), isError); - } + /** + * Creates a new instance of {@link CallToolResult} with a string containing the + * tool result. + * + * @param content The content of the tool result. This will be mapped to a one-sized list + * with a {@link TextContent} element. + * @param isError If true, indicates that the tool execution failed and the content contains error information. + * If false or absent, indicates successful execution. + */ + public CallToolResult(String content, Boolean isError) { + this(List.of(new TextContent(content)), isError); + } - /** - * Creates a builder for {@link CallToolResult}. - * @return a new builder instance - */ - public static Builder builder() { - return new Builder(); - } + /** + * Creates a builder for {@link CallToolResult}. + * @return a new builder instance + */ + public static Builder builder() { + return new Builder(); + } - /** - * Builder for {@link CallToolResult}. - */ - public static class Builder { - private List content = new ArrayList<>(); - private Boolean isError; + /** + * Builder for {@link CallToolResult}. + */ + public static class Builder { + private List content = new ArrayList<>(); + private Boolean isError; - /** - * Sets the content list for the tool result. - * @param content the content list - * @return this builder - */ - public Builder content(List content) { - Assert.notNull(content, "content must not be null"); - this.content = content; - return this; - } + /** + * Sets the content list for the tool result. + * @param content the content list + * @return this builder + */ + public Builder content(List content) { + Assert.notNull(content, "content must not be null"); + this.content = content; + return this; + } - /** - * Sets the text content for the tool result. - * @param textContent the text content - * @return this builder - */ - public Builder textContent(List textContent) { - Assert.notNull(textContent, "textContent must not be null"); - textContent.stream() - .map(TextContent::new) - .forEach(this.content::add); - return this; - } + /** + * Sets the text content for the tool result. + * @param textContent the text content + * @return this builder + */ + public Builder textContent(List textContent) { + Assert.notNull(textContent, "textContent must not be null"); + textContent.stream() + .map(TextContent::new) + .forEach(this.content::add); + return this; + } - /** - * Adds a content item to the tool result. - * @param contentItem the content item to add - * @return this builder - */ - public Builder addContent(Content contentItem) { - Assert.notNull(contentItem, "contentItem must not be null"); - if (this.content == null) { - this.content = new ArrayList<>(); - } - this.content.add(contentItem); - return this; - } + /** + * Adds a content item to the tool result. + * @param contentItem the content item to add + * @return this builder + */ + public Builder addContent(Content contentItem) { + Assert.notNull(contentItem, "contentItem must not be null"); + if (this.content == null) { + this.content = new ArrayList<>(); + } + this.content.add(contentItem); + return this; + } - /** - * Adds a text content item to the tool result. - * @param text the text content - * @return this builder - */ - public Builder addTextContent(String text) { - Assert.notNull(text, "text must not be null"); - return addContent(new TextContent(text)); - } + /** + * Adds a text content item to the tool result. + * @param text the text content + * @return this builder + */ + public Builder addTextContent(String text) { + Assert.notNull(text, "text must not be null"); + return addContent(new TextContent(text)); + } - /** - * Sets whether the tool execution resulted in an error. - * @param isError true if the tool execution failed, false otherwise - * @return this builder - */ - public Builder isError(Boolean isError) { - Assert.notNull(isError, "isError must not be null"); - this.isError = isError; - return this; - } + /** + * Sets whether the tool execution resulted in an error. + * @param isError true if the tool execution failed, false otherwise + * @return this builder + */ + public Builder isError(Boolean isError) { + Assert.notNull(isError, "isError must not be null"); + this.isError = isError; + return this; + } - /** - * Builds a new {@link CallToolResult} instance. - * @return a new CallToolResult instance - */ - public CallToolResult build() { - return new CallToolResult(content, isError); - } - } + /** + * Builds a new {@link CallToolResult} instance. + * @return a new CallToolResult instance + */ + public CallToolResult build() { + return new CallToolResult(content, isError); + } + } - } // @formatter:on + } // @formatter:on // --------------------------- // Sampling Interfaces @@ -1099,53 +1207,53 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ModelPreferences(// @formatter:off - @JsonProperty("hints") List hints, - @JsonProperty("costPriority") Double costPriority, - @JsonProperty("speedPriority") Double speedPriority, - @JsonProperty("intelligencePriority") Double intelligencePriority) { + @JsonProperty("hints") List hints, + @JsonProperty("costPriority") Double costPriority, + @JsonProperty("speedPriority") Double speedPriority, + @JsonProperty("intelligencePriority") Double intelligencePriority) { - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private List hints; - private Double costPriority; - private Double speedPriority; - private Double intelligencePriority; + public static class Builder { + private List hints; + private Double costPriority; + private Double speedPriority; + private Double intelligencePriority; - public Builder hints(List hints) { - this.hints = hints; - return this; - } + public Builder hints(List hints) { + this.hints = hints; + return this; + } - public Builder addHint(String name) { - if (this.hints == null) { - this.hints = new ArrayList<>(); - } - this.hints.add(new ModelHint(name)); - return this; - } + public Builder addHint(String name) { + if (this.hints == null) { + this.hints = new ArrayList<>(); + } + this.hints.add(new ModelHint(name)); + return this; + } - public Builder costPriority(Double costPriority) { - this.costPriority = costPriority; - return this; - } + public Builder costPriority(Double costPriority) { + this.costPriority = costPriority; + return this; + } - public Builder speedPriority(Double speedPriority) { - this.speedPriority = speedPriority; - return this; - } + public Builder speedPriority(Double speedPriority) { + this.speedPriority = speedPriority; + return this; + } - public Builder intelligencePriority(Double intelligencePriority) { - this.intelligencePriority = intelligencePriority; - return this; - } + public Builder intelligencePriority(Double intelligencePriority) { + this.intelligencePriority = intelligencePriority; + return this; + } - public ModelPreferences build() { - return new ModelPreferences(hints, costPriority, speedPriority, intelligencePriority); - } - } + public ModelPreferences build() { + return new ModelPreferences(hints, costPriority, speedPriority, intelligencePriority); + } + } } // @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @@ -1159,22 +1267,22 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record SamplingMessage(// @formatter:off - @JsonProperty("role") Role role, - @JsonProperty("content") Content content) { - } // @formatter:on + @JsonProperty("role") Role role, + @JsonProperty("content") Content content) { + } // @formatter:on // Sampling and Message Creation @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record CreateMessageRequest(// @formatter:off - @JsonProperty("messages") List messages, - @JsonProperty("modelPreferences") ModelPreferences modelPreferences, - @JsonProperty("systemPrompt") String systemPrompt, - @JsonProperty("includeContext") ContextInclusionStrategy includeContext, - @JsonProperty("temperature") Double temperature, - @JsonProperty("maxTokens") int maxTokens, - @JsonProperty("stopSequences") List stopSequences, - @JsonProperty("metadata") Map metadata, + @JsonProperty("messages") List messages, + @JsonProperty("modelPreferences") ModelPreferences modelPreferences, + @JsonProperty("systemPrompt") String systemPrompt, + @JsonProperty("includeContext") ContextInclusionStrategy includeContext, + @JsonProperty("temperature") Double temperature, + @JsonProperty("maxTokens") int maxTokens, + @JsonProperty("stopSequences") List stopSequences, + @JsonProperty("metadata") Map metadata, @JsonProperty("_meta") Map meta) implements Request { @@ -1187,66 +1295,66 @@ public final class McpSchema { stopSequences, metadata, null); } - public enum ContextInclusionStrategy { - @JsonProperty("none") NONE, - @JsonProperty("thisServer") THIS_SERVER, - @JsonProperty("allServers") ALL_SERVERS - } + public enum ContextInclusionStrategy { + @JsonProperty("none") NONE, + @JsonProperty("thisServer") THIS_SERVER, + @JsonProperty("allServers") ALL_SERVERS + } - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private List messages; - private ModelPreferences modelPreferences; - private String systemPrompt; - private ContextInclusionStrategy includeContext; - private Double temperature; - private int maxTokens; - private List stopSequences; - private Map metadata; + public static class Builder { + private List messages; + private ModelPreferences modelPreferences; + private String systemPrompt; + private ContextInclusionStrategy includeContext; + private Double temperature; + private int maxTokens; + private List stopSequences; + private Map metadata; private Map meta; - public Builder messages(List messages) { - this.messages = messages; - return this; - } + public Builder messages(List messages) { + this.messages = messages; + return this; + } - public Builder modelPreferences(ModelPreferences modelPreferences) { - this.modelPreferences = modelPreferences; - return this; - } + public Builder modelPreferences(ModelPreferences modelPreferences) { + this.modelPreferences = modelPreferences; + return this; + } - public Builder systemPrompt(String systemPrompt) { - this.systemPrompt = systemPrompt; - return this; - } + public Builder systemPrompt(String systemPrompt) { + this.systemPrompt = systemPrompt; + return this; + } - public Builder includeContext(ContextInclusionStrategy includeContext) { - this.includeContext = includeContext; - return this; - } + public Builder includeContext(ContextInclusionStrategy includeContext) { + this.includeContext = includeContext; + return this; + } - public Builder temperature(Double temperature) { - this.temperature = temperature; - return this; - } + public Builder temperature(Double temperature) { + this.temperature = temperature; + return this; + } - public Builder maxTokens(int maxTokens) { - this.maxTokens = maxTokens; - return this; - } + public Builder maxTokens(int maxTokens) { + this.maxTokens = maxTokens; + return this; + } - public Builder stopSequences(List stopSequences) { - this.stopSequences = stopSequences; - return this; - } + public Builder stopSequences(List stopSequences) { + this.stopSequences = stopSequences; + return this; + } - public Builder metadata(Map metadata) { - this.metadata = metadata; - return this; - } + public Builder metadata(Map metadata) { + this.metadata = metadata; + return this; + } public Builder meta(Map meta) { this.meta = meta; @@ -1261,82 +1369,82 @@ public final class McpSchema { return this; } - public CreateMessageRequest build() { - return new CreateMessageRequest(messages, modelPreferences, systemPrompt, - includeContext, temperature, maxTokens, stopSequences, metadata, meta); - } - } - }// @formatter:on + public CreateMessageRequest build() { + return new CreateMessageRequest(messages, modelPreferences, systemPrompt, + includeContext, temperature, maxTokens, stopSequences, metadata, meta); + } + } + }// @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record CreateMessageResult(// @formatter:off - @JsonProperty("role") Role role, - @JsonProperty("content") Content content, - @JsonProperty("model") String model, - @JsonProperty("stopReason") StopReason stopReason) { + @JsonProperty("role") Role role, + @JsonProperty("content") Content content, + @JsonProperty("model") String model, + @JsonProperty("stopReason") StopReason stopReason) { - public enum StopReason { - @JsonProperty("endTurn") END_TURN("endTurn"), - @JsonProperty("stopSequence") STOP_SEQUENCE("stopSequence"), - @JsonProperty("maxTokens") MAX_TOKENS("maxTokens"), - @JsonProperty("unknown") UNKNOWN("unknown"); + public enum StopReason { + @JsonProperty("endTurn") END_TURN("endTurn"), + @JsonProperty("stopSequence") STOP_SEQUENCE("stopSequence"), + @JsonProperty("maxTokens") MAX_TOKENS("maxTokens"), + @JsonProperty("unknown") UNKNOWN("unknown"); - private final String value; + private final String value; - StopReason(String value) { - this.value = value; - } + StopReason(String value) { + this.value = value; + } - @JsonCreator - private static StopReason of(String value) { - return Arrays.stream(StopReason.values()) - .filter(stopReason -> stopReason.value.equals(value)) - .findFirst() - .orElse(StopReason.UNKNOWN); - } - } + @JsonCreator + private static StopReason of(String value) { + return Arrays.stream(StopReason.values()) + .filter(stopReason -> stopReason.value.equals(value)) + .findFirst() + .orElse(StopReason.UNKNOWN); + } + } - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private Role role = Role.ASSISTANT; - private Content content; - private String model; - private StopReason stopReason = StopReason.END_TURN; + public static class Builder { + private Role role = Role.ASSISTANT; + private Content content; + private String model; + private StopReason stopReason = StopReason.END_TURN; - public Builder role(Role role) { - this.role = role; - return this; - } + public Builder role(Role role) { + this.role = role; + return this; + } - public Builder content(Content content) { - this.content = content; - return this; - } + public Builder content(Content content) { + this.content = content; + return this; + } - public Builder model(String model) { - this.model = model; - return this; - } + public Builder model(String model) { + this.model = model; + return this; + } - public Builder stopReason(StopReason stopReason) { - this.stopReason = stopReason; - return this; - } + public Builder stopReason(StopReason stopReason) { + this.stopReason = stopReason; + return this; + } - public Builder message(String message) { - this.content = new TextContent(message); - return this; - } + public Builder message(String message) { + this.content = new TextContent(message); + return this; + } - public CreateMessageResult build() { - return new CreateMessageResult(role, content, model, stopReason); - } - } - }// @formatter:on + public CreateMessageResult build() { + return new CreateMessageResult(role, content, model, stopReason); + } + } + }// @formatter:on // Elicitation /** @@ -1348,8 +1456,8 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ElicitRequest(// @formatter:off - @JsonProperty("message") String message, - @JsonProperty("requestedSchema") Map requestedSchema, + @JsonProperty("message") String message, + @JsonProperty("requestedSchema") Map requestedSchema, @JsonProperty("_meta") Map meta) implements Request { // backwards compatibility constructor @@ -1357,24 +1465,24 @@ public final class McpSchema { this(message, requestedSchema, null); } - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private String message; - private Map requestedSchema; + public static class Builder { + private String message; + private Map requestedSchema; private Map meta; - public Builder message(String message) { - this.message = message; - return this; - } + public Builder message(String message) { + this.message = message; + return this; + } - public Builder requestedSchema(Map requestedSchema) { - this.requestedSchema = requestedSchema; - return this; - } + public Builder requestedSchema(Map requestedSchema) { + this.requestedSchema = requestedSchema; + return this; + } public Builder meta(Map meta) { this.meta = meta; @@ -1389,47 +1497,47 @@ public final class McpSchema { return this; } - public ElicitRequest build() { - return new ElicitRequest(message, requestedSchema, meta); - } - } - }// @formatter:on + public ElicitRequest build() { + return new ElicitRequest(message, requestedSchema, meta); + } + } + }// @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ElicitResult(// @formatter:off - @JsonProperty("action") Action action, - @JsonProperty("content") Map content) { + @JsonProperty("action") Action action, + @JsonProperty("content") Map content) { - public enum Action { - @JsonProperty("accept") ACCEPT, - @JsonProperty("decline") DECLINE, - @JsonProperty("cancel") CANCEL - } + public enum Action { + @JsonProperty("accept") ACCEPT, + @JsonProperty("decline") DECLINE, + @JsonProperty("cancel") CANCEL + } - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private Action action; - private Map content; + public static class Builder { + private Action action; + private Map content; - public Builder message(Action action) { - this.action = action; - return this; - } + public Builder message(Action action) { + this.action = action; + return this; + } - public Builder content(Map content) { - this.content = content; - return this; - } + public Builder content(Map content) { + this.content = content; + return this; + } - public ElicitResult build() { - return new ElicitResult(action, content); - } - } - }// @formatter:on + public ElicitResult build() { + return new ElicitResult(action, content); + } + } + }// @formatter:on // --------------------------- // Pagination Interfaces @@ -1437,8 +1545,8 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record PaginatedRequest(// @formatter:off - @JsonProperty("cursor") String cursor, - @JsonProperty("_meta") Map meta) implements Request { // @formatter:on + @JsonProperty("cursor") String cursor, + @JsonProperty("_meta") Map meta) implements Request { // @formatter:on public PaginatedRequest(String cursor) { this(cursor, null); @@ -1474,11 +1582,11 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ProgressNotification(// @formatter:off - @JsonProperty("progressToken") String progressToken, - @JsonProperty("progress") Double progress, - @JsonProperty("total") Double total, + @JsonProperty("progressToken") String progressToken, + @JsonProperty("progress") Double progress, + @JsonProperty("total") Double total, @JsonProperty("message") String message) { - }// @formatter:on + }// @formatter:on /** * The Model Context Protocol (MCP) provides a standardized way for servers to send @@ -1488,8 +1596,8 @@ public final class McpSchema { */ @JsonIgnoreProperties(ignoreUnknown = true) public record ResourcesUpdatedNotification(// @formatter:off - @JsonProperty("uri") String uri) { - }// @formatter:on + @JsonProperty("uri") String uri) { + }// @formatter:on /** * The Model Context Protocol (MCP) provides a standardized way for servers to send @@ -1503,61 +1611,61 @@ public final class McpSchema { */ @JsonIgnoreProperties(ignoreUnknown = true) public record LoggingMessageNotification(// @formatter:off - @JsonProperty("level") LoggingLevel level, - @JsonProperty("logger") String logger, - @JsonProperty("data") String data) { + @JsonProperty("level") LoggingLevel level, + @JsonProperty("logger") String logger, + @JsonProperty("data") String data) { - public static Builder builder() { - return new Builder(); - } + public static Builder builder() { + return new Builder(); + } - public static class Builder { - private LoggingLevel level = LoggingLevel.INFO; - private String logger = "server"; - private String data; + public static class Builder { + private LoggingLevel level = LoggingLevel.INFO; + private String logger = "server"; + private String data; - public Builder level(LoggingLevel level) { - this.level = level; - return this; - } + public Builder level(LoggingLevel level) { + this.level = level; + return this; + } - public Builder logger(String logger) { - this.logger = logger; - return this; - } + public Builder logger(String logger) { + this.logger = logger; + return this; + } - public Builder data(String data) { - this.data = data; - return this; - } + public Builder data(String data) { + this.data = data; + return this; + } - public LoggingMessageNotification build() { - return new LoggingMessageNotification(level, logger, data); - } - } - }// @formatter:on + public LoggingMessageNotification build() { + return new LoggingMessageNotification(level, logger, data); + } + } + }// @formatter:on public enum LoggingLevel {// @formatter:off - @JsonProperty("debug") DEBUG(0), - @JsonProperty("info") INFO(1), - @JsonProperty("notice") NOTICE(2), - @JsonProperty("warning") WARNING(3), - @JsonProperty("error") ERROR(4), - @JsonProperty("critical") CRITICAL(5), - @JsonProperty("alert") ALERT(6), - @JsonProperty("emergency") EMERGENCY(7); + @JsonProperty("debug") DEBUG(0), + @JsonProperty("info") INFO(1), + @JsonProperty("notice") NOTICE(2), + @JsonProperty("warning") WARNING(3), + @JsonProperty("error") ERROR(4), + @JsonProperty("critical") CRITICAL(5), + @JsonProperty("alert") ALERT(6), + @JsonProperty("emergency") EMERGENCY(7); - private final int level; + private final int level; - LoggingLevel(int level) { - this.level = level; - } + LoggingLevel(int level) { + this.level = level; + } - public int level() { - return level; - } + public int level() { + return level; + } - } // @formatter:on + } // @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -1578,61 +1686,62 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record PromptReference(// @formatter:off - @JsonProperty("type") String type, - @JsonProperty("name") String name) implements McpSchema.CompleteReference { + @JsonProperty("type") String type, + @JsonProperty("name") String name, + @JsonProperty("title") String title ) implements McpSchema.CompleteReference, BaseMetadata { - public PromptReference(String name) { - this("ref/prompt", name); - } + public PromptReference(String name) { + this("ref/prompt", name, null); + } - @Override - public String identifier() { - return name(); - } - }// @formatter:on + @Override + public String identifier() { + return name(); + } + }// @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ResourceReference(// @formatter:off - @JsonProperty("type") String type, - @JsonProperty("uri") String uri) implements McpSchema.CompleteReference { + @JsonProperty("type") String type, + @JsonProperty("uri") String uri) implements McpSchema.CompleteReference { - public ResourceReference(String uri) { - this("ref/resource", uri); - } + public ResourceReference(String uri) { + this("ref/resource", uri); + } - @Override - public String identifier() { - return uri(); - } - }// @formatter:on + @Override + public String identifier() { + return uri(); + } + }// @formatter:on @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record CompleteRequest(// @formatter:off - @JsonProperty("ref") McpSchema.CompleteReference ref, - @JsonProperty("argument") CompleteArgument argument, - @JsonProperty("_meta") Map meta) implements Request { + @JsonProperty("ref") McpSchema.CompleteReference ref, + @JsonProperty("argument") CompleteArgument argument, + @JsonProperty("_meta") Map meta) implements Request { - public CompleteRequest(McpSchema.CompleteReference ref, CompleteArgument argument) { - this(ref, argument, null); - } + public CompleteRequest(McpSchema.CompleteReference ref, CompleteArgument argument) { + this(ref, argument, null); + } - public record CompleteArgument( - @JsonProperty("name") String name, - @JsonProperty("value") String value) { - }// @formatter:on + public record CompleteArgument( + @JsonProperty("name") String name, + @JsonProperty("value") String value) { + }// @formatter:on } @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record CompleteResult(@JsonProperty("completion") CompleteCompletion completion) { // @formatter:off - public record CompleteCompletion( - @JsonProperty("values") List values, - @JsonProperty("total") Integer total, - @JsonProperty("hasMore") Boolean hasMore) { - }// @formatter:on + public record CompleteCompletion( + @JsonProperty("values") List values, + @JsonProperty("total") Integer total, + @JsonProperty("hasMore") Boolean hasMore) { + }// @formatter:on } // --------------------------- @@ -1670,8 +1779,8 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record TextContent( // @formatter:off - @JsonProperty("annotations") Annotations annotations, - @JsonProperty("text") String text) implements Annotated, Content { // @formatter:on + @JsonProperty("annotations") Annotations annotations, + @JsonProperty("text") String text) implements Annotated, Content { // @formatter:on public TextContent(String content) { this(null, content); @@ -1705,9 +1814,9 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ImageContent( // @formatter:off - @JsonProperty("annotations") Annotations annotations, - @JsonProperty("data") String data, - @JsonProperty("mimeType") String mimeType) implements Annotated, Content { // @formatter:on + @JsonProperty("annotations") Annotations annotations, + @JsonProperty("data") String data, + @JsonProperty("mimeType") String mimeType) implements Annotated, Content { // @formatter:on /** * @deprecated Only exists for backwards-compatibility purposes. Use @@ -1737,16 +1846,16 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record AudioContent( // @formatter:off - @JsonProperty("annotations") Annotations annotations, - @JsonProperty("data") String data, - @JsonProperty("mimeType") String mimeType) implements Annotated, Content { // @formatter:on + @JsonProperty("annotations") Annotations annotations, + @JsonProperty("data") String data, + @JsonProperty("mimeType") String mimeType) implements Annotated, Content { // @formatter:on } @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record EmbeddedResource( // @formatter:off - @JsonProperty("annotations") Annotations annotations, - @JsonProperty("resource") ResourceContents resource) implements Annotated, Content { // @formatter:on + @JsonProperty("annotations") Annotations annotations, + @JsonProperty("resource") ResourceContents resource) implements Annotated, Content { // @formatter:on /** * @deprecated Only exists for backwards-compatibility purposes. Use @@ -1780,6 +1889,7 @@ public final class McpSchema { * @param uri the URI of the resource. * @param name A human-readable name for this resource. This can be used by clients to * populate UI elements. + * @param title A human-readable title for this resource. * @param description A description of what this resource represents. This can be used * by clients to improve the LLM's understanding of available resources. It can be * thought of like a "hint" to the model. @@ -1793,12 +1903,13 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ResourceLink( // @formatter:off - @JsonProperty("name") String name, - @JsonProperty("uri") String uri, - @JsonProperty("description") String description, - @JsonProperty("mimeType") String mimeType, - @JsonProperty("size") Long size, - @JsonProperty("annotations") Annotations annotations) implements Annotated, Content, ResourceContent { // @formatter:on + @JsonProperty("name") String name, + @JsonProperty("title") String title, + @JsonProperty("uri") String uri, + @JsonProperty("description") String description, + @JsonProperty("mimeType") String mimeType, + @JsonProperty("size") Long size, + @JsonProperty("annotations") Annotations annotations) implements Annotated, Content, ResourceContent { // @formatter:on public static Builder builder() { return new Builder(); @@ -1808,6 +1919,8 @@ public final class McpSchema { private String name; + private String title; + private String uri; private String description; @@ -1823,6 +1936,11 @@ public final class McpSchema { return this; } + public Builder title(String title) { + this.title = title; + return this; + } + public Builder uri(String uri) { this.uri = uri; return this; @@ -1852,7 +1970,7 @@ public final class McpSchema { Assert.hasText(uri, "uri must not be empty"); Assert.hasText(name, "name must not be empty"); - return new ResourceLink(name, uri, description, mimeType, size, annotations); + return new ResourceLink(name, title, uri, description, mimeType, size, annotations); } } @@ -1874,9 +1992,9 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record Root( // @formatter:off - @JsonProperty("uri") String uri, - @JsonProperty("name") String name) { - } // @formatter:on + @JsonProperty("uri") String uri, + @JsonProperty("name") String name) { + } // @formatter:on /** * The client's response to a roots/list request from the server. This result contains @@ -1892,12 +2010,12 @@ public final class McpSchema { @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record ListRootsResult( // @formatter:off - @JsonProperty("roots") List roots, - @JsonProperty("nextCursor") String nextCursor) { + @JsonProperty("roots") List roots, + @JsonProperty("nextCursor") String nextCursor) { - public ListRootsResult(List roots) { - this(roots, null); - } - } // @formatter:on + public ListRootsResult(List roots) { + this(roots, null); + } + } // @formatter:on } diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java index a3edda2..154937d 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java @@ -421,7 +421,7 @@ public abstract class AbstractMcpAsyncClientTests { .consumeNextWith(result -> { assertThat(result.prompts()).isNotNull(); // Verify that the returned list is immutable - assertThatThrownBy(() -> result.prompts().add(new Prompt("test", "test", null))) + assertThatThrownBy(() -> result.prompts().add(new Prompt("test", "test", "test", null))) .isInstanceOf(UnsupportedOperationException.class); }) .verifyComplete(); @@ -605,7 +605,7 @@ public abstract class AbstractMcpAsyncClientTests { assertThat(result.resourceTemplates()).isNotNull(); // Verify that the returned list is immutable assertThatThrownBy(() -> result.resourceTemplates() - .add(new McpSchema.ResourceTemplate("test://template", "test", null, null, null))) + .add(new McpSchema.ResourceTemplate("test://template", "test", "test", null, null, null))) .isInstanceOf(UnsupportedOperationException.class); }) .verifyComplete(); diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java index 3e89c8c..e773c83 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/McpAsyncClientResponseHandlerTests.java @@ -251,8 +251,8 @@ class McpAsyncClientResponseHandlerTests { assertThat(asyncMcpClient.initialize().block()).isNotNull(); // Create a mock prompts list that the server will return - McpSchema.Prompt mockPrompt = new McpSchema.Prompt("test-prompt", "Test Prompt Description", - List.of(new McpSchema.PromptArgument("arg1", "Test argument", true))); + McpSchema.Prompt mockPrompt = new McpSchema.Prompt("test-prompt", "Test Prompt", "Test Prompt Description", + List.of(new McpSchema.PromptArgument("arg1", "Test argument", "Test argument", true))); McpSchema.ListPromptsResult mockPromptsResult = new McpSchema.ListPromptsResult(List.of(mockPrompt), null); // Simulate server sending prompts/list_changed notification diff --git a/mcp/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java b/mcp/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java index dd9f658..41bf0e6 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java @@ -301,7 +301,7 @@ public abstract class AbstractMcpAsyncServerTests { .serverInfo("test-server", "1.0.0") .build(); - Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of()); + Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", "Test Prompt", List.of()); McpServerFeatures.AsyncPromptSpecification specification = new McpServerFeatures.AsyncPromptSpecification( prompt, (exchange, req) -> Mono.just(new GetPromptResult("Test prompt description", List .of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content")))))); @@ -329,7 +329,7 @@ public abstract class AbstractMcpAsyncServerTests { void testRemovePrompt() { String TEST_PROMPT_NAME_TO_REMOVE = "TEST_PROMPT_NAME678"; - Prompt prompt = new Prompt(TEST_PROMPT_NAME_TO_REMOVE, "Test Prompt", List.of()); + Prompt prompt = new Prompt(TEST_PROMPT_NAME_TO_REMOVE, "Test Prompt", "Test Prompt", List.of()); McpServerFeatures.AsyncPromptSpecification specification = new McpServerFeatures.AsyncPromptSpecification( prompt, (exchange, req) -> Mono.just(new GetPromptResult("Test prompt description", List .of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content")))))); diff --git a/mcp/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java b/mcp/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java index 6cbb863..8a79e34 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/server/AbstractMcpSyncServerTests.java @@ -288,7 +288,7 @@ public abstract class AbstractMcpSyncServerTests { .serverInfo("test-server", "1.0.0") .build(); - Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of()); + Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", "Test Prompt", List.of()); McpServerFeatures.SyncPromptSpecification specification = new McpServerFeatures.SyncPromptSpecification(prompt, (exchange, req) -> new GetPromptResult("Test prompt description", List .of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))); @@ -309,7 +309,7 @@ public abstract class AbstractMcpSyncServerTests { @Test void testRemovePrompt() { - Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", List.of()); + Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", "Test Prompt", List.of()); McpServerFeatures.SyncPromptSpecification specification = new McpServerFeatures.SyncPromptSpecification(prompt, (exchange, req) -> new GetPromptResult("Test prompt description", List .of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))); diff --git a/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java b/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java index ea063e4..786be32 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java @@ -207,8 +207,8 @@ public class McpSchemaTests { @Test void testResourceLink() throws Exception { - McpSchema.ResourceLink resourceLink = new McpSchema.ResourceLink("main.rs", "file:///project/src/main.rs", - "Primary application entry point", "text/x-rust", null, null); + McpSchema.ResourceLink resourceLink = new McpSchema.ResourceLink("main.rs", "Main file", + "file:///project/src/main.rs", "Primary application entry point", "text/x-rust", null, null); String value = mapper.writeValueAsString(resourceLink); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -216,7 +216,7 @@ public class McpSchemaTests { .isObject() .isEqualTo( json(""" - {"type":"resource_link","name":"main.rs","uri":"file:///project/src/main.rs","description":"Primary application entry point","mimeType":"text/x-rust"}""")); + {"type":"resource_link","name":"main.rs","title":"Main file","uri":"file:///project/src/main.rs","description":"Primary application entry point","mimeType":"text/x-rust"}""")); } @Test @@ -419,7 +419,7 @@ public class McpSchemaTests { McpSchema.Annotations annotations = new McpSchema.Annotations(Arrays.asList(McpSchema.Role.USER), 0.5); McpSchema.ResourceTemplate template = new McpSchema.ResourceTemplate("resource://{param}/test", "Test Template", - "A test resource template", "text/plain", annotations); + "Test Template", "A test resource template", "text/plain", annotations); String value = mapper.writeValueAsString(template); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -427,7 +427,7 @@ public class McpSchemaTests { .isObject() .isEqualTo( json(""" - {"uriTemplate":"resource://{param}/test","name":"Test Template","description":"A test resource template","mimeType":"text/plain","annotations":{"audience":["user"],"priority":0.5}}""")); + {"uriTemplate":"resource://{param}/test","name":"Test Template","title":"Test Template","description":"A test resource template","mimeType":"text/plain","annotations":{"audience":["user"],"priority":0.5}}""")); } @Test @@ -453,10 +453,10 @@ public class McpSchemaTests { @Test void testListResourceTemplatesResult() throws Exception { McpSchema.ResourceTemplate template1 = new McpSchema.ResourceTemplate("resource://{param}/test1", - "Test Template 1", "First test template", "text/plain", null); + "Test Template 1", "Test Template 1", "First test template", "text/plain", null); McpSchema.ResourceTemplate template2 = new McpSchema.ResourceTemplate("resource://{param}/test2", - "Test Template 2", "Second test template", "application/json", null); + "Test Template 2", "Test Template 2", "Second test template", "application/json", null); McpSchema.ListResourceTemplatesResult result = new McpSchema.ListResourceTemplatesResult( Arrays.asList(template1, template2), "next-cursor"); @@ -467,7 +467,7 @@ public class McpSchemaTests { .isObject() .isEqualTo( json(""" - {"resourceTemplates":[{"uriTemplate":"resource://{param}/test1","name":"Test Template 1","description":"First test template","mimeType":"text/plain"},{"uriTemplate":"resource://{param}/test2","name":"Test Template 2","description":"Second test template","mimeType":"application/json"}],"nextCursor":"next-cursor"}""")); + {"resourceTemplates":[{"uriTemplate":"resource://{param}/test1","name":"Test Template 1","title":"Test Template 1","description":"First test template","mimeType":"text/plain"},{"uriTemplate":"resource://{param}/test2","name":"Test Template 2","title":"Test Template 2","description":"Second test template","mimeType":"application/json"}],"nextCursor":"next-cursor"}""")); } @Test @@ -535,11 +535,13 @@ public class McpSchemaTests { @Test void testPrompt() throws Exception { - McpSchema.PromptArgument arg1 = new McpSchema.PromptArgument("arg1", "First argument", true); + McpSchema.PromptArgument arg1 = new McpSchema.PromptArgument("arg1", "First argument", "First argument", true); - McpSchema.PromptArgument arg2 = new McpSchema.PromptArgument("arg2", "Second argument", false); + McpSchema.PromptArgument arg2 = new McpSchema.PromptArgument("arg2", "Second argument", "Second argument", + false); - McpSchema.Prompt prompt = new McpSchema.Prompt("test-prompt", "A test prompt", Arrays.asList(arg1, arg2)); + McpSchema.Prompt prompt = new McpSchema.Prompt("test-prompt", "Test Prompt", "A test prompt", + Arrays.asList(arg1, arg2)); String value = mapper.writeValueAsString(prompt); assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) @@ -547,7 +549,7 @@ public class McpSchemaTests { .isObject() .isEqualTo( json(""" - {"name":"test-prompt","description":"A test prompt","arguments":[{"name":"arg1","description":"First argument","required":true},{"name":"arg2","description":"Second argument","required":false}]}""")); + {"name":"test-prompt","title":"Test Prompt","description":"A test prompt","arguments":[{"name":"arg1","title":"First argument","description":"First argument","required":true},{"name":"arg2","title":"Second argument","description":"Second argument","required":false}]}""")); } @Test @@ -566,11 +568,13 @@ public class McpSchemaTests { @Test void testListPromptsResult() throws Exception { - McpSchema.PromptArgument arg = new McpSchema.PromptArgument("arg", "An argument", true); + McpSchema.PromptArgument arg = new McpSchema.PromptArgument("arg", "Argument", "An argument", true); - McpSchema.Prompt prompt1 = new McpSchema.Prompt("prompt1", "First prompt", Collections.singletonList(arg)); + McpSchema.Prompt prompt1 = new McpSchema.Prompt("prompt1", "First prompt", "First prompt", + Collections.singletonList(arg)); - McpSchema.Prompt prompt2 = new McpSchema.Prompt("prompt2", "Second prompt", Collections.emptyList()); + McpSchema.Prompt prompt2 = new McpSchema.Prompt("prompt2", "Second prompt", "Second prompt", + Collections.emptyList()); McpSchema.ListPromptsResult result = new McpSchema.ListPromptsResult(Arrays.asList(prompt1, prompt2), "next-cursor"); @@ -581,7 +585,7 @@ public class McpSchemaTests { .isObject() .isEqualTo( json(""" - {"prompts":[{"name":"prompt1","description":"First prompt","arguments":[{"name":"arg","description":"An argument","required":true}]},{"name":"prompt2","description":"Second prompt","arguments":[]}],"nextCursor":"next-cursor"}""")); + {"prompts":[{"name":"prompt1","title":"First prompt","description":"First prompt","arguments":[{"name":"arg","title":"Argument","description":"An argument","required":true}]},{"name":"prompt2","title":"Second prompt","description":"Second prompt","arguments":[]}],"nextCursor":"next-cursor"}""")); } @Test