Skip to main content

agentic/mcp/capabilities/
client_capabilities.rs

1use crate::mcp::support::{is_empty_object, serialize_bool_as_empty_object};
2use serde::de::{MapAccess, Visitor};
3use serde::ser::SerializeMap;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use serde_json::Value;
6use serde_with::skip_serializing_none; // <-- Ensure this is imported
7use std::fmt;
8
9/// Capabilities a client may support. Known capabilities are defined here, in this schema,
10/// but this is not a closed set: any client can define its own, additional capabilities.
11///
12/// TS Ref: `ClientCapabilities`
13#[derive(Debug, Clone, Default, PartialEq)]
14pub struct ClientCapabilities {
15	/// Experimental, non-standard capabilities that the client supports.
16	pub experimental: Option<Value>, // Value allows any JSON object
17
18	/// Present if the client supports listing roots.
19	pub roots: Option<ClientRootsCapabilities>,
20
21	/// Present if the client supports sampling from an LLM.
22	/// Represented as an empty JSON object `{}` when present in JSON.
23	pub sampling: bool,
24}
25
26/// Capabilities related to listing roots supported by the client.
27/// Nested within `ClientCapabilities`.
28///
29/// TS Ref: `ClientCapabilities.roots`
30#[skip_serializing_none]
31#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(rename_all = "camelCase")]
33pub struct ClientRootsCapabilities {
34	/// Whether the client supports notifications for changes to the roots list.
35	pub list_changed: Option<bool>,
36}
37
38// -- Manual Serialize for ClientCapabilities
39impl Serialize for ClientCapabilities {
40	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
41	where
42		S: Serializer,
43	{
44		let mut map = serializer.serialize_map(None)?; // Start map, size unknown initially
45
46		if let Some(experimental) = &self.experimental {
47			map.serialize_entry("experimental", experimental)?;
48		}
49		if let Some(roots) = &self.roots {
50			map.serialize_entry("roots", roots)?;
51		}
52		// Use helper for sampling: serialize as {} if true, skip if false
53		serialize_bool_as_empty_object::<S>(&mut map, "sampling", self.sampling)?;
54
55		map.end()
56	}
57}
58
59// -- Manual Deserialize for ClientCapabilities
60impl<'de> Deserialize<'de> for ClientCapabilities {
61	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
62	where
63		D: Deserializer<'de>,
64	{
65		struct ClientCapabilitiesVisitor;
66
67		impl<'de> Visitor<'de> for ClientCapabilitiesVisitor {
68			type Value = ClientCapabilities;
69
70			fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
71				formatter.write_str("a map representing ClientCapabilities")
72			}
73
74			fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
75			where
76				M: MapAccess<'de>,
77			{
78				let mut experimental: Option<Value> = None;
79				let mut roots: Option<ClientRootsCapabilities> = None;
80				let mut sampling: bool = false; // Default to false
81
82				while let Some(key) = map.next_key::<String>()? {
83					match key.as_str() {
84						"experimental" => {
85							experimental = Some(map.next_value()?);
86						}
87						"roots" => {
88							roots = Some(map.next_value()?);
89						}
90						"sampling" => {
91							// Deserialize the value for sampling first
92							let sampling_value: Value = map.next_value()?;
93							// If it's an empty object {}, set sampling to true
94							if is_empty_object(&sampling_value) {
95								sampling = true;
96							}
97							// Otherwise, it remains false (default)
98						}
99						// Ignore unknown fields gracefully
100						_ => {
101							let _ = map.next_value::<Value>()?;
102						}
103					}
104				}
105
106				Ok(ClientCapabilities {
107					experimental,
108					roots,
109					sampling,
110				})
111			}
112		}
113
114		deserializer.deserialize_map(ClientCapabilitiesVisitor)
115	}
116}