Skip to main content

agent_client_protocol_schema/v1/
agent.rs

1//! Methods and notifications the agent handles/receives.
2//!
3//! This module defines the Agent trait and all associated types for implementing
4//! an AI coding agent that follows the Agent Client Protocol (ACP).
5
6use std::{path::PathBuf, sync::Arc};
7
8use std::collections::HashMap;
9
10use derive_more::{Display, From};
11use serde::{Deserialize, Serialize};
12use serde_with::{DefaultOnError, DefaultOnNull, VecSkipError, serde_as, skip_serializing_none};
13
14use crate::{IntoOption, ProtocolVersion};
15
16use super::{
17    ClientCapabilities, ContentBlock, ExtNotification, ExtRequest, ExtResponse, Meta, SessionId,
18};
19
20#[cfg(feature = "unstable_mcp_over_acp")]
21use super::mcp::{MCP_MESSAGE_METHOD_NAME, MessageMcpNotification};
22
23// Initialize
24
25/// Request parameters for the initialize method.
26///
27/// Sent by the client to establish connection and negotiate capabilities.
28///
29/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
30#[serde_as]
31#[skip_serializing_none]
32#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
34#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
35#[serde(rename_all = "camelCase")]
36#[non_exhaustive]
37pub struct InitializeRequest {
38    /// The latest protocol version supported by the client.
39    pub protocol_version: ProtocolVersion,
40    /// Capabilities supported by the client.
41    #[serde_as(deserialize_as = "DefaultOnError")]
42    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
43    #[serde(default)]
44    pub client_capabilities: ClientCapabilities,
45    /// Information about the Client name and version sent to the Agent.
46    ///
47    /// Note: in future versions of the protocol, this will be required.
48    #[serde_as(deserialize_as = "DefaultOnError")]
49    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
50    #[serde(default)]
51    pub client_info: Option<Implementation>,
52    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
53    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
54    /// these keys.
55    ///
56    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
57    #[serde_as(deserialize_as = "DefaultOnError")]
58    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
59    #[serde(default)]
60    #[serde(rename = "_meta")]
61    pub meta: Option<Meta>,
62}
63
64impl InitializeRequest {
65    /// Builds [`InitializeRequest`] with the required request fields set; optional fields start unset or empty.
66    #[must_use]
67    pub fn new(protocol_version: ProtocolVersion) -> Self {
68        Self {
69            protocol_version,
70            client_capabilities: ClientCapabilities::default(),
71            client_info: None,
72            meta: None,
73        }
74    }
75
76    /// Capabilities supported by the client.
77    #[must_use]
78    pub fn client_capabilities(mut self, client_capabilities: ClientCapabilities) -> Self {
79        self.client_capabilities = client_capabilities;
80        self
81    }
82
83    /// Information about the Client name and version sent to the Agent.
84    #[must_use]
85    pub fn client_info(mut self, client_info: impl IntoOption<Implementation>) -> Self {
86        self.client_info = client_info.into_option();
87        self
88    }
89
90    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
91    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
92    /// these keys.
93    ///
94    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
95    #[must_use]
96    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
97        self.meta = meta.into_option();
98        self
99    }
100}
101
102/// Response to the `initialize` method.
103///
104/// Contains the negotiated protocol version and agent capabilities.
105///
106/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
107#[serde_as]
108#[skip_serializing_none]
109#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
110#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
111#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
112#[serde(rename_all = "camelCase")]
113#[non_exhaustive]
114pub struct InitializeResponse {
115    /// The protocol version the client specified if supported by the agent,
116    /// or the latest protocol version supported by the agent.
117    ///
118    /// The client should disconnect, if it doesn't support this version.
119    pub protocol_version: ProtocolVersion,
120    /// Capabilities supported by the agent.
121    #[serde_as(deserialize_as = "DefaultOnError")]
122    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
123    #[serde(default)]
124    pub agent_capabilities: AgentCapabilities,
125    /// Authentication methods supported by the agent.
126    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
127    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
128    #[serde(default)]
129    pub auth_methods: Vec<AuthMethod>,
130    /// Information about the Agent name and version sent to the Client.
131    ///
132    /// Note: in future versions of the protocol, this will be required.
133    #[serde_as(deserialize_as = "DefaultOnError")]
134    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
135    #[serde(default)]
136    pub agent_info: Option<Implementation>,
137    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
138    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
139    /// these keys.
140    ///
141    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
142    #[serde_as(deserialize_as = "DefaultOnError")]
143    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
144    #[serde(default)]
145    #[serde(rename = "_meta")]
146    pub meta: Option<Meta>,
147}
148
149impl InitializeResponse {
150    /// Builds [`InitializeResponse`] with the required response fields set; optional fields start unset or empty.
151    #[must_use]
152    pub fn new(protocol_version: ProtocolVersion) -> Self {
153        Self {
154            protocol_version,
155            agent_capabilities: AgentCapabilities::default(),
156            auth_methods: vec![],
157            agent_info: None,
158            meta: None,
159        }
160    }
161
162    /// Capabilities supported by the agent.
163    #[must_use]
164    pub fn agent_capabilities(mut self, agent_capabilities: AgentCapabilities) -> Self {
165        self.agent_capabilities = agent_capabilities;
166        self
167    }
168
169    /// Authentication methods supported by the agent.
170    #[must_use]
171    pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
172        self.auth_methods = auth_methods;
173        self
174    }
175
176    /// Information about the Agent name and version sent to the Client.
177    #[must_use]
178    pub fn agent_info(mut self, agent_info: impl IntoOption<Implementation>) -> Self {
179        self.agent_info = agent_info.into_option();
180        self
181    }
182
183    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
184    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
185    /// these keys.
186    ///
187    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
188    #[must_use]
189    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
190        self.meta = meta.into_option();
191        self
192    }
193}
194
195/// Metadata about the implementation of the client or agent.
196/// Describes the name and version of an ACP implementation, with an optional
197/// title for UI representation.
198#[serde_as]
199#[skip_serializing_none]
200#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
201#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
202#[serde(rename_all = "camelCase")]
203#[non_exhaustive]
204pub struct Implementation {
205    /// Intended for programmatic or logical use, but can be used as a display
206    /// name fallback if title isn’t present.
207    pub name: String,
208    /// Intended for UI and end-user contexts — optimized to be human-readable
209    /// and easily understood.
210    ///
211    /// If not provided, the name should be used for display.
212    #[serde_as(deserialize_as = "DefaultOnError")]
213    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
214    #[serde(default)]
215    pub title: Option<String>,
216    /// Version of the implementation. Can be displayed to the user or used
217    /// for debugging or metrics purposes. (e.g. "1.0.0").
218    pub version: String,
219    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
220    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
221    /// these keys.
222    ///
223    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
224    #[serde_as(deserialize_as = "DefaultOnError")]
225    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
226    #[serde(default)]
227    #[serde(rename = "_meta")]
228    pub meta: Option<Meta>,
229}
230
231impl Implementation {
232    /// Builds [`Implementation`] with the required fields set; optional fields start unset or empty.
233    #[must_use]
234    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
235        Self {
236            name: name.into(),
237            title: None,
238            version: version.into(),
239            meta: None,
240        }
241    }
242
243    /// Intended for UI and end-user contexts — optimized to be human-readable
244    /// and easily understood.
245    ///
246    /// If not provided, the name should be used for display.
247    #[must_use]
248    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
249        self.title = title.into_option();
250        self
251    }
252
253    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
254    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
255    /// these keys.
256    ///
257    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
258    #[must_use]
259    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
260        self.meta = meta.into_option();
261        self
262    }
263}
264
265// Authentication
266
267/// Request parameters for the authenticate method.
268///
269/// Specifies which authentication method to use.
270#[serde_as]
271#[skip_serializing_none]
272#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
273#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
274#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTHENTICATE_METHOD_NAME)))]
275#[serde(rename_all = "camelCase")]
276#[non_exhaustive]
277pub struct AuthenticateRequest {
278    /// The ID of the authentication method to use.
279    /// Must be one of the methods advertised in the initialize response.
280    pub method_id: AuthMethodId,
281    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
282    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
283    /// these keys.
284    ///
285    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
286    #[serde_as(deserialize_as = "DefaultOnError")]
287    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
288    #[serde(default)]
289    #[serde(rename = "_meta")]
290    pub meta: Option<Meta>,
291}
292
293impl AuthenticateRequest {
294    /// Builds [`AuthenticateRequest`] with the required request fields set; optional fields start unset or empty.
295    #[must_use]
296    pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
297        Self {
298            method_id: method_id.into(),
299            meta: None,
300        }
301    }
302
303    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
304    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
305    /// these keys.
306    ///
307    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
308    #[must_use]
309    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
310        self.meta = meta.into_option();
311        self
312    }
313}
314
315crate::serde_util::default_on_null! {
316    /// Response to the `authenticate` method.
317    #[serde_as]
318    #[skip_serializing_none]
319    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
320    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
321    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTHENTICATE_METHOD_NAME)))]
322    #[serde(rename_all = "camelCase")]
323    #[non_exhaustive]
324    pub struct AuthenticateResponse {
325        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
326        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
327        /// these keys.
328        ///
329        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
330        #[serde_as(deserialize_as = "DefaultOnError")]
331        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
332        #[serde(default)]
333        #[serde(rename = "_meta")]
334        pub meta: Option<Meta>,
335    }
336}
337
338impl AuthenticateResponse {
339    /// Builds [`AuthenticateResponse`] with the required response fields set; optional fields start unset or empty.
340    #[must_use]
341    pub fn new() -> Self {
342        Self::default()
343    }
344
345    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
346    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
347    /// these keys.
348    ///
349    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
350    #[must_use]
351    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
352        self.meta = meta.into_option();
353        self
354    }
355}
356
357// Logout
358
359crate::serde_util::default_on_null! {
360    /// Request parameters for the logout method.
361    ///
362    /// Terminates the current authenticated session.
363    #[serde_as]
364    #[skip_serializing_none]
365    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
366    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
367    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = LOGOUT_METHOD_NAME)))]
368    #[serde(rename_all = "camelCase")]
369    #[non_exhaustive]
370    pub struct LogoutRequest {
371        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
372        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
373        /// these keys.
374        ///
375        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
376        #[serde_as(deserialize_as = "DefaultOnError")]
377        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
378        #[serde(default)]
379        #[serde(rename = "_meta")]
380        pub meta: Option<Meta>,
381    }
382}
383
384impl LogoutRequest {
385    /// Builds [`LogoutRequest`] with the required request fields set; optional fields start unset or empty.
386    #[must_use]
387    pub fn new() -> Self {
388        Self::default()
389    }
390
391    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
392    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
393    /// these keys.
394    ///
395    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
396    #[must_use]
397    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
398        self.meta = meta.into_option();
399        self
400    }
401}
402
403crate::serde_util::default_on_null! {
404    /// Response to the `logout` method.
405    #[serde_as]
406    #[skip_serializing_none]
407    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
408    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
409    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = LOGOUT_METHOD_NAME)))]
410    #[serde(rename_all = "camelCase")]
411    #[non_exhaustive]
412    pub struct LogoutResponse {
413        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
414        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
415        /// these keys.
416        ///
417        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
418        #[serde_as(deserialize_as = "DefaultOnError")]
419        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
420        #[serde(default)]
421        #[serde(rename = "_meta")]
422        pub meta: Option<Meta>,
423    }
424}
425
426impl LogoutResponse {
427    /// Builds [`LogoutResponse`] with the required response fields set; optional fields start unset or empty.
428    #[must_use]
429    pub fn new() -> Self {
430        Self::default()
431    }
432
433    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
434    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
435    /// these keys.
436    ///
437    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
438    #[must_use]
439    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
440        self.meta = meta.into_option();
441        self
442    }
443}
444
445/// Authentication-related capabilities supported by the agent.
446#[serde_as]
447#[skip_serializing_none]
448#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
449#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
450#[serde(rename_all = "camelCase")]
451#[non_exhaustive]
452pub struct AgentAuthCapabilities {
453    /// Whether the agent supports the logout method.
454    ///
455    /// Optional. Omitted or `null` both mean the agent does not advertise support.
456    /// Supplying `{}` means the agent supports the logout method.
457    #[serde_as(deserialize_as = "DefaultOnError")]
458    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
459    #[serde(default)]
460    pub logout: Option<LogoutCapabilities>,
461    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
462    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
463    /// these keys.
464    ///
465    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
466    #[serde_as(deserialize_as = "DefaultOnError")]
467    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
468    #[serde(default)]
469    #[serde(rename = "_meta")]
470    pub meta: Option<Meta>,
471}
472
473impl AgentAuthCapabilities {
474    /// Builds an empty [`AgentAuthCapabilities`]; use builder methods to advertise supported sub-capabilities.
475    #[must_use]
476    pub fn new() -> Self {
477        Self::default()
478    }
479
480    /// Whether the agent supports the logout method.
481    #[must_use]
482    pub fn logout(mut self, logout: impl IntoOption<LogoutCapabilities>) -> Self {
483        self.logout = logout.into_option();
484        self
485    }
486
487    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
488    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
489    /// these keys.
490    ///
491    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
492    #[must_use]
493    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
494        self.meta = meta.into_option();
495        self
496    }
497}
498
499/// Logout capabilities supported by the agent.
500///
501/// Supplying `{}` means the agent supports the logout method.
502#[serde_as]
503#[skip_serializing_none]
504#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
505#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
506#[non_exhaustive]
507pub struct LogoutCapabilities {
508    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
509    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
510    /// these keys.
511    ///
512    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
513    #[serde_as(deserialize_as = "DefaultOnError")]
514    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
515    #[serde(default)]
516    #[serde(rename = "_meta")]
517    pub meta: Option<Meta>,
518}
519
520impl LogoutCapabilities {
521    /// Builds an empty [`LogoutCapabilities`]; use builder methods to advertise supported sub-capabilities.
522    #[must_use]
523    pub fn new() -> Self {
524        Self::default()
525    }
526
527    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
528    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
529    /// these keys.
530    ///
531    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
532    #[must_use]
533    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
534        self.meta = meta.into_option();
535        self
536    }
537}
538
539/// Typed identifier used for auth method values on the wire.
540#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
541#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
542#[serde(transparent)]
543#[from(Arc<str>, String, &'static str)]
544#[non_exhaustive]
545pub struct AuthMethodId(pub Arc<str>);
546
547impl AuthMethodId {
548    /// Wraps a protocol string as a typed [`AuthMethodId`].
549    #[must_use]
550    pub fn new(id: impl Into<Arc<str>>) -> Self {
551        Self(id.into())
552    }
553}
554
555/// Describes an available authentication method.
556///
557/// The `type` field acts as the discriminator in the serialized JSON form.
558/// When no `type` is present, the method is treated as `agent`.
559#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
560#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
561#[serde(tag = "type", rename_all = "snake_case")]
562#[non_exhaustive]
563pub enum AuthMethod {
564    /// Client runs the configured agent program as a separate interactive
565    /// process, without passing this method to `authenticate`.
566    Terminal(AuthMethodTerminal),
567    /// Agent handles authentication itself through `authenticate`.
568    ///
569    /// This is the default when no `type` is specified.
570    #[serde(untagged, deserialize_with = "deserialize_agent_auth_method_fallback")]
571    Agent(AuthMethodAgent),
572}
573
574/// Deserializes the untagged `agent` fallback of [`AuthMethod`].
575///
576/// A `terminal` method that fails its own schema must not fall through to
577/// `agent`, or the client would pass it to `authenticate`.
578fn deserialize_agent_auth_method_fallback<'de, D>(
579    deserializer: D,
580) -> Result<AuthMethodAgent, D::Error>
581where
582    D: serde::Deserializer<'de>,
583{
584    let value = serde_json::Value::deserialize(deserializer)?;
585    if value.get("type").and_then(serde_json::Value::as_str) == Some("terminal") {
586        return Err(serde::de::Error::custom(
587            "known authentication method `terminal` did not match its schema",
588        ));
589    }
590    AuthMethodAgent::deserialize(value).map_err(serde::de::Error::custom)
591}
592
593impl AuthMethod {
594    /// The unique identifier for this authentication method.
595    #[must_use]
596    pub fn id(&self) -> &AuthMethodId {
597        match self {
598            Self::Agent(a) => &a.id,
599            Self::Terminal(t) => &t.id,
600        }
601    }
602
603    /// The human-readable name of this authentication method.
604    #[must_use]
605    pub fn name(&self) -> &str {
606        match self {
607            Self::Agent(a) => &a.name,
608            Self::Terminal(t) => &t.name,
609        }
610    }
611
612    /// Optional description providing more details about this authentication method.
613    #[must_use]
614    pub fn description(&self) -> Option<&str> {
615        match self {
616            Self::Agent(a) => a.description.as_deref(),
617            Self::Terminal(t) => t.description.as_deref(),
618        }
619    }
620
621    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
622    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
623    /// these keys.
624    ///
625    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
626    #[must_use]
627    pub fn meta(&self) -> Option<&Meta> {
628        match self {
629            Self::Agent(a) => a.meta.as_ref(),
630            Self::Terminal(t) => t.meta.as_ref(),
631        }
632    }
633}
634
635/// Agent handles authentication itself through `authenticate`.
636///
637/// This is the default authentication method type.
638#[serde_as]
639#[skip_serializing_none]
640#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
641#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
642#[serde(rename_all = "camelCase")]
643#[non_exhaustive]
644pub struct AuthMethodAgent {
645    /// Unique identifier for this authentication method.
646    pub id: AuthMethodId,
647    /// Human-readable name of the authentication method.
648    pub name: String,
649    /// Optional description providing more details about this authentication method.
650    #[serde_as(deserialize_as = "DefaultOnError")]
651    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
652    #[serde(default)]
653    pub description: Option<String>,
654    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
655    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
656    /// these keys.
657    ///
658    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
659    #[serde_as(deserialize_as = "DefaultOnError")]
660    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
661    #[serde(default)]
662    #[serde(rename = "_meta")]
663    pub meta: Option<Meta>,
664}
665
666impl AuthMethodAgent {
667    /// Builds [`AuthMethodAgent`] with the required fields set; optional fields start unset or empty.
668    #[must_use]
669    pub fn new(id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
670        Self {
671            id: id.into(),
672            name: name.into(),
673            description: None,
674            meta: None,
675        }
676    }
677
678    /// Optional description providing more details about this authentication method.
679    #[must_use]
680    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
681        self.description = description.into_option();
682        self
683    }
684
685    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
686    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
687    /// these keys.
688    ///
689    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
690    #[must_use]
691    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
692        self.meta = meta.into_option();
693        self
694    }
695}
696
697/// Terminal-based authentication method.
698///
699/// The client runs the configured agent program as a separate interactive
700/// process for the user to authenticate via a TUI. Agents MUST advertise this
701/// method only when the client enabled its terminal authentication capability.
702/// A zero exit status signals success; any other termination signals failure.
703/// The client MUST NOT pass this method to `authenticate`.
704#[serde_as]
705#[skip_serializing_none]
706#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
707#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
708#[serde(rename_all = "camelCase")]
709#[non_exhaustive]
710pub struct AuthMethodTerminal {
711    /// Unique identifier for this authentication method.
712    pub id: AuthMethodId,
713    /// Human-readable name of the authentication method.
714    pub name: String,
715    /// Optional description providing more details about this authentication method.
716    #[serde_as(deserialize_as = "DefaultOnError")]
717    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
718    #[serde(default)]
719    pub description: Option<String>,
720    /// Additional arguments to append to the configured agent invocation for terminal auth.
721    #[serde_as(deserialize_as = "DefaultOnNull")]
722    #[serde(default, skip_serializing_if = "Vec::is_empty")]
723    pub args: Vec<String>,
724    /// Additional environment variables to set on the configured agent invocation for terminal auth.
725    /// These values override same-named variables in the base launch configuration.
726    #[serde_as(deserialize_as = "DefaultOnNull")]
727    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
728    pub env: HashMap<String, String>,
729    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
730    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
731    /// these keys.
732    ///
733    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
734    #[serde_as(deserialize_as = "DefaultOnError")]
735    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
736    #[serde(default)]
737    #[serde(rename = "_meta")]
738    pub meta: Option<Meta>,
739}
740
741impl AuthMethodTerminal {
742    /// Builds [`AuthMethodTerminal`] with the required fields set; optional fields start unset or empty.
743    #[must_use]
744    pub fn new(id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
745        Self {
746            id: id.into(),
747            name: name.into(),
748            description: None,
749            args: Vec::new(),
750            env: HashMap::new(),
751            meta: None,
752        }
753    }
754
755    /// Additional arguments to append to the configured agent invocation for terminal auth.
756    #[must_use]
757    pub fn args(mut self, args: Vec<String>) -> Self {
758        self.args = args;
759        self
760    }
761
762    /// Additional environment variables to set on the configured agent invocation for terminal auth.
763    /// These values override same-named variables in the base launch configuration.
764    #[must_use]
765    pub fn env(mut self, env: HashMap<String, String>) -> Self {
766        self.env = env;
767        self
768    }
769
770    /// Optional description providing more details about this authentication method.
771    #[must_use]
772    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
773        self.description = description.into_option();
774        self
775    }
776
777    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
778    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
779    /// these keys.
780    ///
781    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
782    #[must_use]
783    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
784        self.meta = meta.into_option();
785        self
786    }
787}
788
789// New session
790
791/// Request parameters for creating a new session.
792///
793/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)
794#[serde_as]
795#[skip_serializing_none]
796#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
797#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
798#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
799#[serde(rename_all = "camelCase")]
800#[non_exhaustive]
801pub struct NewSessionRequest {
802    /// The working directory for this session. Must be an absolute path.
803    pub cwd: PathBuf,
804    /// Additional workspace roots for this session. Each path must be absolute.
805    ///
806    /// These expand the session's filesystem scope without changing `cwd`, which
807    /// remains the base for relative paths. When omitted or empty, no
808    /// additional roots are activated for the new session.
809    #[serde_as(deserialize_as = "DefaultOnNull")]
810    #[serde(default, skip_serializing_if = "Vec::is_empty")]
811    pub additional_directories: Vec<PathBuf>,
812    /// List of MCP (Model Context Protocol) servers the agent should connect to.
813    #[serde_as(deserialize_as = "DefaultOnNull")]
814    pub mcp_servers: Vec<McpServer>,
815    /// **UNSTABLE**
816    ///
817    /// This capability is not part of the spec yet, and may be removed or changed at any point.
818    ///
819    /// System-level instructions for the new session.
820    ///
821    /// Optional. Omitted and `null` are equivalent and mean the Agent uses its
822    /// own default instructions. Clients MUST only send this when the Agent
823    /// advertises `sessionCapabilities.systemPrompt`.
824    #[cfg(feature = "unstable_session_system_prompt")]
825    #[serde(default)]
826    pub system_prompt: Option<SystemPrompt>,
827    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
828    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
829    /// these keys.
830    ///
831    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
832    #[serde_as(deserialize_as = "DefaultOnError")]
833    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
834    #[serde(default)]
835    #[serde(rename = "_meta")]
836    pub meta: Option<Meta>,
837}
838
839impl NewSessionRequest {
840    /// Builds [`NewSessionRequest`] with the required request fields set; optional fields start unset or empty.
841    #[must_use]
842    pub fn new(cwd: impl Into<PathBuf>) -> Self {
843        Self {
844            cwd: cwd.into(),
845            additional_directories: vec![],
846            mcp_servers: vec![],
847            #[cfg(feature = "unstable_session_system_prompt")]
848            system_prompt: None,
849            meta: None,
850        }
851    }
852
853    /// Additional workspace roots for this session. Each path must be absolute.
854    #[must_use]
855    pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
856        self.additional_directories = additional_directories;
857        self
858    }
859
860    /// List of MCP (Model Context Protocol) servers the agent should connect to.
861    #[must_use]
862    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
863        self.mcp_servers = mcp_servers;
864        self
865    }
866
867    /// **UNSTABLE**
868    ///
869    /// This capability is not part of the spec yet, and may be removed or changed at any point.
870    ///
871    /// System-level instructions for the new session.
872    #[cfg(feature = "unstable_session_system_prompt")]
873    #[must_use]
874    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
875        self.system_prompt = system_prompt.into_option();
876        self
877    }
878
879    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
880    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
881    /// these keys.
882    ///
883    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
884    #[must_use]
885    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
886        self.meta = meta.into_option();
887        self
888    }
889}
890
891/// Response from creating a new session.
892///
893/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)
894#[serde_as]
895#[skip_serializing_none]
896#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
897#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
898#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
899#[serde(rename_all = "camelCase")]
900#[non_exhaustive]
901pub struct NewSessionResponse {
902    /// Unique identifier for the created session.
903    ///
904    /// Used in all subsequent requests for this conversation.
905    pub session_id: SessionId,
906    /// Initial mode state if supported by the Agent
907    ///
908    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
909    #[serde_as(deserialize_as = "DefaultOnError")]
910    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
911    #[serde(default)]
912    pub modes: Option<SessionModeState>,
913    /// Initial session configuration options if supported by the Agent.
914    #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_>>>")]
915    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
916    #[serde(default)]
917    pub config_options: Option<Vec<SessionConfigOption>>,
918    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
919    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
920    /// these keys.
921    ///
922    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
923    #[serde_as(deserialize_as = "DefaultOnError")]
924    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
925    #[serde(default)]
926    #[serde(rename = "_meta")]
927    pub meta: Option<Meta>,
928}
929
930impl NewSessionResponse {
931    /// Builds [`NewSessionResponse`] with the required response fields set; optional fields start unset or empty.
932    #[must_use]
933    pub fn new(session_id: impl Into<SessionId>) -> Self {
934        Self {
935            session_id: session_id.into(),
936            modes: None,
937            config_options: None,
938            meta: None,
939        }
940    }
941
942    /// Initial mode state if supported by the Agent
943    ///
944    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
945    #[must_use]
946    pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
947        self.modes = modes.into_option();
948        self
949    }
950
951    /// Initial session configuration options if supported by the Agent.
952    #[must_use]
953    pub fn config_options(
954        mut self,
955        config_options: impl IntoOption<Vec<SessionConfigOption>>,
956    ) -> Self {
957        self.config_options = config_options.into_option();
958        self
959    }
960
961    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
962    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
963    /// these keys.
964    ///
965    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
966    #[must_use]
967    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
968        self.meta = meta.into_option();
969        self
970    }
971}
972
973// Load session
974
975/// Request parameters for loading an existing session.
976///
977/// Only available if the Agent supports the `loadSession` capability.
978///
979/// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)
980#[serde_as]
981#[skip_serializing_none]
982#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
983#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
984#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME)))]
985#[serde(rename_all = "camelCase")]
986#[non_exhaustive]
987pub struct LoadSessionRequest {
988    /// List of MCP servers to connect to for this session.
989    #[serde_as(deserialize_as = "DefaultOnNull")]
990    pub mcp_servers: Vec<McpServer>,
991    /// The working directory for this session. Must be an absolute path.
992    pub cwd: PathBuf,
993    /// Additional workspace roots to activate for this session. Each path must be absolute.
994    ///
995    /// When omitted or empty, no additional roots are activated. When non-empty,
996    /// this is the complete resulting additional-root list for the loaded
997    /// session. It may differ from any previously used or reported list as long as
998    /// the request `cwd` matches the session's `cwd`.
999    #[serde_as(deserialize_as = "DefaultOnNull")]
1000    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1001    pub additional_directories: Vec<PathBuf>,
1002    /// The ID of the session to load.
1003    pub session_id: SessionId,
1004    /// **UNSTABLE**
1005    ///
1006    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1007    ///
1008    /// System-level instructions for the loaded session.
1009    ///
1010    /// Optional. Omitted and `null` are equivalent; the Agent may then keep a
1011    /// system prompt it stored with the session or use its default instructions.
1012    /// Clients MUST only send this when the Agent advertises
1013    /// `sessionCapabilities.systemPrompt`, and SHOULD send the same value they used
1014    /// when the session was created.
1015    #[cfg(feature = "unstable_session_system_prompt")]
1016    #[serde(default)]
1017    pub system_prompt: Option<SystemPrompt>,
1018    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1019    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1020    /// these keys.
1021    ///
1022    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1023    #[serde_as(deserialize_as = "DefaultOnError")]
1024    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1025    #[serde(default)]
1026    #[serde(rename = "_meta")]
1027    pub meta: Option<Meta>,
1028}
1029
1030impl LoadSessionRequest {
1031    /// Builds [`LoadSessionRequest`] with the required request fields set; optional fields start unset or empty.
1032    #[must_use]
1033    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1034        Self {
1035            mcp_servers: vec![],
1036            cwd: cwd.into(),
1037            additional_directories: vec![],
1038            session_id: session_id.into(),
1039            #[cfg(feature = "unstable_session_system_prompt")]
1040            system_prompt: None,
1041            meta: None,
1042        }
1043    }
1044
1045    /// Additional workspace roots to activate for this session. Each path must be absolute.
1046    #[must_use]
1047    pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1048        self.additional_directories = additional_directories;
1049        self
1050    }
1051
1052    /// List of MCP servers to connect to for this session.
1053    #[must_use]
1054    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1055        self.mcp_servers = mcp_servers;
1056        self
1057    }
1058
1059    /// **UNSTABLE**
1060    ///
1061    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1062    ///
1063    /// System-level instructions for the loaded session.
1064    #[cfg(feature = "unstable_session_system_prompt")]
1065    #[must_use]
1066    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1067        self.system_prompt = system_prompt.into_option();
1068        self
1069    }
1070
1071    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1072    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1073    /// these keys.
1074    ///
1075    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1076    #[must_use]
1077    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1078        self.meta = meta.into_option();
1079        self
1080    }
1081}
1082
1083crate::serde_util::default_on_null! {
1084    /// Response from loading an existing session.
1085    #[serde_as]
1086    #[skip_serializing_none]
1087    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1088    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1089    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME)))]
1090    #[serde(rename_all = "camelCase")]
1091    #[non_exhaustive]
1092    pub struct LoadSessionResponse {
1093        /// Initial mode state if supported by the Agent
1094        ///
1095        /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1096        #[serde_as(deserialize_as = "DefaultOnError")]
1097        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1098        #[serde(default)]
1099        pub modes: Option<SessionModeState>,
1100        /// Initial session configuration options if supported by the Agent.
1101        #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_>>>")]
1102        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1103        #[serde(default)]
1104        pub config_options: Option<Vec<SessionConfigOption>>,
1105        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1106        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1107        /// these keys.
1108        ///
1109        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1110        #[serde_as(deserialize_as = "DefaultOnError")]
1111        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1112        #[serde(default)]
1113        #[serde(rename = "_meta")]
1114        pub meta: Option<Meta>,
1115    }
1116}
1117
1118impl LoadSessionResponse {
1119    /// Builds [`LoadSessionResponse`] with the required response fields set; optional fields start unset or empty.
1120    #[must_use]
1121    pub fn new() -> Self {
1122        Self::default()
1123    }
1124
1125    /// Initial mode state if supported by the Agent
1126    ///
1127    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1128    #[must_use]
1129    pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
1130        self.modes = modes.into_option();
1131        self
1132    }
1133
1134    /// Initial session configuration options if supported by the Agent.
1135    #[must_use]
1136    pub fn config_options(
1137        mut self,
1138        config_options: impl IntoOption<Vec<SessionConfigOption>>,
1139    ) -> Self {
1140        self.config_options = config_options.into_option();
1141        self
1142    }
1143
1144    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1145    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1146    /// these keys.
1147    ///
1148    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1149    #[must_use]
1150    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1151        self.meta = meta.into_option();
1152        self
1153    }
1154}
1155
1156// Fork session
1157
1158/// **UNSTABLE**
1159///
1160/// This capability is not part of the spec yet, and may be removed or changed at any point.
1161///
1162/// Request parameters for forking an existing session.
1163///
1164/// Creates a new session based on the context of an existing one, allowing
1165/// operations like generating summaries without affecting the original session's history.
1166///
1167/// Only available if the Agent supports the `session.fork` capability.
1168#[cfg(feature = "unstable_session_fork")]
1169#[serde_as]
1170#[skip_serializing_none]
1171#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1172#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1173#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1174#[serde(rename_all = "camelCase")]
1175#[non_exhaustive]
1176pub struct ForkSessionRequest {
1177    /// The ID of the session to fork.
1178    pub session_id: SessionId,
1179    /// The working directory for this session. Must be an absolute path.
1180    pub cwd: PathBuf,
1181    /// Additional workspace roots to activate for this session. Each path must be absolute.
1182    ///
1183    /// When omitted or empty, no additional roots are activated. When non-empty,
1184    /// this is the complete resulting additional-root list for the forked
1185    /// session.
1186    #[serde_as(deserialize_as = "DefaultOnNull")]
1187    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1188    pub additional_directories: Vec<PathBuf>,
1189    /// List of MCP servers to connect to for this session.
1190    #[serde_as(deserialize_as = "DefaultOnNull")]
1191    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1192    pub mcp_servers: Vec<McpServer>,
1193    /// **UNSTABLE**
1194    ///
1195    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1196    ///
1197    /// System-level instructions for the forked session.
1198    ///
1199    /// Optional. Omitted and `null` are equivalent; the Agent may then keep a
1200    /// system prompt it stored with the source session or use its default
1201    /// instructions. Clients MUST only send this when the Agent advertises
1202    /// `sessionCapabilities.systemPrompt`, and SHOULD send the same value they used
1203    /// for the source session.
1204    #[cfg(feature = "unstable_session_system_prompt")]
1205    #[serde(default)]
1206    pub system_prompt: Option<SystemPrompt>,
1207    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1208    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1209    /// these keys.
1210    ///
1211    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1212    #[serde_as(deserialize_as = "DefaultOnError")]
1213    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1214    #[serde(default)]
1215    #[serde(rename = "_meta")]
1216    pub meta: Option<Meta>,
1217}
1218
1219#[cfg(feature = "unstable_session_fork")]
1220impl ForkSessionRequest {
1221    /// Builds [`ForkSessionRequest`] with the required request fields set; optional fields start unset or empty.
1222    #[must_use]
1223    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1224        Self {
1225            session_id: session_id.into(),
1226            cwd: cwd.into(),
1227            additional_directories: vec![],
1228            mcp_servers: vec![],
1229            #[cfg(feature = "unstable_session_system_prompt")]
1230            system_prompt: None,
1231            meta: None,
1232        }
1233    }
1234
1235    /// Additional workspace roots to activate for this session. Each path must be absolute.
1236    #[must_use]
1237    pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1238        self.additional_directories = additional_directories;
1239        self
1240    }
1241
1242    /// List of MCP servers to connect to for this session.
1243    #[must_use]
1244    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1245        self.mcp_servers = mcp_servers;
1246        self
1247    }
1248
1249    /// **UNSTABLE**
1250    ///
1251    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1252    ///
1253    /// System-level instructions for the forked session.
1254    #[cfg(feature = "unstable_session_system_prompt")]
1255    #[must_use]
1256    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1257        self.system_prompt = system_prompt.into_option();
1258        self
1259    }
1260
1261    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1262    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1263    /// these keys.
1264    ///
1265    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1266    #[must_use]
1267    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1268        self.meta = meta.into_option();
1269        self
1270    }
1271}
1272
1273/// **UNSTABLE**
1274///
1275/// This capability is not part of the spec yet, and may be removed or changed at any point.
1276///
1277/// Response from forking an existing session.
1278#[cfg(feature = "unstable_session_fork")]
1279#[serde_as]
1280#[skip_serializing_none]
1281#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1282#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1283#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1284#[serde(rename_all = "camelCase")]
1285#[non_exhaustive]
1286pub struct ForkSessionResponse {
1287    /// Unique identifier for the newly created forked session.
1288    pub session_id: SessionId,
1289    /// Initial mode state if supported by the Agent
1290    ///
1291    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1292    #[serde_as(deserialize_as = "DefaultOnError")]
1293    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1294    #[serde(default)]
1295    pub modes: Option<SessionModeState>,
1296    /// Initial session configuration options if supported by the Agent.
1297    #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_>>>")]
1298    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1299    #[serde(default)]
1300    pub config_options: Option<Vec<SessionConfigOption>>,
1301    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1302    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1303    /// these keys.
1304    ///
1305    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1306    #[serde_as(deserialize_as = "DefaultOnError")]
1307    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1308    #[serde(default)]
1309    #[serde(rename = "_meta")]
1310    pub meta: Option<Meta>,
1311}
1312
1313#[cfg(feature = "unstable_session_fork")]
1314impl ForkSessionResponse {
1315    /// Builds [`ForkSessionResponse`] with the required response fields set; optional fields start unset or empty.
1316    #[must_use]
1317    pub fn new(session_id: impl Into<SessionId>) -> Self {
1318        Self {
1319            session_id: session_id.into(),
1320            modes: None,
1321            config_options: None,
1322            meta: None,
1323        }
1324    }
1325
1326    /// Initial mode state if supported by the Agent
1327    ///
1328    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1329    #[must_use]
1330    pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
1331        self.modes = modes.into_option();
1332        self
1333    }
1334
1335    /// Initial session configuration options if supported by the Agent.
1336    #[must_use]
1337    pub fn config_options(
1338        mut self,
1339        config_options: impl IntoOption<Vec<SessionConfigOption>>,
1340    ) -> Self {
1341        self.config_options = config_options.into_option();
1342        self
1343    }
1344
1345    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1346    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1347    /// these keys.
1348    ///
1349    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1350    #[must_use]
1351    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1352        self.meta = meta.into_option();
1353        self
1354    }
1355}
1356
1357// Resume session
1358
1359/// Request parameters for resuming an existing session.
1360///
1361/// Resumes an existing session without returning previous messages (unlike `session/load`).
1362/// This is useful for agents that can resume sessions but don't implement full session loading.
1363///
1364/// Only available if the Agent supports the `sessionCapabilities.resume` capability.
1365#[serde_as]
1366#[skip_serializing_none]
1367#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1368#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1369#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1370#[serde(rename_all = "camelCase")]
1371#[non_exhaustive]
1372pub struct ResumeSessionRequest {
1373    /// The ID of the session to resume.
1374    pub session_id: SessionId,
1375    /// The working directory for this session. Must be an absolute path.
1376    pub cwd: PathBuf,
1377    /// Additional workspace roots to activate for this session. Each path must be absolute.
1378    ///
1379    /// When omitted or empty, no additional roots are activated. When non-empty,
1380    /// this is the complete resulting additional-root list for the resumed
1381    /// session. It may differ from any previously used or reported list as long as
1382    /// the request `cwd` matches the session's `cwd`.
1383    #[serde_as(deserialize_as = "DefaultOnNull")]
1384    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1385    pub additional_directories: Vec<PathBuf>,
1386    /// List of MCP servers to connect to for this session.
1387    #[serde_as(deserialize_as = "DefaultOnNull")]
1388    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1389    pub mcp_servers: Vec<McpServer>,
1390    /// **UNSTABLE**
1391    ///
1392    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1393    ///
1394    /// System-level instructions for the resumed session.
1395    ///
1396    /// Optional. Omitted and `null` are equivalent; the Agent may then keep a
1397    /// system prompt it stored with the session or use its default instructions.
1398    /// Clients MUST only send this when the Agent advertises
1399    /// `sessionCapabilities.systemPrompt`, and SHOULD send the same value they used
1400    /// when the session was created.
1401    #[cfg(feature = "unstable_session_system_prompt")]
1402    #[serde(default)]
1403    pub system_prompt: Option<SystemPrompt>,
1404    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1405    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1406    /// these keys.
1407    ///
1408    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1409    #[serde_as(deserialize_as = "DefaultOnError")]
1410    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1411    #[serde(default)]
1412    #[serde(rename = "_meta")]
1413    pub meta: Option<Meta>,
1414}
1415
1416impl ResumeSessionRequest {
1417    /// Builds [`ResumeSessionRequest`] with the required request fields set; optional fields start unset or empty.
1418    #[must_use]
1419    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1420        Self {
1421            session_id: session_id.into(),
1422            cwd: cwd.into(),
1423            additional_directories: vec![],
1424            mcp_servers: vec![],
1425            #[cfg(feature = "unstable_session_system_prompt")]
1426            system_prompt: None,
1427            meta: None,
1428        }
1429    }
1430
1431    /// Additional workspace roots to activate for this session. Each path must be absolute.
1432    #[must_use]
1433    pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1434        self.additional_directories = additional_directories;
1435        self
1436    }
1437
1438    /// List of MCP servers to connect to for this session.
1439    #[must_use]
1440    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1441        self.mcp_servers = mcp_servers;
1442        self
1443    }
1444
1445    /// **UNSTABLE**
1446    ///
1447    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1448    ///
1449    /// System-level instructions for the resumed session.
1450    #[cfg(feature = "unstable_session_system_prompt")]
1451    #[must_use]
1452    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1453        self.system_prompt = system_prompt.into_option();
1454        self
1455    }
1456
1457    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1458    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1459    /// these keys.
1460    ///
1461    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1462    #[must_use]
1463    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1464        self.meta = meta.into_option();
1465        self
1466    }
1467}
1468
1469crate::serde_util::default_on_null! {
1470    /// Response from resuming an existing session.
1471    #[serde_as]
1472    #[skip_serializing_none]
1473    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1474    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1475    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1476    #[serde(rename_all = "camelCase")]
1477    #[non_exhaustive]
1478    pub struct ResumeSessionResponse {
1479        /// Initial mode state if supported by the Agent
1480        ///
1481        /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1482        #[serde_as(deserialize_as = "DefaultOnError")]
1483        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1484        #[serde(default)]
1485        pub modes: Option<SessionModeState>,
1486        /// Initial session configuration options if supported by the Agent.
1487        #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_>>>")]
1488        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1489        #[serde(default)]
1490        pub config_options: Option<Vec<SessionConfigOption>>,
1491        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1492        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1493        /// these keys.
1494        ///
1495        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1496        #[serde_as(deserialize_as = "DefaultOnError")]
1497        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1498        #[serde(default)]
1499        #[serde(rename = "_meta")]
1500        pub meta: Option<Meta>,
1501    }
1502}
1503
1504impl ResumeSessionResponse {
1505    /// Builds [`ResumeSessionResponse`] with the required response fields set; optional fields start unset or empty.
1506    #[must_use]
1507    pub fn new() -> Self {
1508        Self::default()
1509    }
1510
1511    /// Initial mode state if supported by the Agent
1512    ///
1513    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1514    #[must_use]
1515    pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
1516        self.modes = modes.into_option();
1517        self
1518    }
1519
1520    /// Initial session configuration options if supported by the Agent.
1521    #[must_use]
1522    pub fn config_options(
1523        mut self,
1524        config_options: impl IntoOption<Vec<SessionConfigOption>>,
1525    ) -> Self {
1526        self.config_options = config_options.into_option();
1527        self
1528    }
1529
1530    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1531    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1532    /// these keys.
1533    ///
1534    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1535    #[must_use]
1536    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1537        self.meta = meta.into_option();
1538        self
1539    }
1540}
1541
1542// Close session
1543
1544/// Request parameters for closing an active session.
1545///
1546/// If supported, the agent **must** cancel any ongoing work related to the session
1547/// (treat it as if `session/cancel` was called) and then free up any resources
1548/// associated with the session.
1549///
1550/// Only available if the Agent supports the `sessionCapabilities.close` capability.
1551#[serde_as]
1552#[skip_serializing_none]
1553#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1554#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1555#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1556#[serde(rename_all = "camelCase")]
1557#[non_exhaustive]
1558pub struct CloseSessionRequest {
1559    /// The ID of the session to close.
1560    pub session_id: SessionId,
1561    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1562    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1563    /// these keys.
1564    ///
1565    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1566    #[serde_as(deserialize_as = "DefaultOnError")]
1567    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1568    #[serde(default)]
1569    #[serde(rename = "_meta")]
1570    pub meta: Option<Meta>,
1571}
1572
1573impl CloseSessionRequest {
1574    /// Builds [`CloseSessionRequest`] with the required request fields set; optional fields start unset or empty.
1575    #[must_use]
1576    pub fn new(session_id: impl Into<SessionId>) -> Self {
1577        Self {
1578            session_id: session_id.into(),
1579            meta: None,
1580        }
1581    }
1582
1583    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1584    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1585    /// these keys.
1586    ///
1587    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1588    #[must_use]
1589    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1590        self.meta = meta.into_option();
1591        self
1592    }
1593}
1594
1595crate::serde_util::default_on_null! {
1596    /// Response from closing a session.
1597    #[serde_as]
1598    #[skip_serializing_none]
1599    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1600    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1601    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1602    #[serde(rename_all = "camelCase")]
1603    #[non_exhaustive]
1604    pub struct CloseSessionResponse {
1605        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1606        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1607        /// these keys.
1608        ///
1609        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1610        #[serde_as(deserialize_as = "DefaultOnError")]
1611        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1612        #[serde(default)]
1613        #[serde(rename = "_meta")]
1614        pub meta: Option<Meta>,
1615    }
1616}
1617
1618impl CloseSessionResponse {
1619    /// Builds [`CloseSessionResponse`] with the required response fields set; optional fields start unset or empty.
1620    #[must_use]
1621    pub fn new() -> Self {
1622        Self::default()
1623    }
1624
1625    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1626    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1627    /// these keys.
1628    ///
1629    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1630    #[must_use]
1631    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1632        self.meta = meta.into_option();
1633        self
1634    }
1635}
1636
1637// List sessions
1638
1639crate::serde_util::default_on_null! {
1640    /// Request parameters for listing existing sessions.
1641    ///
1642    /// Only available if the Agent supports the `sessionCapabilities.list` capability.
1643    #[serde_as]
1644    #[skip_serializing_none]
1645    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1646    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1647    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1648    #[serde(rename_all = "camelCase")]
1649    #[non_exhaustive]
1650    pub struct ListSessionsRequest {
1651        /// Filter sessions by working directory. Must be an absolute path.
1652        #[serde(default)]
1653        pub cwd: Option<PathBuf>,
1654        /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
1655        #[serde(default)]
1656        pub cursor: Option<String>,
1657        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1658        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1659        /// these keys.
1660        ///
1661        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1662        #[serde_as(deserialize_as = "DefaultOnError")]
1663        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1664        #[serde(default)]
1665        #[serde(rename = "_meta")]
1666        pub meta: Option<Meta>,
1667    }
1668}
1669
1670impl ListSessionsRequest {
1671    /// Builds [`ListSessionsRequest`] with the required request fields set; optional fields start unset or empty.
1672    #[must_use]
1673    pub fn new() -> Self {
1674        Self::default()
1675    }
1676
1677    /// Filter sessions by working directory. Must be an absolute path.
1678    #[must_use]
1679    pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1680        self.cwd = cwd.into_option();
1681        self
1682    }
1683
1684    /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
1685    #[must_use]
1686    pub fn cursor(mut self, cursor: impl IntoOption<String>) -> Self {
1687        self.cursor = cursor.into_option();
1688        self
1689    }
1690
1691    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1692    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1693    /// these keys.
1694    ///
1695    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1696    #[must_use]
1697    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1698        self.meta = meta.into_option();
1699        self
1700    }
1701}
1702
1703/// Response from listing sessions.
1704#[serde_as]
1705#[skip_serializing_none]
1706#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1707#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1708#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1709#[serde(rename_all = "camelCase")]
1710#[non_exhaustive]
1711pub struct ListSessionsResponse {
1712    /// Array of session information objects
1713    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1714    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1715    pub sessions: Vec<SessionInfo>,
1716    /// Opaque cursor token. If present, pass this in the next request's cursor parameter
1717    /// to fetch the next page. If absent, there are no more results.
1718    #[serde_as(deserialize_as = "DefaultOnError")]
1719    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1720    #[serde(default)]
1721    pub next_cursor: Option<String>,
1722    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1723    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1724    /// these keys.
1725    ///
1726    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1727    #[serde_as(deserialize_as = "DefaultOnError")]
1728    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1729    #[serde(default)]
1730    #[serde(rename = "_meta")]
1731    pub meta: Option<Meta>,
1732}
1733
1734impl ListSessionsResponse {
1735    /// Builds [`ListSessionsResponse`] with the required response fields set; optional fields start unset or empty.
1736    #[must_use]
1737    pub fn new(sessions: Vec<SessionInfo>) -> Self {
1738        Self {
1739            sessions,
1740            next_cursor: None,
1741            meta: None,
1742        }
1743    }
1744
1745    /// Sets or clears the optional `nextCursor` field.
1746    #[must_use]
1747    pub fn next_cursor(mut self, next_cursor: impl IntoOption<String>) -> Self {
1748        self.next_cursor = next_cursor.into_option();
1749        self
1750    }
1751
1752    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1753    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1754    /// these keys.
1755    ///
1756    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1757    #[must_use]
1758    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1759        self.meta = meta.into_option();
1760        self
1761    }
1762}
1763
1764// Delete session
1765
1766/// Request parameters for deleting an existing session from `session/list`.
1767///
1768/// Only available if the Agent supports the `sessionCapabilities.delete` capability.
1769#[serde_as]
1770#[skip_serializing_none]
1771#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1772#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1773#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1774#[serde(rename_all = "camelCase")]
1775#[non_exhaustive]
1776pub struct DeleteSessionRequest {
1777    /// The ID of the session to delete.
1778    pub session_id: SessionId,
1779    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1780    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1781    /// these keys.
1782    ///
1783    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1784    #[serde_as(deserialize_as = "DefaultOnError")]
1785    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1786    #[serde(default)]
1787    #[serde(rename = "_meta")]
1788    pub meta: Option<Meta>,
1789}
1790
1791impl DeleteSessionRequest {
1792    /// Builds [`DeleteSessionRequest`] with the required request fields set; optional fields start unset or empty.
1793    #[must_use]
1794    pub fn new(session_id: impl Into<SessionId>) -> Self {
1795        Self {
1796            session_id: session_id.into(),
1797            meta: None,
1798        }
1799    }
1800
1801    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1802    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1803    /// these keys.
1804    ///
1805    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1806    #[must_use]
1807    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1808        self.meta = meta.into_option();
1809        self
1810    }
1811}
1812
1813crate::serde_util::default_on_null! {
1814    /// Response from deleting a session.
1815    #[serde_as]
1816    #[skip_serializing_none]
1817    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1818    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1819    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1820    #[serde(rename_all = "camelCase")]
1821    #[non_exhaustive]
1822    pub struct DeleteSessionResponse {
1823        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1824        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1825        /// these keys.
1826        ///
1827        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1828        #[serde_as(deserialize_as = "DefaultOnError")]
1829        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1830        #[serde(default)]
1831        #[serde(rename = "_meta")]
1832        pub meta: Option<Meta>,
1833    }
1834}
1835
1836impl DeleteSessionResponse {
1837    /// Builds [`DeleteSessionResponse`] with the required response fields set; optional fields start unset or empty.
1838    #[must_use]
1839    pub fn new() -> Self {
1840        Self::default()
1841    }
1842
1843    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1844    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1845    /// these keys.
1846    ///
1847    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1848    #[must_use]
1849    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1850        self.meta = meta.into_option();
1851        self
1852    }
1853}
1854
1855/// Information about a session returned by session/list
1856#[serde_as]
1857#[skip_serializing_none]
1858#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1859#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1860#[serde(rename_all = "camelCase")]
1861#[non_exhaustive]
1862pub struct SessionInfo {
1863    /// Unique identifier for the session
1864    pub session_id: SessionId,
1865    /// The working directory for this session. Must be an absolute path.
1866    pub cwd: PathBuf,
1867    /// Additional workspace roots reported for this session. Each path must be absolute.
1868    ///
1869    /// When present, this is the complete ordered additional-root list reported
1870    /// by the Agent. Omitted and empty values are equivalent: the response
1871    /// reports no additional roots.
1872    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1873    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1874    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1875    pub additional_directories: Vec<PathBuf>,
1876
1877    /// Human-readable title for the session
1878    #[serde_as(deserialize_as = "DefaultOnError")]
1879    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1880    #[serde(default)]
1881    pub title: Option<String>,
1882    /// ISO 8601 timestamp of last activity
1883    #[serde_as(deserialize_as = "DefaultOnError")]
1884    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1885    #[serde(default)]
1886    pub updated_at: Option<String>,
1887    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1888    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1889    /// these keys.
1890    ///
1891    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1892    #[serde_as(deserialize_as = "DefaultOnError")]
1893    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1894    #[serde(default)]
1895    #[serde(rename = "_meta")]
1896    pub meta: Option<Meta>,
1897}
1898
1899impl SessionInfo {
1900    /// Builds [`SessionInfo`] with the required fields set; optional fields start unset or empty.
1901    #[must_use]
1902    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1903        Self {
1904            session_id: session_id.into(),
1905            cwd: cwd.into(),
1906            additional_directories: vec![],
1907            title: None,
1908            updated_at: None,
1909            meta: None,
1910        }
1911    }
1912
1913    /// Additional workspace roots reported for this session. Each path must be absolute.
1914    #[must_use]
1915    pub fn additional_directories(mut self, additional_directories: Vec<PathBuf>) -> Self {
1916        self.additional_directories = additional_directories;
1917        self
1918    }
1919
1920    /// Human-readable title for the session
1921    #[must_use]
1922    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
1923        self.title = title.into_option();
1924        self
1925    }
1926
1927    /// ISO 8601 timestamp of last activity
1928    #[must_use]
1929    pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
1930        self.updated_at = updated_at.into_option();
1931        self
1932    }
1933
1934    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1935    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1936    /// these keys.
1937    ///
1938    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1939    #[must_use]
1940    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1941        self.meta = meta.into_option();
1942        self
1943    }
1944}
1945
1946// Session modes
1947
1948/// The set of modes and the one currently active.
1949#[serde_as]
1950#[skip_serializing_none]
1951#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1952#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1953#[serde(rename_all = "camelCase")]
1954#[non_exhaustive]
1955pub struct SessionModeState {
1956    /// The current mode the Agent is in.
1957    pub current_mode_id: SessionModeId,
1958    /// The set of modes that the Agent can operate in
1959    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1960    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1961    pub available_modes: Vec<SessionMode>,
1962    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1963    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1964    /// these keys.
1965    ///
1966    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1967    #[serde_as(deserialize_as = "DefaultOnError")]
1968    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1969    #[serde(default)]
1970    #[serde(rename = "_meta")]
1971    pub meta: Option<Meta>,
1972}
1973
1974impl SessionModeState {
1975    /// Builds [`SessionModeState`] with the required fields set; optional fields start unset or empty.
1976    #[must_use]
1977    pub fn new(
1978        current_mode_id: impl Into<SessionModeId>,
1979        available_modes: Vec<SessionMode>,
1980    ) -> Self {
1981        Self {
1982            current_mode_id: current_mode_id.into(),
1983            available_modes,
1984            meta: None,
1985        }
1986    }
1987
1988    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1989    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1990    /// these keys.
1991    ///
1992    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1993    #[must_use]
1994    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1995        self.meta = meta.into_option();
1996        self
1997    }
1998}
1999
2000/// A mode the agent can operate in.
2001///
2002/// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
2003#[serde_as]
2004#[skip_serializing_none]
2005#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2006#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2007#[serde(rename_all = "camelCase")]
2008#[non_exhaustive]
2009pub struct SessionMode {
2010    /// Stable identifier used to refer to this protocol object in later messages.
2011    pub id: SessionModeId,
2012    /// Human-readable name shown for this protocol object.
2013    pub name: String,
2014    /// Optional human-readable details shown with this protocol object.
2015    #[serde_as(deserialize_as = "DefaultOnError")]
2016    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2017    #[serde(default)]
2018    pub description: Option<String>,
2019    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2020    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2021    /// these keys.
2022    ///
2023    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2024    #[serde_as(deserialize_as = "DefaultOnError")]
2025    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2026    #[serde(default)]
2027    #[serde(rename = "_meta")]
2028    pub meta: Option<Meta>,
2029}
2030
2031impl SessionMode {
2032    /// Builds [`SessionMode`] with the required fields set; optional fields start unset or empty.
2033    #[must_use]
2034    pub fn new(id: impl Into<SessionModeId>, name: impl Into<String>) -> Self {
2035        Self {
2036            id: id.into(),
2037            name: name.into(),
2038            description: None,
2039            meta: None,
2040        }
2041    }
2042
2043    /// Sets or clears the optional `description` field.
2044    #[must_use]
2045    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2046        self.description = description.into_option();
2047        self
2048    }
2049
2050    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2051    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2052    /// these keys.
2053    ///
2054    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2055    #[must_use]
2056    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2057        self.meta = meta.into_option();
2058        self
2059    }
2060}
2061
2062/// Unique identifier for a Session Mode.
2063#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2064#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2065#[serde(transparent)]
2066#[from(Arc<str>, String, &'static str)]
2067#[non_exhaustive]
2068pub struct SessionModeId(pub Arc<str>);
2069
2070impl SessionModeId {
2071    /// Wraps a protocol string as a typed [`SessionModeId`].
2072    #[must_use]
2073    pub fn new(id: impl Into<Arc<str>>) -> Self {
2074        Self(id.into())
2075    }
2076}
2077
2078/// Request parameters for setting a session mode.
2079#[serde_as]
2080#[skip_serializing_none]
2081#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2082#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2083#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODE_METHOD_NAME)))]
2084#[serde(rename_all = "camelCase")]
2085#[non_exhaustive]
2086pub struct SetSessionModeRequest {
2087    /// The ID of the session to set the mode for.
2088    pub session_id: SessionId,
2089    /// The ID of the mode to set.
2090    pub mode_id: SessionModeId,
2091    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2092    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2093    /// these keys.
2094    ///
2095    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2096    #[serde_as(deserialize_as = "DefaultOnError")]
2097    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2098    #[serde(default)]
2099    #[serde(rename = "_meta")]
2100    pub meta: Option<Meta>,
2101}
2102
2103impl SetSessionModeRequest {
2104    /// Builds [`SetSessionModeRequest`] with the required request fields set; optional fields start unset or empty.
2105    #[must_use]
2106    pub fn new(session_id: impl Into<SessionId>, mode_id: impl Into<SessionModeId>) -> Self {
2107        Self {
2108            session_id: session_id.into(),
2109            mode_id: mode_id.into(),
2110            meta: None,
2111        }
2112    }
2113
2114    /// Sets or clears ACP `_meta` extension metadata.
2115    #[must_use]
2116    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2117        self.meta = meta.into_option();
2118        self
2119    }
2120}
2121
2122crate::serde_util::default_on_null! {
2123    /// Response to `session/set_mode` method.
2124    #[serde_as]
2125    #[skip_serializing_none]
2126    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2127    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2128    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODE_METHOD_NAME)))]
2129    #[serde(rename_all = "camelCase")]
2130    #[non_exhaustive]
2131    pub struct SetSessionModeResponse {
2132        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2133        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2134        /// these keys.
2135        ///
2136        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2137        #[serde_as(deserialize_as = "DefaultOnError")]
2138        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2139        #[serde(default)]
2140        #[serde(rename = "_meta")]
2141        pub meta: Option<Meta>,
2142    }
2143}
2144
2145impl SetSessionModeResponse {
2146    /// Builds [`SetSessionModeResponse`] with the required response fields set; optional fields start unset or empty.
2147    #[must_use]
2148    pub fn new() -> Self {
2149        Self::default()
2150    }
2151
2152    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2153    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2154    /// these keys.
2155    ///
2156    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2157    #[must_use]
2158    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2159        self.meta = meta.into_option();
2160        self
2161    }
2162}
2163
2164// Session config options
2165
2166/// Unique identifier for a session configuration option.
2167#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2169#[serde(transparent)]
2170#[from(Arc<str>, String, &'static str)]
2171#[non_exhaustive]
2172pub struct SessionConfigId(pub Arc<str>);
2173
2174impl SessionConfigId {
2175    /// Wraps a protocol string as a typed [`SessionConfigId`].
2176    #[must_use]
2177    pub fn new(id: impl Into<Arc<str>>) -> Self {
2178        Self(id.into())
2179    }
2180}
2181
2182/// Unique identifier for a session configuration option value.
2183#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2184#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2185#[serde(transparent)]
2186#[from(Arc<str>, String, &'static str)]
2187#[non_exhaustive]
2188pub struct SessionConfigValueId(pub Arc<str>);
2189
2190impl SessionConfigValueId {
2191    /// Wraps a protocol string as a typed [`SessionConfigValueId`].
2192    #[must_use]
2193    pub fn new(id: impl Into<Arc<str>>) -> Self {
2194        Self(id.into())
2195    }
2196}
2197
2198/// Unique identifier for a session configuration option value group.
2199#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2201#[serde(transparent)]
2202#[from(Arc<str>, String, &'static str)]
2203#[non_exhaustive]
2204pub struct SessionConfigGroupId(pub Arc<str>);
2205
2206impl SessionConfigGroupId {
2207    /// Wraps a protocol string as a typed [`SessionConfigGroupId`].
2208    #[must_use]
2209    pub fn new(id: impl Into<Arc<str>>) -> Self {
2210        Self(id.into())
2211    }
2212}
2213
2214/// A possible value for a session configuration option.
2215#[serde_as]
2216#[skip_serializing_none]
2217#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2218#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2219#[serde(rename_all = "camelCase")]
2220#[non_exhaustive]
2221pub struct SessionConfigSelectOption {
2222    /// Unique identifier for this option value.
2223    pub value: SessionConfigValueId,
2224    /// Human-readable label for this option value.
2225    pub name: String,
2226    /// Optional description for this option value.
2227    #[serde_as(deserialize_as = "DefaultOnError")]
2228    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2229    #[serde(default)]
2230    pub description: Option<String>,
2231    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2232    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2233    /// these keys.
2234    ///
2235    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2236    #[serde_as(deserialize_as = "DefaultOnError")]
2237    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2238    #[serde(default)]
2239    #[serde(rename = "_meta")]
2240    pub meta: Option<Meta>,
2241}
2242
2243impl SessionConfigSelectOption {
2244    /// Builds [`SessionConfigSelectOption`] with the required fields set; optional fields start unset or empty.
2245    #[must_use]
2246    pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
2247        Self {
2248            value: value.into(),
2249            name: name.into(),
2250            description: None,
2251            meta: None,
2252        }
2253    }
2254
2255    /// Sets or clears the optional `description` field.
2256    #[must_use]
2257    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2258        self.description = description.into_option();
2259        self
2260    }
2261
2262    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2263    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2264    /// these keys.
2265    ///
2266    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2267    #[must_use]
2268    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2269        self.meta = meta.into_option();
2270        self
2271    }
2272}
2273
2274/// A group of possible values for a session configuration option.
2275#[serde_as]
2276#[skip_serializing_none]
2277#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2278#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2279#[serde(rename_all = "camelCase")]
2280#[non_exhaustive]
2281pub struct SessionConfigSelectGroup {
2282    /// Unique identifier for this group.
2283    pub group: SessionConfigGroupId,
2284    /// Human-readable label for this group.
2285    pub name: String,
2286    /// The set of option values in this group.
2287    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2288    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2289    pub options: Vec<SessionConfigSelectOption>,
2290    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2291    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2292    /// these keys.
2293    ///
2294    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2295    #[serde_as(deserialize_as = "DefaultOnError")]
2296    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2297    #[serde(default)]
2298    #[serde(rename = "_meta")]
2299    pub meta: Option<Meta>,
2300}
2301
2302impl SessionConfigSelectGroup {
2303    /// Builds [`SessionConfigSelectGroup`] with the required fields set; optional fields start unset or empty.
2304    #[must_use]
2305    pub fn new(
2306        group: impl Into<SessionConfigGroupId>,
2307        name: impl Into<String>,
2308        options: Vec<SessionConfigSelectOption>,
2309    ) -> Self {
2310        Self {
2311            group: group.into(),
2312            name: name.into(),
2313            options,
2314            meta: None,
2315        }
2316    }
2317
2318    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2319    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2320    /// these keys.
2321    ///
2322    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2323    #[must_use]
2324    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2325        self.meta = meta.into_option();
2326        self
2327    }
2328}
2329
2330/// Possible values for a session configuration option.
2331#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2332#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2333#[serde(untagged)]
2334#[non_exhaustive]
2335pub enum SessionConfigSelectOptions {
2336    /// A flat list of options with no grouping.
2337    Ungrouped(Vec<SessionConfigSelectOption>),
2338    /// A list of options grouped under headers.
2339    Grouped(Vec<SessionConfigSelectGroup>),
2340}
2341
2342impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2343    fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2344        SessionConfigSelectOptions::Ungrouped(options)
2345    }
2346}
2347
2348impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2349    fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2350        SessionConfigSelectOptions::Grouped(groups)
2351    }
2352}
2353
2354/// A single-value selector (dropdown) session configuration option payload.
2355#[skip_serializing_none]
2356#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2357#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2358#[serde(rename_all = "camelCase")]
2359#[non_exhaustive]
2360pub struct SessionConfigSelect {
2361    /// The currently selected value.
2362    pub current_value: SessionConfigValueId,
2363    /// The set of selectable options.
2364    pub options: SessionConfigSelectOptions,
2365}
2366
2367impl SessionConfigSelect {
2368    /// Builds [`SessionConfigSelect`] with the required fields set; optional fields start unset or empty.
2369    #[must_use]
2370    pub fn new(
2371        current_value: impl Into<SessionConfigValueId>,
2372        options: impl Into<SessionConfigSelectOptions>,
2373    ) -> Self {
2374        Self {
2375            current_value: current_value.into(),
2376            options: options.into(),
2377        }
2378    }
2379}
2380
2381/// A boolean on/off toggle session configuration option payload.
2382#[skip_serializing_none]
2383#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2384#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2385#[serde(rename_all = "camelCase")]
2386#[non_exhaustive]
2387pub struct SessionConfigBoolean {
2388    /// The current value of the boolean option.
2389    pub current_value: bool,
2390}
2391
2392impl SessionConfigBoolean {
2393    /// Builds [`SessionConfigBoolean`] with the required fields set; optional fields start unset or empty.
2394    #[must_use]
2395    pub fn new(current_value: bool) -> Self {
2396        Self { current_value }
2397    }
2398}
2399
2400/// Semantic category for a session configuration option.
2401///
2402/// This is intended to help Clients distinguish broadly common selectors (e.g. model selector vs
2403/// session mode selector vs thought/reasoning level) for UX purposes (keyboard shortcuts, icons,
2404/// placement). It MUST NOT be required for correctness. Clients MUST handle missing or unknown
2405/// categories gracefully.
2406///
2407/// Category names beginning with `_` are free for custom use, like other ACP extension methods.
2408/// Category names that do not begin with `_` are reserved for the ACP spec.
2409#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2410#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2411#[serde(rename_all = "snake_case")]
2412#[non_exhaustive]
2413pub enum SessionConfigOptionCategory {
2414    /// Session mode selector.
2415    Mode,
2416    /// Model selector.
2417    Model,
2418    /// Model-related configuration parameter.
2419    ModelConfig,
2420    /// Thought/reasoning level selector.
2421    ThoughtLevel,
2422    /// Unknown / uncategorized selector.
2423    #[serde(untagged)]
2424    Other(String),
2425}
2426
2427/// Type-specific session configuration option payload.
2428#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2429#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2430#[serde(tag = "type", rename_all = "snake_case")]
2431#[cfg_attr(feature = "schemars", schemars(extend("discriminator" = {"propertyName": "type"})))]
2432#[non_exhaustive]
2433pub enum SessionConfigKind {
2434    /// Single-value selector (dropdown).
2435    Select(SessionConfigSelect),
2436    /// Boolean on/off toggle.
2437    Boolean(SessionConfigBoolean),
2438}
2439
2440/// A session configuration option selector and its current state.
2441#[serde_as]
2442#[skip_serializing_none]
2443#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2444#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2445#[serde(rename_all = "camelCase")]
2446#[non_exhaustive]
2447pub struct SessionConfigOption {
2448    /// Unique identifier for the configuration option.
2449    pub id: SessionConfigId,
2450    /// Human-readable label for the option.
2451    pub name: String,
2452    /// Optional description for the Client to display to the user.
2453    #[serde_as(deserialize_as = "DefaultOnError")]
2454    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2455    #[serde(default)]
2456    pub description: Option<String>,
2457    /// Optional semantic category for this option (UX only).
2458    #[serde_as(deserialize_as = "DefaultOnError")]
2459    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2460    #[serde(default)]
2461    pub category: Option<SessionConfigOptionCategory>,
2462    /// Type-specific fields for this configuration option.
2463    #[serde(flatten)]
2464    pub kind: SessionConfigKind,
2465    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2466    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2467    /// these keys.
2468    ///
2469    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2470    #[serde_as(deserialize_as = "DefaultOnError")]
2471    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2472    #[serde(default)]
2473    #[serde(rename = "_meta")]
2474    pub meta: Option<Meta>,
2475}
2476
2477impl SessionConfigOption {
2478    /// Builds [`SessionConfigOption`] with the required fields set; optional fields start unset or empty.
2479    #[must_use]
2480    pub fn new(
2481        id: impl Into<SessionConfigId>,
2482        name: impl Into<String>,
2483        kind: SessionConfigKind,
2484    ) -> Self {
2485        Self {
2486            id: id.into(),
2487            name: name.into(),
2488            description: None,
2489            category: None,
2490            kind,
2491            meta: None,
2492        }
2493    }
2494
2495    /// Builds a select-style session configuration option with its current value and choices.
2496    #[must_use]
2497    pub fn select(
2498        id: impl Into<SessionConfigId>,
2499        name: impl Into<String>,
2500        current_value: impl Into<SessionConfigValueId>,
2501        options: impl Into<SessionConfigSelectOptions>,
2502    ) -> Self {
2503        Self::new(
2504            id,
2505            name,
2506            SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2507        )
2508    }
2509
2510    /// Builds a boolean-style session configuration option with its current value.
2511    #[must_use]
2512    pub fn boolean(
2513        id: impl Into<SessionConfigId>,
2514        name: impl Into<String>,
2515        current_value: bool,
2516    ) -> Self {
2517        Self::new(
2518            id,
2519            name,
2520            SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2521        )
2522    }
2523
2524    /// Sets or clears the optional `description` field.
2525    #[must_use]
2526    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2527        self.description = description.into_option();
2528        self
2529    }
2530
2531    /// Sets or clears the optional `category` field.
2532    #[must_use]
2533    pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2534        self.category = category.into_option();
2535        self
2536    }
2537
2538    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2539    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2540    /// these keys.
2541    ///
2542    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2543    #[must_use]
2544    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2545        self.meta = meta.into_option();
2546        self
2547    }
2548}
2549
2550/// The value to set for a session configuration option.
2551///
2552/// The `type` field acts as the discriminator in the serialized JSON form.
2553/// When no `type` is present, the value is treated as a [`SessionConfigValueId`]
2554/// via the [`ValueId`](Self::ValueId) fallback variant.
2555///
2556/// The `type` discriminator describes the *shape* of the value, not the option
2557/// kind. For example every option kind that picks from a list of ids
2558/// (`select`, `radio`, …) would use [`ValueId`](Self::ValueId), while a
2559/// future freeform text option would get its own variant.
2560#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2561#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2562#[serde(tag = "type", rename_all = "snake_case")]
2563#[non_exhaustive]
2564pub enum SessionConfigOptionValue {
2565    /// A boolean value (`type: "boolean"`).
2566    Boolean {
2567        /// The boolean value.
2568        value: bool,
2569    },
2570    /// A [`SessionConfigValueId`] string value.
2571    ///
2572    /// This is the default when `type` is absent on the wire. Unknown `type`
2573    /// values with string payloads also gracefully deserialize into this
2574    /// variant.
2575    #[serde(untagged)]
2576    ValueId {
2577        /// The value ID.
2578        value: SessionConfigValueId,
2579    },
2580}
2581
2582impl SessionConfigOptionValue {
2583    /// Create a value-id option value (used by `select` and other id-based option types).
2584    #[must_use]
2585    pub fn value_id(id: impl Into<SessionConfigValueId>) -> Self {
2586        Self::ValueId { value: id.into() }
2587    }
2588
2589    /// Create a boolean option value.
2590    #[must_use]
2591    pub fn boolean(val: bool) -> Self {
2592        Self::Boolean { value: val }
2593    }
2594
2595    /// Return the inner [`SessionConfigValueId`] if this is a
2596    /// [`ValueId`](Self::ValueId) value.
2597    #[must_use]
2598    pub fn as_value_id(&self) -> Option<&SessionConfigValueId> {
2599        match self {
2600            Self::ValueId { value } => Some(value),
2601            _ => None,
2602        }
2603    }
2604
2605    /// Return the inner [`bool`] if this is a [`Boolean`](Self::Boolean) value.
2606    #[must_use]
2607    pub fn as_bool(&self) -> Option<bool> {
2608        match self {
2609            Self::Boolean { value } => Some(*value),
2610            _ => None,
2611        }
2612    }
2613}
2614
2615impl From<SessionConfigValueId> for SessionConfigOptionValue {
2616    fn from(value: SessionConfigValueId) -> Self {
2617        Self::ValueId { value }
2618    }
2619}
2620
2621impl From<bool> for SessionConfigOptionValue {
2622    fn from(value: bool) -> Self {
2623        Self::Boolean { value }
2624    }
2625}
2626
2627impl From<&str> for SessionConfigOptionValue {
2628    fn from(value: &str) -> Self {
2629        Self::ValueId {
2630            value: SessionConfigValueId::new(value),
2631        }
2632    }
2633}
2634
2635/// Request parameters for setting a session configuration option.
2636#[serde_as]
2637#[skip_serializing_none]
2638#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2639#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2640#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2641#[serde(rename_all = "camelCase")]
2642#[non_exhaustive]
2643pub struct SetSessionConfigOptionRequest {
2644    /// The ID of the session to set the configuration option for.
2645    pub session_id: SessionId,
2646    /// The ID of the configuration option to set.
2647    pub config_id: SessionConfigId,
2648    /// The value to set, including a `type` discriminator and the raw `value`.
2649    ///
2650    /// When `type` is absent on the wire, defaults to treating the value as a
2651    /// [`SessionConfigValueId`] for `select` options.
2652    #[serde(flatten)]
2653    pub value: SessionConfigOptionValue,
2654    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2655    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2656    /// these keys.
2657    ///
2658    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2659    #[serde_as(deserialize_as = "DefaultOnError")]
2660    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2661    #[serde(default)]
2662    #[serde(rename = "_meta")]
2663    pub meta: Option<Meta>,
2664}
2665
2666impl SetSessionConfigOptionRequest {
2667    /// Builds [`SetSessionConfigOptionRequest`] with the required request fields set; optional fields start unset or empty.
2668    #[must_use]
2669    pub fn new(
2670        session_id: impl Into<SessionId>,
2671        config_id: impl Into<SessionConfigId>,
2672        value: impl Into<SessionConfigOptionValue>,
2673    ) -> Self {
2674        Self {
2675            session_id: session_id.into(),
2676            config_id: config_id.into(),
2677            value: value.into(),
2678            meta: None,
2679        }
2680    }
2681
2682    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2683    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2684    /// these keys.
2685    ///
2686    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2687    #[must_use]
2688    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2689        self.meta = meta.into_option();
2690        self
2691    }
2692}
2693
2694/// Response to `session/set_config_option` method.
2695#[serde_as]
2696#[skip_serializing_none]
2697#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2698#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2699#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2700#[serde(rename_all = "camelCase")]
2701#[non_exhaustive]
2702pub struct SetSessionConfigOptionResponse {
2703    /// The full set of configuration options and their current values.
2704    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2705    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2706    pub config_options: Vec<SessionConfigOption>,
2707    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2708    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2709    /// these keys.
2710    ///
2711    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2712    #[serde_as(deserialize_as = "DefaultOnError")]
2713    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2714    #[serde(default)]
2715    #[serde(rename = "_meta")]
2716    pub meta: Option<Meta>,
2717}
2718
2719impl SetSessionConfigOptionResponse {
2720    /// Builds [`SetSessionConfigOptionResponse`] with the required response fields set; optional fields start unset or empty.
2721    #[must_use]
2722    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2723        Self {
2724            config_options,
2725            meta: None,
2726        }
2727    }
2728
2729    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2730    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2731    /// these keys.
2732    ///
2733    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2734    #[must_use]
2735    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2736        self.meta = meta.into_option();
2737        self
2738    }
2739}
2740
2741// MCP
2742
2743/// Configuration for connecting to an MCP (Model Context Protocol) server.
2744///
2745/// MCP servers provide tools and context that the agent can use when
2746/// processing prompts.
2747///
2748/// See protocol docs: [MCP Servers](https://agentclientprotocol.com/protocol/session-setup#mcp-servers)
2749#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2750#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2751#[serde(tag = "type", rename_all = "snake_case")]
2752#[non_exhaustive]
2753pub enum McpServer {
2754    /// HTTP transport configuration
2755    ///
2756    /// Only available when the Agent capabilities indicate `mcp_capabilities.http` is `true`.
2757    Http(McpServerHttp),
2758    /// SSE transport configuration
2759    ///
2760    /// Only available when the Agent capabilities indicate `mcp_capabilities.sse` is `true`.
2761    Sse(McpServerSse),
2762    /// **UNSTABLE**
2763    ///
2764    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2765    ///
2766    /// ACP transport configuration
2767    ///
2768    /// Only available when the Agent capabilities indicate `mcp_capabilities.acp` is `true`.
2769    /// The MCP server is provided by an ACP component and communicates over the ACP channel.
2770    #[cfg(feature = "unstable_mcp_over_acp")]
2771    Acp(McpServerAcp),
2772    /// Stdio transport configuration
2773    ///
2774    /// All Agents MUST support this transport.
2775    #[serde(untagged)]
2776    Stdio(McpServerStdio),
2777}
2778
2779/// HTTP transport configuration for MCP.
2780#[serde_as]
2781#[skip_serializing_none]
2782#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2783#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2784#[serde(rename_all = "camelCase")]
2785#[non_exhaustive]
2786pub struct McpServerHttp {
2787    /// Human-readable name identifying this MCP server.
2788    pub name: String,
2789    /// URL to the MCP server.
2790    pub url: String,
2791    /// HTTP headers to set when making requests to the MCP server.
2792    #[serde_as(deserialize_as = "DefaultOnNull")]
2793    pub headers: Vec<HttpHeader>,
2794    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2795    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2796    /// these keys.
2797    ///
2798    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2799    #[serde_as(deserialize_as = "DefaultOnError")]
2800    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2801    #[serde(default)]
2802    #[serde(rename = "_meta")]
2803    pub meta: Option<Meta>,
2804}
2805
2806impl McpServerHttp {
2807    /// Builds [`McpServerHttp`] with the required fields set; optional fields start unset or empty.
2808    #[must_use]
2809    pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2810        Self {
2811            name: name.into(),
2812            url: url.into(),
2813            headers: Vec::new(),
2814            meta: None,
2815        }
2816    }
2817
2818    /// HTTP headers to set when making requests to the MCP server.
2819    #[must_use]
2820    pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2821        self.headers = headers;
2822        self
2823    }
2824
2825    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2826    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2827    /// these keys.
2828    ///
2829    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2830    #[must_use]
2831    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2832        self.meta = meta.into_option();
2833        self
2834    }
2835}
2836
2837/// SSE transport configuration for MCP.
2838#[serde_as]
2839#[skip_serializing_none]
2840#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2841#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2842#[serde(rename_all = "camelCase")]
2843#[non_exhaustive]
2844pub struct McpServerSse {
2845    /// Human-readable name identifying this MCP server.
2846    pub name: String,
2847    /// URL to the MCP server.
2848    pub url: String,
2849    /// HTTP headers to set when making requests to the MCP server.
2850    #[serde_as(deserialize_as = "DefaultOnNull")]
2851    pub headers: Vec<HttpHeader>,
2852    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2853    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2854    /// these keys.
2855    ///
2856    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2857    #[serde_as(deserialize_as = "DefaultOnError")]
2858    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2859    #[serde(default)]
2860    #[serde(rename = "_meta")]
2861    pub meta: Option<Meta>,
2862}
2863
2864impl McpServerSse {
2865    /// Builds [`McpServerSse`] with the required fields set; optional fields start unset or empty.
2866    #[must_use]
2867    pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2868        Self {
2869            name: name.into(),
2870            url: url.into(),
2871            headers: Vec::new(),
2872            meta: None,
2873        }
2874    }
2875
2876    /// HTTP headers to set when making requests to the MCP server.
2877    #[must_use]
2878    pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2879        self.headers = headers;
2880        self
2881    }
2882
2883    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2884    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2885    /// these keys.
2886    ///
2887    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2888    #[must_use]
2889    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2890        self.meta = meta.into_option();
2891        self
2892    }
2893}
2894
2895/// **UNSTABLE**
2896///
2897/// This capability is not part of the spec yet, and may be removed or changed at any point.
2898///
2899/// Unique identifier for an MCP server using the ACP transport.
2900///
2901/// The value is opaque and generated by the ACP component providing the MCP server. It is
2902/// used by `mcp/message` to route requests to the component that declared the server.
2903#[cfg(feature = "unstable_mcp_over_acp")]
2904#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2905#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2906#[serde(transparent)]
2907#[from(Arc<str>, String, &'static str)]
2908#[non_exhaustive]
2909pub struct McpServerAcpId(pub Arc<str>);
2910
2911#[cfg(feature = "unstable_mcp_over_acp")]
2912impl McpServerAcpId {
2913    /// Wraps a protocol string as a typed [`McpServerAcpId`].
2914    #[must_use]
2915    pub fn new(id: impl Into<Arc<str>>) -> Self {
2916        Self(id.into())
2917    }
2918}
2919
2920/// **UNSTABLE**
2921///
2922/// This capability is not part of the spec yet, and may be removed or changed at any point.
2923///
2924/// ACP transport configuration for MCP.
2925///
2926/// The MCP server is provided by an ACP component and communicates over the ACP channel
2927/// using `mcp/message`.
2928#[serde_as]
2929#[skip_serializing_none]
2930#[cfg(feature = "unstable_mcp_over_acp")]
2931#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2932#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2933#[serde(rename_all = "camelCase")]
2934#[non_exhaustive]
2935pub struct McpServerAcp {
2936    /// Human-readable name identifying this MCP server.
2937    pub name: String,
2938    /// Unique identifier for this MCP server, generated by the component providing it.
2939    ///
2940    /// Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible
2941    /// on the same ACP connection.
2942    pub server_id: McpServerAcpId,
2943    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2944    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2945    /// these keys.
2946    ///
2947    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2948    #[serde_as(deserialize_as = "DefaultOnError")]
2949    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2950    #[serde(default)]
2951    #[serde(rename = "_meta")]
2952    pub meta: Option<Meta>,
2953}
2954
2955#[cfg(feature = "unstable_mcp_over_acp")]
2956impl McpServerAcp {
2957    /// Builds [`McpServerAcp`] with the required fields set; optional fields start unset or empty.
2958    #[must_use]
2959    pub fn new(name: impl Into<String>, id: impl Into<McpServerAcpId>) -> Self {
2960        Self {
2961            name: name.into(),
2962            server_id: id.into(),
2963            meta: None,
2964        }
2965    }
2966
2967    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2968    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2969    /// these keys.
2970    ///
2971    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2972    #[must_use]
2973    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2974        self.meta = meta.into_option();
2975        self
2976    }
2977}
2978
2979/// Stdio transport configuration for MCP.
2980#[serde_as]
2981#[skip_serializing_none]
2982#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2983#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2984#[serde(rename_all = "camelCase")]
2985#[non_exhaustive]
2986pub struct McpServerStdio {
2987    /// Human-readable name identifying this MCP server.
2988    pub name: String,
2989    /// Absolute path to the MCP server executable.
2990    pub command: PathBuf,
2991    /// Command-line arguments to pass to the MCP server.
2992    #[serde_as(deserialize_as = "DefaultOnNull")]
2993    pub args: Vec<String>,
2994    /// Environment variables to set when launching the MCP server.
2995    #[serde_as(deserialize_as = "DefaultOnNull")]
2996    pub env: Vec<EnvVariable>,
2997    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2998    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2999    /// these keys.
3000    ///
3001    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3002    #[serde_as(deserialize_as = "DefaultOnError")]
3003    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3004    #[serde(default)]
3005    #[serde(rename = "_meta")]
3006    pub meta: Option<Meta>,
3007}
3008
3009impl McpServerStdio {
3010    /// Builds [`McpServerStdio`] with the required fields set; optional fields start unset or empty.
3011    #[must_use]
3012    pub fn new(name: impl Into<String>, command: impl Into<PathBuf>) -> Self {
3013        Self {
3014            name: name.into(),
3015            command: command.into(),
3016            args: Vec::new(),
3017            env: Vec::new(),
3018            meta: None,
3019        }
3020    }
3021
3022    /// Command-line arguments to pass to the MCP server.
3023    #[must_use]
3024    pub fn args(mut self, args: Vec<String>) -> Self {
3025        self.args = args;
3026        self
3027    }
3028
3029    /// Environment variables to set when launching the MCP server.
3030    #[must_use]
3031    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
3032        self.env = env;
3033        self
3034    }
3035
3036    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3037    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3038    /// these keys.
3039    ///
3040    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3041    #[must_use]
3042    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3043        self.meta = meta.into_option();
3044        self
3045    }
3046}
3047
3048/// An environment variable to set when launching an MCP server.
3049#[serde_as]
3050#[skip_serializing_none]
3051#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3052#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3053#[serde(rename_all = "camelCase")]
3054#[non_exhaustive]
3055pub struct EnvVariable {
3056    /// The name of the environment variable.
3057    pub name: String,
3058    /// The value to set for the environment variable.
3059    pub value: String,
3060    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3061    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3062    /// these keys.
3063    ///
3064    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3065    #[serde_as(deserialize_as = "DefaultOnError")]
3066    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3067    #[serde(default)]
3068    #[serde(rename = "_meta")]
3069    pub meta: Option<Meta>,
3070}
3071
3072impl EnvVariable {
3073    /// Builds [`EnvVariable`] with the required fields set; optional fields start unset or empty.
3074    #[must_use]
3075    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3076        Self {
3077            name: name.into(),
3078            value: value.into(),
3079            meta: None,
3080        }
3081    }
3082
3083    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3084    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3085    /// these keys.
3086    ///
3087    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3088    #[must_use]
3089    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3090        self.meta = meta.into_option();
3091        self
3092    }
3093}
3094
3095/// An HTTP header to set when making requests to the MCP server.
3096#[serde_as]
3097#[skip_serializing_none]
3098#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3099#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3100#[serde(rename_all = "camelCase")]
3101#[non_exhaustive]
3102pub struct HttpHeader {
3103    /// The name of the HTTP header.
3104    pub name: String,
3105    /// The value to set for the HTTP header.
3106    pub value: String,
3107    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3108    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3109    /// these keys.
3110    ///
3111    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3112    #[serde_as(deserialize_as = "DefaultOnError")]
3113    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3114    #[serde(default)]
3115    #[serde(rename = "_meta")]
3116    pub meta: Option<Meta>,
3117}
3118
3119impl HttpHeader {
3120    /// Builds [`HttpHeader`] with the required fields set; optional fields start unset or empty.
3121    #[must_use]
3122    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3123        Self {
3124            name: name.into(),
3125            value: value.into(),
3126            meta: None,
3127        }
3128    }
3129
3130    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3131    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3132    /// these keys.
3133    ///
3134    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3135    #[must_use]
3136    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3137        self.meta = meta.into_option();
3138        self
3139    }
3140}
3141
3142// System prompt
3143
3144/// **UNSTABLE**
3145///
3146/// This capability is not part of the spec yet, and may be removed or changed at any point.
3147///
3148/// Client-provided system-level instructions for a session.
3149///
3150/// Clients MUST only send this when the Agent advertises
3151/// `sessionCapabilities.systemPrompt`. Agents that cannot apply it, for example
3152/// because of an unadvertised mode or an unsupported content block type, MUST
3153/// return an error instead of creating, loading, resuming, or forking the
3154/// session.
3155#[cfg(feature = "unstable_session_system_prompt")]
3156#[serde_as]
3157#[skip_serializing_none]
3158#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3159#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3160#[serde(rename_all = "camelCase")]
3161#[non_exhaustive]
3162pub struct SystemPrompt {
3163    /// How the Agent combines `content` with its own instructions.
3164    ///
3165    /// Required on the wire, so every request states its mode. In Rust,
3166    /// [`SystemPrompt::new`] starts it as [`SystemPromptMode::Append`]. Unknown
3167    /// values parse as [`SystemPromptMode::Other`]; Agents MUST return an error
3168    /// for modes they do not support.
3169    pub mode: SystemPromptMode,
3170    /// The system prompt content.
3171    ///
3172    /// Agents advertising the capability MUST accept [`ContentBlock::Text`].
3173    /// Clients MUST NOT send other content block types until a future
3174    /// capability allows them, and Agents MUST return an error for content
3175    /// blocks they do not support.
3176    pub content: Vec<ContentBlock>,
3177    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3178    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3179    /// these keys.
3180    ///
3181    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3182    #[serde_as(deserialize_as = "DefaultOnError")]
3183    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3184    #[serde(default)]
3185    #[serde(rename = "_meta")]
3186    pub meta: Option<Meta>,
3187}
3188
3189#[cfg(feature = "unstable_session_system_prompt")]
3190impl SystemPrompt {
3191    /// Builds [`SystemPrompt`] with the required fields set; optional fields start unset or empty.
3192    ///
3193    /// `mode` starts as [`SystemPromptMode::Append`]; use [`SystemPrompt::mode`]
3194    /// to request another mode.
3195    #[must_use]
3196    pub fn new(content: Vec<ContentBlock>) -> Self {
3197        Self {
3198            mode: SystemPromptMode::default(),
3199            content,
3200            meta: None,
3201        }
3202    }
3203
3204    /// How the Agent combines `content` with its own instructions.
3205    #[must_use]
3206    pub fn mode(mut self, mode: SystemPromptMode) -> Self {
3207        self.mode = mode;
3208        self
3209    }
3210
3211    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3212    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3213    /// these keys.
3214    ///
3215    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3216    #[must_use]
3217    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3218        self.meta = meta.into_option();
3219        self
3220    }
3221}
3222
3223/// **UNSTABLE**
3224///
3225/// This capability is not part of the spec yet, and may be removed or changed at any point.
3226///
3227/// How the Agent combines a client-provided system prompt with its own instructions.
3228///
3229/// Unknown modes parse as [`SystemPromptMode::Other`] so they can be stored,
3230/// proxied, or forwarded. An Agent MUST return an error for a mode it does not
3231/// support rather than treating it as `append` or `override`.
3232#[cfg(feature = "unstable_session_system_prompt")]
3233#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3234#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
3235#[serde(rename_all = "snake_case")]
3236#[non_exhaustive]
3237pub enum SystemPromptMode {
3238    /// Add the content to the Agent's own instructions rather than replacing them.
3239    ///
3240    /// Every Agent that advertises `sessionCapabilities.systemPrompt` supports this mode.
3241    /// Agents SHOULD give the content system-level precedence, for example in the
3242    /// model's system prompt or developer instructions rather than in user-turn
3243    /// content. Where it is placed and how it is formatted is implementation-defined.
3244    #[default]
3245    Append,
3246    /// Replace the Agent's built-in default instructions with the content.
3247    ///
3248    /// This does not change tool availability, MCP servers, permissions, or session
3249    /// configuration. Context the Agent supplies from the workspace or environment
3250    /// MAY still be included. Clients that use this mode take responsibility for any
3251    /// tool guidance or safety instructions the Agent's default instructions
3252    /// provided, and MUST only use it when the Agent advertises
3253    /// `sessionCapabilities.systemPrompt.mode.override`.
3254    Override,
3255    /// Custom or future system prompt mode.
3256    ///
3257    /// Values beginning with `_` are reserved for implementation-specific
3258    /// extensions. Unknown values that do not begin with `_` are reserved for
3259    /// future ACP variants.
3260    #[serde(untagged)]
3261    Other(String),
3262}
3263
3264// Prompt
3265
3266/// Request parameters for sending a user prompt to the agent.
3267///
3268/// Contains the user's message and any additional context.
3269///
3270/// See protocol docs: [User Message](https://agentclientprotocol.com/protocol/prompt-turn#1-user-message)
3271#[serde_as]
3272#[skip_serializing_none]
3273#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3274#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3275#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3276#[serde(rename_all = "camelCase")]
3277#[non_exhaustive]
3278pub struct PromptRequest {
3279    /// The ID of the session to send this user message to
3280    pub session_id: SessionId,
3281    /// The blocks of content that compose the user's message.
3282    ///
3283    /// As a baseline, the Agent MUST support [`ContentBlock::Text`] and [`ContentBlock::ResourceLink`],
3284    /// while other variants are optionally enabled via [`PromptCapabilities`].
3285    ///
3286    /// The Client MUST adapt its interface according to [`PromptCapabilities`].
3287    ///
3288    /// The client MAY include referenced pieces of context as either
3289    /// [`ContentBlock::Resource`] or [`ContentBlock::ResourceLink`].
3290    ///
3291    /// When available, [`ContentBlock::Resource`] is preferred
3292    /// as it avoids extra round-trips and allows the message to include
3293    /// pieces of context from sources the agent may not have access to.
3294    pub prompt: Vec<ContentBlock>,
3295    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3296    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3297    /// these keys.
3298    ///
3299    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3300    #[serde_as(deserialize_as = "DefaultOnError")]
3301    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3302    #[serde(default)]
3303    #[serde(rename = "_meta")]
3304    pub meta: Option<Meta>,
3305}
3306
3307impl PromptRequest {
3308    /// Builds [`PromptRequest`] with the required request fields set; optional fields start unset or empty.
3309    #[must_use]
3310    pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
3311        Self {
3312            session_id: session_id.into(),
3313            prompt,
3314            meta: None,
3315        }
3316    }
3317
3318    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3319    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3320    /// these keys.
3321    ///
3322    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3323    #[must_use]
3324    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3325        self.meta = meta.into_option();
3326        self
3327    }
3328}
3329
3330/// Response from processing a user prompt.
3331///
3332/// See protocol docs: [Check for Completion](https://agentclientprotocol.com/protocol/prompt-turn#4-check-for-completion)
3333#[serde_as]
3334#[skip_serializing_none]
3335#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3336#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3337#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3338#[serde(rename_all = "camelCase")]
3339#[non_exhaustive]
3340pub struct PromptResponse {
3341    /// Indicates why the agent stopped processing the turn.
3342    pub stop_reason: StopReason,
3343    /// **UNSTABLE**
3344    ///
3345    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3346    ///
3347    /// Token usage for this turn (optional).
3348    #[cfg(feature = "unstable_end_turn_token_usage")]
3349    #[serde_as(deserialize_as = "DefaultOnError")]
3350    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3351    #[serde(default)]
3352    pub usage: Option<Usage>,
3353    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3354    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3355    /// these keys.
3356    ///
3357    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3358    #[serde_as(deserialize_as = "DefaultOnError")]
3359    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3360    #[serde(default)]
3361    #[serde(rename = "_meta")]
3362    pub meta: Option<Meta>,
3363}
3364
3365impl PromptResponse {
3366    /// Builds [`PromptResponse`] with the required response fields set; optional fields start unset or empty.
3367    #[must_use]
3368    pub fn new(stop_reason: StopReason) -> Self {
3369        Self {
3370            stop_reason,
3371            #[cfg(feature = "unstable_end_turn_token_usage")]
3372            usage: None,
3373            meta: None,
3374        }
3375    }
3376
3377    /// **UNSTABLE**
3378    ///
3379    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3380    ///
3381    /// Token usage for this turn.
3382    #[cfg(feature = "unstable_end_turn_token_usage")]
3383    #[must_use]
3384    pub fn usage(mut self, usage: impl IntoOption<Usage>) -> Self {
3385        self.usage = usage.into_option();
3386        self
3387    }
3388
3389    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3390    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3391    /// these keys.
3392    ///
3393    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3394    #[must_use]
3395    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3396        self.meta = meta.into_option();
3397        self
3398    }
3399}
3400
3401/// Reasons why an agent stops processing a prompt turn.
3402///
3403/// See protocol docs: [Stop Reasons](https://agentclientprotocol.com/protocol/prompt-turn#stop-reasons)
3404#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3405#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
3406#[serde(rename_all = "snake_case")]
3407#[non_exhaustive]
3408pub enum StopReason {
3409    /// The turn ended successfully.
3410    EndTurn,
3411    /// The turn ended because the agent reached the maximum number of tokens.
3412    MaxTokens,
3413    /// The turn ended because the agent reached the maximum number of allowed
3414    /// agent requests between user turns.
3415    MaxTurnRequests,
3416    /// The turn ended because the agent refused to continue. The user prompt
3417    /// and everything that comes after it won't be included in the next
3418    /// prompt, so this should be reflected in the UI.
3419    Refusal,
3420    /// The turn was cancelled by the client via `session/cancel`.
3421    ///
3422    /// This stop reason MUST be returned when the client sends a `session/cancel`
3423    /// notification, even if the cancellation causes exceptions in underlying operations.
3424    /// Agents should catch these exceptions and return this semantically meaningful
3425    /// response to confirm successful cancellation.
3426    Cancelled,
3427}
3428
3429/// **UNSTABLE**
3430///
3431/// This capability is not part of the spec yet, and may be removed or changed at any point.
3432///
3433/// Token usage information for a prompt turn.
3434#[cfg(feature = "unstable_end_turn_token_usage")]
3435#[serde_as]
3436#[skip_serializing_none]
3437#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3438#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3439#[serde(rename_all = "camelCase")]
3440#[non_exhaustive]
3441pub struct Usage {
3442    /// Sum of all token types across session.
3443    pub total_tokens: u64,
3444    /// Total input tokens across all turns.
3445    pub input_tokens: u64,
3446    /// Total output tokens across all turns.
3447    pub output_tokens: u64,
3448    /// Total thought/reasoning tokens
3449    #[serde_as(deserialize_as = "DefaultOnError")]
3450    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3451    #[serde(default)]
3452    pub thought_tokens: Option<u64>,
3453    /// Total cache read tokens.
3454    #[serde_as(deserialize_as = "DefaultOnError")]
3455    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3456    #[serde(default)]
3457    pub cached_read_tokens: Option<u64>,
3458    /// Total cache write tokens.
3459    #[serde_as(deserialize_as = "DefaultOnError")]
3460    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3461    #[serde(default)]
3462    pub cached_write_tokens: Option<u64>,
3463    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3464    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3465    /// these keys.
3466    ///
3467    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3468    #[serde_as(deserialize_as = "DefaultOnError")]
3469    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3470    #[serde(default)]
3471    #[serde(rename = "_meta")]
3472    pub meta: Option<Meta>,
3473}
3474
3475#[cfg(feature = "unstable_end_turn_token_usage")]
3476impl Usage {
3477    /// Builds [`Usage`] with the required fields set; optional fields start unset or empty.
3478    #[must_use]
3479    pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
3480        Self {
3481            total_tokens,
3482            input_tokens,
3483            output_tokens,
3484            thought_tokens: None,
3485            cached_read_tokens: None,
3486            cached_write_tokens: None,
3487            meta: None,
3488        }
3489    }
3490
3491    /// Total thought/reasoning tokens
3492    #[must_use]
3493    pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
3494        self.thought_tokens = thought_tokens.into_option();
3495        self
3496    }
3497
3498    /// Total cache read tokens.
3499    #[must_use]
3500    pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
3501        self.cached_read_tokens = cached_read_tokens.into_option();
3502        self
3503    }
3504
3505    /// Total cache write tokens.
3506    #[must_use]
3507    pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
3508        self.cached_write_tokens = cached_write_tokens.into_option();
3509        self
3510    }
3511
3512    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3513    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3514    /// these keys.
3515    ///
3516    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3517    #[must_use]
3518    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3519        self.meta = meta.into_option();
3520        self
3521    }
3522}
3523
3524// Providers
3525
3526/// **UNSTABLE**
3527///
3528/// This capability is not part of the spec yet, and may be removed or changed at any point.
3529///
3530/// Well-known API protocol identifiers for LLM providers.
3531///
3532/// Agents and clients MUST handle unknown protocol identifiers gracefully.
3533///
3534/// Protocol names beginning with `_` are free for custom use, like other ACP extension methods.
3535/// Protocol names that do not begin with `_` are reserved for the ACP spec.
3536#[cfg(feature = "unstable_llm_providers")]
3537#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3538#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3539#[serde(rename_all = "snake_case")]
3540#[non_exhaustive]
3541#[expect(clippy::doc_markdown)]
3542pub enum LlmProtocol {
3543    /// Anthropic API protocol.
3544    Anthropic,
3545    /// OpenAI API protocol.
3546    #[serde(rename = "openai")]
3547    OpenAi,
3548    /// Azure OpenAI API protocol.
3549    Azure,
3550    /// Google Vertex AI API protocol.
3551    Vertex,
3552    /// AWS Bedrock API protocol.
3553    Bedrock,
3554    /// Unknown or custom protocol.
3555    #[serde(untagged)]
3556    Other(String),
3557}
3558
3559/// **UNSTABLE**
3560///
3561/// This capability is not part of the spec yet, and may be removed or changed at any point.
3562///
3563/// Current effective non-secret routing configuration for a provider.
3564#[cfg(feature = "unstable_llm_providers")]
3565#[serde_as]
3566#[skip_serializing_none]
3567#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3568#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3569#[serde(rename_all = "camelCase")]
3570#[non_exhaustive]
3571pub struct ProviderCurrentConfig {
3572    /// Protocol currently used by this provider.
3573    pub api_type: LlmProtocol,
3574    /// Base URL currently used by this provider.
3575    pub base_url: String,
3576    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3577    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3578    /// these keys.
3579    ///
3580    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3581    #[serde_as(deserialize_as = "DefaultOnError")]
3582    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3583    #[serde(default)]
3584    #[serde(rename = "_meta")]
3585    pub meta: Option<Meta>,
3586}
3587
3588#[cfg(feature = "unstable_llm_providers")]
3589impl ProviderCurrentConfig {
3590    /// Builds [`ProviderCurrentConfig`] with the required fields set; optional fields start unset or empty.
3591    #[must_use]
3592    pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3593        Self {
3594            api_type,
3595            base_url: base_url.into(),
3596            meta: None,
3597        }
3598    }
3599
3600    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3601    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3602    /// these keys.
3603    ///
3604    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3605    #[must_use]
3606    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3607        self.meta = meta.into_option();
3608        self
3609    }
3610}
3611
3612/// **UNSTABLE**
3613///
3614/// This capability is not part of the spec yet, and may be removed or changed at any point.
3615///
3616/// Unique identifier for a configurable LLM provider.
3617#[cfg(feature = "unstable_llm_providers")]
3618#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3619#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
3620#[serde(transparent)]
3621#[from(Arc<str>, String, &'static str)]
3622#[non_exhaustive]
3623pub struct ProviderId(pub Arc<str>);
3624
3625#[cfg(feature = "unstable_llm_providers")]
3626impl ProviderId {
3627    /// Wraps a protocol string as a typed [`ProviderId`].
3628    #[must_use]
3629    pub fn new(id: impl Into<Arc<str>>) -> Self {
3630        Self(id.into())
3631    }
3632}
3633
3634/// **UNSTABLE**
3635///
3636/// This capability is not part of the spec yet, and may be removed or changed at any point.
3637///
3638/// Information about a configurable LLM provider.
3639#[cfg(feature = "unstable_llm_providers")]
3640#[serde_as]
3641#[skip_serializing_none]
3642#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3643#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3644#[serde(rename_all = "camelCase")]
3645#[non_exhaustive]
3646pub struct ProviderInfo {
3647    /// Provider identifier, for example "main" or "openai".
3648    pub provider_id: ProviderId,
3649    /// Supported protocol types for this provider.
3650    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
3651    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3652    pub supported: Vec<LlmProtocol>,
3653    /// Whether this provider is mandatory and cannot be disabled via `providers/disable`.
3654    /// If true, clients must not call `providers/disable` for this provider ID.
3655    pub required: bool,
3656    /// Current effective non-secret routing config.
3657    /// Null or omitted means provider is disabled.
3658    #[serde(default)]
3659    pub current: Option<ProviderCurrentConfig>,
3660    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3661    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3662    /// these keys.
3663    ///
3664    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3665    #[serde_as(deserialize_as = "DefaultOnError")]
3666    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3667    #[serde(default)]
3668    #[serde(rename = "_meta")]
3669    pub meta: Option<Meta>,
3670}
3671
3672#[cfg(feature = "unstable_llm_providers")]
3673impl ProviderInfo {
3674    /// Builds [`ProviderInfo`] with the required fields set; optional fields start unset or empty.
3675    #[must_use]
3676    pub fn new(
3677        provider_id: impl Into<ProviderId>,
3678        supported: Vec<LlmProtocol>,
3679        required: bool,
3680        current: impl IntoOption<ProviderCurrentConfig>,
3681    ) -> Self {
3682        Self {
3683            provider_id: provider_id.into(),
3684            supported,
3685            required,
3686            current: current.into_option(),
3687            meta: None,
3688        }
3689    }
3690
3691    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3692    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3693    /// these keys.
3694    ///
3695    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3696    #[must_use]
3697    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3698        self.meta = meta.into_option();
3699        self
3700    }
3701}
3702
3703#[cfg(feature = "unstable_llm_providers")]
3704crate::serde_util::default_on_null! {
3705    /// **UNSTABLE**
3706    ///
3707    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3708    ///
3709    /// Request parameters for `providers/list`.
3710    #[serde_as]
3711    #[skip_serializing_none]
3712    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3713    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3714    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3715    #[serde(rename_all = "camelCase")]
3716    #[non_exhaustive]
3717    pub struct ListProvidersRequest {
3718        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3719        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3720        /// these keys.
3721        ///
3722        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3723        #[serde_as(deserialize_as = "DefaultOnError")]
3724        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3725        #[serde(default)]
3726        #[serde(rename = "_meta")]
3727        pub meta: Option<Meta>,
3728    }
3729}
3730
3731#[cfg(feature = "unstable_llm_providers")]
3732impl ListProvidersRequest {
3733    /// Builds [`ListProvidersRequest`] with the required request fields set; optional fields start unset or empty.
3734    #[must_use]
3735    pub fn new() -> Self {
3736        Self::default()
3737    }
3738
3739    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3740    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3741    /// these keys.
3742    ///
3743    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3744    #[must_use]
3745    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3746        self.meta = meta.into_option();
3747        self
3748    }
3749}
3750
3751/// **UNSTABLE**
3752///
3753/// This capability is not part of the spec yet, and may be removed or changed at any point.
3754///
3755/// Response to `providers/list`.
3756#[cfg(feature = "unstable_llm_providers")]
3757#[serde_as]
3758#[skip_serializing_none]
3759#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3760#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3761#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3762#[serde(rename_all = "camelCase")]
3763#[non_exhaustive]
3764pub struct ListProvidersResponse {
3765    /// Configurable providers with current routing info suitable for UI display.
3766    pub providers: Vec<ProviderInfo>,
3767    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3768    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3769    /// these keys.
3770    ///
3771    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3772    #[serde_as(deserialize_as = "DefaultOnError")]
3773    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3774    #[serde(default)]
3775    #[serde(rename = "_meta")]
3776    pub meta: Option<Meta>,
3777}
3778
3779#[cfg(feature = "unstable_llm_providers")]
3780impl ListProvidersResponse {
3781    /// Builds [`ListProvidersResponse`] with the required response fields set; optional fields start unset or empty.
3782    #[must_use]
3783    pub fn new(providers: Vec<ProviderInfo>) -> Self {
3784        Self {
3785            providers,
3786            meta: None,
3787        }
3788    }
3789
3790    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3791    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3792    /// these keys.
3793    ///
3794    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3795    #[must_use]
3796    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3797        self.meta = meta.into_option();
3798        self
3799    }
3800}
3801
3802/// **UNSTABLE**
3803///
3804/// This capability is not part of the spec yet, and may be removed or changed at any point.
3805///
3806/// Request parameters for `providers/set`.
3807///
3808/// Replaces the full configuration for one provider ID.
3809#[cfg(feature = "unstable_llm_providers")]
3810#[serde_as]
3811#[skip_serializing_none]
3812#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3813#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3814#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3815#[serde(rename_all = "camelCase")]
3816#[non_exhaustive]
3817pub struct SetProviderRequest {
3818    /// Provider ID to configure.
3819    pub provider_id: ProviderId,
3820    /// Protocol type for this provider.
3821    pub api_type: LlmProtocol,
3822    /// Base URL for requests sent through this provider.
3823    pub base_url: String,
3824    /// Full headers map for this provider.
3825    /// May include authorization, routing, or other integration-specific headers.
3826    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3827    pub headers: HashMap<String, String>,
3828    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3829    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3830    /// these keys.
3831    ///
3832    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3833    #[serde_as(deserialize_as = "DefaultOnError")]
3834    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3835    #[serde(default)]
3836    #[serde(rename = "_meta")]
3837    pub meta: Option<Meta>,
3838}
3839
3840#[cfg(feature = "unstable_llm_providers")]
3841impl SetProviderRequest {
3842    /// Builds [`SetProviderRequest`] with the required request fields set; optional fields start unset or empty.
3843    #[must_use]
3844    pub fn new(
3845        provider_id: impl Into<ProviderId>,
3846        api_type: LlmProtocol,
3847        base_url: impl Into<String>,
3848    ) -> Self {
3849        Self {
3850            provider_id: provider_id.into(),
3851            api_type,
3852            base_url: base_url.into(),
3853            headers: HashMap::new(),
3854            meta: None,
3855        }
3856    }
3857
3858    /// Full headers map for this provider.
3859    /// May include authorization, routing, or other integration-specific headers.
3860    #[must_use]
3861    pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
3862        self.headers = headers;
3863        self
3864    }
3865
3866    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3867    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3868    /// these keys.
3869    ///
3870    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3871    #[must_use]
3872    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3873        self.meta = meta.into_option();
3874        self
3875    }
3876}
3877
3878#[cfg(feature = "unstable_llm_providers")]
3879crate::serde_util::default_on_null! {
3880    /// **UNSTABLE**
3881    ///
3882    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3883    ///
3884    /// Response to `providers/set`.
3885    #[serde_as]
3886    #[skip_serializing_none]
3887    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3888    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3889    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3890    #[serde(rename_all = "camelCase")]
3891    #[non_exhaustive]
3892    pub struct SetProviderResponse {
3893        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3894        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3895        /// these keys.
3896        ///
3897        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3898        #[serde_as(deserialize_as = "DefaultOnError")]
3899        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3900        #[serde(default)]
3901        #[serde(rename = "_meta")]
3902        pub meta: Option<Meta>,
3903    }
3904}
3905
3906#[cfg(feature = "unstable_llm_providers")]
3907impl SetProviderResponse {
3908    /// Builds [`SetProviderResponse`] with the required response fields set; optional fields start unset or empty.
3909    #[must_use]
3910    pub fn new() -> Self {
3911        Self::default()
3912    }
3913
3914    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3915    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3916    /// these keys.
3917    ///
3918    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3919    #[must_use]
3920    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3921        self.meta = meta.into_option();
3922        self
3923    }
3924}
3925
3926/// **UNSTABLE**
3927///
3928/// This capability is not part of the spec yet, and may be removed or changed at any point.
3929///
3930/// Request parameters for `providers/disable`.
3931#[cfg(feature = "unstable_llm_providers")]
3932#[serde_as]
3933#[skip_serializing_none]
3934#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3935#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3936#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3937#[serde(rename_all = "camelCase")]
3938#[non_exhaustive]
3939pub struct DisableProviderRequest {
3940    /// Provider ID to disable.
3941    pub provider_id: ProviderId,
3942    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3943    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3944    /// these keys.
3945    ///
3946    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3947    #[serde_as(deserialize_as = "DefaultOnError")]
3948    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3949    #[serde(default)]
3950    #[serde(rename = "_meta")]
3951    pub meta: Option<Meta>,
3952}
3953
3954#[cfg(feature = "unstable_llm_providers")]
3955impl DisableProviderRequest {
3956    /// Builds [`DisableProviderRequest`] with the required request fields set; optional fields start unset or empty.
3957    #[must_use]
3958    pub fn new(provider_id: impl Into<ProviderId>) -> Self {
3959        Self {
3960            provider_id: provider_id.into(),
3961            meta: None,
3962        }
3963    }
3964
3965    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3966    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3967    /// these keys.
3968    ///
3969    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3970    #[must_use]
3971    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3972        self.meta = meta.into_option();
3973        self
3974    }
3975}
3976
3977#[cfg(feature = "unstable_llm_providers")]
3978crate::serde_util::default_on_null! {
3979    /// **UNSTABLE**
3980    ///
3981    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3982    ///
3983    /// Response to `providers/disable`.
3984    #[serde_as]
3985    #[skip_serializing_none]
3986    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3987    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3988    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3989    #[serde(rename_all = "camelCase")]
3990    #[non_exhaustive]
3991    pub struct DisableProviderResponse {
3992        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3993        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3994        /// these keys.
3995        ///
3996        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3997        #[serde_as(deserialize_as = "DefaultOnError")]
3998        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3999        #[serde(default)]
4000        #[serde(rename = "_meta")]
4001        pub meta: Option<Meta>,
4002    }
4003}
4004
4005#[cfg(feature = "unstable_llm_providers")]
4006impl DisableProviderResponse {
4007    /// Builds [`DisableProviderResponse`] with the required response fields set; optional fields start unset or empty.
4008    #[must_use]
4009    pub fn new() -> Self {
4010        Self::default()
4011    }
4012
4013    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4014    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4015    /// these keys.
4016    ///
4017    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4018    #[must_use]
4019    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4020        self.meta = meta.into_option();
4021        self
4022    }
4023}
4024
4025// Capabilities
4026
4027/// Capabilities supported by the agent.
4028///
4029/// Advertised during initialization to inform the client about
4030/// available features and content types.
4031///
4032/// See protocol docs: [Agent Capabilities](https://agentclientprotocol.com/protocol/initialization#agent-capabilities)
4033#[serde_as]
4034#[skip_serializing_none]
4035#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4036#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4037#[serde(rename_all = "camelCase")]
4038#[non_exhaustive]
4039pub struct AgentCapabilities {
4040    /// Whether the agent supports `session/load`.
4041    #[serde_as(deserialize_as = "DefaultOnError")]
4042    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4043    #[serde(default)]
4044    pub load_session: bool,
4045    /// Prompt capabilities supported by the agent.
4046    #[serde_as(deserialize_as = "DefaultOnError")]
4047    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4048    #[serde(default)]
4049    pub prompt_capabilities: PromptCapabilities,
4050    /// MCP capabilities supported by the agent.
4051    #[serde_as(deserialize_as = "DefaultOnError")]
4052    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4053    #[serde(default)]
4054    pub mcp_capabilities: McpCapabilities,
4055    /// Session lifecycle and prompt capabilities advertised by the agent.
4056    #[serde_as(deserialize_as = "DefaultOnError")]
4057    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4058    #[serde(default)]
4059    pub session_capabilities: SessionCapabilities,
4060    /// Authentication-related capabilities supported by the agent.
4061    #[serde_as(deserialize_as = "DefaultOnError")]
4062    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4063    #[serde(default)]
4064    pub auth: AgentAuthCapabilities,
4065    /// **UNSTABLE**
4066    ///
4067    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4068    ///
4069    /// Provider configuration capabilities supported by the agent.
4070    ///
4071    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4072    /// Supplying `{}` means the agent supports provider configuration methods.
4073    #[cfg(feature = "unstable_llm_providers")]
4074    #[serde_as(deserialize_as = "DefaultOnError")]
4075    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4076    #[serde(default)]
4077    pub providers: Option<ProvidersCapabilities>,
4078    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4079    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4080    /// these keys.
4081    ///
4082    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4083    #[serde_as(deserialize_as = "DefaultOnError")]
4084    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4085    #[serde(default)]
4086    #[serde(rename = "_meta")]
4087    pub meta: Option<Meta>,
4088}
4089
4090impl AgentCapabilities {
4091    /// Builds an empty [`AgentCapabilities`]; use builder methods to advertise supported sub-capabilities.
4092    #[must_use]
4093    pub fn new() -> Self {
4094        Self::default()
4095    }
4096
4097    /// Whether the agent supports `session/load`.
4098    #[must_use]
4099    pub fn load_session(mut self, load_session: bool) -> Self {
4100        self.load_session = load_session;
4101        self
4102    }
4103
4104    /// Prompt capabilities supported by the agent.
4105    #[must_use]
4106    pub fn prompt_capabilities(mut self, prompt_capabilities: PromptCapabilities) -> Self {
4107        self.prompt_capabilities = prompt_capabilities;
4108        self
4109    }
4110
4111    /// MCP capabilities supported by the agent.
4112    #[must_use]
4113    pub fn mcp_capabilities(mut self, mcp_capabilities: McpCapabilities) -> Self {
4114        self.mcp_capabilities = mcp_capabilities;
4115        self
4116    }
4117
4118    /// Session capabilities supported by the agent.
4119    #[must_use]
4120    pub fn session_capabilities(mut self, session_capabilities: SessionCapabilities) -> Self {
4121        self.session_capabilities = session_capabilities;
4122        self
4123    }
4124
4125    /// Authentication-related capabilities supported by the agent.
4126    #[must_use]
4127    pub fn auth(mut self, auth: AgentAuthCapabilities) -> Self {
4128        self.auth = auth;
4129        self
4130    }
4131
4132    /// **UNSTABLE**
4133    ///
4134    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4135    ///
4136    /// Provider configuration capabilities supported by the agent.
4137    #[cfg(feature = "unstable_llm_providers")]
4138    #[must_use]
4139    pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
4140        self.providers = providers.into_option();
4141        self
4142    }
4143
4144    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4145    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4146    /// these keys.
4147    ///
4148    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4149    #[must_use]
4150    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4151        self.meta = meta.into_option();
4152        self
4153    }
4154}
4155
4156/// **UNSTABLE**
4157///
4158/// This capability is not part of the spec yet, and may be removed or changed at any point.
4159///
4160/// Provider configuration capabilities supported by the agent.
4161///
4162/// Supplying `{}` means the agent supports provider configuration methods.
4163#[cfg(feature = "unstable_llm_providers")]
4164#[serde_as]
4165#[skip_serializing_none]
4166#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4167#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4168#[non_exhaustive]
4169pub struct ProvidersCapabilities {
4170    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4171    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4172    /// these keys.
4173    ///
4174    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4175    #[serde_as(deserialize_as = "DefaultOnError")]
4176    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4177    #[serde(default)]
4178    #[serde(rename = "_meta")]
4179    pub meta: Option<Meta>,
4180}
4181
4182#[cfg(feature = "unstable_llm_providers")]
4183impl ProvidersCapabilities {
4184    /// Builds an empty [`ProvidersCapabilities`]; use builder methods to advertise supported sub-capabilities.
4185    #[must_use]
4186    pub fn new() -> Self {
4187        Self::default()
4188    }
4189
4190    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4191    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4192    /// these keys.
4193    ///
4194    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4195    #[must_use]
4196    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4197        self.meta = meta.into_option();
4198        self
4199    }
4200}
4201
4202/// Session capabilities supported by the agent.
4203///
4204/// As a baseline, all Agents **MUST** support `session/new`, `session/prompt`, `session/cancel`, and `session/update`.
4205///
4206/// Optionally, they **MAY** support other session methods and notifications by specifying additional capabilities.
4207///
4208/// Note: `session/load` is still handled by the top-level `load_session` capability. This will be unified in future versions of the protocol.
4209///
4210/// See protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/initialization#session-capabilities)
4211#[serde_as]
4212#[skip_serializing_none]
4213#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4214#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4215#[serde(rename_all = "camelCase")]
4216#[non_exhaustive]
4217pub struct SessionCapabilities {
4218    /// Whether the agent supports `session/list`.
4219    ///
4220    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4221    /// Supplying `{}` means the agent supports listing sessions.
4222    #[serde_as(deserialize_as = "DefaultOnError")]
4223    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4224    #[serde(default)]
4225    pub list: Option<SessionListCapabilities>,
4226    /// Whether the agent supports `session/delete`.
4227    ///
4228    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4229    /// Supplying `{}` means the agent supports deleting sessions from `session/list`.
4230    #[serde_as(deserialize_as = "DefaultOnError")]
4231    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4232    #[serde(default)]
4233    pub delete: Option<SessionDeleteCapabilities>,
4234    /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests.
4235    ///
4236    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4237    /// Supplying `{}` means the agent supports `additionalDirectories` on
4238    /// supported session lifecycle requests.
4239    ///
4240    /// Agents that also support `session/list` may return
4241    /// `SessionInfo.additionalDirectories` to report the complete ordered
4242    /// additional-root list associated with a listed session.
4243    #[serde_as(deserialize_as = "DefaultOnError")]
4244    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4245    #[serde(default)]
4246    pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
4247    /// **UNSTABLE**
4248    ///
4249    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4250    ///
4251    /// Whether the agent supports `systemPrompt` on session lifecycle requests.
4252    ///
4253    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4254    /// Supplying `{}` means the agent supports `systemPrompt` in `append` mode.
4255    #[cfg(feature = "unstable_session_system_prompt")]
4256    #[serde_as(deserialize_as = "DefaultOnError")]
4257    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4258    #[serde(default)]
4259    pub system_prompt: Option<SessionSystemPromptCapabilities>,
4260    /// **UNSTABLE**
4261    ///
4262    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4263    ///
4264    /// Whether the agent supports `session/fork`.
4265    ///
4266    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4267    /// Supplying `{}` means the agent supports forking sessions.
4268    #[cfg(feature = "unstable_session_fork")]
4269    #[serde_as(deserialize_as = "DefaultOnError")]
4270    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4271    #[serde(default)]
4272    pub fork: Option<SessionForkCapabilities>,
4273    /// Whether the agent supports `session/resume`.
4274    ///
4275    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4276    /// Supplying `{}` means the agent supports resuming sessions.
4277    #[serde_as(deserialize_as = "DefaultOnError")]
4278    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4279    #[serde(default)]
4280    pub resume: Option<SessionResumeCapabilities>,
4281    /// Whether the agent supports `session/close`.
4282    ///
4283    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4284    /// Supplying `{}` means the agent supports closing sessions.
4285    #[serde_as(deserialize_as = "DefaultOnError")]
4286    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4287    #[serde(default)]
4288    pub close: Option<SessionCloseCapabilities>,
4289    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4290    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4291    /// these keys.
4292    ///
4293    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4294    #[serde_as(deserialize_as = "DefaultOnError")]
4295    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4296    #[serde(default)]
4297    #[serde(rename = "_meta")]
4298    pub meta: Option<Meta>,
4299}
4300
4301impl SessionCapabilities {
4302    /// Builds an empty [`SessionCapabilities`]; use builder methods to advertise supported sub-capabilities.
4303    #[must_use]
4304    pub fn new() -> Self {
4305        Self::default()
4306    }
4307
4308    /// Whether the agent supports `session/list`.
4309    ///
4310    /// Omitted or `null` both mean the agent does not advertise support.
4311    /// Supplying `{}` means the agent supports listing sessions.
4312    #[must_use]
4313    pub fn list(mut self, list: impl IntoOption<SessionListCapabilities>) -> Self {
4314        self.list = list.into_option();
4315        self
4316    }
4317
4318    /// Whether the agent supports `session/delete`.
4319    ///
4320    /// Omitted or `null` both mean the agent does not advertise support.
4321    /// Supplying `{}` means the agent supports deleting sessions from `session/list`.
4322    #[must_use]
4323    pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
4324        self.delete = delete.into_option();
4325        self
4326    }
4327
4328    /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests.
4329    ///
4330    /// Omitted or `null` both mean the agent does not advertise support.
4331    /// Supplying `{}` means the agent supports `additionalDirectories` on
4332    /// supported session lifecycle requests.
4333    ///
4334    /// Agents that also support `session/list` may return
4335    /// `SessionInfo.additionalDirectories` to report the complete ordered
4336    /// additional-root list associated with a listed session.
4337    #[must_use]
4338    pub fn additional_directories(
4339        mut self,
4340        additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
4341    ) -> Self {
4342        self.additional_directories = additional_directories.into_option();
4343        self
4344    }
4345
4346    /// **UNSTABLE**
4347    ///
4348    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4349    ///
4350    /// Whether the agent supports `systemPrompt` on session lifecycle requests.
4351    ///
4352    /// Omitted or `null` both mean the agent does not advertise support.
4353    /// Supplying `{}` means the agent supports `systemPrompt` in `append` mode.
4354    #[cfg(feature = "unstable_session_system_prompt")]
4355    #[must_use]
4356    pub fn system_prompt(
4357        mut self,
4358        system_prompt: impl IntoOption<SessionSystemPromptCapabilities>,
4359    ) -> Self {
4360        self.system_prompt = system_prompt.into_option();
4361        self
4362    }
4363
4364    #[cfg(feature = "unstable_session_fork")]
4365    /// Whether the agent supports `session/fork`.
4366    ///
4367    /// Omitted or `null` both mean the agent does not advertise support.
4368    /// Supplying `{}` means the agent supports forking sessions.
4369    #[must_use]
4370    pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
4371        self.fork = fork.into_option();
4372        self
4373    }
4374
4375    /// Whether the agent supports `session/resume`.
4376    ///
4377    /// Omitted or `null` both mean the agent does not advertise support.
4378    /// Supplying `{}` means the agent supports resuming sessions.
4379    #[must_use]
4380    pub fn resume(mut self, resume: impl IntoOption<SessionResumeCapabilities>) -> Self {
4381        self.resume = resume.into_option();
4382        self
4383    }
4384
4385    /// Whether the agent supports `session/close`.
4386    ///
4387    /// Omitted or `null` both mean the agent does not advertise support.
4388    /// Supplying `{}` means the agent supports closing sessions.
4389    #[must_use]
4390    pub fn close(mut self, close: impl IntoOption<SessionCloseCapabilities>) -> Self {
4391        self.close = close.into_option();
4392        self
4393    }
4394
4395    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4396    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4397    /// these keys.
4398    ///
4399    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4400    #[must_use]
4401    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4402        self.meta = meta.into_option();
4403        self
4404    }
4405}
4406
4407/// Capabilities for the `session/list` method.
4408///
4409/// Supplying `{}` means the agent supports listing sessions.
4410#[serde_as]
4411#[skip_serializing_none]
4412#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4413#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4414#[non_exhaustive]
4415pub struct SessionListCapabilities {
4416    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4417    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4418    /// these keys.
4419    ///
4420    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4421    #[serde_as(deserialize_as = "DefaultOnError")]
4422    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4423    #[serde(default)]
4424    #[serde(rename = "_meta")]
4425    pub meta: Option<Meta>,
4426}
4427
4428impl SessionListCapabilities {
4429    /// Builds an empty [`SessionListCapabilities`]; use builder methods to advertise supported sub-capabilities.
4430    #[must_use]
4431    pub fn new() -> Self {
4432        Self::default()
4433    }
4434
4435    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4436    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4437    /// these keys.
4438    ///
4439    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4440    #[must_use]
4441    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4442        self.meta = meta.into_option();
4443        self
4444    }
4445}
4446
4447/// Capabilities for the `session/delete` method.
4448///
4449/// Supplying `{}` means the agent supports deleting sessions from `session/list`.
4450#[serde_as]
4451#[skip_serializing_none]
4452#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4453#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4454#[non_exhaustive]
4455pub struct SessionDeleteCapabilities {
4456    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4457    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4458    /// these keys.
4459    ///
4460    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4461    #[serde_as(deserialize_as = "DefaultOnError")]
4462    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4463    #[serde(default)]
4464    #[serde(rename = "_meta")]
4465    pub meta: Option<Meta>,
4466}
4467
4468impl SessionDeleteCapabilities {
4469    /// Builds an empty [`SessionDeleteCapabilities`]; use builder methods to advertise supported sub-capabilities.
4470    #[must_use]
4471    pub fn new() -> Self {
4472        Self::default()
4473    }
4474
4475    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4476    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4477    /// these keys.
4478    ///
4479    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4480    #[must_use]
4481    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4482        self.meta = meta.into_option();
4483        self
4484    }
4485}
4486
4487/// Capabilities for additional session directories support.
4488///
4489/// Supplying `{}` means the agent supports the `additionalDirectories` field on
4490/// supported session lifecycle requests. Agents that also support
4491/// `session/list` may return `SessionInfo.additionalDirectories` to report the
4492/// complete ordered additional-root list associated with a listed session.
4493#[serde_as]
4494#[skip_serializing_none]
4495#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4496#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4497#[non_exhaustive]
4498pub struct SessionAdditionalDirectoriesCapabilities {
4499    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4500    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4501    /// these keys.
4502    ///
4503    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4504    #[serde_as(deserialize_as = "DefaultOnError")]
4505    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4506    #[serde(default)]
4507    #[serde(rename = "_meta")]
4508    pub meta: Option<Meta>,
4509}
4510
4511impl SessionAdditionalDirectoriesCapabilities {
4512    /// Builds an empty [`SessionAdditionalDirectoriesCapabilities`]; use builder methods to advertise supported sub-capabilities.
4513    #[must_use]
4514    pub fn new() -> Self {
4515        Self::default()
4516    }
4517
4518    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4519    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4520    /// these keys.
4521    ///
4522    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4523    #[must_use]
4524    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4525        self.meta = meta.into_option();
4526        self
4527    }
4528}
4529
4530/// **UNSTABLE**
4531///
4532/// This capability is not part of the spec yet, and may be removed or changed at any point.
4533///
4534/// Capabilities for client-provided system prompts.
4535///
4536/// Supplying `{}` means the agent supports `systemPrompt` on session lifecycle
4537/// requests in `append` mode.
4538#[cfg(feature = "unstable_session_system_prompt")]
4539#[serde_as]
4540#[skip_serializing_none]
4541#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4542#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4543#[serde(rename_all = "camelCase")]
4544#[non_exhaustive]
4545pub struct SessionSystemPromptCapabilities {
4546    /// Delivery modes the agent supports in addition to `append`.
4547    ///
4548    /// Optional. Omitted or `null` both mean the agent supports only `append`.
4549    #[serde_as(deserialize_as = "DefaultOnError")]
4550    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4551    #[serde(default)]
4552    pub mode: Option<SessionSystemPromptModeCapabilities>,
4553    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4554    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4555    /// these keys.
4556    ///
4557    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4558    #[serde_as(deserialize_as = "DefaultOnError")]
4559    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4560    #[serde(default)]
4561    #[serde(rename = "_meta")]
4562    pub meta: Option<Meta>,
4563}
4564
4565#[cfg(feature = "unstable_session_system_prompt")]
4566impl SessionSystemPromptCapabilities {
4567    /// Builds an empty [`SessionSystemPromptCapabilities`]; use builder methods to advertise supported sub-capabilities.
4568    #[must_use]
4569    pub fn new() -> Self {
4570        Self::default()
4571    }
4572
4573    /// Delivery modes the agent supports in addition to `append`.
4574    ///
4575    /// Omitted or `null` both mean the agent supports only `append`.
4576    #[must_use]
4577    pub fn mode(mut self, mode: impl IntoOption<SessionSystemPromptModeCapabilities>) -> Self {
4578        self.mode = mode.into_option();
4579        self
4580    }
4581
4582    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4583    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4584    /// these keys.
4585    ///
4586    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4587    #[must_use]
4588    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4589        self.meta = meta.into_option();
4590        self
4591    }
4592}
4593
4594/// **UNSTABLE**
4595///
4596/// This capability is not part of the spec yet, and may be removed or changed at any point.
4597///
4598/// System prompt delivery modes the agent supports in addition to `append`.
4599///
4600/// `append` has no entry because every agent advertising
4601/// `sessionCapabilities.systemPrompt` supports it.
4602#[cfg(feature = "unstable_session_system_prompt")]
4603#[serde_as]
4604#[skip_serializing_none]
4605#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4606#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4607#[serde(rename_all = "camelCase")]
4608#[non_exhaustive]
4609pub struct SessionSystemPromptModeCapabilities {
4610    /// Whether the agent supports `override` mode.
4611    ///
4612    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4613    /// Supplying `{}` means the agent supports replacing its built-in default
4614    /// instructions with the client-provided system prompt.
4615    #[serde_as(deserialize_as = "DefaultOnError")]
4616    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4617    #[serde(default, rename = "override")]
4618    pub override_: Option<SessionSystemPromptOverrideCapabilities>,
4619    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4620    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4621    /// these keys.
4622    ///
4623    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4624    #[serde_as(deserialize_as = "DefaultOnError")]
4625    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4626    #[serde(default)]
4627    #[serde(rename = "_meta")]
4628    pub meta: Option<Meta>,
4629}
4630
4631#[cfg(feature = "unstable_session_system_prompt")]
4632impl SessionSystemPromptModeCapabilities {
4633    /// Builds an empty [`SessionSystemPromptModeCapabilities`]; use builder methods to advertise supported sub-capabilities.
4634    #[must_use]
4635    pub fn new() -> Self {
4636        Self::default()
4637    }
4638
4639    /// Whether the agent supports `override` mode.
4640    ///
4641    /// Omitted or `null` both mean the agent does not advertise support.
4642    /// Supplying `{}` means the agent supports replacing its built-in default
4643    /// instructions with the client-provided system prompt.
4644    #[must_use]
4645    pub fn override_(
4646        mut self,
4647        override_: impl IntoOption<SessionSystemPromptOverrideCapabilities>,
4648    ) -> Self {
4649        self.override_ = override_.into_option();
4650        self
4651    }
4652
4653    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4654    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4655    /// these keys.
4656    ///
4657    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4658    #[must_use]
4659    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4660        self.meta = meta.into_option();
4661        self
4662    }
4663}
4664
4665/// **UNSTABLE**
4666///
4667/// This capability is not part of the spec yet, and may be removed or changed at any point.
4668///
4669/// Capabilities for the `override` system prompt mode.
4670///
4671/// Supplying `{}` means the agent supports replacing its built-in default
4672/// instructions with the client-provided system prompt.
4673#[cfg(feature = "unstable_session_system_prompt")]
4674#[serde_as]
4675#[skip_serializing_none]
4676#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4677#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4678#[non_exhaustive]
4679pub struct SessionSystemPromptOverrideCapabilities {
4680    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4681    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4682    /// these keys.
4683    ///
4684    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4685    #[serde_as(deserialize_as = "DefaultOnError")]
4686    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4687    #[serde(default)]
4688    #[serde(rename = "_meta")]
4689    pub meta: Option<Meta>,
4690}
4691
4692#[cfg(feature = "unstable_session_system_prompt")]
4693impl SessionSystemPromptOverrideCapabilities {
4694    /// Builds an empty [`SessionSystemPromptOverrideCapabilities`]; use builder methods to advertise supported sub-capabilities.
4695    #[must_use]
4696    pub fn new() -> Self {
4697        Self::default()
4698    }
4699
4700    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4701    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4702    /// these keys.
4703    ///
4704    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4705    #[must_use]
4706    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4707        self.meta = meta.into_option();
4708        self
4709    }
4710}
4711
4712/// **UNSTABLE**
4713///
4714/// This capability is not part of the spec yet, and may be removed or changed at any point.
4715///
4716/// Capabilities for the `session/fork` method.
4717///
4718/// Supplying `{}` means the agent supports forking sessions.
4719#[cfg(feature = "unstable_session_fork")]
4720#[serde_as]
4721#[skip_serializing_none]
4722#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4723#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4724#[non_exhaustive]
4725pub struct SessionForkCapabilities {
4726    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4727    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4728    /// these keys.
4729    ///
4730    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4731    #[serde_as(deserialize_as = "DefaultOnError")]
4732    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4733    #[serde(default)]
4734    #[serde(rename = "_meta")]
4735    pub meta: Option<Meta>,
4736}
4737
4738#[cfg(feature = "unstable_session_fork")]
4739impl SessionForkCapabilities {
4740    /// Builds an empty [`SessionForkCapabilities`]; use builder methods to advertise supported sub-capabilities.
4741    #[must_use]
4742    pub fn new() -> Self {
4743        Self::default()
4744    }
4745
4746    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4747    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4748    /// these keys.
4749    ///
4750    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4751    #[must_use]
4752    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4753        self.meta = meta.into_option();
4754        self
4755    }
4756}
4757
4758/// Capabilities for the `session/resume` method.
4759///
4760/// Supplying `{}` means the agent supports resuming sessions.
4761#[serde_as]
4762#[skip_serializing_none]
4763#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4764#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4765#[non_exhaustive]
4766pub struct SessionResumeCapabilities {
4767    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4768    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4769    /// these keys.
4770    ///
4771    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4772    #[serde_as(deserialize_as = "DefaultOnError")]
4773    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4774    #[serde(default)]
4775    #[serde(rename = "_meta")]
4776    pub meta: Option<Meta>,
4777}
4778
4779impl SessionResumeCapabilities {
4780    /// Builds an empty [`SessionResumeCapabilities`]; use builder methods to advertise supported sub-capabilities.
4781    #[must_use]
4782    pub fn new() -> Self {
4783        Self::default()
4784    }
4785
4786    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4787    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4788    /// these keys.
4789    ///
4790    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4791    #[must_use]
4792    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4793        self.meta = meta.into_option();
4794        self
4795    }
4796}
4797
4798/// Capabilities for the `session/close` method.
4799///
4800/// Supplying `{}` means the agent supports closing sessions.
4801#[serde_as]
4802#[skip_serializing_none]
4803#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4804#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4805#[non_exhaustive]
4806pub struct SessionCloseCapabilities {
4807    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4808    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4809    /// these keys.
4810    ///
4811    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4812    #[serde_as(deserialize_as = "DefaultOnError")]
4813    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4814    #[serde(default)]
4815    #[serde(rename = "_meta")]
4816    pub meta: Option<Meta>,
4817}
4818
4819impl SessionCloseCapabilities {
4820    /// Builds an empty [`SessionCloseCapabilities`]; use builder methods to advertise supported sub-capabilities.
4821    #[must_use]
4822    pub fn new() -> Self {
4823        Self::default()
4824    }
4825
4826    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4827    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4828    /// these keys.
4829    ///
4830    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4831    #[must_use]
4832    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4833        self.meta = meta.into_option();
4834        self
4835    }
4836}
4837
4838/// Prompt capabilities supported by the agent in `session/prompt` requests.
4839///
4840/// Baseline agent functionality requires support for [`ContentBlock::Text`]
4841/// and [`ContentBlock::ResourceLink`] in prompt requests.
4842///
4843/// Other variants must be explicitly opted in to.
4844/// Capabilities for different types of content in prompt requests.
4845///
4846/// Indicates which content types beyond the baseline (text and resource links)
4847/// the agent can process.
4848///
4849/// See protocol docs: [Prompt Capabilities](https://agentclientprotocol.com/protocol/initialization#prompt-capabilities)
4850#[serde_as]
4851#[skip_serializing_none]
4852#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4853#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4854#[serde(rename_all = "camelCase")]
4855#[non_exhaustive]
4856pub struct PromptCapabilities {
4857    /// Agent supports [`ContentBlock::Image`].
4858    #[serde_as(deserialize_as = "DefaultOnError")]
4859    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4860    #[serde(default)]
4861    pub image: bool,
4862    /// Agent supports [`ContentBlock::Audio`].
4863    #[serde_as(deserialize_as = "DefaultOnError")]
4864    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4865    #[serde(default)]
4866    pub audio: bool,
4867    /// Agent supports embedded context in `session/prompt` requests.
4868    ///
4869    /// When enabled, the Client is allowed to include [`ContentBlock::Resource`]
4870    /// in prompt requests for pieces of context that are referenced in the message.
4871    #[serde_as(deserialize_as = "DefaultOnError")]
4872    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4873    #[serde(default)]
4874    pub embedded_context: bool,
4875    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4876    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4877    /// these keys.
4878    ///
4879    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4880    #[serde_as(deserialize_as = "DefaultOnError")]
4881    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4882    #[serde(default)]
4883    #[serde(rename = "_meta")]
4884    pub meta: Option<Meta>,
4885}
4886
4887impl PromptCapabilities {
4888    /// Builds an empty [`PromptCapabilities`]; use builder methods to advertise supported sub-capabilities.
4889    #[must_use]
4890    pub fn new() -> Self {
4891        Self::default()
4892    }
4893
4894    /// Agent supports [`ContentBlock::Image`].
4895    #[must_use]
4896    pub fn image(mut self, image: bool) -> Self {
4897        self.image = image;
4898        self
4899    }
4900
4901    /// Agent supports [`ContentBlock::Audio`].
4902    #[must_use]
4903    pub fn audio(mut self, audio: bool) -> Self {
4904        self.audio = audio;
4905        self
4906    }
4907
4908    /// Agent supports embedded context in `session/prompt` requests.
4909    ///
4910    /// When enabled, the Client is allowed to include [`ContentBlock::Resource`]
4911    /// in prompt requests for pieces of context that are referenced in the message.
4912    #[must_use]
4913    pub fn embedded_context(mut self, embedded_context: bool) -> Self {
4914        self.embedded_context = embedded_context;
4915        self
4916    }
4917
4918    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4919    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4920    /// these keys.
4921    ///
4922    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4923    #[must_use]
4924    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4925        self.meta = meta.into_option();
4926        self
4927    }
4928}
4929
4930/// MCP capabilities supported by the agent
4931#[serde_as]
4932#[skip_serializing_none]
4933#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4934#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4935#[serde(rename_all = "camelCase")]
4936#[non_exhaustive]
4937pub struct McpCapabilities {
4938    /// Agent supports [`McpServer::Http`].
4939    #[serde_as(deserialize_as = "DefaultOnError")]
4940    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4941    #[serde(default)]
4942    pub http: bool,
4943    /// Agent supports [`McpServer::Sse`].
4944    #[serde_as(deserialize_as = "DefaultOnError")]
4945    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4946    #[serde(default)]
4947    pub sse: bool,
4948    /// **UNSTABLE**
4949    ///
4950    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4951    ///
4952    /// Agent supports [`McpServer::Acp`].
4953    #[cfg(feature = "unstable_mcp_over_acp")]
4954    #[serde_as(deserialize_as = "DefaultOnError")]
4955    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4956    #[serde(default)]
4957    pub acp: bool,
4958    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4959    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4960    /// these keys.
4961    ///
4962    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4963    #[serde_as(deserialize_as = "DefaultOnError")]
4964    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4965    #[serde(default)]
4966    #[serde(rename = "_meta")]
4967    pub meta: Option<Meta>,
4968}
4969
4970impl McpCapabilities {
4971    /// Builds an empty [`McpCapabilities`]; use builder methods to advertise supported sub-capabilities.
4972    #[must_use]
4973    pub fn new() -> Self {
4974        Self::default()
4975    }
4976
4977    /// Agent supports [`McpServer::Http`].
4978    #[must_use]
4979    pub fn http(mut self, http: bool) -> Self {
4980        self.http = http;
4981        self
4982    }
4983
4984    /// Agent supports [`McpServer::Sse`].
4985    #[must_use]
4986    pub fn sse(mut self, sse: bool) -> Self {
4987        self.sse = sse;
4988        self
4989    }
4990
4991    /// **UNSTABLE**
4992    ///
4993    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4994    ///
4995    /// Agent supports [`McpServer::Acp`].
4996    #[cfg(feature = "unstable_mcp_over_acp")]
4997    #[must_use]
4998    pub fn acp(mut self, acp: bool) -> Self {
4999        self.acp = acp;
5000        self
5001    }
5002
5003    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5004    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
5005    /// these keys.
5006    ///
5007    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
5008    #[must_use]
5009    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5010        self.meta = meta.into_option();
5011        self
5012    }
5013}
5014
5015// Method schema
5016
5017/// Names of all methods that agents handle.
5018///
5019/// Provides a centralized definition of method names used in the protocol.
5020#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5021#[non_exhaustive]
5022pub struct AgentMethodNames {
5023    /// Method for initializing the connection.
5024    pub initialize: &'static str,
5025    /// Method for authenticating with the agent.
5026    pub authenticate: &'static str,
5027    /// Method for listing configurable providers.
5028    #[cfg(feature = "unstable_llm_providers")]
5029    pub providers_list: &'static str,
5030    /// Method for setting provider configuration.
5031    #[cfg(feature = "unstable_llm_providers")]
5032    pub providers_set: &'static str,
5033    /// Method for disabling a provider.
5034    #[cfg(feature = "unstable_llm_providers")]
5035    pub providers_disable: &'static str,
5036    /// Method for creating a new session.
5037    pub session_new: &'static str,
5038    /// Method for loading an existing session.
5039    pub session_load: &'static str,
5040    /// Method for setting the mode for a session.
5041    pub session_set_mode: &'static str,
5042    /// Method for setting a configuration option for a session.
5043    pub session_set_config_option: &'static str,
5044    /// Method for sending a prompt to the agent.
5045    pub session_prompt: &'static str,
5046    /// Notification for cancelling operations.
5047    pub session_cancel: &'static str,
5048    /// Method for exchanging MCP-over-ACP messages.
5049    #[cfg(feature = "unstable_mcp_over_acp")]
5050    pub mcp_message: &'static str,
5051    /// Method for listing existing sessions.
5052    pub session_list: &'static str,
5053    /// Method for deleting an existing session.
5054    pub session_delete: &'static str,
5055    /// Method for forking an existing session.
5056    #[cfg(feature = "unstable_session_fork")]
5057    pub session_fork: &'static str,
5058    /// Method for resuming an existing session.
5059    pub session_resume: &'static str,
5060    /// Method for closing an active session.
5061    pub session_close: &'static str,
5062    /// Method for logging out of an authenticated session.
5063    pub logout: &'static str,
5064}
5065
5066/// Constant containing all agent method names.
5067pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
5068    initialize: INITIALIZE_METHOD_NAME,
5069    authenticate: AUTHENTICATE_METHOD_NAME,
5070    #[cfg(feature = "unstable_llm_providers")]
5071    providers_list: PROVIDERS_LIST_METHOD_NAME,
5072    #[cfg(feature = "unstable_llm_providers")]
5073    providers_set: PROVIDERS_SET_METHOD_NAME,
5074    #[cfg(feature = "unstable_llm_providers")]
5075    providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
5076    session_new: SESSION_NEW_METHOD_NAME,
5077    session_load: SESSION_LOAD_METHOD_NAME,
5078    session_set_mode: SESSION_SET_MODE_METHOD_NAME,
5079    session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
5080    session_prompt: SESSION_PROMPT_METHOD_NAME,
5081    session_cancel: SESSION_CANCEL_METHOD_NAME,
5082    #[cfg(feature = "unstable_mcp_over_acp")]
5083    mcp_message: MCP_MESSAGE_METHOD_NAME,
5084    session_list: SESSION_LIST_METHOD_NAME,
5085    session_delete: SESSION_DELETE_METHOD_NAME,
5086    #[cfg(feature = "unstable_session_fork")]
5087    session_fork: SESSION_FORK_METHOD_NAME,
5088    session_resume: SESSION_RESUME_METHOD_NAME,
5089    session_close: SESSION_CLOSE_METHOD_NAME,
5090    logout: LOGOUT_METHOD_NAME,
5091};
5092
5093/// Method name for the initialize request.
5094pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
5095/// Method name for the authenticate request.
5096pub(crate) const AUTHENTICATE_METHOD_NAME: &str = "authenticate";
5097/// Method name for listing configurable providers.
5098#[cfg(feature = "unstable_llm_providers")]
5099pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
5100/// Method name for setting provider configuration.
5101#[cfg(feature = "unstable_llm_providers")]
5102pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
5103/// Method name for disabling a provider.
5104#[cfg(feature = "unstable_llm_providers")]
5105pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
5106/// Method name for creating a new session.
5107pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
5108/// Method name for loading an existing session.
5109pub(crate) const SESSION_LOAD_METHOD_NAME: &str = "session/load";
5110/// Method name for setting the mode for a session.
5111pub(crate) const SESSION_SET_MODE_METHOD_NAME: &str = "session/set_mode";
5112/// Method name for setting a configuration option for a session.
5113pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
5114/// Method name for sending a prompt.
5115pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
5116/// Method name for the cancel notification.
5117pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
5118/// Method name for listing existing sessions.
5119pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
5120/// Method name for deleting an existing session.
5121pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
5122/// Method name for forking an existing session.
5123#[cfg(feature = "unstable_session_fork")]
5124pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
5125/// Method name for resuming an existing session.
5126pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
5127/// Method name for closing an active session.
5128pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
5129/// Method name for logging out of an authenticated session.
5130pub(crate) const LOGOUT_METHOD_NAME: &str = "logout";
5131
5132/// All possible requests that a client can send to an agent.
5133///
5134/// This enum is used internally for routing RPC requests. You typically won't need
5135/// to use this directly.
5136///
5137/// This enum encompasses all method calls from client to agent.
5138#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5139#[derive(Clone, Debug, Serialize, Deserialize)]
5140#[serde(untagged)]
5141#[cfg_attr(feature = "schemars", schemars(inline))]
5142#[non_exhaustive]
5143#[allow(clippy::large_enum_variant)]
5144pub enum ClientRequest {
5145    /// Establishes the connection with a client and negotiates protocol capabilities.
5146    ///
5147    /// This method is called once at the beginning of the connection to:
5148    /// - Negotiate the protocol version to use
5149    /// - Exchange capability information between client and agent
5150    /// - Determine available authentication methods
5151    ///
5152    /// The agent should respond with its supported protocol version and capabilities.
5153    ///
5154    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
5155    InitializeRequest(InitializeRequest),
5156    /// Authenticates the client using the specified authentication method.
5157    ///
5158    /// Called when the agent requires authentication before allowing session creation.
5159    /// The client provides an authentication method ID that was advertised during
5160    /// initialization and whose type defines the `authenticate` flow.
5161    ///
5162    /// After successful authentication, the client can proceed to create sessions with
5163    /// `new_session` without receiving an `auth_required` error.
5164    ///
5165    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
5166    AuthenticateRequest(AuthenticateRequest),
5167    /// **UNSTABLE**
5168    ///
5169    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5170    ///
5171    /// Lists providers that can be configured by the client.
5172    #[cfg(feature = "unstable_llm_providers")]
5173    ListProvidersRequest(ListProvidersRequest),
5174    /// **UNSTABLE**
5175    ///
5176    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5177    ///
5178    /// Replaces the configuration for a provider.
5179    #[cfg(feature = "unstable_llm_providers")]
5180    SetProviderRequest(SetProviderRequest),
5181    /// **UNSTABLE**
5182    ///
5183    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5184    ///
5185    /// Disables a provider.
5186    #[cfg(feature = "unstable_llm_providers")]
5187    DisableProviderRequest(DisableProviderRequest),
5188    /// Logs out of the current authenticated state.
5189    ///
5190    /// After a successful logout, all new sessions will require authentication.
5191    /// There is no guarantee about the behavior of already running sessions.
5192    LogoutRequest(LogoutRequest),
5193    /// Creates a new conversation session with the agent.
5194    ///
5195    /// Sessions represent independent conversation contexts with their own history and state.
5196    ///
5197    /// The agent should:
5198    /// - Create a new session context
5199    /// - Connect to any specified MCP servers
5200    /// - Return a unique session ID for future requests
5201    ///
5202    /// May return an `auth_required` error if the agent requires authentication.
5203    ///
5204    /// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/session-setup)
5205    NewSessionRequest(NewSessionRequest),
5206    /// Loads an existing session to resume a previous conversation.
5207    ///
5208    /// This method is only available if the agent advertises the `loadSession` capability.
5209    ///
5210    /// The agent should:
5211    /// - Restore the session context and conversation history
5212    /// - Connect to the specified MCP servers
5213    /// - Stream the entire conversation history back to the client via notifications
5214    ///
5215    /// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)
5216    LoadSessionRequest(LoadSessionRequest),
5217    /// Lists existing sessions known to the agent.
5218    ///
5219    /// This method is only available if the agent advertises the `sessionCapabilities.list` capability.
5220    ///
5221    /// The agent should return metadata about sessions with optional filtering and pagination support.
5222    ListSessionsRequest(ListSessionsRequest),
5223    /// Deletes an existing session from `session/list`.
5224    ///
5225    /// This method is only available if the agent advertises the `sessionCapabilities.delete` capability.
5226    DeleteSessionRequest(DeleteSessionRequest),
5227    #[cfg(feature = "unstable_session_fork")]
5228    /// **UNSTABLE**
5229    ///
5230    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5231    ///
5232    /// Forks an existing session to create a new independent session.
5233    ///
5234    /// This method is only available if the agent advertises the `session.fork` capability.
5235    ///
5236    /// The agent should create a new session with the same conversation context as the
5237    /// original, allowing operations like generating summaries without affecting the
5238    /// original session's history.
5239    ///
5240    /// As with `session/new`, the forked session is active once the agent responds,
5241    /// set up with the request's `cwd`, `additionalDirectories`, and `mcpServers`.
5242    /// The client can prompt it without calling `session/load` or `session/resume` first.
5243    ForkSessionRequest(ForkSessionRequest),
5244    /// Resumes an existing session without returning previous messages.
5245    ///
5246    /// This method is only available if the agent advertises the `sessionCapabilities.resume` capability.
5247    ///
5248    /// The agent should resume the session context, allowing the conversation to continue
5249    /// without replaying the message history (unlike `session/load`).
5250    ResumeSessionRequest(ResumeSessionRequest),
5251    /// Closes an active session and frees up any resources associated with it.
5252    ///
5253    /// This method is only available if the agent advertises the `sessionCapabilities.close` capability.
5254    ///
5255    /// The agent must cancel any ongoing work (as if `session/cancel` was called)
5256    /// and then free up any resources associated with the session.
5257    CloseSessionRequest(CloseSessionRequest),
5258    /// Sets the current mode for a session.
5259    ///
5260    /// Allows switching between different agent modes (e.g., "ask", "architect", "code")
5261    /// that affect system prompts, tool availability, and permission behaviors.
5262    ///
5263    /// The mode must be one of the modes advertised in `availableModes` during session
5264    /// creation or loading. Agents may also change modes autonomously and notify the
5265    /// client via `current_mode_update` notifications.
5266    ///
5267    /// This method can be called at any time during a session, whether the Agent is
5268    /// idle or actively generating a response.
5269    ///
5270    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
5271    SetSessionModeRequest(SetSessionModeRequest),
5272    /// Sets the current value for a session configuration option.
5273    SetSessionConfigOptionRequest(SetSessionConfigOptionRequest),
5274    /// Processes a user prompt within a session.
5275    ///
5276    /// This method handles the whole lifecycle of a prompt:
5277    /// - Receives user messages with optional context (files, images, etc.)
5278    /// - Processes the prompt using language models
5279    /// - Reports language model content and tool calls to the Clients
5280    /// - Requests permission to run tools
5281    /// - Executes any requested tool calls
5282    /// - Returns when the turn is complete with a stop reason
5283    ///
5284    /// See protocol docs: [Prompt Turn](https://agentclientprotocol.com/protocol/prompt-turn)
5285    PromptRequest(PromptRequest),
5286    /// Handles extension method requests from the client.
5287    ///
5288    /// Extension methods provide a way to add custom functionality while maintaining
5289    /// protocol compatibility.
5290    ///
5291    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
5292    ExtMethodRequest(ExtRequest),
5293}
5294
5295impl ClientRequest {
5296    /// Returns the corresponding method name of the request.
5297    #[must_use]
5298    pub fn method(&self) -> &str {
5299        match self {
5300            Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
5301            Self::AuthenticateRequest(_) => AGENT_METHOD_NAMES.authenticate,
5302            #[cfg(feature = "unstable_llm_providers")]
5303            Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
5304            #[cfg(feature = "unstable_llm_providers")]
5305            Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
5306            #[cfg(feature = "unstable_llm_providers")]
5307            Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
5308            Self::LogoutRequest(_) => AGENT_METHOD_NAMES.logout,
5309            Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
5310            Self::LoadSessionRequest(_) => AGENT_METHOD_NAMES.session_load,
5311            Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
5312            Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
5313            #[cfg(feature = "unstable_session_fork")]
5314            Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
5315            Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
5316            Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
5317            Self::SetSessionModeRequest(_) => AGENT_METHOD_NAMES.session_set_mode,
5318            Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
5319            Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
5320            Self::ExtMethodRequest(ext_request) => &ext_request.method,
5321        }
5322    }
5323}
5324
5325/// All possible responses that an agent can send to a client.
5326///
5327/// This enum is used internally for routing RPC responses. You typically won't need
5328/// to use this directly - the responses are handled automatically by the connection.
5329///
5330/// These are responses to the corresponding `ClientRequest` variants.
5331#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5332#[derive(Clone, Debug, Serialize, Deserialize)]
5333#[serde(untagged)]
5334#[cfg_attr(feature = "schemars", schemars(inline))]
5335#[non_exhaustive]
5336#[allow(clippy::large_enum_variant)]
5337pub enum AgentResponse {
5338    /// Successful result returned for a `initialize` request.
5339    InitializeResponse(InitializeResponse),
5340    /// Successful result returned for a `authenticate` request.
5341    AuthenticateResponse(#[serde(default)] AuthenticateResponse),
5342    /// Successful result returned for a `providers/list` request.
5343    #[cfg(feature = "unstable_llm_providers")]
5344    ListProvidersResponse(ListProvidersResponse),
5345    /// Successful result returned for a `providers/set` request.
5346    #[cfg(feature = "unstable_llm_providers")]
5347    SetProviderResponse(#[serde(default)] SetProviderResponse),
5348    /// Successful result returned for a `providers/disable` request.
5349    #[cfg(feature = "unstable_llm_providers")]
5350    DisableProviderResponse(#[serde(default)] DisableProviderResponse),
5351    /// Successful result returned for a `logout` request.
5352    LogoutResponse(#[serde(default)] LogoutResponse),
5353    /// Successful result returned for a `session/new` request.
5354    NewSessionResponse(NewSessionResponse),
5355    /// Successful result returned for a `session/load` request.
5356    LoadSessionResponse(#[serde(default)] LoadSessionResponse),
5357    /// Successful result returned for a `session/list` request.
5358    ListSessionsResponse(ListSessionsResponse),
5359    /// Successful result returned for a `session/delete` request.
5360    DeleteSessionResponse(#[serde(default)] DeleteSessionResponse),
5361    /// Successful result returned for a `session/fork` request.
5362    #[cfg(feature = "unstable_session_fork")]
5363    ForkSessionResponse(ForkSessionResponse),
5364    /// Successful result returned for a `session/resume` request.
5365    ResumeSessionResponse(#[serde(default)] ResumeSessionResponse),
5366    /// Successful result returned for a `session/close` request.
5367    CloseSessionResponse(#[serde(default)] CloseSessionResponse),
5368    /// Successful result returned for a `session/set_mode` request.
5369    SetSessionModeResponse(#[serde(default)] SetSessionModeResponse),
5370    /// Successful result returned for a `session/set_config_option` request.
5371    SetSessionConfigOptionResponse(SetSessionConfigOptionResponse),
5372    /// Successful result returned for a `session/prompt` request.
5373    PromptResponse(PromptResponse),
5374    /// Successful result returned by an extension method outside the core ACP method set.
5375    ExtMethodResponse(ExtResponse),
5376}
5377
5378/// All possible notifications that a client can send to an agent.
5379///
5380/// This enum is used internally for routing RPC notifications. You typically won't need
5381/// to use this directly.
5382///
5383/// Notifications do not expect a response.
5384#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5385#[derive(Clone, Debug, Serialize, Deserialize)]
5386#[serde(untagged)]
5387#[cfg_attr(feature = "schemars", schemars(inline))]
5388#[non_exhaustive]
5389#[allow(clippy::large_enum_variant)]
5390pub enum ClientNotification {
5391    /// Cancels ongoing operations for a session.
5392    ///
5393    /// This is a notification sent by the client to cancel an ongoing prompt turn.
5394    ///
5395    /// Upon receiving this notification, the Agent SHOULD:
5396    /// - Stop all language model requests as soon as possible
5397    /// - Abort all tool call invocations in progress
5398    /// - Send any pending `session/update` notifications
5399    /// - Respond to the original `session/prompt` request with `StopReason::Cancelled`
5400    ///
5401    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
5402    CancelNotification(CancelNotification),
5403    /// **UNSTABLE**
5404    ///
5405    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5406    ///
5407    /// Sends an MCP-over-ACP notification.
5408    #[cfg(feature = "unstable_mcp_over_acp")]
5409    MessageMcpNotification(MessageMcpNotification),
5410    /// Handles extension notifications from the client.
5411    ///
5412    /// Extension notifications provide a way to send one-way messages for custom functionality
5413    /// while maintaining protocol compatibility.
5414    ///
5415    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
5416    ExtNotification(ExtNotification),
5417}
5418
5419impl ClientNotification {
5420    /// Returns the corresponding method name of the notification.
5421    #[must_use]
5422    pub fn method(&self) -> &str {
5423        match self {
5424            Self::CancelNotification(_) => AGENT_METHOD_NAMES.session_cancel,
5425            #[cfg(feature = "unstable_mcp_over_acp")]
5426            Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
5427            Self::ExtNotification(ext_notification) => &ext_notification.method,
5428        }
5429    }
5430}
5431
5432/// Notification to cancel ongoing operations for a session.
5433///
5434/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
5435#[serde_as]
5436#[skip_serializing_none]
5437#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5438#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5439#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
5440#[serde(rename_all = "camelCase")]
5441#[non_exhaustive]
5442pub struct CancelNotification {
5443    /// The ID of the session to cancel operations for.
5444    pub session_id: SessionId,
5445    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5446    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
5447    /// these keys.
5448    ///
5449    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
5450    #[serde_as(deserialize_as = "DefaultOnError")]
5451    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5452    #[serde(default)]
5453    #[serde(rename = "_meta")]
5454    pub meta: Option<Meta>,
5455}
5456
5457impl CancelNotification {
5458    /// Builds [`CancelNotification`] with the required notification fields set; optional fields start unset or empty.
5459    #[must_use]
5460    pub fn new(session_id: impl Into<SessionId>) -> Self {
5461        Self {
5462            session_id: session_id.into(),
5463            meta: None,
5464        }
5465    }
5466
5467    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5468    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
5469    /// these keys.
5470    ///
5471    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
5472    #[must_use]
5473    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5474        self.meta = meta.into_option();
5475        self
5476    }
5477}
5478
5479#[cfg(test)]
5480mod test_serialization {
5481    use super::*;
5482    use serde_json::json;
5483
5484    fn test_meta() -> Meta {
5485        json!({ "source": "test" }).as_object().unwrap().clone()
5486    }
5487
5488    fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
5489        serde_json::to_string(value)
5490            .unwrap()
5491            .matches("\"_meta\"")
5492            .count()
5493    }
5494
5495    /// v1 reports prompt failures as JSON-RPC errors. Only the unstable subagent
5496    /// snapshot has an `error` stop reason, because a child has no prompt response.
5497    #[test]
5498    fn prompt_response_has_no_error_stop_reason() {
5499        assert_eq!(
5500            serde_json::from_value::<PromptResponse>(json!({ "stopReason": "end_turn" }))
5501                .unwrap()
5502                .stop_reason,
5503            StopReason::EndTurn
5504        );
5505        assert!(
5506            serde_json::from_value::<PromptResponse>(json!({ "stopReason": "error" })).is_err()
5507        );
5508    }
5509
5510    #[test]
5511    fn test_initialize_capabilities_default_on_malformed_values() {
5512        let request: InitializeRequest = serde_json::from_value(json!({
5513            "protocolVersion": 1,
5514            "clientCapabilities": false
5515        }))
5516        .unwrap();
5517        assert_eq!(request.client_capabilities, ClientCapabilities::default());
5518
5519        let response: InitializeResponse = serde_json::from_value(json!({
5520            "protocolVersion": 1,
5521            "agentCapabilities": false
5522        }))
5523        .unwrap();
5524        assert_eq!(response.agent_capabilities, AgentCapabilities::default());
5525    }
5526
5527    #[test]
5528    fn test_agent_capabilities_default_on_malformed_values() {
5529        let capabilities: AgentCapabilities = serde_json::from_value(json!({
5530            "loadSession": "yes",
5531            "promptCapabilities": {
5532                "image": "yes",
5533                "audio": true,
5534                "embeddedContext": {}
5535            },
5536            "mcpCapabilities": {
5537                "http": "yes",
5538                "sse": true
5539            },
5540            "sessionCapabilities": false,
5541            "auth": false
5542        }))
5543        .unwrap();
5544
5545        assert!(!capabilities.load_session);
5546        assert!(!capabilities.prompt_capabilities.image);
5547        assert!(capabilities.prompt_capabilities.audio);
5548        assert!(!capabilities.prompt_capabilities.embedded_context);
5549        assert!(!capabilities.mcp_capabilities.http);
5550        assert!(capabilities.mcp_capabilities.sse);
5551        assert_eq!(
5552            capabilities.session_capabilities,
5553            SessionCapabilities::default()
5554        );
5555        assert_eq!(capabilities.auth, AgentAuthCapabilities::default());
5556    }
5557
5558    #[test]
5559    fn test_mcp_server_stdio_serialization() {
5560        let server = McpServer::Stdio(
5561            McpServerStdio::new("test-server", "/usr/bin/server")
5562                .args(vec!["--port".to_string(), "3000".to_string()])
5563                .env(vec![EnvVariable::new("API_KEY", "secret123")]),
5564        );
5565
5566        let json = serde_json::to_value(&server).unwrap();
5567        assert_eq!(
5568            json,
5569            json!({
5570                "name": "test-server",
5571                "command": "/usr/bin/server",
5572                "args": ["--port", "3000"],
5573                "env": [
5574                    {
5575                        "name": "API_KEY",
5576                        "value": "secret123"
5577                    }
5578                ]
5579            })
5580        );
5581
5582        let deserialized: McpServer = serde_json::from_value(json).unwrap();
5583        match deserialized {
5584            McpServer::Stdio(McpServerStdio {
5585                name,
5586                command,
5587                args,
5588                env,
5589                meta: _,
5590            }) => {
5591                assert_eq!(name, "test-server");
5592                assert_eq!(command, PathBuf::from("/usr/bin/server"));
5593                assert_eq!(args, vec!["--port", "3000"]);
5594                assert_eq!(env.len(), 1);
5595                assert_eq!(env[0].name, "API_KEY");
5596                assert_eq!(env[0].value, "secret123");
5597            }
5598            _ => panic!("Expected Stdio variant"),
5599        }
5600    }
5601
5602    #[test]
5603    fn test_mcp_server_http_serialization() {
5604        let server = McpServer::Http(
5605            McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
5606                HttpHeader::new("Authorization", "Bearer token123"),
5607                HttpHeader::new("Content-Type", "application/json"),
5608            ]),
5609        );
5610
5611        let json = serde_json::to_value(&server).unwrap();
5612        assert_eq!(
5613            json,
5614            json!({
5615                "type": "http",
5616                "name": "http-server",
5617                "url": "https://api.example.com",
5618                "headers": [
5619                    {
5620                        "name": "Authorization",
5621                        "value": "Bearer token123"
5622                    },
5623                    {
5624                        "name": "Content-Type",
5625                        "value": "application/json"
5626                    }
5627                ]
5628            })
5629        );
5630
5631        let deserialized: McpServer = serde_json::from_value(json).unwrap();
5632        match deserialized {
5633            McpServer::Http(McpServerHttp {
5634                name,
5635                url,
5636                headers,
5637                meta: _,
5638            }) => {
5639                assert_eq!(name, "http-server");
5640                assert_eq!(url, "https://api.example.com");
5641                assert_eq!(headers.len(), 2);
5642                assert_eq!(headers[0].name, "Authorization");
5643                assert_eq!(headers[0].value, "Bearer token123");
5644                assert_eq!(headers[1].name, "Content-Type");
5645                assert_eq!(headers[1].value, "application/json");
5646            }
5647            _ => panic!("Expected Http variant"),
5648        }
5649    }
5650
5651    #[cfg(feature = "unstable_mcp_over_acp")]
5652    #[test]
5653    fn test_mcp_server_acp_serialization() {
5654        let server = McpServer::Acp(McpServerAcp::new("project-tools", "project-tools-id"));
5655
5656        let json = serde_json::to_value(&server).unwrap();
5657        assert_eq!(
5658            json,
5659            json!({
5660                "type": "acp",
5661                "name": "project-tools",
5662                "serverId": "project-tools-id"
5663            })
5664        );
5665
5666        let deserialized: McpServer = serde_json::from_value(json).unwrap();
5667        match deserialized {
5668            McpServer::Acp(McpServerAcp {
5669                name,
5670                server_id: id,
5671                meta: _,
5672            }) => {
5673                assert_eq!(name, "project-tools");
5674                assert_eq!(id, McpServerAcpId::new("project-tools-id"));
5675            }
5676            _ => panic!("Expected Acp variant"),
5677        }
5678    }
5679
5680    #[cfg(feature = "unstable_mcp_over_acp")]
5681    #[test]
5682    fn test_client_mcp_message_method_names() {
5683        use serde_json::json;
5684
5685        assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
5686
5687        let notification =
5688            MessageMcpNotification::new("server-1", "req-1", "notifications/progress");
5689        assert_eq!(
5690            ClientNotification::MessageMcpNotification(notification.clone()).method(),
5691            "mcp/message"
5692        );
5693        assert_eq!(
5694            serde_json::to_value(notification).unwrap(),
5695            json!({
5696                "serverId": "server-1",
5697                "requestId": "req-1",
5698                "method": "notifications/progress"
5699            })
5700        );
5701        let notification: MessageMcpNotification = serde_json::from_value(json!({
5702            "serverId": "server-1", "requestId": "req-1", "method": "notifications/progress",
5703            "params": null, "_meta": null
5704        }))
5705        .unwrap();
5706        assert_eq!(notification.params, None);
5707        assert_eq!(notification.meta, None);
5708        for key in ["serverId", "requestId", "method"] {
5709            let mut value = json!({"serverId":"server-1", "requestId":"req-1", "method":"notifications/progress"});
5710            value.as_object_mut().unwrap().remove(key);
5711            assert!(serde_json::from_value::<MessageMcpNotification>(value).is_err());
5712        }
5713    }
5714
5715    #[cfg(all(feature = "unstable_mcp_over_acp", feature = "schemars"))]
5716    #[test]
5717    fn test_mcp_server_acp_schema() {
5718        let mcp_server_schema = serde_json::to_value(schemars::schema_for!(McpServer)).unwrap();
5719        assert!(json_contains_entry(
5720            &mcp_server_schema,
5721            "const",
5722            &json!("acp")
5723        ));
5724        assert!(json_contains_entry(
5725            &mcp_server_schema,
5726            "$ref",
5727            &json!("#/$defs/McpServerAcp")
5728        ));
5729
5730        let capabilities_schema =
5731            serde_json::to_value(schemars::schema_for!(McpCapabilities)).unwrap();
5732        assert!(json_contains_key(&capabilities_schema, "acp"));
5733    }
5734
5735    #[cfg(all(feature = "unstable_mcp_over_acp", feature = "schemars"))]
5736    fn json_contains_entry(
5737        value: &serde_json::Value,
5738        key: &str,
5739        expected: &serde_json::Value,
5740    ) -> bool {
5741        match value {
5742            serde_json::Value::Object(map) => {
5743                map.get(key) == Some(expected)
5744                    || map
5745                        .values()
5746                        .any(|value| json_contains_entry(value, key, expected))
5747            }
5748            serde_json::Value::Array(values) => values
5749                .iter()
5750                .any(|value| json_contains_entry(value, key, expected)),
5751            _ => false,
5752        }
5753    }
5754
5755    #[cfg(all(feature = "unstable_mcp_over_acp", feature = "schemars"))]
5756    fn json_contains_key(value: &serde_json::Value, key: &str) -> bool {
5757        match value {
5758            serde_json::Value::Object(map) => {
5759                map.contains_key(key) || map.values().any(|value| json_contains_key(value, key))
5760            }
5761            serde_json::Value::Array(values) => {
5762                values.iter().any(|value| json_contains_key(value, key))
5763            }
5764            _ => false,
5765        }
5766    }
5767
5768    #[test]
5769    fn test_mcp_server_sse_serialization() {
5770        let server = McpServer::Sse(
5771            McpServerSse::new("sse-server", "https://sse.example.com/events")
5772                .headers(vec![HttpHeader::new("X-API-Key", "apikey456")]),
5773        );
5774
5775        let json = serde_json::to_value(&server).unwrap();
5776        assert_eq!(
5777            json,
5778            json!({
5779                "type": "sse",
5780                "name": "sse-server",
5781                "url": "https://sse.example.com/events",
5782                "headers": [
5783                    {
5784                        "name": "X-API-Key",
5785                        "value": "apikey456"
5786                    }
5787                ]
5788            })
5789        );
5790
5791        let deserialized: McpServer = serde_json::from_value(json).unwrap();
5792        match deserialized {
5793            McpServer::Sse(McpServerSse {
5794                name,
5795                url,
5796                headers,
5797                meta: _,
5798            }) => {
5799                assert_eq!(name, "sse-server");
5800                assert_eq!(url, "https://sse.example.com/events");
5801                assert_eq!(headers.len(), 1);
5802                assert_eq!(headers[0].name, "X-API-Key");
5803                assert_eq!(headers[0].value, "apikey456");
5804            }
5805            _ => panic!("Expected Sse variant"),
5806        }
5807    }
5808
5809    #[test]
5810    fn test_session_config_option_category_known_variants() {
5811        // Test serialization of known variants
5812        assert_eq!(
5813            serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
5814            json!("mode")
5815        );
5816        assert_eq!(
5817            serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
5818            json!("model")
5819        );
5820        assert_eq!(
5821            serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
5822            json!("model_config")
5823        );
5824        assert_eq!(
5825            serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
5826            json!("thought_level")
5827        );
5828
5829        // Test deserialization of known variants
5830        assert_eq!(
5831            serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
5832            SessionConfigOptionCategory::Mode
5833        );
5834        assert_eq!(
5835            serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
5836            SessionConfigOptionCategory::Model
5837        );
5838        assert_eq!(
5839            serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
5840            SessionConfigOptionCategory::ModelConfig
5841        );
5842        assert_eq!(
5843            serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
5844            SessionConfigOptionCategory::ThoughtLevel
5845        );
5846    }
5847
5848    #[test]
5849    fn test_session_config_option_category_unknown_variants() {
5850        // Test that unknown strings are captured in Other variant
5851        let unknown: SessionConfigOptionCategory =
5852            serde_json::from_str("\"some_future_category\"").unwrap();
5853        assert_eq!(
5854            unknown,
5855            SessionConfigOptionCategory::Other("some_future_category".to_string())
5856        );
5857
5858        // Test round-trip of unknown category
5859        let json = serde_json::to_value(&unknown).unwrap();
5860        assert_eq!(json, json!("some_future_category"));
5861    }
5862
5863    #[test]
5864    fn test_session_config_option_category_custom_categories() {
5865        // Category names beginning with `_` are free for custom use
5866        let custom: SessionConfigOptionCategory =
5867            serde_json::from_str("\"_my_custom_category\"").unwrap();
5868        assert_eq!(
5869            custom,
5870            SessionConfigOptionCategory::Other("_my_custom_category".to_string())
5871        );
5872
5873        // Test round-trip preserves the custom category name
5874        let json = serde_json::to_value(&custom).unwrap();
5875        assert_eq!(json, json!("_my_custom_category"));
5876
5877        // Deserialize back and verify
5878        let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
5879        assert_eq!(
5880            deserialized,
5881            SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
5882        );
5883    }
5884
5885    #[test]
5886    fn test_auth_method_agent_serialization() {
5887        let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
5888
5889        let json = serde_json::to_value(&method).unwrap();
5890        assert_eq!(
5891            json,
5892            json!({
5893                "id": "default-auth",
5894                "name": "Default Auth"
5895            })
5896        );
5897        // description should be omitted when None
5898        assert!(!json.as_object().unwrap().contains_key("description"));
5899        // Agent variant should not emit a `type` field (backward compat)
5900        assert!(!json.as_object().unwrap().contains_key("type"));
5901
5902        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5903        match deserialized {
5904            AuthMethod::Agent(AuthMethodAgent { id, name, .. }) => {
5905                assert_eq!(id.0.as_ref(), "default-auth");
5906                assert_eq!(name, "Default Auth");
5907            }
5908            _ => panic!("Expected Agent variant"),
5909        }
5910    }
5911
5912    #[test]
5913    fn test_auth_method_explicit_agent_deserialization() {
5914        // An explicit `"type": "agent"` should also deserialize to Agent
5915        let json = json!({
5916            "id": "agent-auth",
5917            "name": "Agent Auth",
5918            "type": "agent"
5919        });
5920
5921        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
5922        assert!(matches!(deserialized, AuthMethod::Agent(_)));
5923    }
5924
5925    #[test]
5926    fn test_session_delete_serialization() {
5927        assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
5928        assert_eq!(
5929            ClientRequest::DeleteSessionRequest(DeleteSessionRequest::new("sess_abc123")).method(),
5930            "session/delete"
5931        );
5932        assert_eq!(
5933            serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
5934            json!({
5935                "sessionId": "sess_abc123"
5936            })
5937        );
5938        assert_eq!(
5939            serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
5940            json!({})
5941        );
5942        assert_eq!(
5943            serde_json::to_value(
5944                SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
5945            )
5946            .unwrap(),
5947            json!({
5948                "delete": {}
5949            })
5950        );
5951    }
5952    #[test]
5953    fn test_session_additional_directories_serialization() {
5954        assert_eq!(
5955            serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
5956            json!({
5957                "cwd": "/home/user/project",
5958                "mcpServers": []
5959            })
5960        );
5961        assert_eq!(
5962            serde_json::to_value(
5963                NewSessionRequest::new("/home/user/project").additional_directories(vec![
5964                    PathBuf::from("/home/user/shared-lib"),
5965                    PathBuf::from("/home/user/product-docs"),
5966                ])
5967            )
5968            .unwrap(),
5969            json!({
5970                "cwd": "/home/user/project",
5971                "additionalDirectories": [
5972                    "/home/user/shared-lib",
5973                    "/home/user/product-docs"
5974                ],
5975                "mcpServers": []
5976            })
5977        );
5978        assert_eq!(
5979            serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
5980            json!({
5981                "sessionId": "sess_abc123",
5982                "cwd": "/home/user/project"
5983            })
5984        );
5985        assert_eq!(
5986            serde_json::to_value(
5987                SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
5988                    PathBuf::from("/home/user/shared-lib"),
5989                    PathBuf::from("/home/user/product-docs"),
5990                ])
5991            )
5992            .unwrap(),
5993            json!({
5994                "sessionId": "sess_abc123",
5995                "cwd": "/home/user/project",
5996                "additionalDirectories": [
5997                    "/home/user/shared-lib",
5998                    "/home/user/product-docs"
5999                ]
6000            })
6001        );
6002        assert_eq!(
6003            serde_json::from_value::<SessionInfo>(json!({
6004                "sessionId": "sess_abc123",
6005                "cwd": "/home/user/project"
6006            }))
6007            .unwrap()
6008            .additional_directories,
6009            Vec::<PathBuf>::new()
6010        );
6011    }
6012    #[test]
6013    fn test_session_additional_directories_capabilities_serialization() {
6014        assert_eq!(
6015            serde_json::to_value(
6016                SessionCapabilities::new()
6017                    .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
6018            )
6019            .unwrap(),
6020            json!({
6021                "additionalDirectories": {}
6022            })
6023        );
6024    }
6025
6026    #[cfg(feature = "unstable_session_system_prompt")]
6027    #[test]
6028    fn test_session_system_prompt_capabilities_serialization() {
6029        assert_eq!(
6030            serde_json::to_value(
6031                SessionCapabilities::new().system_prompt(SessionSystemPromptCapabilities::new())
6032            )
6033            .unwrap(),
6034            json!({ "systemPrompt": {} })
6035        );
6036        assert_eq!(
6037            serde_json::to_value(
6038                SessionCapabilities::new().system_prompt(
6039                    SessionSystemPromptCapabilities::new().mode(
6040                        SessionSystemPromptModeCapabilities::new()
6041                            .override_(SessionSystemPromptOverrideCapabilities::new())
6042                    )
6043                )
6044            )
6045            .unwrap(),
6046            json!({ "systemPrompt": { "mode": { "override": {} } } })
6047        );
6048        assert_eq!(
6049            serde_json::from_value::<SessionCapabilities>(
6050                json!({ "systemPrompt": { "mode": { "override": {} } } })
6051            )
6052            .unwrap()
6053            .system_prompt
6054            .and_then(|capabilities| capabilities.mode)
6055            .and_then(|mode| mode.override_),
6056            Some(SessionSystemPromptOverrideCapabilities::new())
6057        );
6058
6059        // Capabilities fall back to "not advertised" when malformed.
6060        assert_eq!(
6061            serde_json::from_value::<SessionCapabilities>(json!({ "systemPrompt": "yes" }))
6062                .unwrap()
6063                .system_prompt,
6064            None
6065        );
6066        assert_eq!(
6067            serde_json::from_value::<SessionCapabilities>(
6068                json!({ "systemPrompt": { "mode": { "override": true } } })
6069            )
6070            .unwrap()
6071            .system_prompt,
6072            Some(
6073                SessionSystemPromptCapabilities::new()
6074                    .mode(SessionSystemPromptModeCapabilities::new())
6075            )
6076        );
6077        assert_eq!(
6078            serde_json::from_value::<SessionCapabilities>(
6079                json!({ "systemPrompt": { "mode": "override" } })
6080            )
6081            .unwrap()
6082            .system_prompt,
6083            Some(SessionSystemPromptCapabilities::new())
6084        );
6085    }
6086
6087    #[cfg(feature = "unstable_session_system_prompt")]
6088    #[test]
6089    fn test_system_prompt_lifecycle_request_serialization() {
6090        let system_prompt = SystemPrompt::new(vec![ContentBlock::from(
6091            "You are a code review specialist.",
6092        )]);
6093        let system_prompt_json = json!({
6094            "mode": "append",
6095            "content": [{ "type": "text", "text": "You are a code review specialist." }]
6096        });
6097
6098        let request =
6099            NewSessionRequest::new("/home/user/project").system_prompt(system_prompt.clone());
6100        let request_json = json!({
6101            "cwd": "/home/user/project",
6102            "mcpServers": [],
6103            "systemPrompt": system_prompt_json
6104        });
6105        assert_eq!(serde_json::to_value(&request).unwrap(), request_json);
6106        assert_eq!(
6107            serde_json::from_value::<NewSessionRequest>(request_json).unwrap(),
6108            request
6109        );
6110
6111        assert_eq!(
6112            serde_json::to_value(
6113                LoadSessionRequest::new("sess_abc123", "/home/user/project")
6114                    .system_prompt(system_prompt.clone())
6115            )
6116            .unwrap()["systemPrompt"],
6117            system_prompt_json
6118        );
6119        assert_eq!(
6120            serde_json::to_value(
6121                ResumeSessionRequest::new("sess_abc123", "/home/user/project")
6122                    .system_prompt(system_prompt.clone())
6123            )
6124            .unwrap()["systemPrompt"],
6125            system_prompt_json
6126        );
6127        #[cfg(feature = "unstable_session_fork")]
6128        assert_eq!(
6129            serde_json::to_value(
6130                ForkSessionRequest::new("sess_abc123", "/home/user/project")
6131                    .system_prompt(system_prompt.clone())
6132            )
6133            .unwrap()["systemPrompt"],
6134            system_prompt_json
6135        );
6136
6137        let override_prompt =
6138            SystemPrompt::new(vec![ContentBlock::from("...")]).mode(SystemPromptMode::Override);
6139        let override_json = json!({
6140            "mode": "override",
6141            "content": [{ "type": "text", "text": "..." }]
6142        });
6143        assert_eq!(
6144            serde_json::to_value(&override_prompt).unwrap(),
6145            override_json
6146        );
6147        assert_eq!(
6148            serde_json::from_value::<SystemPrompt>(override_json).unwrap(),
6149            override_prompt
6150        );
6151    }
6152
6153    /// `SystemPrompt::new` defaults to `append` in Rust, but `mode` is required on
6154    /// the wire, so it is always serialized.
6155    #[cfg(feature = "unstable_session_system_prompt")]
6156    #[test]
6157    fn test_system_prompt_new_defaults_to_append() {
6158        let system_prompt = SystemPrompt::new(vec![ContentBlock::from("...")]);
6159        assert_eq!(system_prompt.mode, SystemPromptMode::Append);
6160        assert_eq!(
6161            serde_json::to_value(&system_prompt).unwrap(),
6162            json!({ "mode": "append", "content": [{ "type": "text", "text": "..." }] })
6163        );
6164    }
6165
6166    /// Unknown modes follow the enum variant extension rule: they parse as
6167    /// `SystemPromptMode::Other` so they are not dropped, and an agent that does
6168    /// not support them must reject the request.
6169    #[cfg(feature = "unstable_session_system_prompt")]
6170    #[test]
6171    fn test_system_prompt_preserves_unknown_modes() {
6172        for mode in ["_vendor_mode", "prepend"] {
6173            let system_prompt_json =
6174                json!({ "mode": mode, "content": [{ "type": "text", "text": "..." }] });
6175            let system_prompt: SystemPrompt =
6176                serde_json::from_value(system_prompt_json.clone()).unwrap();
6177            assert_eq!(
6178                system_prompt.mode,
6179                SystemPromptMode::Other(mode.to_string())
6180            );
6181            assert_eq!(
6182                serde_json::to_value(&system_prompt).unwrap(),
6183                system_prompt_json
6184            );
6185        }
6186    }
6187
6188    /// The receiver acts on `systemPrompt`, so malformed values fail the request
6189    /// (`-32602`) instead of creating a session without the prompt, and `null`
6190    /// is equivalent to omitting the field.
6191    #[cfg(feature = "unstable_session_system_prompt")]
6192    fn assert_system_prompt_is_strict<T>(base: &serde_json::Value)
6193    where
6194        T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
6195    {
6196        let name = std::any::type_name::<T>();
6197        let with = |system_prompt: serde_json::Value| {
6198            let mut request = base.clone();
6199            request["systemPrompt"] = system_prompt;
6200            serde_json::from_value::<T>(request)
6201        };
6202
6203        for system_prompt in [
6204            json!("You are a code review specialist."),
6205            json!({ "content": [{ "type": "text", "text": "..." }] }),
6206            json!({ "mode": null, "content": [{ "type": "text", "text": "..." }] }),
6207            json!({ "mode": 1, "content": [{ "type": "text", "text": "..." }] }),
6208            json!({ "mode": "append" }),
6209            json!({ "mode": "append", "content": "..." }),
6210            json!({ "mode": "append", "content": [{ "type": "text" }] }),
6211            json!({ "mode": "append", "content": [{ "type": "video", "uri": "..." }] }),
6212        ] {
6213            assert!(
6214                with(system_prompt.clone()).is_err(),
6215                "{name}.systemPrompt: {system_prompt}"
6216            );
6217        }
6218
6219        assert_eq!(
6220            with(serde_json::Value::Null).unwrap(),
6221            serde_json::from_value::<T>(base.clone()).unwrap(),
6222            "{name}.systemPrompt"
6223        );
6224    }
6225
6226    #[cfg(feature = "unstable_session_system_prompt")]
6227    #[test]
6228    fn test_session_setup_requests_reject_malformed_system_prompts() {
6229        assert!(
6230            serde_json::to_value(NewSessionRequest::new("/repo"))
6231                .unwrap()
6232                .get("systemPrompt")
6233                .is_none()
6234        );
6235        assert_system_prompt_is_strict::<NewSessionRequest>(&json!({
6236            "cwd": "/repo",
6237            "mcpServers": []
6238        }));
6239        assert_system_prompt_is_strict::<LoadSessionRequest>(&json!({
6240            "sessionId": "sess-1",
6241            "cwd": "/repo",
6242            "mcpServers": []
6243        }));
6244        assert_system_prompt_is_strict::<ResumeSessionRequest>(&json!({
6245            "sessionId": "sess-1",
6246            "cwd": "/repo"
6247        }));
6248        #[cfg(feature = "unstable_session_fork")]
6249        assert_system_prompt_is_strict::<ForkSessionRequest>(&json!({
6250            "sessionId": "sess-1",
6251            "cwd": "/repo"
6252        }));
6253    }
6254
6255    #[test]
6256    fn test_auth_method_terminal_serialization() {
6257        let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
6258
6259        let json = serde_json::to_value(&method).unwrap();
6260        assert_eq!(
6261            json,
6262            json!({
6263                "id": "tui-auth",
6264                "name": "Terminal Auth",
6265                "type": "terminal"
6266            })
6267        );
6268        // args and env should be omitted when empty
6269        assert!(!json.as_object().unwrap().contains_key("args"));
6270        assert!(!json.as_object().unwrap().contains_key("env"));
6271
6272        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6273        match deserialized {
6274            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6275                assert_eq!(args, Vec::<String>::new());
6276                assert!(env.is_empty());
6277            }
6278            _ => panic!("Expected Terminal variant"),
6279        }
6280    }
6281
6282    #[test]
6283    fn test_auth_method_terminal_with_args_and_env_serialization() {
6284        use std::collections::HashMap;
6285
6286        let mut env = HashMap::new();
6287        env.insert("TERM".to_string(), "xterm-256color".to_string());
6288
6289        let method = AuthMethod::Terminal(
6290            AuthMethodTerminal::new("tui-auth", "Terminal Auth")
6291                .args(vec!["--interactive".to_string(), "--color".to_string()])
6292                .env(env),
6293        );
6294
6295        let json = serde_json::to_value(&method).unwrap();
6296        assert_eq!(
6297            json,
6298            json!({
6299                "id": "tui-auth",
6300                "name": "Terminal Auth",
6301                "type": "terminal",
6302                "args": ["--interactive", "--color"],
6303                "env": {
6304                    "TERM": "xterm-256color"
6305                }
6306            })
6307        );
6308
6309        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6310        match deserialized {
6311            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6312                assert_eq!(args, vec!["--interactive", "--color"]);
6313                assert_eq!(env.len(), 1);
6314                assert_eq!(env.get("TERM").unwrap(), "xterm-256color");
6315            }
6316            _ => panic!("Expected Terminal variant"),
6317        }
6318    }
6319
6320    #[test]
6321    fn test_session_config_option_value_id_serialize() {
6322        let val = SessionConfigOptionValue::value_id("model-1");
6323        let json = serde_json::to_value(&val).unwrap();
6324        // ValueId omits the "type" field (it's the default)
6325        assert_eq!(json, json!({ "value": "model-1" }));
6326        assert!(!json.as_object().unwrap().contains_key("type"));
6327    }
6328
6329    #[test]
6330    fn test_session_config_option_value_boolean_serialize() {
6331        let val = SessionConfigOptionValue::boolean(true);
6332        let json = serde_json::to_value(&val).unwrap();
6333        assert_eq!(json, json!({ "type": "boolean", "value": true }));
6334    }
6335
6336    #[test]
6337    fn test_session_config_option_value_deserialize_no_type() {
6338        // Missing "type" should default to ValueId
6339        let json = json!({ "value": "model-1" });
6340        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6341        assert_eq!(val, SessionConfigOptionValue::value_id("model-1"));
6342        assert_eq!(val.as_value_id().unwrap().to_string(), "model-1");
6343    }
6344
6345    #[test]
6346    fn test_session_config_option_value_deserialize_boolean() {
6347        let json = json!({ "type": "boolean", "value": true });
6348        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6349        assert_eq!(val, SessionConfigOptionValue::boolean(true));
6350        assert_eq!(val.as_bool(), Some(true));
6351    }
6352
6353    #[test]
6354    fn test_session_config_option_value_deserialize_boolean_false() {
6355        let json = json!({ "type": "boolean", "value": false });
6356        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6357        assert_eq!(val, SessionConfigOptionValue::boolean(false));
6358        assert_eq!(val.as_bool(), Some(false));
6359    }
6360
6361    #[test]
6362    fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
6363        // Unknown type with a string value gracefully falls back to ValueId
6364        let json = json!({ "type": "text", "value": "freeform input" });
6365        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6366        assert_eq!(val.as_value_id().unwrap().to_string(), "freeform input");
6367    }
6368
6369    #[test]
6370    fn test_session_config_option_value_roundtrip_value_id() {
6371        let original = SessionConfigOptionValue::value_id("option-a");
6372        let json = serde_json::to_value(&original).unwrap();
6373        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6374        assert_eq!(original, roundtripped);
6375    }
6376
6377    #[test]
6378    fn test_session_config_option_value_roundtrip_boolean() {
6379        let original = SessionConfigOptionValue::boolean(false);
6380        let json = serde_json::to_value(&original).unwrap();
6381        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6382        assert_eq!(original, roundtripped);
6383    }
6384
6385    #[test]
6386    fn test_session_config_option_value_type_mismatch_boolean_with_string() {
6387        // type says "boolean" but value is a string — falls to untagged ValueId
6388        let json = json!({ "type": "boolean", "value": "not a bool" });
6389        let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6390        // serde tries Boolean first (fails), then falls to untagged ValueId (succeeds)
6391        assert!(result.is_ok());
6392        assert_eq!(
6393            result.unwrap().as_value_id().unwrap().to_string(),
6394            "not a bool"
6395        );
6396    }
6397
6398    #[test]
6399    fn test_session_config_option_value_from_impls() {
6400        let from_str: SessionConfigOptionValue = "model-1".into();
6401        assert_eq!(from_str.as_value_id().unwrap().to_string(), "model-1");
6402
6403        let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
6404        assert_eq!(from_id.as_value_id().unwrap().to_string(), "model-2");
6405
6406        let from_bool: SessionConfigOptionValue = true.into();
6407        assert_eq!(from_bool.as_bool(), Some(true));
6408    }
6409
6410    #[test]
6411    fn test_set_session_config_option_request_value_id() {
6412        let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
6413        let json = serde_json::to_value(&req).unwrap();
6414        assert_eq!(
6415            json,
6416            json!({
6417                "sessionId": "sess_1",
6418                "configId": "model",
6419                "value": "model-1"
6420            })
6421        );
6422        // No "type" field for value_id
6423        assert!(!json.as_object().unwrap().contains_key("type"));
6424    }
6425
6426    #[test]
6427    fn test_set_session_config_option_request_boolean() {
6428        let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
6429        let json = serde_json::to_value(&req).unwrap();
6430        assert_eq!(
6431            json,
6432            json!({
6433                "sessionId": "sess_1",
6434                "configId": "brave_mode",
6435                "type": "boolean",
6436                "value": true
6437            })
6438        );
6439    }
6440
6441    #[test]
6442    fn test_set_session_config_option_request_deserialize_no_type() {
6443        // Backwards-compatible: no "type" field → value_id
6444        let json = json!({
6445            "sessionId": "sess_1",
6446            "configId": "model",
6447            "value": "model-1"
6448        });
6449        let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6450        assert_eq!(req.session_id.to_string(), "sess_1");
6451        assert_eq!(req.config_id.to_string(), "model");
6452        assert_eq!(req.value.as_value_id().unwrap().to_string(), "model-1");
6453    }
6454
6455    #[test]
6456    fn test_set_session_config_option_request_deserialize_boolean() {
6457        let json = json!({
6458            "sessionId": "sess_1",
6459            "configId": "brave_mode",
6460            "type": "boolean",
6461            "value": true
6462        });
6463        let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6464        assert_eq!(req.value.as_bool(), Some(true));
6465    }
6466
6467    #[test]
6468    fn test_set_session_config_option_request_roundtrip_value_id() {
6469        let original = SetSessionConfigOptionRequest::new("s", "c", "v");
6470        let json = serde_json::to_value(&original).unwrap();
6471        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6472        assert_eq!(original, roundtripped);
6473    }
6474
6475    #[test]
6476    fn test_set_session_config_option_request_roundtrip_boolean() {
6477        let original = SetSessionConfigOptionRequest::new("s", "c", false);
6478        let json = serde_json::to_value(&original).unwrap();
6479        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6480        assert_eq!(original, roundtripped);
6481    }
6482
6483    #[test]
6484    fn test_session_config_boolean_serialization() {
6485        let cfg = SessionConfigBoolean::new(true);
6486        let json = serde_json::to_value(&cfg).unwrap();
6487        assert_eq!(json, json!({ "currentValue": true }));
6488
6489        let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
6490        assert!(deserialized.current_value);
6491    }
6492
6493    #[test]
6494    fn test_session_config_option_boolean_variant() {
6495        let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
6496            .description("Skip confirmation prompts")
6497            .meta(test_meta());
6498        assert_eq!(serialized_meta_key_count(&opt), 1);
6499
6500        let json = serde_json::to_value(&opt).unwrap();
6501        assert_eq!(
6502            json,
6503            json!({
6504                "id": "brave_mode",
6505                "name": "Brave Mode",
6506                "description": "Skip confirmation prompts",
6507                "type": "boolean",
6508                "currentValue": false,
6509                "_meta": {
6510                    "source": "test"
6511                }
6512            })
6513        );
6514
6515        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6516        assert_eq!(deserialized.id.to_string(), "brave_mode");
6517        assert_eq!(deserialized.name, "Brave Mode");
6518        match deserialized.kind {
6519            SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
6520            _ => panic!("Expected Boolean kind"),
6521        }
6522    }
6523
6524    #[test]
6525    fn test_session_config_option_select_still_works() {
6526        // Make sure existing select options are unaffected
6527        let opt = SessionConfigOption::select(
6528            "model",
6529            "Model",
6530            "model-1",
6531            vec![
6532                SessionConfigSelectOption::new("model-1", "Model 1"),
6533                SessionConfigSelectOption::new("model-2", "Model 2"),
6534            ],
6535        )
6536        .meta(test_meta());
6537        assert_eq!(serialized_meta_key_count(&opt), 1);
6538
6539        let json = serde_json::to_value(&opt).unwrap();
6540        assert_eq!(json["type"], "select");
6541        assert_eq!(json["currentValue"], "model-1");
6542        assert_eq!(json["options"].as_array().unwrap().len(), 2);
6543        assert_eq!(json["_meta"]["source"], "test");
6544
6545        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6546        match deserialized.kind {
6547            SessionConfigKind::Select(ref s) => {
6548                assert_eq!(s.current_value.to_string(), "model-1");
6549            }
6550            _ => panic!("Expected Select kind"),
6551        }
6552    }
6553
6554    #[cfg(feature = "unstable_llm_providers")]
6555    #[test]
6556    fn test_llm_protocol_known_variants() {
6557        assert_eq!(
6558            serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
6559            json!("anthropic")
6560        );
6561        assert_eq!(
6562            serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
6563            json!("openai")
6564        );
6565        assert_eq!(
6566            serde_json::to_value(&LlmProtocol::Azure).unwrap(),
6567            json!("azure")
6568        );
6569        assert_eq!(
6570            serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
6571            json!("vertex")
6572        );
6573        assert_eq!(
6574            serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
6575            json!("bedrock")
6576        );
6577
6578        assert_eq!(
6579            serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
6580            LlmProtocol::Anthropic
6581        );
6582        assert_eq!(
6583            serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
6584            LlmProtocol::OpenAi
6585        );
6586        assert_eq!(
6587            serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
6588            LlmProtocol::Azure
6589        );
6590        assert_eq!(
6591            serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
6592            LlmProtocol::Vertex
6593        );
6594        assert_eq!(
6595            serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
6596            LlmProtocol::Bedrock
6597        );
6598    }
6599
6600    #[cfg(feature = "unstable_llm_providers")]
6601    #[test]
6602    fn test_llm_protocol_unknown_variant() {
6603        let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
6604        assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
6605
6606        let json = serde_json::to_value(&unknown).unwrap();
6607        assert_eq!(json, json!("cohere"));
6608    }
6609
6610    #[cfg(feature = "unstable_llm_providers")]
6611    #[test]
6612    fn test_provider_current_config_serialization() {
6613        let config =
6614            ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
6615
6616        let json = serde_json::to_value(&config).unwrap();
6617        assert_eq!(
6618            json,
6619            json!({
6620                "apiType": "anthropic",
6621                "baseUrl": "https://api.anthropic.com"
6622            })
6623        );
6624
6625        let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
6626        assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
6627        assert_eq!(deserialized.base_url, "https://api.anthropic.com");
6628    }
6629
6630    #[cfg(feature = "unstable_llm_providers")]
6631    #[test]
6632    fn test_provider_info_with_current_config() {
6633        let info = ProviderInfo::new(
6634            "main",
6635            vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
6636            true,
6637            Some(ProviderCurrentConfig::new(
6638                LlmProtocol::Anthropic,
6639                "https://api.anthropic.com",
6640            )),
6641        );
6642
6643        let json = serde_json::to_value(&info).unwrap();
6644        assert_eq!(
6645            json,
6646            json!({
6647                "providerId": "main",
6648                "supported": ["anthropic", "openai"],
6649                "required": true,
6650                "current": {
6651                    "apiType": "anthropic",
6652                    "baseUrl": "https://api.anthropic.com"
6653                }
6654            })
6655        );
6656
6657        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6658        assert_eq!(deserialized.provider_id.to_string(), "main");
6659        assert_eq!(deserialized.supported.len(), 2);
6660        assert!(deserialized.required);
6661        assert!(deserialized.current.is_some());
6662        assert_eq!(
6663            deserialized.current.as_ref().unwrap().api_type,
6664            LlmProtocol::Anthropic
6665        );
6666    }
6667
6668    #[cfg(feature = "unstable_llm_providers")]
6669    #[test]
6670    fn test_provider_info_disabled() {
6671        let info = ProviderInfo::new(
6672            "secondary",
6673            vec![LlmProtocol::OpenAi],
6674            false,
6675            None::<ProviderCurrentConfig>,
6676        );
6677
6678        let json = serde_json::to_value(&info).unwrap();
6679        assert_eq!(
6680            json,
6681            json!({
6682                "providerId": "secondary",
6683                "supported": ["openai"],
6684                "required": false
6685            })
6686        );
6687
6688        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6689        assert_eq!(deserialized.provider_id.to_string(), "secondary");
6690        assert!(!deserialized.required);
6691        assert!(deserialized.current.is_none());
6692    }
6693
6694    #[cfg(feature = "unstable_llm_providers")]
6695    #[test]
6696    fn test_provider_info_missing_current_defaults_to_none() {
6697        // current is optional; omitting it should decode as None
6698        let json = json!({
6699            "providerId": "main",
6700            "supported": ["anthropic"],
6701            "required": true
6702        });
6703        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6704        assert!(deserialized.current.is_none());
6705    }
6706
6707    #[cfg(feature = "unstable_llm_providers")]
6708    #[test]
6709    fn test_provider_info_explicit_null_current_decodes_to_none() {
6710        // current: null and an omitted current are equivalent on the wire;
6711        // both must deserialize into None so the disabled state is preserved
6712        // regardless of which form the peer chose to send.
6713        let json = json!({
6714            "providerId": "main",
6715            "supported": ["anthropic"],
6716            "required": true,
6717            "current": null
6718        });
6719        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6720        assert!(deserialized.current.is_none());
6721    }
6722
6723    #[cfg(feature = "unstable_llm_providers")]
6724    #[test]
6725    fn test_list_providers_response_serialization() {
6726        let response = ListProvidersResponse::new(vec![ProviderInfo::new(
6727            "main",
6728            vec![LlmProtocol::Anthropic],
6729            true,
6730            Some(ProviderCurrentConfig::new(
6731                LlmProtocol::Anthropic,
6732                "https://api.anthropic.com",
6733            )),
6734        )]);
6735
6736        let json = serde_json::to_value(&response).unwrap();
6737        assert_eq!(json["providers"].as_array().unwrap().len(), 1);
6738        assert_eq!(json["providers"][0]["providerId"], "main");
6739
6740        let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
6741        assert_eq!(deserialized.providers.len(), 1);
6742    }
6743
6744    #[cfg(feature = "unstable_llm_providers")]
6745    #[test]
6746    fn test_set_provider_request_serialization() {
6747        use std::collections::HashMap;
6748
6749        let mut headers = HashMap::new();
6750        headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
6751
6752        let request =
6753            SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
6754                .headers(headers);
6755
6756        let json = serde_json::to_value(&request).unwrap();
6757        assert_eq!(
6758            json,
6759            json!({
6760                "providerId": "main",
6761                "apiType": "openai",
6762                "baseUrl": "https://api.openai.com/v1",
6763                "headers": {
6764                    "Authorization": "Bearer sk-test"
6765                }
6766            })
6767        );
6768
6769        let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
6770        assert_eq!(deserialized.provider_id.to_string(), "main");
6771        assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
6772        assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
6773        assert_eq!(deserialized.headers.len(), 1);
6774        assert_eq!(
6775            deserialized.headers.get("Authorization").unwrap(),
6776            "Bearer sk-test"
6777        );
6778    }
6779
6780    #[cfg(feature = "unstable_llm_providers")]
6781    #[test]
6782    fn test_set_provider_request_omits_empty_headers() {
6783        let request =
6784            SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
6785
6786        let json = serde_json::to_value(&request).unwrap();
6787        // headers should be omitted when empty
6788        assert!(!json.as_object().unwrap().contains_key("headers"));
6789    }
6790
6791    #[cfg(feature = "unstable_llm_providers")]
6792    #[test]
6793    fn test_disable_provider_request_serialization() {
6794        let request = DisableProviderRequest::new("secondary");
6795
6796        let json = serde_json::to_value(&request).unwrap();
6797        assert_eq!(json, json!({ "providerId": "secondary" }));
6798
6799        let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
6800        assert_eq!(deserialized.provider_id.to_string(), "secondary");
6801    }
6802
6803    #[cfg(feature = "unstable_llm_providers")]
6804    #[test]
6805    fn test_providers_capabilities_serialization() {
6806        let caps = ProvidersCapabilities::new();
6807
6808        let json = serde_json::to_value(&caps).unwrap();
6809        assert_eq!(json, json!({}));
6810
6811        let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
6812        assert!(deserialized.meta.is_none());
6813    }
6814
6815    #[cfg(feature = "unstable_llm_providers")]
6816    #[test]
6817    fn test_agent_capabilities_with_providers() {
6818        let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
6819
6820        let json = serde_json::to_value(&caps).unwrap();
6821        assert_eq!(json["providers"], json!({}));
6822
6823        let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
6824        assert!(deserialized.providers.is_some());
6825    }
6826
6827    #[test]
6828    fn prompt_request_rejects_malformed_content_block() {
6829        use serde_json::json;
6830
6831        assert!(
6832            serde_json::from_value::<PromptRequest>(json!({
6833                "sessionId": "sess-1",
6834                "prompt": [{"type": "text"}]
6835            }))
6836            .is_err()
6837        );
6838    }
6839
6840    #[test]
6841    fn prompt_request_rejects_non_array_prompt() {
6842        use serde_json::json;
6843
6844        assert!(
6845            serde_json::from_value::<PromptRequest>(json!({
6846                "sessionId": "sess-1",
6847                "prompt": "hello"
6848            }))
6849            .is_err()
6850        );
6851    }
6852
6853    fn assert_session_setup_lists_are_strict<T>(base: &serde_json::Value)
6854    where
6855        T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
6856    {
6857        let name = std::any::type_name::<T>();
6858        let with = |field: &str, value: serde_json::Value| {
6859            let mut request = base.clone();
6860            request[field] = value;
6861            serde_json::from_value::<T>(request)
6862        };
6863
6864        for (field, value) in [
6865            // Dropping the malformed server would start the session without it.
6866            (
6867                "mcpServers",
6868                json!([
6869                    {"name": "github", "command": "/usr/bin/gh-mcp", "args": [], "env": [{"name": "GITHUB_TOKEN", "value": null}]},
6870                    {"name": "fs", "command": "/usr/bin/fs-mcp", "args": [], "env": []}
6871                ]),
6872            ),
6873            ("mcpServers", json!({"name": "fs"})),
6874            ("additionalDirectories", json!(["/repo/lib", 42])),
6875            ("additionalDirectories", json!("/repo/lib")),
6876        ] {
6877            assert!(
6878                with(field, value.clone()).is_err(),
6879                "{name}.{field}: {value}"
6880            );
6881        }
6882
6883        let empty = serde_json::from_value::<T>(base.clone()).unwrap();
6884        for field in ["mcpServers", "additionalDirectories"] {
6885            assert_eq!(
6886                with(field, serde_json::Value::Null).unwrap(),
6887                empty,
6888                "{name}.{field}"
6889            );
6890        }
6891    }
6892
6893    #[test]
6894    fn session_setup_requests_reject_malformed_mcp_servers_and_directories() {
6895        assert_session_setup_lists_are_strict::<NewSessionRequest>(&json!({
6896            "cwd": "/repo",
6897            "mcpServers": []
6898        }));
6899        assert_session_setup_lists_are_strict::<LoadSessionRequest>(&json!({
6900            "sessionId": "sess-1",
6901            "cwd": "/repo",
6902            "mcpServers": []
6903        }));
6904        assert_session_setup_lists_are_strict::<ResumeSessionRequest>(&json!({
6905            "sessionId": "sess-1",
6906            "cwd": "/repo"
6907        }));
6908        #[cfg(feature = "unstable_session_fork")]
6909        assert_session_setup_lists_are_strict::<ForkSessionRequest>(&json!({
6910            "sessionId": "sess-1",
6911            "cwd": "/repo"
6912        }));
6913    }
6914
6915    #[test]
6916    fn mcp_server_lists_treat_null_as_empty() {
6917        let servers: Vec<McpServer> = serde_json::from_value(json!([
6918            {"name": "fs", "command": "/usr/bin/fs-mcp", "args": null, "env": null},
6919            {"type": "http", "name": "api", "url": "https://example.com/mcp", "headers": null},
6920            {"type": "sse", "name": "events", "url": "https://example.com/sse", "headers": null}
6921        ]))
6922        .unwrap();
6923
6924        assert_eq!(
6925            servers,
6926            vec![
6927                McpServer::Stdio(McpServerStdio::new("fs", "/usr/bin/fs-mcp")),
6928                McpServer::Http(McpServerHttp::new("api", "https://example.com/mcp")),
6929                McpServer::Sse(McpServerSse::new("events", "https://example.com/sse")),
6930            ]
6931        );
6932    }
6933
6934    #[test]
6935    fn terminal_auth_methods_reject_malformed_launch_config() {
6936        // The client spawns a process from `args` and `env`, so a malformed
6937        // method is dropped from `authMethods` instead of launched with
6938        // altered arguments or reinterpreted as an `agent` method.
6939        let response: InitializeResponse = serde_json::from_value(json!({
6940            "protocolVersion": 1,
6941            "authMethods": [
6942                {"type": "terminal", "id": "bad-args", "name": "Log in", "args": ["auth", "--port", 8123]},
6943                {"type": "terminal", "id": "bad-env", "name": "Log in", "env": {"AUTH_MODE": "device", "DEBUG": 1}},
6944                {"type": "terminal", "id": "null-config", "name": "Log in", "args": null, "env": null},
6945                {"id": "agent", "name": "Agent login"}
6946            ]
6947        }))
6948        .unwrap();
6949
6950        assert_eq!(
6951            response.auth_methods,
6952            vec![
6953                AuthMethod::Terminal(AuthMethodTerminal::new("null-config", "Log in")),
6954                AuthMethod::Agent(AuthMethodAgent::new("agent", "Agent login")),
6955            ]
6956        );
6957        assert!(
6958            serde_json::from_value::<AuthMethod>(json!({
6959                "type": "terminal",
6960                "id": "bad-args",
6961                "name": "Log in",
6962                "args": ["auth", 8123]
6963            }))
6964            .is_err()
6965        );
6966    }
6967}