From c5b9f0681ce7905949c2c1afba84955ada25db42 Mon Sep 17 00:00:00 2001 From: Martin Emde Date: Fri, 4 Jul 2025 08:57:04 -0700 Subject: [PATCH] mcp: ensure required fields are not omitted in ImageContent and AudioContent ImageContent and AudioContent now use custom MarshalJSON methods with inline structs to ensure data and mimeType fields are always included in JSON output, even when empty. This matches the approach used for TextContent.Text and follows TypeScript schema requirements. Updated tests to verify empty fields are serialized as empty strings rather than being omitted. --- 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"),