Skip to content

fix(rmcp): flatten Resource variant of PromptMessageContent#843

Open
ynishi wants to merge 1 commit intomodelcontextprotocol:mainfrom
ynishi:fix/prompt-resource-double-nested
Open

fix(rmcp): flatten Resource variant of PromptMessageContent#843
ynishi wants to merge 1 commit intomodelcontextprotocol:mainfrom
ynishi:fix/prompt-resource-double-nested

Conversation

@ynishi
Copy link
Copy Markdown

@ynishi ynishi commented May 11, 2026

Fixes #842

Motivation and Context

PromptMessageContent::Resource currently serializes with a doubly-nested
resource object, producing:

{
  "type": "resource",
  "resource": {
    "resource": { "uri": "file:///example.md", "text": "...", "mimeType": "text/markdown" }
  }
}

The MCP specification (Prompts — Embedded Resources) defines the wire shape
as a single, flat resource field:

{
  "type": "resource",
  "resource": { "uri": "file:///example.md", "text": "...", "mimeType": "text/markdown" }
}

The other variants of PromptMessageContent (Text, Image, ResourceLink)
already use #[serde(flatten)] and serialize flat. Resource is the only
outlier: the resource: EmbeddedResource field was missing #[serde(flatten)],
so the outer enum tag wrapper plus the inner struct's own resource field both
serialize, yielding the double nesting.

This breaks interop with any MCP client that follows the spec literally — the
inner {uri, text, mimeType} is unreachable without descending two levels.

How Has This Been Tested?

  • Added a regression test (test_prompt_message_resource_serialization_is_flat)
    that constructs a PromptMessage::new_resource(...), serializes it, and
    asserts:
    • content.type == "resource"
    • content.resource.uri, content.resource.mimeType, content.resource.text
      are reachable directly (flat shape).
    • content.resource.resource is absent (regression guard against the
      doubly-nested shape).
  • Regenerated the affected schema snapshot via UPDATE_SCHEMA=1.
  • cargo test --all-features passes locally for the rmcp crate (lib + all
    integration tests).
  • cargo +nightly fmt --all -- --check and
    cargo clippy --all-targets --all-features -- -D warnings pass.

Breaking Changes

Wire format: yes, but in the spec-compliance direction. Any client that
relied on the previous (non-spec) doubly-nested shape would break, but such
clients are already non-conformant against the MCP spec and against the other
content variants in this same enum.

Rust API: none. The variant's field name (resource) and type
(EmbeddedResource) are unchanged; only the serde representation is corrected.
cargo semver-checks should be unaffected.

Types of Changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Additional Context

Single-line behavioral fix in crates/rmcp/src/model/prompt.rs: add
#[serde(flatten)] to the Resource variant's inner field, matching the
pattern used by Image and ResourceLink in the same enum.

The Resource variant of PromptMessageContent was missing #[serde(flatten)],
causing the embedded resource content block to serialize as a double-nested
shape `{ "type": "resource", "resource": { "resource": {...} } }` instead of
the spec-compliant flat shape `{ "type": "resource", "resource": {uri, mimeType, text} }`.

This caused Zod-based MCP clients (e.g. Claude Code) to reject prompts/get
responses containing embedded resource messages with InvalidUnion errors.

The Image and ResourceLink variants already use #[serde(flatten)] correctly;
only Resource was missing it.

Fix: add #[serde(flatten)] so EmbeddedResource (=Annotated<RawEmbeddedResource>)
fields _meta / annotations / resource are flattened to the content-block level,
matching the MCP spec for prompts embedded resources.

Regression test: test_prompt_message_resource_serialization_is_flat verifies
content.resource.uri is reachable and content.resource.resource is absent.

Schema snapshots regenerated via UPDATE_SCHEMA=1.
@ynishi ynishi requested a review from a team as a code owner May 11, 2026 10:12
@github-actions github-actions Bot added T-test Testing related changes T-config Configuration file changes T-core Core library changes T-model Model/data structure changes labels May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-config Configuration file changes T-core Core library changes T-model Model/data structure changes T-test Testing related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: PromptMessageContent::Resource serializes with doubly-nested resource field (violates MCP spec)

1 participant