Skip to main content

agent_client_protocol/schema/
mod.rs

1//! ACP protocol schema types and message implementations.
2//!
3//! This module contains all the types from the Agent-Client Protocol schema,
4//! including requests, responses, notifications, and supporting types.
5//! All types are re-exported flatly from this module.
6
7// ---------------------------------------------------------------------------
8// Macros for implementing JsonRpc traits on schema types
9// ---------------------------------------------------------------------------
10
11/// Implement `JsonRpcMessage`, `JsonRpcRequest`, and `JsonRpcResponse` for a
12/// request/response pair from the schema crate.
13///
14/// ```ignore
15/// impl_jsonrpc_request!(PromptRequest, PromptResponse, "session/prompt");
16/// ```
17macro_rules! impl_jsonrpc_request {
18    ($req:ty, $resp:ty, $method:literal) => {
19        impl $crate::JsonRpcMessage for $req {
20            fn matches_method(method: &str) -> bool {
21                method == $method
22            }
23
24            fn method(&self) -> &str {
25                $method
26            }
27
28            fn to_untyped_message(&self) -> Result<$crate::UntypedMessage, $crate::Error> {
29                $crate::UntypedMessage::new($method, self)
30            }
31
32            fn parse_message(
33                method: &str,
34                params: &impl serde::Serialize,
35            ) -> Result<Self, $crate::Error> {
36                if method != $method {
37                    return Err($crate::Error::method_not_found());
38                }
39                $crate::util::json_cast_params(params)
40            }
41        }
42
43        impl $crate::JsonRpcRequest for $req {
44            type Response = $resp;
45        }
46
47        impl $crate::JsonRpcResponse for $resp {
48            fn into_json(self, _method: &str) -> Result<serde_json::Value, $crate::Error> {
49                serde_json::to_value(self).map_err($crate::Error::into_internal_error)
50            }
51
52            fn from_value(_method: &str, value: serde_json::Value) -> Result<Self, $crate::Error> {
53                $crate::util::json_cast(&value)
54            }
55        }
56    };
57}
58
59/// Implement `JsonRpcMessage` and `JsonRpcNotification` for a notification type
60/// from the schema crate.
61///
62/// ```ignore
63/// impl_jsonrpc_notification!(CancelNotification, "session/cancel");
64/// ```
65macro_rules! impl_jsonrpc_notification {
66    ($notif:ty, $method:literal) => {
67        impl $crate::JsonRpcMessage for $notif {
68            fn matches_method(method: &str) -> bool {
69                method == $method
70            }
71
72            fn method(&self) -> &str {
73                $method
74            }
75
76            fn to_untyped_message(&self) -> Result<$crate::UntypedMessage, $crate::Error> {
77                $crate::UntypedMessage::new($method, self)
78            }
79
80            fn parse_message(
81                method: &str,
82                params: &impl serde::Serialize,
83            ) -> Result<Self, $crate::Error> {
84                if method != $method {
85                    return Err($crate::Error::method_not_found());
86                }
87                $crate::util::json_cast_params(params)
88            }
89        }
90
91        impl $crate::JsonRpcNotification for $notif {}
92    };
93}
94
95/// Implement `JsonRpcMessage` and `JsonRpcRequest` for an enum that dispatches
96/// across multiple request types, with an extension method fallback.
97///
98/// Variants can optionally have `#[cfg(...)]` attributes for conditional compilation.
99///
100/// ```ignore
101/// impl_jsonrpc_request_enum!(ClientRequest {
102///     InitializeRequest => "initialize",
103///     PromptRequest => "session/prompt",
104///     [ext] ExtMethodRequest,
105/// });
106/// ```
107macro_rules! impl_jsonrpc_request_enum {
108    ($enum:ty {
109        $( $(#[$meta:meta])* $variant:ident => $method:literal, )*
110        [ext] $ext_variant:ident,
111    }) => {
112        impl $crate::JsonRpcMessage for $enum {
113            fn matches_method(_method: &str) -> bool {
114                true
115            }
116
117            fn method(&self) -> &str {
118                match self {
119                    $( $(#[$meta])* Self::$variant(_) => $method, )*
120                    Self::$ext_variant(ext) => &ext.method,
121                    _ => "_unknown",
122                }
123            }
124
125            fn to_untyped_message(&self) -> Result<$crate::UntypedMessage, $crate::Error> {
126                $crate::UntypedMessage::new(self.method(), self)
127            }
128
129            fn parse_message(
130                method: &str,
131                params: &impl serde::Serialize,
132            ) -> Result<Self, $crate::Error> {
133                match method {
134                    $( $(#[$meta])* $method => $crate::util::json_cast_params(params).map(Self::$variant), )*
135                    _ => {
136                        if let Some(custom_method) = method.strip_prefix('_') {
137                            $crate::util::json_cast_params(params).map(
138                                |ext_req: $crate::schema::v1::ExtRequest| {
139                                    Self::$ext_variant($crate::schema::v1::ExtRequest::new(
140                                        custom_method.to_string(),
141                                        ext_req.params,
142                                    ))
143                                },
144                            )
145                        } else {
146                            Err($crate::Error::method_not_found())
147                        }
148                    }
149                }
150            }
151        }
152
153        impl $crate::JsonRpcRequest for $enum {
154            type Response = serde_json::Value;
155        }
156    };
157}
158
159/// Implement `JsonRpcMessage` and `JsonRpcNotification` for an enum that
160/// dispatches across multiple notification types, with an extension fallback.
161///
162/// Variants can optionally have `#[cfg(...)]` attributes for conditional compilation.
163///
164/// ```ignore
165/// impl_jsonrpc_notification_enum!(AgentNotification {
166///     SessionNotification => "session/update",
167///     [ext] ExtNotification,
168/// });
169/// ```
170macro_rules! impl_jsonrpc_notification_enum {
171    ($enum:ty {
172        $( $(#[$meta:meta])* $variant:ident => $method:literal, )*
173        [ext] $ext_variant:ident,
174    }) => {
175        impl $crate::JsonRpcMessage for $enum {
176            fn matches_method(_method: &str) -> bool {
177                true
178            }
179
180            fn method(&self) -> &str {
181                match self {
182                    $( $(#[$meta])* Self::$variant(_) => $method, )*
183                    Self::$ext_variant(ext) => &ext.method,
184                    _ => "_unknown",
185                }
186            }
187
188            fn to_untyped_message(&self) -> Result<$crate::UntypedMessage, $crate::Error> {
189                $crate::UntypedMessage::new(self.method(), self)
190            }
191
192            fn parse_message(
193                method: &str,
194                params: &impl serde::Serialize,
195            ) -> Result<Self, $crate::Error> {
196                match method {
197                    $( $(#[$meta])* $method => $crate::util::json_cast_params(params).map(Self::$variant), )*
198                    _ => {
199                        if let Some(custom_method) = method.strip_prefix('_') {
200                            $crate::util::json_cast_params(params).map(
201                                |ext_notif: $crate::schema::v1::ExtNotification| {
202                                    Self::$ext_variant($crate::schema::v1::ExtNotification::new(
203                                        custom_method.to_string(),
204                                        ext_notif.params,
205                                    ))
206                                },
207                            )
208                        } else {
209                            Err($crate::Error::method_not_found())
210                        }
211                    }
212                }
213            }
214        }
215
216        impl $crate::JsonRpcNotification for $enum {}
217    };
218}
219
220/// Implement `JsonRpcMessage` and `JsonRpcNotification` for a protocol-level
221/// notification enum (`$/`-prefixed methods), shared between the v1 and v2
222/// schema namespaces.
223///
224/// The incoming side (`matches_method`, `parse_message`) only recognizes the
225/// methods listed in the macro invocation: when the schema crate adds a
226/// protocol-level notification, list it here to parse it. The outgoing side
227/// (`method`, `to_untyped_message`) instead delegates to the schema enum's
228/// inherent `method()` and untagged serialization, which cover every variant,
229/// so unlisted variants still serialize with the correct method name.
230///
231/// ```ignore
232/// impl_jsonrpc_protocol_level_notification_enum!(ProtocolLevelNotification {
233///     CancelRequestNotification => "$/cancel_request",
234/// });
235/// ```
236#[cfg(feature = "unstable_cancel_request")]
237macro_rules! impl_jsonrpc_protocol_level_notification_enum {
238    ($enum:ty {
239        $( $variant:ident => $method:literal, )*
240    }) => {
241        impl $crate::JsonRpcMessage for $enum {
242            fn matches_method(method: &str) -> bool {
243                matches!(method, $( $method )|*)
244            }
245
246            fn method(&self) -> &str {
247                // Resolves to the schema enum's *inherent* `method()` (path
248                // syntax prefers inherent items over trait items), which
249                // matches its variants exhaustively: the enum is only
250                // non-exhaustive downstream.
251                <$enum>::method(self)
252            }
253
254            fn to_untyped_message(&self) -> Result<$crate::UntypedMessage, $crate::Error> {
255                // The schema enum is `#[serde(untagged)]`, so serializing the
256                // enum serializes the inner notification.
257                $crate::UntypedMessage::new(<$enum>::method(self), self)
258            }
259
260            fn parse_message(
261                method: &str,
262                params: &impl serde::Serialize,
263            ) -> Result<Self, $crate::Error> {
264                match method {
265                    $( $method => $crate::util::json_cast_params(params).map(Self::$variant), )*
266                    _ => Err($crate::Error::method_not_found()),
267                }
268            }
269        }
270
271        impl $crate::JsonRpcNotification for $enum {}
272    };
273}
274
275/// Implement `JsonRpcResponse` for an enum that dispatches across multiple
276/// response types, with an extension method fallback.
277macro_rules! impl_jsonrpc_response_enum {
278    ($enum:ty {
279        $( $(#[$meta:meta])* $variant:ident => $method:literal, )*
280        [ext] $ext_variant:ident,
281    }) => {
282        impl $crate::JsonRpcResponse for $enum {
283            fn into_json(
284                self,
285                _method: &str,
286            ) -> Result<serde_json::Value, $crate::Error> {
287                serde_json::to_value(self).map_err($crate::Error::into_internal_error)
288            }
289
290            fn from_value(
291                method: &str,
292                value: serde_json::Value,
293            ) -> Result<Self, $crate::Error> {
294                match method {
295                    $( $(#[$meta])* $method => $crate::util::json_cast(value).map(Self::$variant), )*
296                    _ => {
297                        if method.starts_with('_') {
298                            $crate::util::json_cast(value).map(Self::$ext_variant)
299                        } else {
300                            Err($crate::Error::method_not_found())
301                        }
302                    }
303                }
304            }
305        }
306    };
307}
308
309// Internal organization
310mod agent_to_client;
311mod client_to_agent;
312mod enum_impls;
313mod protocol_level;
314mod proxy_protocol;
315#[cfg(feature = "unstable_protocol_v2")]
316mod v2_impls;
317
318/// Agent Client Protocol v1 schema types.
319pub mod v1 {
320    pub use agent_client_protocol_schema::v1::*;
321}
322
323/// Agent Client Protocol v2 draft schema types.
324#[cfg(feature = "unstable_protocol_v2")]
325pub mod v2 {
326    pub use agent_client_protocol_schema::v2::*;
327}
328
329pub use agent_client_protocol_schema::{
330    IntoMaybeUndefined, IntoOption, MaybeUndefined, ProtocolVersion,
331};
332
333// Re-export SDK-local proxy/MCP bridge protocol types flatly.
334pub use proxy_protocol::*;