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