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