Skip to content

Commit d22688b

Browse files
author
Sainath Reddy Bobbala
committed
feat: Add SEP-973 icons and metadata support
Add Icon record and icons field to Implementation, Resource, ResourceTemplate, Prompt, and Tool records per SEP-973. Add websiteUrl and description fields to Implementation. All fields are optional and backward compatible. Existing constructors and builders continue to work unchanged. Icon.src is validated as required per the spec. Icon.theme field supports light/dark theme variants. Includes serialization, deserialization, round-trip, and backward compatibility tests for all modified records. Closes #694
1 parent 5895b2e commit d22688b

2 files changed

Lines changed: 315 additions & 17 deletions

File tree

mcp-core/src/main/java/io/modelcontextprotocol/spec/McpSchema.java

Lines changed: 94 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,20 @@
1010
import java.util.List;
1111
import java.util.Map;
1212

13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
1316
import com.fasterxml.jackson.annotation.JsonCreator;
1417
import com.fasterxml.jackson.annotation.JsonIgnore;
1518
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
1619
import com.fasterxml.jackson.annotation.JsonInclude;
1720
import com.fasterxml.jackson.annotation.JsonProperty;
1821
import com.fasterxml.jackson.annotation.JsonSubTypes;
1922
import com.fasterxml.jackson.annotation.JsonTypeInfo;
23+
2024
import io.modelcontextprotocol.json.McpJsonMapper;
2125
import io.modelcontextprotocol.json.TypeRef;
2226
import io.modelcontextprotocol.util.Assert;
23-
import org.slf4j.Logger;
24-
import org.slf4j.LoggerFactory;
2527

2628
/**
2729
* Based on the <a href="http://www.jsonrpc.org/specification">JSON-RPC 2.0
@@ -661,16 +663,63 @@ public ServerCapabilities build() {
661663
* past specs or fallback (if title isn't present).
662664
* @param title Intended for UI and end-user contexts
663665
* @param version The version of the implementation.
666+
* @param description An optional human-readable description of this implementation.
667+
* @param icons An optional list of icons for this implementation.
668+
* @param websiteUrl An optional URL of the website for this implementation.
664669
*/
665670
@JsonInclude(JsonInclude.Include.NON_ABSENT)
666671
@JsonIgnoreProperties(ignoreUnknown = true)
667672
public record Implementation( // @formatter:off
668673
@JsonProperty("name") String name,
669674
@JsonProperty("title") String title,
670-
@JsonProperty("version") String version) implements Identifier { // @formatter:on
675+
@JsonProperty("version") String version,
676+
@JsonProperty("description") String description,
677+
@JsonProperty("icons") List<Icon> icons,
678+
@JsonProperty("websiteUrl") String websiteUrl) implements Identifier { // @formatter:on
671679

672680
public Implementation(String name, String version) {
673-
this(name, null, version);
681+
this(name, null, version, null, null, null);
682+
}
683+
684+
public Implementation(String name, String title, String version) {
685+
this(name, title, version, null, null, null);
686+
}
687+
}
688+
689+
/**
690+
* Represents an icon that can be displayed in a user interface.
691+
*
692+
* @param src A URI pointing to an icon resource or a base64-encoded data URI.
693+
* @param mimeType Optional MIME type override if the server's MIME type is missing or
694+
* generic.
695+
* @param sizes Optional array of strings specifying sizes at which the icon can be
696+
* used. Each string should be in WxH format (e.g., "48x48", "96x96") or "any" for
697+
* scalable formats like SVG.
698+
* @param theme Optional specifier for the theme this icon is designed for. "light"
699+
* indicates the icon is designed for a light background, "dark" indicates the icon is
700+
* designed for a dark background. If not provided, the client should assume the icon
701+
* can be used with any theme.
702+
* @see <a href=
703+
* "https://github.com/modelcontextprotocol/modelcontextprotocol/issues/973">SEP-973</a>
704+
*/
705+
@JsonInclude(JsonInclude.Include.NON_ABSENT)
706+
@JsonIgnoreProperties(ignoreUnknown = true)
707+
public record Icon( // @formatter:off
708+
@JsonProperty("src") String src,
709+
@JsonProperty("mimeType") String mimeType,
710+
@JsonProperty("sizes") List<String> sizes,
711+
@JsonProperty("theme") String theme) { // @formatter:on
712+
713+
public Icon {
714+
Assert.hasText(src, "Icon src must not be empty");
715+
}
716+
717+
public Icon(String src, String mimeType) {
718+
this(src, mimeType, null, null);
719+
}
720+
721+
public Icon(String src, String mimeType, List<String> sizes) {
722+
this(src, mimeType, sizes, null);
674723
}
675724
}
676725

