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