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