From dced3e4503f474bc36b48dc3b1038aec2df7ffd8 Mon Sep 17 00:00:00 2001 From: Martin Emde Date: Sat, 19 Jul 2025 14:08:56 -0700 Subject: [PATCH] mcp: don't omit required fields in ImageContent and AudioContent (#95) Update `ImageContent` and `AudioContent` to use an inline wire format to ensure that required fields are not omitted during JSON marshaling, matching TypeScript schema requirements. This follows the TextContent approach. - `ImageContent` requires `data` and `mimeType` fields. - `AudioContent` requires `data` and `mimeType` fields. --- mcp/content.go | 36 ++++++++++++++++++++++++++++++------ mcp/content_test.go | 16 ++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/mcp/content.go b/mcp/content.go index fd027cf..04888de 100644 --- a/mcp/content.go +++ b/mcp/content.go @@ -58,13 +58,25 @@ type ImageContent struct { } func (c *ImageContent) MarshalJSON() ([]byte, error) { - return json.Marshal(&wireContent{ + // Custom wire format to ensure required fields are always included, even when empty. + data := c.Data + if data == nil { + data = []byte{} + } + wire := struct { + Type string `json:"type"` + MIMEType string `json:"mimeType"` + Data []byte `json:"data"` + Meta Meta `json:"_meta,omitempty"` + Annotations *Annotations `json:"annotations,omitempty"` + }{ Type: "image", MIMEType: c.MIMEType, - Data: c.Data, + Data: data, Meta: c.Meta, Annotations: c.Annotations, - }) + } + return json.Marshal(wire) } func (c *ImageContent) fromWire(wire *wireContent) { @@ -83,13 +95,25 @@ type AudioContent struct { } func (c AudioContent) MarshalJSON() ([]byte, error) { - return json.Marshal(&wireContent{ + // Custom wire format to ensure required fields are always included, even when empty. + data := c.Data + if data == nil { + data = []byte{} + } + wire := struct { + Type string `json:"type"` + MIMEType string `json:"mimeType"` + Data []byte `json:"data"` + Meta Meta `json:"_meta,omitempty"` + Annotations *Annotations `json:"annotations,omitempty"` + }{ Type: "audio", MIMEType: c.MIMEType, - Data: c.Data, + Data: data, Meta: c.Meta, Annotations: c.Annotations, - }) + } + return json.Marshal(wire) } func (c *AudioContent) fromWire(wire *wireContent) { diff --git a/mcp/content_test.go b/mcp/content_test.go index 7a549be..9366b0d 100644 --- a/mcp/content_test.go +++ b/mcp/content_test.go @@ -45,6 +45,14 @@ func TestContent(t *testing.T) { }, `{"type":"image","mimeType":"image/png","data":"YTFiMmMz"}`, }, + { + &mcp.ImageContent{MIMEType: "image/png", Data: []byte{}}, + `{"type":"image","mimeType":"image/png","data":""}`, + }, + { + &mcp.ImageContent{Data: []byte("test")}, + `{"type":"image","mimeType":"","data":"dGVzdA=="}`, + }, { &mcp.ImageContent{ Data: []byte("a1b2c3"), @@ -61,6 +69,14 @@ func TestContent(t *testing.T) { }, `{"type":"audio","mimeType":"audio/wav","data":"YTFiMmMz"}`, }, + { + &mcp.AudioContent{MIMEType: "audio/wav", Data: []byte{}}, + `{"type":"audio","mimeType":"audio/wav","data":""}`, + }, + { + &mcp.AudioContent{Data: []byte("test")}, + `{"type":"audio","mimeType":"","data":"dGVzdA=="}`, + }, { &mcp.AudioContent{ Data: []byte("a1b2c3"),