Skip to content

fix(elicitation): preserve enumNames through ElicitationSchema serde round-trip#905

Open
abdouloued wants to merge 2 commits into
modelcontextprotocol:mainfrom
abdouloued:fix/elicitation-enum-names-serde-roundtrip
Open

fix(elicitation): preserve enumNames through ElicitationSchema serde round-trip#905
abdouloued wants to merge 2 commits into
modelcontextprotocol:mainfrom
abdouloued:fix/elicitation-enum-names-serde-roundtrip

Conversation

@abdouloued

Copy link
Copy Markdown

Fixes #903.

Problem

EnumSchema is #[serde(untagged)] with variants ordered Single → Multi → Legacy. SingleSelectEnumSchema is itself untagged with Untitled before Titled. UntitledSingleSelectEnumSchema did not have deny_unknown_fields, so a legacy payload like:

{"type":"string","enum":["opt1","opt2","opt3"],"enumNames":["Option One","Option Two","Option Three"]}

was silently matched by UntitledSingleSelectEnumSchema (which has no enumNames field). The array was dropped, and LegacyEnumSchema was never tried. On re-serialization, enumNames was gone.

A secondary issue: LegacyEnumSchema::enum_names lacked skip_serializing_if = "Option::is_none", so a Legacy with enum_names: None would serialize "enumNames": null.

Fix

  • Add deny_unknown_fields to UntitledSingleSelectEnumSchema. When enumNames (or any unrecognised field) is present, serde rejects this variant and falls through to LegacyEnumSchema.
  • Add skip_serializing_if = "Option::is_none" to LegacyEnumSchema::enum_names.

Two regression tests added:

  1. test_legacy_enum_schema_roundtrip_preserves_enum_names — mirrors the exact repro from the issue; verifies that a full ElicitationSchema round-trip keeps enumNames intact.
  2. test_legacy_enum_schema_no_enum_names_omits_field — verifies LegacyEnumSchema with enum_names: None does not emit "enumNames": null.

All existing elicitation tests continue to pass.

…round-trip

UntitledSingleSelectEnumSchema lacked deny_unknown_fields, so a legacy
enum payload containing enumNames was silently matched by that variant
(ignoring the field) rather than falling through to LegacyEnumSchema.
The enumNames array was lost on re-serialization.

Add deny_unknown_fields to UntitledSingleSelectEnumSchema so that any
unknown field (including enumNames) causes serde to try the next
untagged variant, reaching LegacyEnumSchema correctly.

Also add skip_serializing_if = "Option::is_none" to
LegacyEnumSchema::enum_names so that an untitled legacy enum without
enumNames does not serialize "enumNames": null.

Fixes modelcontextprotocol#903
@abdouloued abdouloued requested a review from a team as a code owner June 15, 2026 08:28
@github-actions github-actions Bot added T-core Core library changes T-model Model/data structure changes labels Jun 15, 2026
michaelneale
michaelneale previously approved these changes Jun 16, 2026
@michaelneale

Copy link
Copy Markdown
Contributor

@abdouloued do you want to fix up the failures and we can take a look again?

…_fields

deny_unknown_fields on UntitledSingleSelectEnumSchema makes schemars
emit additionalProperties: false for that definition. Regenerate the
golden schema fixtures to match (UPDATE_SCHEMA=1 cargo test -p rmcp
--test test_message_schema --all-features).
@github-actions github-actions Bot added T-test Testing related changes T-config Configuration file changes labels Jun 19, 2026
@abdouloued

Copy link
Copy Markdown
Author

@abdouloued do you want to fix up the failures and we can take a look again?

Pushed a fix. The failure appears to be a stale golden-file schema snapshot (tests/test_message_schema/server_json_rpc_message_schema.json). The deny_unknown_fields change makes schemars add "additionalProperties": false to UntitledSingleSelectEnumSchema, which the snapshot test caught.

Regenerated it with UPDATE_SCHEMA=1 cargo test -p rmcp --test test_message_schema --all-features — no other changes needed, verified locally that it passes.

Looks like the workflow run needs approval to kick off again on this push.

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.

ElicitationSchema serde round-trip silently drops enumNames (untagged EnumSchema matches the legacy form as Untitled)

2 participants