mcp: add ResourceLink type (#76)

This PR introduces the ResourceLink type to represent links to
resources. The new type includes the following fields: `uri`, `name`,
`title`, `description`, `mimeType`, `size`, and `_meta`.

This implementation conforms to the `ResourceLink` schema introduced in
the [MCP schema
2025-06-18](1cfdf1e7a8/schema/2025-06-18/schema.json (L1826)).

Note: `ResourceLink` extends from the `Resource` type in the MCP schema.
As such, it inherits support for optional fields like `title`, `_meta`,
etc. Thus, `URI`, `Name`, `Title`, `Description`, and `Size` are added
to the `wireContent`

For reference, the [TypeScript SDK test
suite](fb4d07f0ee/src/types.test.ts (L43))
provides examples demonstrating valid ResourceLink objects with all
optional fields populated.

https://modelcontextprotocol.io/specification/2025-06-18/server/tools#resource-links
- [ResourceLink JSON
Schema](1cfdf1e7a8/schema/2025-06-18/schema.json (L1826))

Signed-off-by: Xin Fu <xfu83@bloomberg.net>

Fixes #14.
This commit is contained in:
Xin 2025-07-01 17:41:22 +01:00 • committed by GitHub
commit 92b6983c1c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 69 additions and 7 deletions

View file

@ -10,10 +10,8 @@ import (
"fmt"
)
// A Content is a [TextContent], [ImageContent], [AudioContent] or
// [EmbeddedResource].
//
// TODO(rfindley): add ResourceLink.
// A Content is a [TextContent], [ImageContent], [AudioContent],
// [ResourceLink], or [EmbeddedResource].
type Content interface {
MarshalJSON() ([]byte, error)
fromWire(*wireContent)
@ -91,6 +89,43 @@ func (c *AudioContent) fromWire(wire *wireContent) {
c.Annotations = wire.Annotations
}
// ResourceLink is a link to a resource
type ResourceLink struct {
URI string
Name string
Title string
Description string
MIMEType string
Size *int64
Meta Meta
Annotations *Annotations
}
func (c *ResourceLink) MarshalJSON() ([]byte, error) {
return json.Marshal(&wireContent{
Type: "resource_link",
URI: c.URI,
Name: c.Name,
Title: c.Title,
Description: c.Description,
MIMEType: c.MIMEType,
Size: c.Size,
Meta: c.Meta,
Annotations: c.Annotations,
})
}
func (c *ResourceLink) fromWire(wire *wireContent) {
c.URI = wire.URI
c.Name = wire.Name
c.Title = wire.Title
c.Description = wire.Description
c.MIMEType = wire.MIMEType
c.Size = wire.Size
c.Meta = wire.Meta
c.Annotations = wire.Annotations
}
// EmbeddedResource contains embedded resources.
type EmbeddedResource struct {
Resource *ResourceContents
@ -151,17 +186,22 @@ func (r ResourceContents) MarshalJSON() ([]byte, error) {
}
// wireContent is the wire format for content.
// It represents the protocol types TextContent, ImageContent, AudioContent
// and EmbeddedResource.
// It represents the protocol types TextContent, ImageContent, AudioContent,
// ResourceLink, 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.
// At most one of Text, Data, Resource, and URI is non-zero.
type wireContent struct {
Type string `json:"type"`
Text string `json:"text,omitempty"`
MIMEType string `json:"mimeType,omitempty"`
Data []byte `json:"data,omitempty"`
Resource *ResourceContents `json:"resource,omitempty"`
URI string `json:"uri,omitempty"`
Name string `json:"name,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Size *int64 `json:"size,omitempty"`
Meta Meta `json:"_meta,omitempty"`
Annotations *Annotations `json:"annotations,omitempty"`
}
@ -195,6 +235,10 @@ func contentFromWire(wire *wireContent, allow map[string]bool) (Content, error)
v := new(AudioContent)
v.fromWire(wire)
return v, nil
case "resource_link":
v := new(ResourceLink)
v.fromWire(wire)
return v, nil
case "resource":
v := new(EmbeddedResource)
v.fromWire(wire)

View file

@ -82,6 +82,24 @@ func TestContent(t *testing.T) {
},
`{"type":"resource","resource":{"uri":"file://foo","mimeType":"text","text":"abc"},"_meta":{"key":"value"},"annotations":{"priority":1}}`,
},
{
&mcp.ResourceLink{
URI: "file:///path/to/file.txt",
Name: "file.txt",
},
`{"type":"resource_link","uri":"file:///path/to/file.txt","name":"file.txt"}`,
},
{
&mcp.ResourceLink{
URI: "https://example.com/resource",
Name: "Example Resource",
Title: "A comprehensive example resource",
Description: "This resource demonstrates all fields",
MIMEType: "text/plain",
Meta: mcp.Meta{"custom": "metadata"},
},
`{"type":"resource_link","mimeType":"text/plain","uri":"https://example.com/resource","name":"Example Resource","title":"A comprehensive example resource","description":"This resource demonstrates all fields","_meta":{"custom":"metadata"}}`,
},
}
for _, test := range tests {