Skip to content

Commit f56e646

Browse files
committed
chore: apply coderabbit nits.
1 parent 5eff53a commit f56e646

File tree

3 files changed

+135
-19
lines changed

3 files changed

+135
-19
lines changed

mcp/types.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -716,8 +716,9 @@ type ResourceContents interface {
716716
}
717717

718718
type TextResourceContents struct {
719-
// Meta is a metadata object that is reserved by MCP for storing additional information.
720-
Meta map[string]interface{} `json:"_meta,omitempty"`
719+
// Raw per‑resource metadata; pass‑through as defined by MCP. Not the same as mcp.Meta.
720+
// Allows _meta to be used for MCP-UI features for example. Does not assume any specific format.
721+
Meta map[string]any `json:"_meta,omitempty"`
721722
// The URI of this resource.
722723
URI string `json:"uri"`
723724
// The MIME type of this resource, if known.
@@ -730,8 +731,9 @@ type TextResourceContents struct {
730731
func (TextResourceContents) isResourceContents() {}
731732

732733
type BlobResourceContents struct {
733-
// Meta is a metadata object that is reserved by MCP for storing additional information.
734-
Meta map[string]interface{} `json:"_meta,omitempty"`
734+
// Raw per‑resource metadata; pass‑through as defined by MCP. Not the same as mcp.Meta.
735+
// Allows _meta to be used for MCP-UI features for example. Does not assume any specific format.
736+
Meta map[string]any `json:"_meta,omitempty"`
735737
// The URI of this resource.
736738
URI string `json:"uri"`
737739
// The MIME type of this resource, if known.

mcp/types_test.go

Lines changed: 122 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,19 @@ func TestResourceContentsMetaField(t *testing.T) {
144144
name string
145145
inputJSON string
146146
expectedType string
147-
expectedMeta map[string]interface{}
147+
expectedMeta map[string]any
148148
}{
149+
{
150+
name: "TextResourceContents with empty _meta",
151+
inputJSON: `{
152+
"uri":"file://empty-meta.txt",
153+
"mimeType":"text/plain",
154+
"text":"x",
155+
"_meta": {}
156+
}`,
157+
expectedType: "text",
158+
expectedMeta: map[string]any{},
159+
},
149160
{
150161
name: "TextResourceContents with _meta field",
151162
inputJSON: `{
@@ -160,9 +171,9 @@ func TestResourceContentsMetaField(t *testing.T) {
160171
}
161172
}`,
162173
expectedType: "text",
163-
expectedMeta: map[string]interface{}{
174+
expectedMeta: map[string]any{
164175
"mcpui.dev/ui-preferred-frame-size": []interface{}{"800px", "600px"},
165-
"mcpui.dev/ui-initial-render-data": map[string]interface{}{
176+
"mcpui.dev/ui-initial-render-data": map[string]any{
166177
"test": "value",
167178
},
168179
},
@@ -180,7 +191,7 @@ func TestResourceContentsMetaField(t *testing.T) {
180191
}
181192
}`,
182193
expectedType: "blob",
183-
expectedMeta: map[string]interface{}{
194+
expectedMeta: map[string]any{
184195
"width": float64(100), // JSON numbers are always float64
185196
"height": float64(100),
186197
"format": "PNG",
@@ -211,7 +222,7 @@ func TestResourceContentsMetaField(t *testing.T) {
211222
for _, tc := range tests {
212223
t.Run(tc.name, func(t *testing.T) {
213224
// Parse the JSON as a generic map first
214-
var contentMap map[string]interface{}
225+
var contentMap map[string]any
215226
err := json.Unmarshal([]byte(tc.inputJSON), &contentMap)
216227
require.NoError(t, err)
217228

@@ -250,16 +261,119 @@ func TestResourceContentsMetaField(t *testing.T) {
250261
marshaledJSON, err := json.Marshal(resourceContent)
251262
require.NoError(t, err)
252263

253-
var marshaledMap map[string]interface{}
264+
var marshaledMap map[string]any
254265
err = json.Unmarshal(marshaledJSON, &marshaledMap)
255266
require.NoError(t, err)
256267

257268
// Verify _meta field is preserved in marshaled output
269+
v, ok := marshaledMap["_meta"]
258270
if tc.expectedMeta != nil {
259-
assert.Equal(t, tc.expectedMeta, marshaledMap["_meta"])
271+
// Special case: empty maps are omitted due to omitempty tag
272+
if len(tc.expectedMeta) == 0 {
273+
assert.False(t, ok, "_meta should be omitted when empty due to omitempty")
274+
} else {
275+
require.True(t, ok, "_meta should be present")
276+
assert.Equal(t, tc.expectedMeta, v)
277+
}
260278
} else {
261-
assert.Nil(t, marshaledMap["_meta"])
279+
assert.False(t, ok, "_meta should be omitted when nil")
262280
}
263281
})
264282
}
265283
}
284+
285+
func TestParseResourceContentsInvalidMeta(t *testing.T) {
286+
tests := []struct {
287+
name string
288+
inputJSON string
289+
expectedErr string
290+
}{
291+
{
292+
name: "TextResourceContents with invalid _meta (string)",
293+
inputJSON: `{
294+
"uri": "file://test.txt",
295+
"mimeType": "text/plain",
296+
"text": "Hello World",
297+
"_meta": "invalid_meta_string"
298+
}`,
299+
expectedErr: "_meta must be an object",
300+
},
301+
{
302+
name: "TextResourceContents with invalid _meta (number)",
303+
inputJSON: `{
304+
"uri": "file://test.txt",
305+
"mimeType": "text/plain",
306+
"text": "Hello World",
307+
"_meta": 123
308+
}`,
309+
expectedErr: "_meta must be an object",
310+
},
311+
{
312+
name: "TextResourceContents with invalid _meta (array)",
313+
inputJSON: `{
314+
"uri": "file://test.txt",
315+
"mimeType": "text/plain",
316+
"text": "Hello World",
317+
"_meta": ["invalid", "array"]
318+
}`,
319+
expectedErr: "_meta must be an object",
320+
},
321+
{
322+
name: "TextResourceContents with invalid _meta (boolean)",
323+
inputJSON: `{
324+
"uri": "file://test.txt",
325+
"mimeType": "text/plain",
326+
"text": "Hello World",
327+
"_meta": true
328+
}`,
329+
expectedErr: "_meta must be an object",
330+
},
331+
{
332+
name: "TextResourceContents with invalid _meta (null)",
333+
inputJSON: `{
334+
"uri": "file://test.txt",
335+
"mimeType": "text/plain",
336+
"text": "Hello World",
337+
"_meta": null
338+
}`,
339+
expectedErr: "_meta must be an object",
340+
},
341+
{
342+
name: "BlobResourceContents with invalid _meta (string)",
343+
inputJSON: `{
344+
"uri": "file://image.png",
345+
"mimeType": "image/png",
346+
"blob": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
347+
"_meta": "invalid_meta_string"
348+
}`,
349+
expectedErr: "_meta must be an object",
350+
},
351+
{
352+
name: "BlobResourceContents with invalid _meta (number)",
353+
inputJSON: `{
354+
"uri": "file://image.png",
355+
"mimeType": "image/png",
356+
"blob": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
357+
"_meta": 456
358+
}`,
359+
expectedErr: "_meta must be an object",
360+
},
361+
}
362+
363+
for _, tc := range tests {
364+
t.Run(tc.name, func(t *testing.T) {
365+
// Parse the JSON as a generic map first
366+
var contentMap map[string]any
367+
err := json.Unmarshal([]byte(tc.inputJSON), &contentMap)
368+
require.NoError(t, err)
369+
370+
// Use ParseResourceContents to convert to ResourceContents
371+
resourceContent, err := ParseResourceContents(contentMap)
372+
373+
// Expect an error
374+
require.Error(t, err)
375+
assert.Contains(t, err.Error(), tc.expectedErr)
376+
assert.Nil(t, resourceContent)
377+
})
378+
}
379+
}

mcp/utils.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -705,28 +705,28 @@ func ParseResourceContents(contentMap map[string]any) (ResourceContents, error)
705705

706706
mimeType := ExtractString(contentMap, "mimeType")
707707

708-
var meta map[string]interface{}
709-
if metaValue, ok := contentMap["_meta"]; ok {
710-
if metaMap, ok := metaValue.(map[string]interface{}); ok {
711-
meta = metaMap
712-
}
708+
meta := ExtractMap(contentMap, "_meta")
709+
710+
// Validate _meta field if present
711+
if _, present := contentMap["_meta"]; present && meta == nil {
712+
return nil, fmt.Errorf("_meta must be an object")
713713
}
714714

715715
if text := ExtractString(contentMap, "text"); text != "" {
716716
return TextResourceContents{
717+
Meta: meta,
717718
URI: uri,
718719
MIMEType: mimeType,
719720
Text: text,
720-
Meta: meta,
721721
}, nil
722722
}
723723

724724
if blob := ExtractString(contentMap, "blob"); blob != "" {
725725
return BlobResourceContents{
726+
Meta: meta,
726727
URI: uri,
727728
MIMEType: mimeType,
728729
Blob: blob,
729-
Meta: meta,
730730
}, nil
731731
}
732732

0 commit comments

Comments
 (0)