Skip to main content

agentic/mcp/messages/
mcp_notification.rs

1use crate::mcp::{Error, Result};
2use rpc_router::RpcNotification;
3use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeError};
4use serde_json::Value;
5
6#[derive(Debug, Clone)]
7pub struct McpNotification<P = Value> {
8	/// The Method of this notification
9	pub method: String,
10
11	/// The Params
12	pub params: Option<P>,
13}
14
15impl<P: Serialize> McpNotification<P> {
16	pub fn stringify(&self) -> Result<String> {
17		serde_json::to_string(&self).map_err(Error::custom_from_err)
18	}
19	pub fn stringify_pretty(&self) -> Result<String> {
20		serde_json::to_string_pretty(&self).map_err(Error::custom_from_err)
21	}
22}
23
24// region:    --- IntoMcpNotification
25
26pub trait IntoMcpNotification: Sized {
27	const METHOD: &'static str;
28
29	fn into_mcp_notification(self) -> McpNotification<Self> {
30		self.into()
31	}
32}
33
34impl<T: IntoMcpNotification> From<T> for McpNotification<T> {
35	fn from(params: T) -> Self {
36		McpNotification {
37			method: T::METHOD.to_string(),
38			params: Some(params),
39		}
40	}
41}
42
43// endregion: --- IntoMcpNotification
44
45// region:    --- Custom De/Serialization
46
47impl<P> Serialize for McpNotification<P>
48where
49	P: Serialize,
50{
51	fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
52	where
53		S: Serializer,
54	{
55		// Convert params to Option<Value> if present
56		let params_value = match &self.params {
57			Some(p) => Some(serde_json::to_value(p).map_err(serde::ser::Error::custom)?),
58			None => None,
59		};
60
61		// Create RpcRequest
62		let rpc_request = RpcNotification {
63			method: self.method.clone(),
64			params: params_value,
65		};
66
67		// Serialize the RpcRequest
68		rpc_request.serialize(serializer)
69	}
70}
71
72impl<'de, P> Deserialize<'de> for McpNotification<P>
73where
74	P: Deserialize<'de>,
75{
76	fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
77	where
78		D: Deserializer<'de>,
79	{
80		// Deserialize into RpcRequest first to leverage its validation
81		let rpc_request = RpcNotification::deserialize(deserializer)?;
82
83		// Convert params from Option<Value> to Option<P>
84		let params = match rpc_request.params {
85			Some(value) => {
86				let p = P::deserialize(value).map_err(DeError::custom)?;
87				Some(p)
88			}
89			None => None,
90		};
91
92		// Construct the Request
93		Ok(McpNotification {
94			method: rpc_request.method,
95			params,
96		})
97	}
98}
99
100// endregion: --- Custom De/Serialization