Skip to content

Elicitation requestedSchema silently drops $schema and unknown property keywords #1168

Description

@chrisbennight

Describe the bug

rmcp deserializes an elicitation requestedSchema into typed structs and silently discards fields those structs do not model.

I found two related cases:

  1. The top-level $schema field, which is present in the MCP protocol schemas starting with 2025-11-25:
requestedSchema: {
  $schema?: string;
  type: "object";
  properties: { [key: string]: PrimitiveSchemaDefinition };
  required?: string[];
};

See [ElicitRequestFormParams](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.ts).

This is particularly significant when a schema explicitly declares a non-default dialect. [SEP-1613](modelcontextprotocol/modelcontextprotocol#1613) allows embedded schemas to declare an alternative dialect through $schema, and [rust-sdk issue #525](#525) explicitly includes elicitation requestedSchema in that requirement.

[PR #549](#549) changed generated tool schemas to JSON Schema 2020-12, but it did not add $schema support to ElicitationSchema. It looks like that part of issue #525 was missed.

  1. Unknown keywords inside individual property schemas.

PrimitiveSchemaDefinition is a fixed, untagged enum of typed schema variants. A property containing an additional keyword is accepted, but that keyword is silently discarded and cannot be recovered through the decoded model.

These cases are slightly different: $schema is a missing protocol field that can be modeled directly, while preserving unrecognized property keywords would require a catch-all or another lossless representation.

To reproduce

Using rmcp 3.1.2, deserialize this request into rmcp::model::InputRequest and serialize it again:

let decoded: rmcp::model::InputRequest =
    serde_json::from_value(wire.clone()).unwrap();

println!("IN : {}", serde_json::to_string(&wire).unwrap());
println!("OUT: {}", serde_json::to_string(&decoded).unwrap());

Input:

{
  "method": "elicitation/create",
  "params": {
    "message": "attach the report",
    "mode": "form",
    "requestedSchema": {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "properties": {
        "report": {
          "description": "the quarterly report",
          "format": "uri",
          "type": "string",
          "x-mcp-file": {
            "accept": ["application/pdf"],
            "transferModes": ["upload"]
          }
        }
      },
      "required": ["report"],
      "type": "object"
    }
  }
}

Output:

{
  "method": "elicitation/create",
  "params": {
    "message": "attach the report",
    "mode": "form",
    "requestedSchema": {
      "properties": {
        "report": {
          "description": "the quarterly report",
          "format": "uri",
          "type": "string"
        }
      },
      "required": ["report"],
      "type": "object"
    }
  }
}

description, format, type, and required survive. $schema and x-mcp-file do not.

I originally reproduced this on 3.1.0 and have confirmed the same behavior on 3.1.2. The current main branch still lacks fields for $schema and unrecognized property keywords.

Expected behavior

At minimum, the spec-defined $schema field should remain available after deserialization.

Separately, preserving unmodeled property keywords in an extra map or another lossless raw representation would make inbound decoding forward-compatible with JSON Schema annotations and newer MCP extension keywords.

For example, draft [SEP-2631](modelcontextprotocol/modelcontextprotocol#2631) carries forward the proposed x-mcp-file inline schema annotation. An older rmcp version does not need to understand the keyword’s semantics, but silently discarding it prevents consumers that do understand the extension from accessing it without intercepting the raw JSON before rmcp decodes the request.

The current MCP specification does not prescribe a particular SDK representation for unknown keywords, so I see this as a forward-compatibility improvement rather than the same normative omission as $schema.

Affected paths

The same ElicitationSchema and InputRequest models are used by:

  • Direct elicitation/create requests, including legacy form requests and the current form mode
  • Task input steering through TaskPayload::InputRequired
  • MRTR through InputRequiredResult.inputRequests

I confirmed the loss through all three paths on rmcp 3.1.2.

Inbound clients and gateways lose the fields during decoding. Outbound servers also cannot express $schema or extension keywords through the typed ElicitationSchema API.

Tool schemas do not have this problem. Tool::input_schema and output_schema retain their underlying JsonObject, so JSON Schema keywords survive a round trip. That behavior is discussed in [#874](#874) and exercised by the keyword-preservation work in [#1003](#1003) and [#1018](#1018).

Practical impact

I’m building a gateway that brokers MCP file transfer while keeping file bytes out of model context. When an upstream server pauses for elicitation, the gateway relays that request downstream. On retry, it needs to know which response fields may contain files.

[SEP-2356](modelcontextprotocol/modelcontextprotocol#2356) originally proposed the x-mcp-file declaration. It was closed in favor of draft [SEP-2631](modelcontextprotocol/modelcontextprotocol#2631), which carries that declaration forward and builds its transfer flow around it. rmcp drops the declaration before the gateway can inspect it.

I cannot safely rely on a schema copy echoed by the client during retry. The [MRTR requirements](https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr) require servers to treat requestState as attacker-controlled and protect its integrity when it affects authorization, resource access, or business logic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is not working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions