@@ -138,3 +138,242 @@ func TestCallToolResultWithResourceLink(t *testing.T) {
138
138
assert .Equal (t , "A test document" , resourceLink .Description )
139
139
assert .Equal (t , "application/pdf" , resourceLink .MIMEType )
140
140
}
141
+
142
+ func TestResourceContentsMetaField (t * testing.T ) {
143
+ tests := []struct {
144
+ name string
145
+ inputJSON string
146
+ expectedType string
147
+ expectedMeta map [string ]any
148
+ }{
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
+ },
160
+ {
161
+ name : "TextResourceContents with _meta field" ,
162
+ inputJSON : `{
163
+ "uri": "file://test.txt",
164
+ "mimeType": "text/plain",
165
+ "text": "Hello World",
166
+ "_meta": {
167
+ "mcpui.dev/ui-preferred-frame-size": ["800px", "600px"],
168
+ "mcpui.dev/ui-initial-render-data": {
169
+ "test": "value"
170
+ }
171
+ }
172
+ }` ,
173
+ expectedType : "text" ,
174
+ expectedMeta : map [string ]any {
175
+ "mcpui.dev/ui-preferred-frame-size" : []interface {}{"800px" , "600px" },
176
+ "mcpui.dev/ui-initial-render-data" : map [string ]any {
177
+ "test" : "value" ,
178
+ },
179
+ },
180
+ },
181
+ {
182
+ name : "BlobResourceContents with _meta field" ,
183
+ inputJSON : `{
184
+ "uri": "file://image.png",
185
+ "mimeType": "image/png",
186
+ "blob": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
187
+ "_meta": {
188
+ "width": 100,
189
+ "height": 100,
190
+ "format": "PNG"
191
+ }
192
+ }` ,
193
+ expectedType : "blob" ,
194
+ expectedMeta : map [string ]any {
195
+ "width" : float64 (100 ), // JSON numbers are always float64
196
+ "height" : float64 (100 ),
197
+ "format" : "PNG" ,
198
+ },
199
+ },
200
+ {
201
+ name : "TextResourceContents without _meta field" ,
202
+ inputJSON : `{
203
+ "uri": "file://simple.txt",
204
+ "mimeType": "text/plain",
205
+ "text": "Simple content"
206
+ }` ,
207
+ expectedType : "text" ,
208
+ expectedMeta : nil ,
209
+ },
210
+ {
211
+ name : "BlobResourceContents without _meta field" ,
212
+ inputJSON : `{
213
+ "uri": "file://simple.png",
214
+ "mimeType": "image/png",
215
+ "blob": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=="
216
+ }` ,
217
+ expectedType : "blob" ,
218
+ expectedMeta : nil ,
219
+ },
220
+ }
221
+
222
+ for _ , tc := range tests {
223
+ t .Run (tc .name , func (t * testing.T ) {
224
+ // Parse the JSON as a generic map first
225
+ var contentMap map [string ]any
226
+ err := json .Unmarshal ([]byte (tc .inputJSON ), & contentMap )
227
+ require .NoError (t , err )
228
+
229
+ // Use ParseResourceContents to convert to ResourceContents
230
+ resourceContent , err := ParseResourceContents (contentMap )
231
+ require .NoError (t , err )
232
+ require .NotNil (t , resourceContent )
233
+
234
+ // Test based on expected type
235
+ if tc .expectedType == "text" {
236
+ textContent , ok := resourceContent .(TextResourceContents )
237
+ require .True (t , ok , "Expected TextResourceContents" )
238
+
239
+ // Verify standard fields
240
+ assert .Equal (t , contentMap ["uri" ], textContent .URI )
241
+ assert .Equal (t , contentMap ["mimeType" ], textContent .MIMEType )
242
+ assert .Equal (t , contentMap ["text" ], textContent .Text )
243
+
244
+ // Verify _meta field
245
+ assert .Equal (t , tc .expectedMeta , textContent .Meta )
246
+
247
+ } else if tc .expectedType == "blob" {
248
+ blobContent , ok := resourceContent .(BlobResourceContents )
249
+ require .True (t , ok , "Expected BlobResourceContents" )
250
+
251
+ // Verify standard fields
252
+ assert .Equal (t , contentMap ["uri" ], blobContent .URI )
253
+ assert .Equal (t , contentMap ["mimeType" ], blobContent .MIMEType )
254
+ assert .Equal (t , contentMap ["blob" ], blobContent .Blob )
255
+
256
+ // Verify _meta field
257
+ assert .Equal (t , tc .expectedMeta , blobContent .Meta )
258
+ }
259
+
260
+ // Test round-trip marshaling to ensure _meta is preserved
261
+ marshaledJSON , err := json .Marshal (resourceContent )
262
+ require .NoError (t , err )
263
+
264
+ var marshaledMap map [string ]any
265
+ err = json .Unmarshal (marshaledJSON , & marshaledMap )
266
+ require .NoError (t , err )
267
+
268
+ // Verify _meta field is preserved in marshaled output
269
+ v , ok := marshaledMap ["_meta" ]
270
+ if tc .expectedMeta != nil {
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
+ }
278
+ } else {
279
+ assert .False (t , ok , "_meta should be omitted when nil" )
280
+ }
281
+ })
282
+ }
283
+ }
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
+ }
0 commit comments