@@ -6,123 +6,137 @@ package mcp
66
77import (
88 "encoding/json"
9- "errors"
109 "fmt"
1110)
1211
13- // Content is the wire format for content.
14- // It represents the protocol types TextContent, ImageContent, AudioContent
15- // and EmbeddedResource.
16- // Use [NewTextContent], [NewImageContent], [NewAudioContent] or [NewResourceContent]
17- // to create one.
18- //
19- // The Type field must be one of "text", "image", "audio" or "resource". The
20- // constructors above populate this field appropriately.
21- // Although at most one of Text, Data, and Resource should be non-zero, consumers of Content
22- // use the Type field to determine which value to use; values in the other fields are ignored.
23- type Content struct {
24- Type string `json:"type"`
25- Text string `json:"text,omitempty"`
26- MIMEType string `json:"mimeType,omitempty"`
27- Data []byte `json:"data,omitempty"`
28- Resource * ResourceContents `json:"resource,omitempty"`
29- Annotations * Annotations `json:"annotations,omitempty"`
12+ // A Content is a [TextContent], [ImageContent], [AudioContent] or
13+ // [EmbeddedResource].
14+ type Content interface {
15+ MarshalJSON () ([]byte , error )
16+ fromWire (* wireContent )
3017}
3118
32- func (c * Content ) UnmarshalJSON (data []byte ) error {
33- type wireContent Content // for naive unmarshaling
34- var c2 wireContent
35- if err := json .Unmarshal (data , & c2 ); err != nil {
36- return err
37- }
38- switch c2 .Type {
39- case "text" , "image" , "audio" , "resource" :
40- default :
41- return fmt .Errorf ("unrecognized content type %s" , c .Type )
42- }
43- * c = Content (c2 )
44- return nil
19+ // TextContent is a textual content.
20+ type TextContent struct {
21+ Text string
4522}
4623
47- // NewTextContent creates a [Content] with text.
48- func NewTextContent (text string ) * Content {
49- return & Content {Type : "text" , Text : text }
24+ func (c * TextContent ) MarshalJSON () ([]byte , error ) {
25+ return json .Marshal (& wireContent {Type : "text" , Text : c .Text })
5026}
5127
52- // NewImageContent creates a [Content] with image data.
53- func NewImageContent (data []byte , mimeType string ) * Content {
54- return & Content {Type : "image" , Data : data , MIMEType : mimeType }
28+ func (c * TextContent ) fromWire (wire * wireContent ) {
29+ c .Text = wire .Text
5530}
5631
57- // NewAudioContent creates a [Content] with audio data.
58- func NewAudioContent (data []byte , mimeType string ) * Content {
59- return & Content {Type : "audio" , Data : data , MIMEType : mimeType }
32+ // ImageContent contains base64-encoded image data.
33+ type ImageContent struct {
34+ Data []byte // base64-encoded
35+ MIMEType string
6036}
6137
62- // NewResourceContent creates a [Content] with an embedded resource.
63- func NewResourceContent (resource * ResourceContents ) * Content {
64- return & Content {Type : "resource" , Resource : resource }
38+ func (c * ImageContent ) MarshalJSON () ([]byte , error ) {
39+ return json .Marshal (& wireContent {Type : "image" , MIMEType : c .MIMEType , Data : c .Data })
6540}
6641
67- // ResourceContents represents the union of the spec's {Text,Blob}ResourceContents types.
68- // See https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-03-26/schema.ts#L524-L551
69- // for the inheritance structure.
42+ func (c * ImageContent ) fromWire (wire * wireContent ) {
43+ c .MIMEType = wire .MIMEType
44+ c .Data = wire .Data
45+ }
46+
47+ // AudioContent contains base64-encoded audio data.
48+ type AudioContent struct {
49+ Data []byte
50+ MIMEType string
51+ }
52+
53+ func (c AudioContent ) MarshalJSON () ([]byte , error ) {
54+ return json .Marshal (& wireContent {Type : "audio" , MIMEType : c .MIMEType , Data : c .Data })
55+ }
56+
57+ func (c * AudioContent ) fromWire (wire * wireContent ) {
58+ c .MIMEType = wire .MIMEType
59+ c .Data = wire .Data
60+ }
61+
62+ // EmbeddedResource contains embedded resources.
63+ type EmbeddedResource struct {
64+ Resource * ResourceContents
65+ }
7066
71- // A ResourceContents is either a TextResourceContents or a BlobResourceContents.
72- // Use [NewTextResourceContents] or [NextBlobResourceContents] to create one.
67+ func (r * EmbeddedResource ) MarshalJSON () ([]byte , error ) {
68+ return json .Marshal (& wireContent {Type : "resource" , Resource : r .Resource })
69+ }
70+
71+ func (c * EmbeddedResource ) fromWire (wire * wireContent ) {
72+ c .Resource = wire .Resource
73+ }
74+
75+ // FIXME: doc
7376type ResourceContents struct {
74- URI string `json:"uri"` // resource location; must not be empty
77+ URI string `json:"uri,"`
7578 MIMEType string `json:"mimeType,omitempty"`
76- Text string `json:"text"`
77- Blob []byte `json:"blob,omitempty"` // if nil, then text; else blob
79+ Text string `json:"text,omitempty "`
80+ Blob []byte `json:"blob,omitzero"`
7881}
7982
80- func (r ResourceContents ) MarshalJSON () ([]byte , error ) {
81- // If we could assume Go 1.24, we could use omitzero for Blob and avoid this method.
82- if r .URI == "" {
83- return nil , errors .New ("ResourceContents missing URI" )
84- }
85- if r .Blob == nil {
86- // Text. Marshal normally.
87- type wireResourceContents ResourceContents // (lacks MarshalJSON method)
88- return json .Marshal ((wireResourceContents )(r ))
89- }
90- // Blob.
91- if r .Text != "" {
92- return nil , errors .New ("ResourceContents has non-zero Text and Blob fields" )
93- }
94- // r.Blob may be the empty slice, so marshal with an alternative definition.
95- br := struct {
96- URI string `json:"uri,omitempty"`
97- MIMEType string `json:"mimeType,omitempty"`
98- Blob []byte `json:"blob"`
99- }{
100- URI : r .URI ,
101- MIMEType : r .MIMEType ,
102- Blob : r .Blob ,
103- }
104- return json .Marshal (br )
83+ // wireContent is the wire format for content.
84+ // It represents the protocol types TextContent, ImageContent, AudioContent
85+ // and EmbeddedResource.
86+ // The Type field distinguishes them. In the protocol, each type has a constant
87+ // value for the field.
88+ // At most one of Text, Data, and Resource is non-zero.
89+ type wireContent struct {
90+ Type string `json:"type"`
91+ Text string `json:"text,omitempty"`
92+ MIMEType string `json:"mimeType,omitempty"`
93+ Data []byte `json:"data,omitempty"`
94+ Resource * ResourceContents `json:"resource,omitempty"`
95+ Annotations * Annotations `json:"annotations,omitempty"`
96+ }
97+
98+ // A wireResource is either a TextResourceContents or a BlobResourceContents.
99+ // See https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-03-26/schema.ts#L524-L551
100+ // for the inheritance structure.
101+ // If Blob is nil, this is a TextResourceContents; otherwise it's a BlobResourceContents.
102+ //
103+ // The URI field describes the resource location.
104+ type wireResource struct {
105105}
106106
107- // NewTextResourceContents returns a [ResourceContents] containing text.
108- func NewTextResourceContents (uri , mimeType , text string ) * ResourceContents {
109- return & ResourceContents {
110- URI : uri ,
111- MIMEType : mimeType ,
112- Text : text ,
113- // Blob is nil, indicating this is a TextResourceContents.
107+ func contentsFromWire (wires []* wireContent , allow map [string ]bool ) ([]Content , error ) {
108+ var blocks []Content
109+ for _ , wire := range wires {
110+ block , err := contentFromWire (wire , allow )
111+ if err != nil {
112+ return nil , err
113+ }
114+ blocks = append (blocks , block )
114115 }
116+ return blocks , nil
115117}
116118
117- // NewBlobResourceContents returns a [ResourceContents] containing a byte slice.
118- func NewBlobResourceContents (uri , mimeType string , blob []byte ) * ResourceContents {
119- // The only way to distinguish text from blob is a non-nil Blob field.
120- if blob == nil {
121- blob = []byte {}
119+ func contentFromWire (wire * wireContent , allow map [string ]bool ) (Content , error ) {
120+ if allow != nil && ! allow [wire .Type ] {
121+ return nil , fmt .Errorf ("invalid content type %q" , wire .Type )
122122 }
123- return & ResourceContents {
124- URI : uri ,
125- MIMEType : mimeType ,
126- Blob : blob ,
123+ switch wire .Type {
124+ case "text" :
125+ v := new (TextContent )
126+ v .fromWire (wire )
127+ return v , nil
128+ case "image" :
129+ v := new (ImageContent )
130+ v .fromWire (wire )
131+ return v , nil
132+ case "audio" :
133+ v := new (AudioContent )
134+ v .fromWire (wire )
135+ return v , nil
136+ case "resource" :
137+ v := new (EmbeddedResource )
138+ v .fromWire (wire )
139+ return v , nil
127140 }
141+ return nil , fmt .Errorf ("internal error: unrecognized content type %s" , wire .Type )
128142}
0 commit comments