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
17impl JrMessage for McpConnectRequest {
18 fn into_untyped_message(self) -> Result<UntypedMessage, sacp::Error> {
19 UntypedMessage::new(METHOD_MCP_CONNECT_REQUEST, self)
20 }
21
22 fn method(&self) -> &str {
23 METHOD_MCP_CONNECT_REQUEST
24 }
25
26 fn parse_request(method: &str, params: &impl Serialize) -> Option<Result<Self, sacp::Error>> {
27 if method != METHOD_MCP_CONNECT_REQUEST {
28 return None;
29 }
30 Some(sacp::util::json_cast(params))
31 }
32
33 fn parse_notification(
34 _method: &str,
35 _params: &impl Serialize,
36 ) -> Option<Result<Self, sacp::Error>> {
37 None
39 }
40}
41
42impl JrRequest for McpConnectRequest {
43 type Response = McpConnectResponse;
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct McpConnectResponse {
49 pub connection_id: String,
51}
52
53impl JrResponsePayload for McpConnectResponse {
54 fn into_json(self, _method: &str) -> Result<serde_json::Value, sacp::Error> {
55 serde_json::to_value(self).map_err(sacp::Error::into_internal_error)
56 }
57
58 fn from_value(_method: &str, value: serde_json::Value) -> Result<Self, sacp::Error> {
59 serde_json::from_value(value).map_err(|_| sacp::Error::invalid_params())
60 }
61}
62
63pub const METHOD_MCP_DISCONNECT_NOTIFICATION: &str = "_mcp/disconnect";
65
66#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
68pub struct McpDisconnectNotification {
69 pub connection_id: String,
71}
72
73impl JrMessage for McpDisconnectNotification {
74 fn into_untyped_message(self) -> Result<UntypedMessage, sacp::Error> {
75 UntypedMessage::new(METHOD_MCP_DISCONNECT_NOTIFICATION, self)
76 }
77
78 fn method(&self) -> &str {
79 METHOD_MCP_DISCONNECT_NOTIFICATION
80 }
81
82 fn parse_request(_method: &str, _params: &impl Serialize) -> Option<Result<Self, sacp::Error>> {
83 None
85 }
86
87 fn parse_notification(
88 method: &str,
89 params: &impl Serialize,
90 ) -> Option<Result<Self, sacp::Error>> {
91 if method != METHOD_MCP_DISCONNECT_NOTIFICATION {
92 return None;
93 }
94 Some(sacp::util::json_cast(params))
95 }
96}
97
98impl JrNotification for McpDisconnectNotification {}
99
100pub const METHOD_MCP_REQUEST: &str = "_mcp/request";
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
108#[serde(rename_all = "camelCase")]
109pub struct McpOverAcpRequest<R> {
110 pub connection_id: String,
112
113 #[serde(flatten)]
115 pub request: R,
116}
117
118impl<R: JrRequest> JrMessage for McpOverAcpRequest<R> {
119 fn into_untyped_message(self) -> Result<UntypedMessage, sacp::Error> {
120 let message = self.request.into_untyped_message()?;
121 UntypedMessage::new(
122 METHOD_MCP_REQUEST,
123 McpOverAcpRequest {
124 connection_id: self.connection_id,
125 request: message,
126 },
127 )
128 }
129
130 fn method(&self) -> &str {
131 METHOD_MCP_REQUEST
132 }
133
134 fn parse_request(method: &str, params: &impl Serialize) -> Option<Result<Self, sacp::Error>> {
135 if method == METHOD_MCP_REQUEST {
136 match json_cast::<_, McpOverAcpRequest<UntypedMessage>>(params) {
137 Ok(outer) => match R::parse_request(&outer.request.method, &outer.request.params) {
138 Some(Ok(request)) => Some(Ok(McpOverAcpRequest {
139 connection_id: outer.connection_id,
140 request,
141 })),
142 Some(Err(err)) => Some(Err(err)),
143 None => None,
144 },
145 Err(err) => Some(Err(err)),
146 }
147 } else {
148 None
149 }
150 }
151
152 fn parse_notification(
153 _method: &str,
154 _params: &impl Serialize,
155 ) -> Option<Result<Self, sacp::Error>> {
156 None }
158}
159
160impl<R: JrRequest> JrRequest for McpOverAcpRequest<R> {
161 type Response = R::Response;
162}
163
164pub const METHOD_MCP_NOTIFICATION: &str = "_mcp/notification";
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
173#[serde(rename_all = "camelCase")]
174pub struct McpOverAcpNotification<R> {
175 pub connection_id: String,
177
178 #[serde(flatten)]
180 pub notification: R,
181}
182
183impl<R: JrMessage> JrMessage for McpOverAcpNotification<R> {
184 fn into_untyped_message(self) -> Result<UntypedMessage, sacp::Error> {
185 let params = self.notification.into_untyped_message()?;
186 UntypedMessage::new(
187 METHOD_MCP_NOTIFICATION,
188 McpOverAcpNotification {
189 connection_id: self.connection_id,
190 notification: params,
191 },
192 )
193 }
194
195 fn method(&self) -> &str {
196 METHOD_MCP_NOTIFICATION
197 }
198
199 fn parse_request(_method: &str, _params: &impl Serialize) -> Option<Result<Self, sacp::Error>> {
200 None }
202
203 fn parse_notification(
204 method: &str,
205 params: &impl Serialize,
206 ) -> Option<Result<Self, sacp::Error>> {
207 if method == METHOD_MCP_NOTIFICATION {
208 match json_cast::<_, McpOverAcpNotification<UntypedMessage>>(params) {
209 Ok(outer) => match R::parse_notification(
210 &outer.notification.method,
211 &outer.notification.params,
212 ) {
213 Some(Ok(notification)) => Some(Ok(McpOverAcpNotification {
214 connection_id: outer.connection_id,
215 notification,
216 })),
217 Some(Err(err)) => Some(Err(err)),
218 None => None,
219 },
220 Err(err) => Some(Err(err)),
221 }
222 } else {
223 None
224 }
225 }
226}
227
228impl<R: JrMessage> JrNotification for McpOverAcpNotification<R> {}