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::ExtRequest| {
139                                    Self::$ext_variant($crate::schema::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::ExtNotification| {
202                                    Self::$ext_variant($crate::schema::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 `JsonRpcResponse` for an enum that dispatches across multiple
221/// response types, with an extension method fallback.
222macro_rules! impl_jsonrpc_response_enum {
223    ($enum:ty {
224        $( $(#[$meta:meta])* $variant:ident => $method:literal, )*
225        [ext] $ext_variant:ident,
226    }) => {
227        impl $crate::JsonRpcResponse for $enum {
228            fn into_json(
229                self,
230                _method: &str,
231            ) -> Result<serde_json::Value, $crate::Error> {
232                serde_json::to_value(self).map_err($crate::Error::into_internal_error)
233            }
234
235            fn from_value(
236                method: &str,
237                value: serde_json::Value,
238            ) -> Result<Self, $crate::Error> {
239                match method {
240                    $( $(#[$meta])* $method => $crate::util::json_cast(value).map(Self::$variant), )*
241                    _ => {
242                        if method.starts_with('_') {
243                            $crate::util::json_cast(value).map(Self::$ext_variant)
244                        } else {
245                            Err($crate::Error::method_not_found())
246                        }
247                    }
248                }
249            }
250        }
251    };
252}
253
254// Internal organization
255mod agent_to_client;
256mod client_to_agent;
257mod enum_impls;
258mod proxy_protocol;
259#[cfg(feature = "unstable_protocol_v2")]
260mod v2_impls;
261
262// Re-export everything from agent_client_protocol_schema
263pub use agent_client_protocol_schema::*;
264
265// Re-export proxy/MCP protocol types
266pub use proxy_protocol::*;