Skip to content

Conversation

rabbah
Copy link
Contributor

@rabbah rabbah commented Sep 18, 2025

Description

Adds support for parsing the optional _meta field in resource contents. This field is used in server-client interactions and a prime example of that is MCP UI.

- Parses optional `_meta` field on the text/blob resource contents
- Adds `TestResourceContentsMetaField` to verify `_meta` field handling
- Ensures backward compatibility with resources without `_meta`

Fixes #585.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • MCP spec compatibility implementation
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring (no functional changes)
  • Performance improvement
  • Tests only (no functional changes)
  • Other (please describe):

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation accordingly

MCP Spec Compliance

  • This PR implements a feature defined in the MCP specification
  • Link to relevant spec section: Link text
  • Implementation follows the specification exactly

Additional Information

Summary by CodeRabbit

  • New Features
    • Optional per-resource metadata via a _meta object on text and binary resources; preserved when present and omitted when absent.
  • Tests
    • Added coverage for _meta presence/absence, JSON round-trip, and numeric type behavior in metadata.
  • Bug Fixes
    • Validation rejects invalid _meta values and returns a clear error when _meta is not an object.

    - Parses optional _meta field on the text/blob resource
    - Add TestResourceContentsMetaField to verify _meta field handling
    - Ensure backward compatibility with resources without _meta
Copy link
Contributor

coderabbitai bot commented Sep 18, 2025

Walkthrough

Convert TextResourceContents.Meta and BlobResourceContents.Meta from *Meta to map[string]any, parse and validate an optional _meta object in ParseResourceContents, and add tests covering valid _meta handling, round-trip JSON behavior, and invalid _meta error cases.

Changes

Cohort / File(s) Summary
Type definitions
mcp/types.go
Change TextResourceContents.Meta and BlobResourceContents.Meta from *Meta to map[string]any to represent the _meta object; update comments to indicate passthrough/raw metadata.
Parsing logic
mcp/utils.go
ParseResourceContents extracts _meta using ExtractMap(..., "_meta"), validates that a present _meta is an object (returns error if not), and sets Meta: meta on returned TextResourceContents/BlobResourceContents.
Tests
mcp/types_test.go
Add TestResourceContentsMetaField to assert parsing, type discrimination (text/blob), meta preservation on marshal/unmarshal, and JSON number type (float64) in meta; add TestParseResourceContentsInvalidMeta to assert error when _meta is not an object.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

type: enhancement, area: mcp spec

Suggested reviewers

  • pottekkat
  • robert-jackson-glean
  • ezynda3

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues Check ✅ Passed The changes implement the objectives of linked issue #585: the _meta field was added to TextResourceContents and BlobResourceContents as a map[string]any, ParseResourceContents was updated to extract and validate an optional _meta object, and tests cover valid, absent, and invalid _meta cases; this matches the requested spec-compatible behavior. The implemented type and validation align with the issue's requirement to preserve arbitrary JSON metadata and to error on invalid non-object _meta values.
Out of Scope Changes Check ✅ Passed Based on the provided summaries, changes are limited to mcp/types.go, mcp/utils.go, and mcp/types_test.go and are focused on adding the Meta field, updating parsing/validation, and adding tests; there are no other unrelated refactors or file changes indicated, so no out-of-scope changes were detected.
Title Check ✅ Passed The title succinctly captures the core change by stating it is a new feature (“feat:”) that updates the type definition of the _meta field for both text and blob resources, which aligns directly with the primary modifications in mcp/types.go and related tests. It is clear, concise, and follows the conventional commit style without extraneous detail or vagueness. This makes it easy for team members to understand the main purpose of the pull request at a glance.
Description Check ✅ Passed The description faithfully follows the repository’s template by including a clear Description section that explains the addition of the optional _meta field and references the related issue, a filled-out Type of Change block, a complete Checklist, and an MCP Spec Compliance section with a link to the spec. All required headings are present and populated appropriately, and non-critical sections such as Additional Information are optional. The content clearly communicates the purpose, scope, and compliance of the changes, making the description comprehensive and aligned with expectations.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (9)
mcp/types.go (3)

718-728: Normalize to map[string]any and document pass‑through semantics.

