feat: add AudioContent support to MCP schema (#243)

- Add AudioContent record class implementing Annotated and Content interfaces
- Update Content sealed interface to permit AudioContent
- Add audio type handling in Content.type() method
- Include AudioContent in JsonSubTypes annotation for polymorphic deserialization
- Add comprehensive tests for AudioContent serialization and deserialization
- Update error message test to include audio in known type ids

c87a0da6d8/schema/2025-03-26/schema.ts (L987-L1009)
This commit is contained in:
Luca Chang 2025-05-16 15:53:33 -07:00 • committed by Christian Tzolov
commit 9a63538840
2 changed files with 36 additions and 2 deletions

View file

@ -1359,8 +1359,9 @@ public final class McpSchema {
@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) {
@ -1369,6 +1370,9 @@ public final class McpSchema {
else if (this instanceof ImageContent) {
return "image";
}
else if (this instanceof AudioContent) {
return "audio";
}
else if (this instanceof EmbeddedResource) {
return "resource";
}
@ -1398,6 +1402,14 @@ public final class McpSchema {
@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

View file

@ -59,7 +59,7 @@ public class McpSchemaTests {
{"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 @@ public class McpSchemaTests {
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",