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