Use any for consistency across the file, and make it explicit this _meta is raw pass‑through (unlike *Meta used elsewhere).

 type TextResourceContents struct {
   // Meta is a metadata object that is reserved by MCP for storing additional information.
-  Meta map[string]interface{} `json:"_meta,omitempty"`
+  // Raw per‑resource metadata; pass‑through as defined by MCP. Not the same as mcp.Meta.
+  Meta map[string]any `json:"_meta,omitempty"`
   // The URI of this resource.
   URI string `json:"uri"`

732-741: Apply the same any normalization to BlobResourceContents.

Keeps types consistent across the codebase.

 type BlobResourceContents struct {
   // Meta is a metadata object that is reserved by MCP for storing additional information.
-  Meta map[string]interface{} `json:"_meta,omitempty"`
+  Meta map[string]any `json:"_meta,omitempty"`
   // The URI of this resource.
   URI string `json:"uri"`

718-735: Intentional divergence from *Meta elsewhere – add a brief package comment or doc note.

Since other structs use *Meta while resource contents use a raw map, add a short note in package docs or near the type explaining why (spec requires arbitrary keys; no progressToken semantics).

Would you like me to draft a short doc blurb for package comments?

mcp/utils.go (3)

708-714: Use ExtractMap and any for brevity and consistency.

Leverages the existing helper and aligns with the suggested map[string]any change.

-  var meta map[string]interface{}
-  if metaValue, ok := contentMap["_meta"]; ok {
-    if metaMap, ok := metaValue.(map[string]interface{}); ok {
-      meta = metaMap
-    }
-  }
+  meta := ExtractMap(contentMap, "_meta")

715-731: Option: validate _meta type when present.

If _meta exists but isn’t an object, consider returning an error for clearer diagnostics. Backward‑compat risk is low.

   if text := ExtractString(contentMap, "text"); text != "" {
     return TextResourceContents{
       URI:      uri,
       MIMEType: mimeType,
       Text:     text,
-      Meta:     meta,
+      Meta:     meta,
     }, nil
   }
 
   if blob := ExtractString(contentMap, "blob"); blob != "" {
     return BlobResourceContents{
       URI:      uri,
       MIMEType: mimeType,
       Blob:     blob,
-      Meta:     meta,
+      Meta:     meta,
     }, nil
   }
 
+  // If _meta was present but invalid (not an object), surface a helpful error.
+  if _, present := contentMap["_meta"]; present && meta == nil {
+    return nil, fmt.Errorf("_meta must be an object")
+  }
+
   return nil, fmt.Errorf("unsupported resource type")

If you prefer to keep lenient parsing, at least log/trace this case during tests.


715-724: Consider presence‑check vs non‑empty for text/blob (spec nuance).

Current logic treats empty string as “absent.” If zero‑length text/blob is valid per MCP, switch to presence checks (_, ok := contentMap["text"]).

I can send a small diff once we confirm the spec intent.

mcp/types_test.go (3)

142-209: Good coverage of _meta on text/blob; add empty‑object case.

Add a case where _meta is {} to ensure it round‑trips as an empty object (not omitted).

   tests := []struct {
     name         string
     inputJSON    string
     expectedType string
-    expectedMeta map[string]interface{}
+    expectedMeta map[string]any
   }{
+    {
+      name: "TextResourceContents with empty _meta",
+      inputJSON: `{
+        "uri":"file://empty-meta.txt",
+        "mimeType":"text/plain",
+        "text":"x",
+        "_meta": {}
+      }`,
+      expectedType: "text",
+      expectedMeta: map[string]any{},
+    },

211-265: Strengthen assertions: distinguish “missing” vs “null”, and use any.

Avoid false positives by checking the map lookup boolean; also normalize types to any.

-      var contentMap map[string]interface{}
+      var contentMap map[string]any
       err := json.Unmarshal([]byte(tc.inputJSON), &contentMap)
       require.NoError(t, err)
@@
-      marshaledJSON, err := json.Marshal(resourceContent)
+      marshaledJSON, err := json.Marshal(resourceContent)
       require.NoError(t, err)
 
-      var marshaledMap map[string]interface{}
+      var marshaledMap map[string]any
       err = json.Unmarshal(marshaledJSON, &marshaledMap)
       require.NoError(t, err)
 
-      // Verify _meta field is preserved in marshaled output
-      if tc.expectedMeta != nil {
-        assert.Equal(t, tc.expectedMeta, marshaledMap["_meta"])
-      } else {
-        assert.Nil(t, marshaledMap["_meta"])
-      }
+      // Verify _meta field is preserved in marshaled output
+      v, ok := marshaledMap["_meta"]
+      if tc.expectedMeta != nil {
+        require.True(t, ok, "_meta should be present")
+        assert.Equal(t, tc.expectedMeta, v)
+      } else {
+        assert.False(t, ok, "_meta should be omitted when nil")
+      }

142-265: Optional: add a negative test for non‑object _meta.

E.g., _meta: "oops" should error (if you adopt the stricter validation), or be ignored (if you keep lenient).

Want me to draft this test both ways and guard it with a build tag until a decision is made?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 840879b and 5eff53a.

📒 Files selected for processing (3)
  • mcp/types.go (2 hunks)
  • mcp/types_test.go (1 hunks)
  • mcp/utils.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
mcp/types_test.go (2)
mcp/utils.go (1)
  • ParseResourceContents (700-734)
mcp/types.go (5)
  • TextResourceContents (718-728)
  • TextResourceContents (730-730)
  • Meta (121-133)
  • BlobResourceContents (732-741)
  • BlobResourceContents (743-743)
mcp/utils.go (1)
mcp/types.go (3)
  • TextResourceContents (718-728)
  • TextResourceContents (730-730)
  • Meta (121-133)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
mcp/utils.go (2)

708-714: _meta validation is correct; nit: avoid double lookup and make flow explicit.

Single-pass check is a touch cleaner and avoids two map lookups.

-	meta := ExtractMap(contentMap, "_meta")
-
-	// Validate _meta field if present
-	if _, present := contentMap["_meta"]; present && meta == nil {
-		return nil, fmt.Errorf("_meta must be an object")
-	}
+	var meta map[string]any
+	if v, present := contentMap["_meta"]; present {
+		m, ok := v.(map[string]any)
+		if !ok {
+			return nil, fmt.Errorf("_meta must be an object")
+		}
+		meta = m
+	}

715-731: Union guard: reject resources that specify both text and blob.

Spec-wise these are disjoint variants; being permissive can hide producer bugs.

-	if text := ExtractString(contentMap, "text"); text != "" {
-		return TextResourceContents{
-			Meta:     meta,
-			URI:      uri,
-			MIMEType: mimeType,
-			Text:     text,
-		}, nil
-	}
-
-	if blob := ExtractString(contentMap, "blob"); blob != "" {
-		return BlobResourceContents{
-			Meta:     meta,
-			URI:      uri,
-			MIMEType: mimeType,
-			Blob:     blob,
-		}, nil
-	}
+	text := ExtractString(contentMap, "text")
+	blob := ExtractString(contentMap, "blob")
+	if text != "" && blob != "" {
+		return nil, fmt.Errorf("resource must not specify both text and blob")
+	}
+	if text != "" {
+		return TextResourceContents{
+			Meta:     meta,
+			URI:      uri,
+			MIMEType: mimeType,
+			Text:     text,
+		}, nil
+	}
+	if blob != "" {
+		return BlobResourceContents{
+			Meta:     meta,
+			URI:      uri,
+			MIMEType: mimeType,
+			Blob:     blob,
+		}, nil
+	}
mcp/types_test.go (2)

260-281: Optional: assert type of marshaled _meta and add an ambiguity test.

  • Add assert.IsType(t, map[string]any{}, v) when _meta is present (non-empty).
  • Consider a test where both "text" and "blob" are set to document expected behavior (ideally reject).

285-379: Negative cases look solid; minor gap for blob variants.

You could mirror the array/boolean/null invalid _meta cases for blob, for symmetry.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5eff53a and f56e646.

📒 Files selected for processing (3)
  • mcp/types.go (2 hunks)
  • mcp/types_test.go (1 hunks)
  • mcp/utils.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • mcp/types.go
🧰 Additional context used
🧬 Code graph analysis (2)
mcp/types_test.go (2)
mcp/utils.go (1)
  • ParseResourceContents (700-734)
mcp/types.go (5)
  • TextResourceContents (718-729)
  • TextResourceContents (731-731)
  • Meta (121-133)
  • BlobResourceContents (733-743)
  • BlobResourceContents (745-745)
mcp/utils.go (1)
mcp/types.go (3)
  • TextResourceContents (718-729)
  • TextResourceContents (731-731)
  • Meta (121-133)
🔇 Additional comments (3)
mcp/utils.go (2)

716-721: LGTM: _meta is plumbed through for text resources.


726-730: LGTM: _meta is plumbed through for blob resources.

mcp/types_test.go (1)

142-283: Good coverage for presence/absence and round‑trip of _meta.

Covers empty map omission and float64 number decoding.

@ezynda3 ezynda3 added the type: enhancement New feature or enhancement request label Sep 19, 2025
@ezynda3
Copy link
Contributor

ezynda3 commented Sep 30, 2025

@rabbah if you could fix the golangci-lint issues I'll go ahead and merge this.

@ezynda3 ezynda3 added the status: needs submitter response Waiting for feedback from issue opener label Sep 30, 2025
@ezynda3 ezynda3 merged commit 45826c4 into mark3labs:main Sep 30, 2025
3 of 4 checks passed
elewis787 pushed a commit to elewis787/mcp-go that referenced this pull request Oct 1, 2025
…ark3labs#591)

* feat: update type definition of _meta field to text/blob resources.

    - Parses optional _meta field on the text/blob resource
    - Add TestResourceContentsMetaField to verify _meta field handling
    - Ensure backward compatibility with resources without _meta

* chore: apply coderabbit nits.
@coderabbitai coderabbitai bot mentioned this pull request Oct 1, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs submitter response Waiting for feedback from issue opener type: enhancement New feature or enhancement request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feature: add _meta field to resource content types
2 participants