From 99943aa0153faf6c7defb4e9d6f345582bae78a8 Mon Sep 17 00:00:00 2001 From: Luca Chang Date: Thu, 19 Jun 2025 14:41:56 -0700 Subject: [PATCH 1/2] feat: add optional size property to Resource with builder --- .../modelcontextprotocol/spec/McpSchema.java | 60 +++++++++++++++++++ .../spec/McpSchemaTests.java | 23 +++++++ 2 files changed, 83 insertions(+) diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index a78600af1..48c4ab126 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -482,6 +482,9 @@ public record Annotations( // @formatter:off * by clients to improve the LLM's understanding of available resources. It can be * thought of like a "hint" to the model. * @param mimeType The MIME type of this resource, if known. + * @param size The size of the raw resource content, in bytes (i.e., before base64 + * encoding or any tokenization), if known. This can be used by Hosts to display file + * sizes and estimate context window usage. * @param annotations Optional annotations for the client. The client can use * annotations to inform how objects are used or displayed. */ @@ -492,7 +495,64 @@ public record Resource( // @formatter:off @JsonProperty("name") String name, @JsonProperty("description") String description, @JsonProperty("mimeType") String mimeType, + @JsonProperty("size") Long size, @JsonProperty("annotations") Annotations annotations) implements Annotated { + + /** + * @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); + } + + 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 Builder uri(String uri) { + this.uri = uri; + return this; + } + + public Builder name(String name) { + this.name = name; + 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() { + return new Resource(uri, name, description, mimeType, size, annotations); + } + } } // @formatter:on /** diff --git a/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java b/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java index 6d027b55b..a60ba93be 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java @@ -295,6 +295,29 @@ void testResource() throws Exception { {"uri":"resource://test","name":"Test Resource","description":"A test resource","mimeType":"text/plain","annotations":{"audience":["user","assistant"],"priority":0.8}}""")); } + @Test + void testResourceBuilder() throws Exception { + McpSchema.Annotations annotations = new McpSchema.Annotations( + Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8); + + McpSchema.Resource resource = McpSchema.Resource.builder() + .uri("resource://test") + .name("Test Resource") + .description("A test resource") + .mimeType("text/plain") + .size(256L) + .annotations(annotations) + .build(); + + String value = mapper.writeValueAsString(resource); + assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) + .when(Option.IGNORING_EXTRA_ARRAY_ITEMS) + .isObject() + .isEqualTo( + json(""" + {"uri":"resource://test","name":"Test Resource","description":"A test resource","mimeType":"text/plain","size":256,"annotations":{"audience":["user","assistant"],"priority":0.8}}""")); + } + @Test void testResourceTemplate() throws Exception { McpSchema.Annotations annotations = new McpSchema.Annotations(Arrays.asList(McpSchema.Role.USER), 0.5); From 33a25a14dc02ffaa1166c2b899b377fd5113cb84 Mon Sep 17 00:00:00 2001 From: Luca Chang Date: Mon, 23 Jun 2025 10:30:37 -0700 Subject: [PATCH 2/2] chore: make Resource.uri and Resource.name required --- .../modelcontextprotocol/spec/McpSchema.java | 8 +++++ .../spec/McpSchemaTests.java | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index 48c4ab126..d03befd6a 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -550,6 +550,14 @@ public Builder annotations(Annotations annotations) { } public Resource build() { + if (uri == null) { + throw new IllegalStateException("uri cannot be null"); + } + + if (name == null) { + throw new IllegalStateException("name cannot be null"); + } + return new Resource(uri, name, description, mimeType, size, annotations); } } diff --git a/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java b/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java index a60ba93be..e99b2788a 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java @@ -318,6 +318,36 @@ void testResourceBuilder() throws Exception { {"uri":"resource://test","name":"Test Resource","description":"A test resource","mimeType":"text/plain","size":256,"annotations":{"audience":["user","assistant"],"priority":0.8}}""")); } + @Test + void testResourceBuilderUriRequired() { + McpSchema.Annotations annotations = new McpSchema.Annotations( + Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8); + + McpSchema.Resource.Builder resourceBuilder = McpSchema.Resource.builder() + .name("Test Resource") + .description("A test resource") + .mimeType("text/plain") + .size(256L) + .annotations(annotations); + + assertThatThrownBy(resourceBuilder::build).isInstanceOf(IllegalStateException.class); + } + + @Test + void testResourceBuilderNameRequired() { + McpSchema.Annotations annotations = new McpSchema.Annotations( + Arrays.asList(McpSchema.Role.USER, McpSchema.Role.ASSISTANT), 0.8); + + McpSchema.Resource.Builder resourceBuilder = McpSchema.Resource.builder() + .uri("resource://test") + .description("A test resource") + .mimeType("text/plain") + .size(256L) + .annotations(annotations); + + assertThatThrownBy(resourceBuilder::build).isInstanceOf(IllegalStateException.class); + } + @Test void testResourceTemplate() throws Exception { McpSchema.Annotations annotations = new McpSchema.Annotations(Arrays.asList(McpSchema.Role.USER), 0.5);