From fab39ab33b63f4bf1018bb4a39b94691d16d0e63 Mon Sep 17 00:00:00 2001 From: Luca Chang Date: Fri, 16 May 2025 15:53:33 -0700 Subject: [PATCH] feat: implement audio content Implements the AudioContent type as defined by the spec. https://github.com/modelcontextprotocol/modelcontextprotocol/blob/c87a0da6d8c2436d56a6398023c80b0562224454/schema/2025-03-26/schema.ts#L987-L1009 --- .../modelcontextprotocol/spec/McpSchema.java | 14 ++++++++++- .../spec/McpSchemaTests.java | 24 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java index 8df8a158..57d9258e 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java +++ b/mcp/src/main/java/io/modelcontextprotocol/spec/McpSchema.java @@ -1256,8 +1256,9 @@ public record CompleteCompletion( @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = TextContent.class, name = "text"), @JsonSubTypes.Type(value = ImageContent.class, name = "image"), + @JsonSubTypes.Type(value = AudioContent.class, name = "audio"), @JsonSubTypes.Type(value = EmbeddedResource.class, name = "resource") }) - public sealed interface Content permits TextContent, ImageContent, EmbeddedResource { + public sealed interface Content permits TextContent, ImageContent, AudioContent, EmbeddedResource { default String type() { if (this instanceof TextContent) { @@ -1266,6 +1267,9 @@ default String type() { else if (this instanceof ImageContent) { return "image"; } + else if (this instanceof AudioContent) { + return "audio"; + } else if (this instanceof EmbeddedResource) { return "resource"; } @@ -1295,6 +1299,14 @@ public record ImageContent( // @formatter:off @JsonProperty("mimeType") String mimeType) implements Content { // @formatter:on } + @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 + } + @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record EmbeddedResource( // @formatter:off diff --git a/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java b/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java index ff78c1bf..0ec5c15f 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/spec/McpSchemaTests.java @@ -59,7 +59,7 @@ void testContentDeserializationWrongType() throws Exception { {"type":"WRONG","text":"XXX"}""", McpSchema.TextContent.class)) .isInstanceOf(InvalidTypeIdException.class) .hasMessageContaining( - "Could not resolve type id 'WRONG' as a subtype of `io.modelcontextprotocol.spec.McpSchema$TextContent`: known type ids = [image, resource, text]"); + "Could not resolve type id 'WRONG' as a subtype of `io.modelcontextprotocol.spec.McpSchema$TextContent`: known type ids = [audio, image, resource, text]"); } @Test @@ -84,6 +84,28 @@ void testImageContentDeserialization() throws Exception { assertThat(imageContent.mimeType()).isEqualTo("image/png"); } + @Test + void testAudioContent() throws Exception { + McpSchema.AudioContent audioContent = new McpSchema.AudioContent(null, "base64encodeddata", "audio/wav"); + String value = mapper.writeValueAsString(audioContent); + + assertThatJson(value).when(Option.IGNORING_ARRAY_ORDER) + .when(Option.IGNORING_EXTRA_ARRAY_ITEMS) + .isObject() + .isEqualTo(json(""" + {"type":"audio","data":"base64encodeddata","mimeType":"audio/wav"}""")); + } + + @Test + void testAudioContentDeserialization() throws Exception { + McpSchema.AudioContent audioContent = mapper.readValue(""" + {"type":"audio","data":"base64encodeddata","mimeType":"audio/wav"}""", McpSchema.AudioContent.class); + assertThat(audioContent).isNotNull(); + assertThat(audioContent.type()).isEqualTo("audio"); + assertThat(audioContent.data()).isEqualTo("base64encodeddata"); + assertThat(audioContent.mimeType()).isEqualTo("audio/wav"); + } + @Test void testEmbeddedResource() throws Exception { McpSchema.TextResourceContents resourceContents = new McpSchema.TextResourceContents("resource://test",