diff --git a/crates/rmcp/src/model.rs b/crates/rmcp/src/model.rs index 9dd04564e..a2e8e684e 100644 --- a/crates/rmcp/src/model.rs +++ b/crates/rmcp/src/model.rs @@ -3934,6 +3934,7 @@ enum ElicitRequestParamsWire { meta: Option, message: String, url: String, + #[serde(default)] elicitation_id: String, }, #[serde(untagged, rename_all = "camelCase")] @@ -4043,6 +4044,9 @@ pub enum ElicitRequestParams { /// The client should direct the user to this URL to complete the elicitation. url: String, /// The unique identifier for this elicitation request. + /// Protocol `2026-07-28` removed this field: it is empty when absent, + /// and an empty id is not serialized. + #[serde(skip_serializing_if = "String::is_empty")] elicitation_id: String, }, } @@ -6138,6 +6142,24 @@ mod tests { assert_eq!(json_url, expected_url_json); } + #[test] + fn test_url_elicitation_without_elicitation_id() { + // 2026-07-28 removed `elicitationId` from URL mode requests. + let json_data = json!({ + "mode": "url", + "url": "https://mcp.example.com/ui/set_api_key", + "message": "Please provide your API key to continue." + }); + let params: ElicitRequestParams = + serde_json::from_value(json_data.clone()).expect("Deserialization failed"); + assert!(matches!( + ¶ms, + ElicitRequestParams::UrlElicitationParams { elicitation_id, .. } + if elicitation_id.is_empty() + )); + assert_eq!(serde_json::to_value(¶ms).unwrap(), json_data); + } + #[test] fn notification_without_params_should_deserialize_as_bare_jsonrpc_message() { let payload = b"{\"method\":\"notifications/initialized\",\"jsonrpc\":\"2.0\"}"; diff --git a/crates/rmcp/src/model/mrtr.rs b/crates/rmcp/src/model/mrtr.rs index 8dac9a391..a26f09ca9 100644 --- a/crates/rmcp/src/model/mrtr.rs +++ b/crates/rmcp/src/model/mrtr.rs @@ -457,6 +457,35 @@ mod tests { ); } + #[test] + fn deserializes_url_elicitation_without_elicitation_id() { + let json = serde_json::json!({ + "resultType": "input_required", + "inputRequests": { + "api_key": { + "method": "elicitation/create", + "params": { + "mode": "url", + "url": "https://mcp.example.com/ui/set_api_key", + "message": "Please provide your API key to continue." + } + } + }, + "requestState": "abc123" + }); + + let result: ServerResult = serde_json::from_value(json).unwrap(); + + let ServerResult::InputRequiredResult(result) = result else { + panic!("expected InputRequiredResult, got {result:?}"); + }; + let requests = result.input_requests.expect("should have input_requests"); + assert!(matches!( + requests.get("api_key"), + Some(InputRequest::Elicitation(_)) + )); + } + #[test] fn rejects_missing_input_requests_and_request_state() { let json = serde_json::json!({ diff --git a/crates/rmcp/tests/test_message_schema/server_json_rpc_message_schema.json b/crates/rmcp/tests/test_message_schema/server_json_rpc_message_schema.json index cebb3ed37..46389b629 100644 --- a/crates/rmcp/tests/test_message_schema/server_json_rpc_message_schema.json +++ b/crates/rmcp/tests/test_message_schema/server_json_rpc_message_schema.json @@ -828,7 +828,8 @@ ] }, "elicitationId": { - "type": "string" + "type": "string", + "default": "" }, "message": { "type": "string" @@ -844,8 +845,7 @@ "required": [ "mode", "message", - "url", - "elicitationId" + "url" ] }, {