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