1use sacp;
2use sacp::{
3 JrMessage, JrNotification, JrRequest, JrResponsePayload, UntypedMessage, util::json_cast,
4};
5use serde::{Deserialize, Serialize};
6
7pub const METHOD_MCP_CONNECT_REQUEST: &str = "_mcp/connect";
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct McpConnectRequest {
13 pub acp_url: String,
15
16 #[serde(skip_serializing_if = "Option::is_none")]
18 pub meta: Option<serde_json::Value>,
19}
20
21impl JrMessage for McpConnectRequest {
22 fn to_untyped_message(&self) -> Result<UntypedMessage, sacp::Error> {
23 UntypedMessage::new(METHOD_MCP_CONNECT_REQUEST, self)
24 }
25
26 fn method(&self) -> &str {
27 METHOD_MCP_CONNECT_REQUEST
28 }
29
30 fn parse_request(method: &str, params: &impl Serialize) -> Option<Result<Self, sacp::Error>> {
31 if method != METHOD_MCP_CONNECT_REQUEST {
32 return None;
33 }
34 Some(sacp::util::json_cast(params))
35 }
36
37 fn parse_notification(
38 _method: &str,
39 _params: &impl Serialize,
40 ) -> Option<Result<Self, sacp::Error>> {
41 None
43 }
44}
45
46impl JrRequest for McpConnectRequest {
47 type Response = McpConnectResponse;
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct McpConnectResponse {
53 pub connection_id: String,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub meta: Option<serde_json::Value>,
59}
60
61impl JrResponsePayload for McpConnectResponse {
62 fn into_json(self, _method: &str) -> Result<serde_json::Value, sacp::Error> {
63 serde_json::to_value(self).map_err(sacp::Error::into_internal_error)
64 }
65
66 fn from_value(_method: &str, value: serde_json::Value) -> Result<Self, sacp::Error> {
67 serde_json::from_value(value).map_err(|_| sacp::Error::invalid_params())
68 }
69}
70
71pub const METHOD_MCP_DISCONNECT_NOTIFICATION: &str = "_mcp/disconnect";
73
74#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
76pub struct McpDisconnectNotification {
77 pub connection_id: String,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub meta: Option<serde_json::Value>,
83}
84
85impl JrMessage for McpDisconnectNotification {
86 fn to_untyped_message(&self) -> Result<UntypedMessage, sacp::Error> {
87 UntypedMessage::new(METHOD_MCP_DISCONNECT_NOTIFICATION, self)
88 }
89
90 fn method(&self) -> &str {
91 METHOD_MCP_DISCONNECT_NOTIFICATION
92 }
93
94 fn parse_request(_method: &str, _params: &impl Serialize) -> Option<Result<Self, sacp::Error>> {
95 None
97 }
98
99 fn parse_notification(
100 method: &str,
101 params: &impl Serialize,
102 ) -> Option<Result<Self, sacp::Error>> {
103 if method != METHOD_MCP_DISCONNECT_NOTIFICATION {
104 return None;
105 }
106 Some(sacp::util::json_cast(params))
107 }
108}
109
110impl JrNotification for McpDisconnectNotification {}
111
112pub const METHOD_MCP_REQUEST: &str = "_mcp/request";
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
120#[serde(rename_all = "camelCase")]
121pub struct McpOverAcpRequest<R> {
122 pub connection_id: String,
124
125 #[serde(flatten)]
127 pub request: R,
128
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub meta: Option<serde_json::Value>,
132}
133
134impl<R: JrRequest> JrMessage for McpOverAcpRequest<R> {
135 fn to_untyped_message(&self) -> Result<UntypedMessage, sacp::Error> {
136 let message = self.request.to_untyped_message()?;
137 UntypedMessage::new(
138 METHOD_MCP_REQUEST,
139 McpOverAcpRequest {
140 connection_id: self.connection_id.clone(),
141 request: message,
142 meta: self.meta.clone(),
143 },
144 )
145 }
146
147 fn method(&self) -> &str {
148 METHOD_MCP_REQUEST
149 }
150
151 fn parse_request(method: &str, params: &impl Serialize) -> Option<Result<Self, sacp::Error>> {
152 if method == METHOD_MCP_REQUEST {
153 match json_cast::<_, McpOverAcpRequest<UntypedMessage>>(params) {
154 Ok(outer) => match R::parse_request(&outer.request.method, &outer.request.params) {
155 Some(Ok(request)) => Some(Ok(McpOverAcpRequest {
156 connection_id: outer.connection_id,
157 request,
158 meta: outer.meta,
159 })),
160 Some(Err(err)) => Some(Err(err)),
161 None => None,
162 },
163 Err(err) => Some(Err(err)),
164 }
165 } else {
166 None
167 }
168 }
169
170 fn parse_notification(
171 _method: &str,
172 _params: &impl Serialize,
173 ) -> Option<Result<Self, sacp::Error>> {
174 None }
176}
177
178impl<R: JrRequest> JrRequest for McpOverAcpRequest<R> {
179 type Response = R::Response;
180}
181
182pub const METHOD_MCP_NOTIFICATION: &str = "_mcp/notification";
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
191#[serde(rename_all = "camelCase")]
192pub struct McpOverAcpNotification<R> {
193 pub connection_id: String,
195
196 #[serde(flatten)]
198 pub notification: R,
199
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub meta: Option<serde_json::Value>,
203}
204
205impl<R: JrMessage> JrMessage for McpOverAcpNotification<R> {
206 fn to_untyped_message(&self) -> Result<UntypedMessage, sacp::Error> {
207 let params = self.notification.to_untyped_message()?;
208 UntypedMessage::new(
209 METHOD_MCP_NOTIFICATION,
210 McpOverAcpNotification {
211 connection_id: self.connection_id.clone(),
212 notification: params,
213 meta: self.meta.clone(),
214 },
215 )
216 }
217
218 fn method(&self) -> &str {
219 METHOD_MCP_NOTIFICATION
220 }
221
222 fn parse_request(_method: &str, _params: &impl Serialize) -> Option<Result<Self, sacp::Error>> {
223 None }
225
226 fn parse_notification(
227 method: &str,
228 params: &impl Serialize,
229 ) -> Option<Result<Self, sacp::Error>> {
230 if method == METHOD_MCP_NOTIFICATION {
231 match json_cast::<_, McpOverAcpNotification<UntypedMessage>>(params) {
232 Ok(outer) => match R::parse_notification(
233 &outer.notification.method,
234 &outer.notification.params,
235 ) {
236 Some(Ok(notification)) => Some(Ok(McpOverAcpNotification {
237 connection_id: outer.connection_id,
238 notification,
239 meta: outer.meta,
240 })),
241 Some(Err(err)) => Some(Err(err)),
242 None => None,
243 },
244 Err(err) => Some(Err(err)),
245 }
246 } else {
247 None
248 }
249 }
250}
251
252impl<R: JrMessage> JrNotification for McpOverAcpNotification<R> {}