@@ -792,6 +841,7 @@ public record Resource( // @formatter:off
792841
@JsonProperty("mimeType") String mimeType,
793842
@JsonProperty("size") Long size,
794843
@JsonProperty("annotations") Annotations annotations,
844+
@JsonProperty("icons") List<Icon> icons,
795845
@JsonProperty("_meta") Map<String, Object> meta) implements ResourceContent { // @formatter:on
796846

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

815865
private Annotations annotations;
816866

867+
private List<Icon> icons;
868+
817869
private Map<String, Object> meta;
818870

819871
public Builder uri(String uri) {
@@ -851,6 +903,11 @@ public Builder annotations(Annotations annotations) {
851903
return this;
852904
}
853905

906+
public Builder icons(List<Icon> icons) {
907+
this.icons = icons;
908+
return this;
909+
}
910+
854911
public Builder meta(Map<String, Object> meta) {
855912
this.meta = meta;
856913
return this;
@@ -860,7 +917,7 @@ public Resource build() {
860917
Assert.hasText(uri, "uri must not be empty");
861918
Assert.hasText(name, "name must not be empty");
862919

863-
return new Resource(uri, name, title, description, mimeType, size, annotations, meta);
920+
return new Resource(uri, name, title, description, mimeType, size, annotations, icons, meta);
864921
}
865922

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

898956
public ResourceTemplate(String uriTemplate, String name, String title, String description, String mimeType,
899957
Annotations annotations) {
900-
this(uriTemplate, name, title, description, mimeType, annotations, null);
958+
this(uriTemplate, name, title, description, mimeType, annotations, null, null);
901959
}
902960

903961
public ResourceTemplate(String uriTemplate, String name, String description, String mimeType,
904962
Annotations annotations) {
905963
this(uriTemplate, name, null, description, mimeType, annotations);
906964
}
907965

966+
public ResourceTemplate(String uriTemplate, String name, String title, String description, String mimeType,
967+
Annotations annotations, Map<String, Object> meta) {
968+
this(uriTemplate, name, title, description, mimeType, annotations, null, meta);
969+
}
970+
908971
public static Builder builder() {
909972
return new Builder();
910973
}
@@ -923,6 +986,8 @@ public static class Builder {
923986

924987
private Annotations annotations;
925988

989+
private List<Icon> icons;
990+
926991
private Map<String, Object> meta;
927992

928993
public Builder uriTemplate(String uri) {
@@ -955,6 +1020,11 @@ public Builder annotations(Annotations annotations) {
9551020
return this;
9561021
}
9571022

1023+
public Builder icons(List<Icon> icons) {
1024+
this.icons = icons;
1025+
return this;
1026+
}
1027+
9581028
public Builder meta(Map<String, Object> meta) {
9591029
this.meta = meta;
9601030
return this;
@@ -964,7 +1034,7 @@ public ResourceTemplate build() {
9641034
Assert.hasText(uriTemplate, "uri must not be empty");
9651035
Assert.hasText(name, "name must not be empty");
9661036

967-
return new ResourceTemplate(uriTemplate, name, title, description, mimeType, annotations, meta);
1037+
return new ResourceTemplate(uriTemplate, name, title, description, mimeType, annotations, icons, meta);
9681038
}
9691039

9701040
}
@@ -1168,14 +1238,20 @@ public record Prompt( // @formatter:off
11681238
@JsonProperty("title") String title,
11691239
@JsonProperty("description") String description,
11701240
@JsonProperty("arguments") List<PromptArgument> arguments,
1241+
@JsonProperty("icons") List<Icon> icons,
11711242
@JsonProperty("_meta") Map<String, Object> meta) implements Identifier { // @formatter:on
11721243

11731244
public Prompt(String name, String description, List<PromptArgument> arguments) {
1174-
this(name, null, description, arguments, null);
1245+
this(name, null, description, arguments, null, null);
11751246
}
11761247

11771248
public Prompt(String name, String title, String description, List<PromptArgument> arguments) {
1178-
this(name, title, description, arguments, null);
1249+
this(name, title, description, arguments, null, null);
1250+
}
1251+
1252+
public Prompt(String name, String title, String description, List<PromptArgument> arguments,
1253+
Map<String, Object> meta) {
1254+
this(name, title, description, arguments, null, meta);
11791255
}
11801256
}
11811257

@@ -1367,6 +1443,7 @@ public record Tool( // @formatter:off
13671443
@JsonProperty("inputSchema") Map<String, Object> inputSchema,
13681444
@JsonProperty("outputSchema") Map<String, Object> outputSchema,
13691445
@JsonProperty("annotations") ToolAnnotations annotations,
1446+
@JsonProperty("icons") List<Icon> icons,
13701447
@JsonProperty("_meta") Map<String, Object> meta) { // @formatter:on
13711448

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

13881465
private ToolAnnotations annotations;
13891466

1467+
private List<Icon> icons;
1468+
13901469
private Map<String, Object> meta;
13911470

13921471
public Builder name(String name) {
@@ -1450,14 +1529,19 @@ public Builder annotations(ToolAnnotations annotations) {
14501529
return this;
14511530
}
14521531

1532+
public Builder icons(List<Icon> icons) {
1533+
this.icons = icons;
1534+
return this;
1535+
}
1536+
14531537
public Builder meta(Map<String, Object> meta) {
14541538
this.meta = meta;
14551539
return this;
14561540
}
14571541

14581542
public Tool build() {
14591543
Assert.hasText(name, "name must not be empty");
1460-
return new Tool(name, title, description, inputSchema, outputSchema, annotations, meta);
1544+
return new Tool(name, title, description, inputSchema, outputSchema, annotations, icons, meta);
14611545
}
14621546

14631547
}

0 commit comments

Comments
 (0)