Skip to main content

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