agent_client_protocol_schema/v1/mcp.rs
1//! MCP-over-ACP transport types.
2
3use std::sync::Arc;
4
5use derive_more::{Display, From};
6use serde::{Deserialize, Serialize};
7use serde_json::value::RawValue;
8use serde_with::{DefaultOnError, serde_as, skip_serializing_none};
9
10use crate::IntoOption;
11
12use super::{McpServerAcpId, Meta};
13
14/// **UNSTABLE**
15///
16/// This capability is not part of the spec yet, and may be removed or changed at any point.
17///
18/// A unique identifier for an active MCP-over-ACP connection.
19#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
21#[serde(transparent)]
22#[from(Arc<str>, String, &'static str)]
23#[non_exhaustive]
24pub struct McpConnectionId(pub Arc<str>);
25
26impl McpConnectionId {
27 /// Wraps a protocol string as a typed [`McpConnectionId`].
28 #[must_use]
29 pub fn new(id: impl Into<Arc<str>>) -> Self {
30 Self(id.into())
31 }
32}
33
34/// **UNSTABLE**
35///
36/// This capability is not part of the spec yet, and may be removed or changed at any point.
37///
38/// Request parameters for `mcp/connect`.
39#[serde_as]
40#[skip_serializing_none]
41#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
43#[serde(rename_all = "camelCase")]
44#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = MCP_CONNECT_METHOD_NAME)))]
45#[non_exhaustive]
46pub struct ConnectMcpRequest {
47 /// The ACP MCP server ID that was provided by the component declaring the MCP server.
48 pub server_id: McpServerAcpId,
49 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
50 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
51 /// these keys.
52 ///
53 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
54 #[serde_as(deserialize_as = "DefaultOnError")]
55 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
56 #[serde(default)]
57 #[serde(rename = "_meta")]
58 pub meta: Option<Meta>,
59}
60
61impl ConnectMcpRequest {
62 /// Builds [`ConnectMcpRequest`] with the required request fields set; optional fields start unset or empty.
63 #[must_use]
64 pub fn new(server_id: impl Into<McpServerAcpId>) -> Self {
65 Self {
66 server_id: server_id.into(),
67 meta: None,
68 }
69 }
70
71 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
72 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
73 /// these keys.
74 ///
75 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
76 #[must_use]
77 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
78 self.meta = meta.into_option();
79 self
80 }
81}
82
83/// **UNSTABLE**
84///
85/// This capability is not part of the spec yet, and may be removed or changed at any point.
86///
87/// Response to `mcp/connect`.
88#[serde_as]
89#[skip_serializing_none]
90#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
91#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
92#[serde(rename_all = "camelCase")]
93#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = MCP_CONNECT_METHOD_NAME)))]
94#[non_exhaustive]
95pub struct ConnectMcpResponse {
96 /// The unique identifier for this MCP-over-ACP connection.
97 pub connection_id: McpConnectionId,
98 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
99 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
100 /// these keys.
101 ///
102 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
103 #[serde_as(deserialize_as = "DefaultOnError")]
104 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
105 #[serde(default)]
106 #[serde(rename = "_meta")]
107 pub meta: Option<Meta>,
108}
109
110impl ConnectMcpResponse {
111 /// Builds [`ConnectMcpResponse`] with the required response fields set; optional fields start unset or empty.
112 #[must_use]
113 pub fn new(connection_id: impl Into<McpConnectionId>) -> Self {
114 Self {
115 connection_id: connection_id.into(),
116 meta: None,
117 }
118 }
119
120 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
121 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
122 /// these keys.
123 ///
124 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
125 #[must_use]
126 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
127 self.meta = meta.into_option();
128 self
129 }
130}
131
132/// **UNSTABLE**
133///
134/// This capability is not part of the spec yet, and may be removed or changed at any point.
135///
136/// Request parameters for `mcp/message`.
137#[serde_as]
138#[skip_serializing_none]
139#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
140#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
141#[serde(rename_all = "camelCase")]
142#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "both", "x-method" = MCP_MESSAGE_METHOD_NAME)))]
143#[non_exhaustive]
144pub struct MessageMcpRequest {
145 /// The MCP-over-ACP connection this message is sent on.
146 pub connection_id: McpConnectionId,
147 /// The inner MCP method name.
148 pub method: String,
149 /// Optional inner MCP params.
150 ///
151 /// If omitted or set to `null`, the inner MCP message has no params.
152 #[serde(default)]
153 pub params: Option<serde_json::Map<String, serde_json::Value>>,
154 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
155 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
156 /// these keys.
157 ///
158 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
159 #[serde_as(deserialize_as = "DefaultOnError")]
160 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
161 #[serde(default)]
162 #[serde(rename = "_meta")]
163 pub meta: Option<Meta>,
164}
165
166impl MessageMcpRequest {
167 /// Builds [`MessageMcpRequest`] with the required request fields set; optional fields start unset or empty.
168 #[must_use]
169 pub fn new(connection_id: impl Into<McpConnectionId>, method: impl Into<String>) -> Self {
170 Self {
171 connection_id: connection_id.into(),
172 method: method.into(),
173 params: None,
174 meta: None,
175 }
176 }
177
178 /// Optional inner MCP params.
179 ///
180 /// If omitted or set to `null`, the inner MCP message has no params.
181 #[must_use]
182 pub fn params(
183 mut self,
184 params: impl IntoOption<serde_json::Map<String, serde_json::Value>>,
185 ) -> Self {
186 self.params = params.into_option();
187 self
188 }
189
190 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
191 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
192 /// these keys.
193 ///
194 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
195 #[must_use]
196 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
197 self.meta = meta.into_option();
198 self
199 }
200}
201
202/// **UNSTABLE**
203///
204/// This capability is not part of the spec yet, and may be removed or changed at any point.
205///
206/// Notification parameters for `mcp/message`.
207///
208/// This is used when the wrapped MCP message is a notification and the outer JSON-RPC
209/// envelope has no `id`.
210#[serde_as]
211#[skip_serializing_none]
212#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
213#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
214#[serde(rename_all = "camelCase")]
215#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "both", "x-method" = MCP_MESSAGE_METHOD_NAME)))]
216#[non_exhaustive]
217pub struct MessageMcpNotification {
218 /// The MCP-over-ACP connection this message is sent on.
219 pub connection_id: McpConnectionId,
220 /// The inner MCP method name.
221 pub method: String,
222 /// Optional inner MCP params.
223 ///
224 /// If omitted or set to `null`, the inner MCP message has no params.
225 #[serde_as(deserialize_as = "DefaultOnError")]
226 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
227 #[serde(default)]
228 pub params: Option<serde_json::Map<String, serde_json::Value>>,
229 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
230 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
231 /// these keys.
232 ///
233 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
234 #[serde_as(deserialize_as = "DefaultOnError")]
235 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
236 #[serde(default)]
237 #[serde(rename = "_meta")]
238 pub meta: Option<Meta>,
239}
240
241impl MessageMcpNotification {
242 /// Builds [`MessageMcpNotification`] with the required notification fields set; optional fields start unset or empty.
243 #[must_use]
244 pub fn new(connection_id: impl Into<McpConnectionId>, method: impl Into<String>) -> Self {
245 Self {
246 connection_id: connection_id.into(),
247 method: method.into(),
248 params: None,
249 meta: None,
250 }
251 }
252
253 /// Optional inner MCP params.
254 ///
255 /// If omitted or set to `null`, the inner MCP message has no params.
256 #[must_use]
257 pub fn params(
258 mut self,
259 params: impl IntoOption<serde_json::Map<String, serde_json::Value>>,
260 ) -> Self {
261 self.params = params.into_option();
262 self
263 }
264
265 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
266 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
267 /// these keys.
268 ///
269 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
270 #[must_use]
271 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
272 self.meta = meta.into_option();
273 self
274 }
275}
276
277/// **UNSTABLE**
278///
279/// This capability is not part of the spec yet, and may be removed or changed at any point.
280///
281/// Response to `mcp/message`.
282///
283/// This is the inner MCP response result payload. Any JSON value is valid.
284#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
285#[derive(Debug, Clone, Serialize, Deserialize, From)]
286#[serde(transparent)]
287#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "both", "x-method" = MCP_MESSAGE_METHOD_NAME)))]
288#[non_exhaustive]
289pub struct MessageMcpResponse(
290 #[cfg_attr(feature = "schemars", schemars(with = "serde_json::Value"))] pub Arc<RawValue>,
291);
292
293impl MessageMcpResponse {
294 /// Builds [`MessageMcpResponse`] with the required response fields set; optional fields start unset or empty.
295 #[must_use]
296 pub fn new(result: Arc<RawValue>) -> Self {
297 Self(result)
298 }
299}
300
301/// **UNSTABLE**
302///
303/// This capability is not part of the spec yet, and may be removed or changed at any point.
304///
305/// Request parameters for `mcp/disconnect`.
306#[serde_as]
307#[skip_serializing_none]
308#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
309#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
310#[serde(rename_all = "camelCase")]
311#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = MCP_DISCONNECT_METHOD_NAME)))]
312#[non_exhaustive]
313pub struct DisconnectMcpRequest {
314 /// The MCP-over-ACP connection to close.
315 pub connection_id: McpConnectionId,
316 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
317 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
318 /// these keys.
319 ///
320 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
321 #[serde_as(deserialize_as = "DefaultOnError")]
322 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
323 #[serde(default)]
324 #[serde(rename = "_meta")]
325 pub meta: Option<Meta>,
326}
327
328impl DisconnectMcpRequest {
329 /// Builds [`DisconnectMcpRequest`] with the required request fields set; optional fields start unset or empty.
330 #[must_use]
331 pub fn new(connection_id: impl Into<McpConnectionId>) -> Self {
332 Self {
333 connection_id: connection_id.into(),
334 meta: None,
335 }
336 }
337
338 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
339 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
340 /// these keys.
341 ///
342 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
343 #[must_use]
344 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
345 self.meta = meta.into_option();
346 self
347 }
348}
349
350/// **UNSTABLE**
351///
352/// This capability is not part of the spec yet, and may be removed or changed at any point.
353///
354/// Response to `mcp/disconnect`.
355#[serde_as]
356#[skip_serializing_none]
357#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
358#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
359#[serde(rename_all = "camelCase")]
360#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = MCP_DISCONNECT_METHOD_NAME)))]
361#[non_exhaustive]
362pub struct DisconnectMcpResponse {
363 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
364 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
365 /// these keys.
366 ///
367 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
368 #[serde_as(deserialize_as = "DefaultOnError")]
369 #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
370 #[serde(default)]
371 #[serde(rename = "_meta")]
372 pub meta: Option<Meta>,
373}
374
375impl DisconnectMcpResponse {
376 /// Builds [`DisconnectMcpResponse`] with the required response fields set; optional fields start unset or empty.
377 #[must_use]
378 pub fn new() -> Self {
379 Self::default()
380 }
381
382 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
383 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
384 /// these keys.
385 ///
386 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
387 #[must_use]
388 pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
389 self.meta = meta.into_option();
390 self
391 }
392}
393
394/// Method name for opening an MCP-over-ACP connection.
395pub(crate) const MCP_CONNECT_METHOD_NAME: &str = "mcp/connect";
396/// Method name for exchanging MCP-over-ACP messages.
397pub(crate) const MCP_MESSAGE_METHOD_NAME: &str = "mcp/message";
398/// Method name for closing an MCP-over-ACP connection.
399pub(crate) const MCP_DISCONNECT_METHOD_NAME: &str = "mcp/disconnect";