Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 94 additions & 10 deletions mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

import io.modelcontextprotocol.json.McpJsonMapper;
import io.modelcontextprotocol.json.TypeRef;
import io.modelcontextprotocol.util.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Based on the <a href="http://www.jsonrpc.org/specification">JSON-RPC 2.0
Expand Down Expand Up @@ -661,16 +663,63 @@ public ServerCapabilities build() {
* 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.
* @param description An optional human-readable description of this implementation.
* @param icons An optional list of icons for this implementation.
* @param websiteUrl An optional URL of the website for this implementation.
*/
@JsonInclude(JsonInclude.Include.NON_ABSENT)
@JsonIgnoreProperties(ignoreUnknown = true)
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,
@JsonProperty("description") String description,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 new optional fields indicate we need to add a builder with a static method that creates the builder instance with the required fields (name version) and all the others are optionally configured.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Added Implementation.builder(String name, String version) with optional .title(), .description(), .icons(), .websiteUrl() setters. The existing 2-arg and 3-arg constructors are preserved as @Deprecated overloads for backward compatibility.

@JsonProperty("icons") List<Icon> icons,
@JsonProperty("websiteUrl") String websiteUrl) implements Identifier { // @formatter:on

public Implementation(String name, String version) {
this(name, null, version);
this(name, null, version, null, null, null);
}

public Implementation(String name, String title, String version) {
this(name, title, version, null, null, null);
}
}

/**
* Represents an icon that can be displayed in a user interface.
*
* @param src A URI pointing to an icon resource or a base64-encoded data URI.
* @param mimeType Optional MIME type override if the server's MIME type is missing or
* generic.
* @param sizes Optional array of strings specifying sizes at which the icon can be
* used. Each string should be in WxH format (e.g., "48x48", "96x96") or "any" for
* scalable formats like SVG.
* @param theme Optional specifier for the theme this icon is designed for. "light"
* indicates the icon is designed for a light background, "dark" indicates the icon is
* designed for a dark background. If not provided, the client should assume the icon
* can be used with any theme.
* @see <a href=
* "https://github.com/modelcontextprotocol/modelcontextprotocol/issues/973">SEP-973</a>
*/
@JsonInclude(JsonInclude.Include.NON_ABSENT)
@JsonIgnoreProperties(ignoreUnknown = true)
public record Icon( // @formatter:off
@JsonProperty("src") String src,
@JsonProperty("mimeType") String mimeType,
@JsonProperty("sizes") List<String> sizes,
@JsonProperty("theme") String theme) { // @formatter:on
Comment on lines +1112 to +1115
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a builder that accepts src as the required argument and allows configuring the other fields as optional.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Added Icon.builder(String src) with optional .mimeType(), .sizes(), .theme(). Also added a @JsonCreator fromJson factory per Case B Rule 2 so deserialization doesn't crash if a peer omits src.


public Icon {
Assert.hasText(src, "Icon src must not be empty");
}

public Icon(String src, String mimeType) {
this(src, mimeType, null, null);
}

public Icon(String src, String mimeType, List<String> sizes) {
this(src, mimeType, sizes, null);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove these constructors and use the builder pattern as documented in the CONTRIBUTING.md file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed. The only constructors on Icon now are the canonical 4-arg constructor and the fromJson factory. All public construction goes through the builder.

}
}

