diff --git a/crates/rmcp/src/model.rs b/crates/rmcp/src/model.rs index 538061516..2a79dc746 100644 --- a/crates/rmcp/src/model.rs +++ b/crates/rmcp/src/model.rs @@ -3834,4 +3834,15 @@ mod tests { }); assert_eq!(json_url, expected_url_json); } + + #[test] + fn notification_without_params_should_deserialize_as_bare_jsonrpc_message() { + let payload = b"{\"method\":\"notifications/initialized\",\"jsonrpc\":\"2.0\"}"; + let result: Result = serde_json::from_slice(payload); + assert!( + matches!(result, Ok(JsonRpcMessage::Notification(_))), + "Expected Ok(Notification), got: {:?}", + result + ); + } } diff --git a/crates/rmcp/src/model/serde_impl.rs b/crates/rmcp/src/model/serde_impl.rs index c262d6acd..f8996f318 100644 --- a/crates/rmcp/src/model/serde_impl.rs +++ b/crates/rmcp/src/model/serde_impl.rs @@ -246,8 +246,19 @@ where where D: serde::Deserializer<'de>, { - let body = Proxy::deserialize(deserializer)?; - let _meta = body.params._meta.map(|m| m.into_owned()); + let body = ProxyOptionalParam::<'_, _, R>::deserialize(deserializer)?; + let (_meta, params) = match body.params { + Some(with_meta) => { + let meta = with_meta._meta.map(|m| m.into_owned()); + (meta, with_meta._rest) + } + None => { + // JSON-RPC 2.0: params is optional. Treat absent params as {}. + let empty = serde_json::Value::Object(serde_json::Map::new()); + let r = R::deserialize(empty).map_err(serde::de::Error::custom)?; + (None, r) + } + }; let mut extensions = Extensions::new(); if let Some(meta) = _meta { extensions.insert(meta); @@ -255,7 +266,7 @@ where Ok(Notification { extensions, method: body.method, - params: body.params._rest, + params, }) } }