internal/mcp: document content constructors
Also, make sure that NewBlobResourceContents creates a Blob. Change-Id: I2f0fe2ac6410d6b155deb2c8643a40bc73e25cd4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/672418 Reviewed-by: Robert Findley <rfindley@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
aedbba98ee
commit
7b100d7b30
1 changed files with 31 additions and 27 deletions
|
|
@ -10,16 +10,16 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
// The []byte fields below are marked omitzero, not omitempty:
|
||||
// we want to marshal an empty byte slice.
|
||||
// TODO(jba): figure out how to fix this for 1.23.
|
||||
|
||||
// Content is the wire format for content.
|
||||
// It represents the protocol types TextContent, ImageContent, AudioContent
|
||||
// and EmbeddedResource.
|
||||
// The Type field distinguishes them. In the protocol, each type has a constant
|
||||
// value for the field.
|
||||
// At most one of Text, Data, and Resource is non-zero.
|
||||
// Use [NewTextContent], [NewImageContent], [NewAudioContent] or [NewResourceContent]
|
||||
// to create one.
|
||||
//
|
||||
// The Type field must be one of "text", "image", "audio" or "resource". The
|
||||
// constructors above populate this field appropriately.
|
||||
// Although at most one of Text, Data, and Resource should be non-zero, consumers of Content
|
||||
// use the Type field to determine which value to use; values in the other fields are ignored.
|
||||
type Content struct {
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text,omitempty"`
|
||||
|
|
@ -44,42 +44,47 @@ func (c *Content) UnmarshalJSON(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// NewTextContent creates a [Content] with text.
|
||||
func NewTextContent(text string) Content {
|
||||
return Content{Type: "text", Text: text}
|
||||
}
|
||||
|
||||
// NewImageContent creates a [Content] with image data.
|
||||
func NewImageContent(data []byte, mimeType string) Content {
|
||||
return Content{Type: "image", Data: data, MIMEType: mimeType}
|
||||
}
|
||||
|
||||
// NewAudioContent creates a [Content] with audio data.
|
||||
func NewAudioContent(data []byte, mimeType string) Content {
|
||||
return Content{Type: "audio", Data: data, MIMEType: mimeType}
|
||||
}
|
||||
|
||||
func NewResourceContent(resource ResourceContents) Content {
|
||||
return Content{Type: "resource", Resource: &resource}
|
||||
// NewResourceContent creates a [Content] with an embedded resource.
|
||||
func NewResourceContent(resource *ResourceContents) Content {
|
||||
return Content{Type: "resource", Resource: resource}
|
||||
}
|
||||
|
||||
// A ResourceContents is either a TextResourceContents or a BlobResourceContents.
|
||||
// ResourceContents represents the union of the spec's {Text,Blob}ResourceContents types.
|
||||
// See https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-03-26/schema.ts#L524-L551
|
||||
// for the inheritance structure.
|
||||
// If Blob is nil, this is a TextResourceContents; otherwise it's a BlobResourceContents.
|
||||
//
|
||||
// The URI field describes the resource location.
|
||||
|
||||
// A ResourceContents is either a TextResourceContents or a BlobResourceContents.
|
||||
// Use [NewTextResourceContents] or [NextBlobResourceContents] to create one.
|
||||
type ResourceContents struct {
|
||||
URI string `json:"uri,"`
|
||||
URI string `json:"uri"` // resource location; must not be empty
|
||||
MIMEType string `json:"mimeType,omitempty"`
|
||||
Text string `json:"text"`
|
||||
Blob []byte `json:"blob,omitempty"`
|
||||
Blob []byte `json:"blob,omitempty"` // if nil, then text; else blob
|
||||
}
|
||||
|
||||
func (r ResourceContents) MarshalJSON() ([]byte, error) {
|
||||
// If we could assume Go 1.24, we could use omitzero for Blob and avoid this method.
|
||||
if r.URI == "" {
|
||||
return nil, errors.New("ResourceContents missing URI")
|
||||
}
|
||||
if r.Blob == nil {
|
||||
// Text. Marshal normally.
|
||||
type wireResourceContents ResourceContents
|
||||
type wireResourceContents ResourceContents // (lacks MarshalJSON method)
|
||||
return json.Marshal((wireResourceContents)(r))
|
||||
}
|
||||
// Blob.
|
||||
|
|
@ -99,8 +104,9 @@ func (r ResourceContents) MarshalJSON() ([]byte, error) {
|
|||
return json.Marshal(br)
|
||||
}
|
||||
|
||||
func NewTextResourceContents(uri, mimeType, text string) ResourceContents {
|
||||
return ResourceContents{
|
||||
// NewTextResourceContents returns a [ResourceContents] containing text.
|
||||
func NewTextResourceContents(uri, mimeType, text string) *ResourceContents {
|
||||
return &ResourceContents{
|
||||
URI: uri,
|
||||
MIMEType: mimeType,
|
||||
Text: text,
|
||||
|
|
@ -108,15 +114,13 @@ func NewTextResourceContents(uri, mimeType, text string) ResourceContents {
|
|||
}
|
||||
}
|
||||
|
||||
// A BlobResourceContents is the contents of a blob resource.
|
||||
type BlobResourceContents struct {
|
||||
URI string
|
||||
MIMEType string
|
||||
Blob []byte
|
||||
}
|
||||
|
||||
func NewBlobResourceContents(uri, mimeType string, blob []byte) ResourceContents {
|
||||
return ResourceContents{
|
||||
// NewTextResourceContents returns a [ResourceContents] containing a byte slice.
|
||||
func NewBlobResourceContents(uri, mimeType string, blob []byte) *ResourceContents {
|
||||
// The only way to distinguish text from blob is a non-nil Blob field.
|
||||
if blob == nil {
|
||||
blob = []byte{}
|
||||
}
|
||||
return &ResourceContents{
|
||||
URI: uri,
|
||||
MIMEType: mimeType,
|
||||
Blob: blob,
|
||||
|
|
|
|||
Loading…
Reference in a new issue