Expand Down Expand Up @@ -792,6 +841,7 @@ public record Resource( // @formatter:off
@JsonProperty("mimeType") String mimeType,
@JsonProperty("size") Long size,
@JsonProperty("annotations") Annotations annotations,
@JsonProperty("icons") List<Icon> icons,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a backwards incompatible change. Please check the process described in CONTRIBUTING.md

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Added deprecated backward-compatible overload constructors for Resource, ResourceTemplate, Prompt, and Tool that delegate to the new canonical constructor with null for icons. Existing callers compile unchanged. Applied the same pattern to all modified records.

@JsonProperty("_meta") Map<String, Object> meta) implements ResourceContent { // @formatter:on

public static Builder builder() {
Expand All @@ -814,6 +864,8 @@ public static class Builder {

private Annotations annotations;

private List<Icon> icons;

private Map<String, Object> meta;

public Builder uri(String uri) {
Expand Down Expand Up @@ -851,6 +903,11 @@ public Builder annotations(Annotations annotations) {
return this;
}

public Builder icons(List<Icon> icons) {
this.icons = icons;
return this;
}

public Builder meta(Map<String, Object> meta) {
this.meta = meta;
return this;
Expand All @@ -860,7 +917,7 @@ 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, meta);
return new Resource(uri, name, title, description, mimeType, size, annotations, icons, meta);
}

}
Expand Down Expand Up @@ -893,18 +950,24 @@ public record ResourceTemplate( // @formatter:off
@JsonProperty("description") String description,
@JsonProperty("mimeType") String mimeType,
@JsonProperty("annotations") Annotations annotations,
@JsonProperty("icons") List<Icon> icons,
@JsonProperty("_meta") Map<String, Object> meta) implements Annotated, Identifier, Meta { // @formatter:on

public ResourceTemplate(String uriTemplate, String name, String title, String description, String mimeType,
Annotations annotations) {
this(uriTemplate, name, title, description, mimeType, annotations, null);
this(uriTemplate, name, title, description, mimeType, annotations, null, null);
}

public ResourceTemplate(String uriTemplate, String name, String description, String mimeType,
Annotations annotations) {
this(uriTemplate, name, null, description, mimeType, annotations);
}

public ResourceTemplate(String uriTemplate, String name, String title, String description, String mimeType,
Annotations annotations, Map<String, Object> meta) {
this(uriTemplate, name, title, description, mimeType, annotations, null, meta);
}

public static Builder builder() {
return new Builder();
}
Expand All @@ -923,6 +986,8 @@ public static class Builder {

private Annotations annotations;

private List<Icon> icons;

private Map<String, Object> meta;

public Builder uriTemplate(String uri) {
Expand Down Expand Up @@ -955,6 +1020,11 @@ public Builder annotations(Annotations annotations) {
return this;
}

public Builder icons(List<Icon> icons) {
this.icons = icons;
return this;
}

public Builder meta(Map<String, Object> meta) {
this.meta = meta;
return this;
Expand All @@ -964,7 +1034,7 @@ public ResourceTemplate build() {
Assert.hasText(uriTemplate, "uri must not be empty");
Assert.hasText(name, "name must not be empty");

return new ResourceTemplate(uriTemplate, name, title, description, mimeType, annotations, meta);
return new ResourceTemplate(uriTemplate, name, title, description, mimeType, annotations, icons, meta);
}

}
Expand Down Expand Up @@ -1168,14 +1238,20 @@ public record Prompt( // @formatter:off
@JsonProperty("title") String title,
@JsonProperty("description") String description,
@JsonProperty("arguments") List<PromptArgument> arguments,
@JsonProperty("icons") List<Icon> icons,
@JsonProperty("_meta") Map<String, Object> meta) implements Identifier { // @formatter:on

public Prompt(String name, String description, List<PromptArgument> arguments) {
this(name, null, description, arguments, null);
this(name, null, description, arguments, null, null);
}

public Prompt(String name, String title, String description, List<PromptArgument> arguments) {
this(name, title, description, arguments, null);
this(name, title, description, arguments, null, null);
}

public Prompt(String name, String title, String description, List<PromptArgument> arguments,
Map<String, Object> meta) {
this(name, title, description, arguments, null, meta);
}
}

Expand Down Expand Up @@ -1367,6 +1443,7 @@ public record Tool( // @formatter:off
@JsonProperty("inputSchema") Map<String, Object> inputSchema,
@JsonProperty("outputSchema") Map<String, Object> outputSchema,
@JsonProperty("annotations") ToolAnnotations annotations,
@JsonProperty("icons") List<Icon> icons,
@JsonProperty("_meta") Map<String, Object> meta) { // @formatter:on

public static Builder builder() {
Expand All @@ -1387,6 +1464,8 @@ public static class Builder {

private ToolAnnotations annotations;

private List<Icon> icons;

private Map<String, Object> meta;

public Builder name(String name) {
Expand Down Expand Up @@ -1450,14 +1529,19 @@ public Builder annotations(ToolAnnotations annotations) {
return this;
}

public Builder icons(List<Icon> icons) {
this.icons = icons;
return this;
}

public Builder meta(Map<String, Object> meta) {
this.meta = meta;
return this;
}

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, annotations, icons, meta);
}

}
Expand Down
Loading