Skip to main content

agent_client_protocol_schema/
agent.rs

1//! Methods and notifications the agent handles/receives.
2//!
3//! This module defines the Agent trait and all associated types for implementing
4//! an AI coding agent that follows the Agent Client Protocol (ACP).
5
6use std::{path::PathBuf, sync::Arc};
7
8#[cfg(feature = "unstable_auth_methods")]
9use std::collections::HashMap;
10
11use derive_more::{Display, From};
12use schemars::JsonSchema;
13use serde::{Deserialize, Serialize};
14
15use crate::{
16    ClientCapabilities, ContentBlock, ExtNotification, ExtRequest, ExtResponse, IntoOption, Meta,
17    ProtocolVersion, SessionId,
18};
19
20// Initialize
21
22/// Request parameters for the initialize method.
23///
24/// Sent by the client to establish connection and negotiate capabilities.
25///
26/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
27#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
28#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
29#[serde(rename_all = "camelCase")]
30#[non_exhaustive]
31pub struct InitializeRequest {
32    /// The latest protocol version supported by the client.
33    pub protocol_version: ProtocolVersion,
34    /// Capabilities supported by the client.
35    #[serde(default)]
36    pub client_capabilities: ClientCapabilities,
37    /// Information about the Client name and version sent to the Agent.
38    ///
39    /// Note: in future versions of the protocol, this will be required.
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub client_info: Option<Implementation>,
42    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
43    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
44    /// these keys.
45    ///
46    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
47    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
48    pub meta: Option<Meta>,
49}
50
51impl InitializeRequest {
52    #[must_use]
53    pub fn new(protocol_version: ProtocolVersion) -> Self {
54        Self {
55            protocol_version,
56            client_capabilities: ClientCapabilities::default(),
57            client_info: None,
58            meta: None,
59        }
60    }
61
62    /// Capabilities supported by the client.
63    #[must_use]
64    pub fn client_capabilities(mut self, client_capabilities: ClientCapabilities) -> Self {
65        self.client_capabilities = client_capabilities;
66        self
67    }
68
69    /// Information about the Client name and version sent to the Agent.
70    #[must_use]
71    pub fn client_info(mut self, client_info: impl IntoOption<Implementation>) -> Self {
72        self.client_info = client_info.into_option();
73        self
74    }
75
76    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
77    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
78    /// these keys.
79    ///
80    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
81    #[must_use]
82    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
83        self.meta = meta.into_option();
84        self
85    }
86}
87
88/// Response to the `initialize` method.
89///
90/// Contains the negotiated protocol version and agent capabilities.
91///
92/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
93#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
94#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
95#[serde(rename_all = "camelCase")]
96#[non_exhaustive]
97pub struct InitializeResponse {
98    /// The protocol version the client specified if supported by the agent,
99    /// or the latest protocol version supported by the agent.
100    ///
101    /// The client should disconnect, if it doesn't support this version.
102    pub protocol_version: ProtocolVersion,
103    /// Capabilities supported by the agent.
104    #[serde(default)]
105    pub agent_capabilities: AgentCapabilities,
106    /// Authentication methods supported by the agent.
107    #[serde(default)]
108    pub auth_methods: Vec<AuthMethod>,
109    /// Information about the Agent name and version sent to the Client.
110    ///
111    /// Note: in future versions of the protocol, this will be required.
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub agent_info: Option<Implementation>,
114    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
115    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
116    /// these keys.
117    ///
118    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
119    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
120    pub meta: Option<Meta>,
121}
122
123impl InitializeResponse {
124    #[must_use]
125    pub fn new(protocol_version: ProtocolVersion) -> Self {
126        Self {
127            protocol_version,
128            agent_capabilities: AgentCapabilities::default(),
129            auth_methods: vec![],
130            agent_info: None,
131            meta: None,
132        }
133    }
134
135    /// Capabilities supported by the agent.
136    #[must_use]
137    pub fn agent_capabilities(mut self, agent_capabilities: AgentCapabilities) -> Self {
138        self.agent_capabilities = agent_capabilities;
139        self
140    }
141
142    /// Authentication methods supported by the agent.
143    #[must_use]
144    pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
145        self.auth_methods = auth_methods;
146        self
147    }
148
149    /// Information about the Agent name and version sent to the Client.
150    #[must_use]
151    pub fn agent_info(mut self, agent_info: impl IntoOption<Implementation>) -> Self {
152        self.agent_info = agent_info.into_option();
153        self
154    }
155
156    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
157    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
158    /// these keys.
159    ///
160    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
161    #[must_use]
162    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
163        self.meta = meta.into_option();
164        self
165    }
166}
167
168/// Metadata about the implementation of the client or agent.
169/// Describes the name and version of an MCP implementation, with an optional
170/// title for UI representation.
171#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
172#[serde(rename_all = "camelCase")]
173#[non_exhaustive]
174pub struct Implementation {
175    /// Intended for programmatic or logical use, but can be used as a display
176    /// name fallback if title isn’t present.
177    pub name: String,
178    /// Intended for UI and end-user contexts — optimized to be human-readable
179    /// and easily understood.
180    ///
181    /// If not provided, the name should be used for display.
182    pub title: Option<String>,
183    /// Version of the implementation. Can be displayed to the user or used
184    /// for debugging or metrics purposes. (e.g. "1.0.0").
185    pub version: String,
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    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
192    pub meta: Option<Meta>,
193}
194
195impl Implementation {
196    #[must_use]
197    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
198        Self {
199            name: name.into(),
200            title: None,
201            version: version.into(),
202            meta: None,
203        }
204    }
205
206    /// Intended for UI and end-user contexts — optimized to be human-readable
207    /// and easily understood.
208    ///
209    /// If not provided, the name should be used for display.
210    #[must_use]
211    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
212        self.title = title.into_option();
213        self
214    }
215
216    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
217    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
218    /// these keys.
219    ///
220    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
221    #[must_use]
222    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
223        self.meta = meta.into_option();
224        self
225    }
226}
227
228// Authentication
229
230/// Request parameters for the authenticate method.
231///
232/// Specifies which authentication method to use.
233#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
234#[schemars(extend("x-side" = "agent", "x-method" = AUTHENTICATE_METHOD_NAME))]
235#[serde(rename_all = "camelCase")]
236#[non_exhaustive]
237pub struct AuthenticateRequest {
238    /// The ID of the authentication method to use.
239    /// Must be one of the methods advertised in the initialize response.
240    pub method_id: AuthMethodId,
241    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
242    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
243    /// these keys.
244    ///
245    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
246    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
247    pub meta: Option<Meta>,
248}
249
250impl AuthenticateRequest {
251    #[must_use]
252    pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
253        Self {
254            method_id: method_id.into(),
255            meta: None,
256        }
257    }
258
259    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
260    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
261    /// these keys.
262    ///
263    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
264    #[must_use]
265    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
266        self.meta = meta.into_option();
267        self
268    }
269}
270
271/// Response to the `authenticate` method.
272#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
273#[schemars(extend("x-side" = "agent", "x-method" = AUTHENTICATE_METHOD_NAME))]
274#[serde(rename_all = "camelCase")]
275#[non_exhaustive]
276pub struct AuthenticateResponse {
277    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
278    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
279    /// these keys.
280    ///
281    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
282    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
283    pub meta: Option<Meta>,
284}
285
286impl AuthenticateResponse {
287    #[must_use]
288    pub fn new() -> Self {
289        Self::default()
290    }
291
292    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
293    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
294    /// these keys.
295    ///
296    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
297    #[must_use]
298    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
299        self.meta = meta.into_option();
300        self
301    }
302}
303
304// Logout
305
306/// **UNSTABLE**
307///
308/// This capability is not part of the spec yet, and may be removed or changed at any point.
309///
310/// Request parameters for the logout method.
311///
312/// Terminates the current authenticated session.
313#[cfg(feature = "unstable_logout")]
314#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
315#[schemars(extend("x-side" = "agent", "x-method" = LOGOUT_METHOD_NAME))]
316#[serde(rename_all = "camelCase")]
317#[non_exhaustive]
318pub struct LogoutRequest {
319    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
320    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
321    /// these keys.
322    ///
323    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
324    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
325    pub meta: Option<Meta>,
326}
327
328#[cfg(feature = "unstable_logout")]
329impl LogoutRequest {
330    #[must_use]
331    pub fn new() -> Self {
332        Self::default()
333    }
334
335    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
336    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
337    /// these keys.
338    ///
339    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
340    #[must_use]
341    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
342        self.meta = meta.into_option();
343        self
344    }
345}
346
347/// **UNSTABLE**
348///
349/// This capability is not part of the spec yet, and may be removed or changed at any point.
350///
351/// Response to the `logout` method.
352#[cfg(feature = "unstable_logout")]
353#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
354#[schemars(extend("x-side" = "agent", "x-method" = LOGOUT_METHOD_NAME))]
355#[serde(rename_all = "camelCase")]
356#[non_exhaustive]
357pub struct LogoutResponse {
358    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
359    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
360    /// these keys.
361    ///
362    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
363    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
364    pub meta: Option<Meta>,
365}
366
367#[cfg(feature = "unstable_logout")]
368impl LogoutResponse {
369    #[must_use]
370    pub fn new() -> Self {
371        Self::default()
372    }
373
374    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
375    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
376    /// these keys.
377    ///
378    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
379    #[must_use]
380    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
381        self.meta = meta.into_option();
382        self
383    }
384}
385
386/// **UNSTABLE**
387///
388/// This capability is not part of the spec yet, and may be removed or changed at any point.
389///
390/// Authentication-related capabilities supported by the agent.
391#[cfg(feature = "unstable_logout")]
392#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
393#[serde(rename_all = "camelCase")]
394#[non_exhaustive]
395pub struct AgentAuthCapabilities {
396    /// Whether the agent supports the logout method.
397    ///
398    /// By supplying `{}` it means that the agent supports the logout method.
399    #[serde(skip_serializing_if = "Option::is_none")]
400    pub logout: Option<LogoutCapabilities>,
401    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
402    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
403    /// these keys.
404    ///
405    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
406    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
407    pub meta: Option<Meta>,
408}
409
410#[cfg(feature = "unstable_logout")]
411impl AgentAuthCapabilities {
412    #[must_use]
413    pub fn new() -> Self {
414        Self::default()
415    }
416
417    /// Whether the agent supports the logout method.
418    #[must_use]
419    pub fn logout(mut self, logout: impl IntoOption<LogoutCapabilities>) -> Self {
420        self.logout = logout.into_option();
421        self
422    }
423
424    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
425    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
426    /// these keys.
427    ///
428    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
429    #[must_use]
430    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
431        self.meta = meta.into_option();
432        self
433    }
434}
435
436/// **UNSTABLE**
437///
438/// This capability is not part of the spec yet, and may be removed or changed at any point.
439///
440/// Logout capabilities supported by the agent.
441///
442/// By supplying `{}` it means that the agent supports the logout method.
443#[cfg(feature = "unstable_logout")]
444#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
445#[non_exhaustive]
446pub struct LogoutCapabilities {
447    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
448    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
449    /// these keys.
450    ///
451    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
452    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
453    pub meta: Option<Meta>,
454}
455
456#[cfg(feature = "unstable_logout")]
457impl LogoutCapabilities {
458    #[must_use]
459    pub fn new() -> Self {
460        Self::default()
461    }
462
463    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
464    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
465    /// these keys.
466    ///
467    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
468    #[must_use]
469    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
470        self.meta = meta.into_option();
471        self
472    }
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
476#[serde(transparent)]
477#[from(Arc<str>, String, &'static str)]
478#[non_exhaustive]
479pub struct AuthMethodId(pub Arc<str>);
480
481impl AuthMethodId {
482    #[must_use]
483    pub fn new(id: impl Into<Arc<str>>) -> Self {
484        Self(id.into())
485    }
486}
487
488/// Describes an available authentication method.
489///
490/// The `type` field acts as the discriminator in the serialized JSON form.
491/// When no `type` is present, the method is treated as `agent`.
492#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
493#[serde(tag = "type", rename_all = "snake_case")]
494#[non_exhaustive]
495pub enum AuthMethod {
496    /// **UNSTABLE**
497    ///
498    /// This capability is not part of the spec yet, and may be removed or changed at any point.
499    ///
500    /// User provides a key that the client passes to the agent as an environment variable.
501    #[cfg(feature = "unstable_auth_methods")]
502    EnvVar(AuthMethodEnvVar),
503    /// **UNSTABLE**
504    ///
505    /// This capability is not part of the spec yet, and may be removed or changed at any point.
506    ///
507    /// Client runs an interactive terminal for the user to authenticate via a TUI.
508    #[cfg(feature = "unstable_auth_methods")]
509    Terminal(AuthMethodTerminal),
510    /// Agent handles authentication itself.
511    ///
512    /// This is the default when no `type` is specified.
513    #[serde(untagged)]
514    Agent(AuthMethodAgent),
515}
516
517impl AuthMethod {
518    /// The unique identifier for this authentication method.
519    #[must_use]
520    pub fn id(&self) -> &AuthMethodId {
521        match self {
522            Self::Agent(a) => &a.id,
523            #[cfg(feature = "unstable_auth_methods")]
524            Self::EnvVar(e) => &e.id,
525            #[cfg(feature = "unstable_auth_methods")]
526            Self::Terminal(t) => &t.id,
527        }
528    }
529
530    /// The human-readable name of this authentication method.
531    #[must_use]
532    pub fn name(&self) -> &str {
533        match self {
534            Self::Agent(a) => &a.name,
535            #[cfg(feature = "unstable_auth_methods")]
536            Self::EnvVar(e) => &e.name,
537            #[cfg(feature = "unstable_auth_methods")]
538            Self::Terminal(t) => &t.name,
539        }
540    }
541
542    /// Optional description providing more details about this authentication method.
543    #[must_use]
544    pub fn description(&self) -> Option<&str> {
545        match self {
546            Self::Agent(a) => a.description.as_deref(),
547            #[cfg(feature = "unstable_auth_methods")]
548            Self::EnvVar(e) => e.description.as_deref(),
549            #[cfg(feature = "unstable_auth_methods")]
550            Self::Terminal(t) => t.description.as_deref(),
551        }
552    }
553
554    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
555    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
556    /// these keys.
557    ///
558    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
559    #[must_use]
560    pub fn meta(&self) -> Option<&Meta> {
561        match self {
562            Self::Agent(a) => a.meta.as_ref(),
563            #[cfg(feature = "unstable_auth_methods")]
564            Self::EnvVar(e) => e.meta.as_ref(),
565            #[cfg(feature = "unstable_auth_methods")]
566            Self::Terminal(t) => t.meta.as_ref(),
567        }
568    }
569}
570
571/// Agent handles authentication itself.
572///
573/// This is the default authentication method type.
574#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
575#[serde(rename_all = "camelCase")]
576#[non_exhaustive]
577pub struct AuthMethodAgent {
578    /// Unique identifier for this authentication method.
579    pub id: AuthMethodId,
580    /// Human-readable name of the authentication method.
581    pub name: String,
582    /// Optional description providing more details about this authentication method.
583    #[serde(skip_serializing_if = "Option::is_none")]
584    pub description: Option<String>,
585    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
586    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
587    /// these keys.
588    ///
589    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
590    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
591    pub meta: Option<Meta>,
592}
593
594impl AuthMethodAgent {
595    #[must_use]
596    pub fn new(id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
597        Self {
598            id: id.into(),
599            name: name.into(),
600            description: None,
601            meta: None,
602        }
603    }
604
605    /// Optional description providing more details about this authentication method.
606    #[must_use]
607    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
608        self.description = description.into_option();
609        self
610    }
611
612    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
613    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
614    /// these keys.
615    ///
616    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
617    #[must_use]
618    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
619        self.meta = meta.into_option();
620        self
621    }
622}
623
624/// **UNSTABLE**
625///
626/// This capability is not part of the spec yet, and may be removed or changed at any point.
627///
628/// Environment variable authentication method.
629///
630/// The user provides credentials that the client passes to the agent as environment variables.
631#[cfg(feature = "unstable_auth_methods")]
632#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
633#[serde(rename_all = "camelCase")]
634#[non_exhaustive]
635pub struct AuthMethodEnvVar {
636    /// Unique identifier for this authentication method.
637    pub id: AuthMethodId,
638    /// Human-readable name of the authentication method.
639    pub name: String,
640    /// Optional description providing more details about this authentication method.
641    #[serde(skip_serializing_if = "Option::is_none")]
642    pub description: Option<String>,
643    /// The environment variables the client should set.
644    pub vars: Vec<AuthEnvVar>,
645    /// Optional link to a page where the user can obtain their credentials.
646    #[serde(skip_serializing_if = "Option::is_none")]
647    pub link: Option<String>,
648    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
649    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
650    /// these keys.
651    ///
652    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
653    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
654    pub meta: Option<Meta>,
655}
656
657#[cfg(feature = "unstable_auth_methods")]
658impl AuthMethodEnvVar {
659    #[must_use]
660    pub fn new(
661        id: impl Into<AuthMethodId>,
662        name: impl Into<String>,
663        vars: Vec<AuthEnvVar>,
664    ) -> Self {
665        Self {
666            id: id.into(),
667            name: name.into(),
668            description: None,
669            vars,
670            link: None,
671            meta: None,
672        }
673    }
674
675    /// Optional link to a page where the user can obtain their credentials.
676    #[must_use]
677    pub fn link(mut self, link: impl IntoOption<String>) -> Self {
678        self.link = link.into_option();
679        self
680    }
681
682    /// Optional description providing more details about this authentication method.
683    #[must_use]
684    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
685        self.description = description.into_option();
686        self
687    }
688
689    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
690    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
691    /// these keys.
692    ///
693    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
694    #[must_use]
695    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
696        self.meta = meta.into_option();
697        self
698    }
699}
700
701/// **UNSTABLE**
702///
703/// This capability is not part of the spec yet, and may be removed or changed at any point.
704///
705/// Describes a single environment variable for an [`AuthMethodEnvVar`] authentication method.
706#[cfg(feature = "unstable_auth_methods")]
707#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
708#[serde(rename_all = "camelCase")]
709#[non_exhaustive]
710pub struct AuthEnvVar {
711    /// The environment variable name (e.g. `"OPENAI_API_KEY"`).
712    pub name: String,
713    /// Human-readable label for this variable, displayed in client UI.
714    #[serde(skip_serializing_if = "Option::is_none")]
715    pub label: Option<String>,
716    /// Whether this value is a secret (e.g. API key, token).
717    /// Clients should use a password-style input for secret vars.
718    ///
719    /// Defaults to `true`.
720    #[serde(default = "default_true", skip_serializing_if = "is_true")]
721    #[schemars(extend("default" = true))]
722    pub secret: bool,
723    /// Whether this variable is optional.
724    ///
725    /// Defaults to `false`.
726    #[serde(default, skip_serializing_if = "is_false")]
727    #[schemars(extend("default" = false))]
728    pub optional: bool,
729    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
730    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
731    /// these keys.
732    ///
733    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
734    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
735    pub meta: Option<Meta>,
736}
737
738#[cfg(feature = "unstable_auth_methods")]
739fn default_true() -> bool {
740    true
741}
742
743#[cfg(feature = "unstable_auth_methods")]
744#[expect(clippy::trivially_copy_pass_by_ref)]
745fn is_true(v: &bool) -> bool {
746    *v
747}
748
749#[cfg(feature = "unstable_auth_methods")]
750#[expect(clippy::trivially_copy_pass_by_ref)]
751fn is_false(v: &bool) -> bool {
752    !*v
753}
754
755#[cfg(feature = "unstable_auth_methods")]
756impl AuthEnvVar {
757    /// Creates a new auth env var.
758    #[must_use]
759    pub fn new(name: impl Into<String>) -> Self {
760        Self {
761            name: name.into(),
762            label: None,
763            secret: true,
764            optional: false,
765            meta: None,
766        }
767    }
768
769    /// Human-readable label for this variable, displayed in client UI.
770    #[must_use]
771    pub fn label(mut self, label: impl IntoOption<String>) -> Self {
772        self.label = label.into_option();
773        self
774    }
775
776    /// Whether this value is a secret (e.g. API key, token).
777    /// Clients should use a password-style input for secret vars.
778    #[must_use]
779    pub fn secret(mut self, secret: bool) -> Self {
780        self.secret = secret;
781        self
782    }
783
784    /// Whether this variable is optional.
785    #[must_use]
786    pub fn optional(mut self, optional: bool) -> Self {
787        self.optional = optional;
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/// Terminal-based authentication method.
808///
809/// The client runs an interactive terminal for the user to authenticate via a TUI.
810#[cfg(feature = "unstable_auth_methods")]
811#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
812#[serde(rename_all = "camelCase")]
813#[non_exhaustive]
814pub struct AuthMethodTerminal {
815    /// Unique identifier for this authentication method.
816    pub id: AuthMethodId,
817    /// Human-readable name of the authentication method.
818    pub name: String,
819    /// Optional description providing more details about this authentication method.
820    #[serde(skip_serializing_if = "Option::is_none")]
821    pub description: Option<String>,
822    /// Additional arguments to pass when running the agent binary for terminal auth.
823    #[serde(default, skip_serializing_if = "Vec::is_empty")]
824    pub args: Vec<String>,
825    /// Additional environment variables to set when running the agent binary for terminal auth.
826    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
827    pub env: HashMap<String, String>,
828    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
829    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
830    /// these keys.
831    ///
832    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
833    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
834    pub meta: Option<Meta>,
835}
836
837#[cfg(feature = "unstable_auth_methods")]
838impl AuthMethodTerminal {
839    #[must_use]
840    pub fn new(id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
841        Self {
842            id: id.into(),
843            name: name.into(),
844            description: None,
845            args: Vec::new(),
846            env: HashMap::new(),
847            meta: None,
848        }
849    }
850
851    /// Additional arguments to pass when running the agent binary for terminal auth.
852    #[must_use]
853    pub fn args(mut self, args: Vec<String>) -> Self {
854        self.args = args;
855        self
856    }
857
858    /// Additional environment variables to set when running the agent binary for terminal auth.
859    #[must_use]
860    pub fn env(mut self, env: HashMap<String, String>) -> Self {
861        self.env = env;
862        self
863    }
864
865    /// Optional description providing more details about this authentication method.
866    #[must_use]
867    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
868        self.description = description.into_option();
869        self
870    }
871
872    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
873    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
874    /// these keys.
875    ///
876    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
877    #[must_use]
878    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
879        self.meta = meta.into_option();
880        self
881    }
882}
883
884// New session
885
886/// Request parameters for creating a new session.
887///
888/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)
889#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
890#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
891#[serde(rename_all = "camelCase")]
892#[non_exhaustive]
893pub struct NewSessionRequest {
894    /// The working directory for this session. Must be an absolute path.
895    pub cwd: PathBuf,
896    /// List of MCP (Model Context Protocol) servers the agent should connect to.
897    pub mcp_servers: Vec<McpServer>,
898    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
899    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
900    /// these keys.
901    ///
902    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
903    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
904    pub meta: Option<Meta>,
905}
906
907impl NewSessionRequest {
908    #[must_use]
909    pub fn new(cwd: impl Into<PathBuf>) -> Self {
910        Self {
911            cwd: cwd.into(),
912            mcp_servers: vec![],
913            meta: None,
914        }
915    }
916
917    /// List of MCP (Model Context Protocol) servers the agent should connect to.
918    #[must_use]
919    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
920        self.mcp_servers = mcp_servers;
921        self
922    }
923
924    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
925    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
926    /// these keys.
927    ///
928    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
929    #[must_use]
930    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
931        self.meta = meta.into_option();
932        self
933    }
934}
935
936/// Response from creating a new session.
937///
938/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)
939#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
940#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
941#[serde(rename_all = "camelCase")]
942#[non_exhaustive]
943pub struct NewSessionResponse {
944    /// Unique identifier for the created session.
945    ///
946    /// Used in all subsequent requests for this conversation.
947    pub session_id: SessionId,
948    /// Initial mode state if supported by the Agent
949    ///
950    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
951    #[serde(skip_serializing_if = "Option::is_none")]
952    pub modes: Option<SessionModeState>,
953    /// **UNSTABLE**
954    ///
955    /// This capability is not part of the spec yet, and may be removed or changed at any point.
956    ///
957    /// Initial model state if supported by the Agent
958    #[cfg(feature = "unstable_session_model")]
959    #[serde(skip_serializing_if = "Option::is_none")]
960    pub models: Option<SessionModelState>,
961    /// Initial session configuration options if supported by the Agent.
962    #[serde(skip_serializing_if = "Option::is_none")]
963    pub config_options: Option<Vec<SessionConfigOption>>,
964    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
965    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
966    /// these keys.
967    ///
968    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
969    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
970    pub meta: Option<Meta>,
971}
972
973impl NewSessionResponse {
974    #[must_use]
975    pub fn new(session_id: impl Into<SessionId>) -> Self {
976        Self {
977            session_id: session_id.into(),
978            modes: None,
979            #[cfg(feature = "unstable_session_model")]
980            models: None,
981            config_options: None,
982            meta: None,
983        }
984    }
985
986    /// Initial mode state if supported by the Agent
987    ///
988    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
989    #[must_use]
990    pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
991        self.modes = modes.into_option();
992        self
993    }
994
995    /// **UNSTABLE**
996    ///
997    /// This capability is not part of the spec yet, and may be removed or changed at any point.
998    ///
999    /// Initial model state if supported by the Agent
1000    #[cfg(feature = "unstable_session_model")]
1001    #[must_use]
1002    pub fn models(mut self, models: impl IntoOption<SessionModelState>) -> Self {
1003        self.models = models.into_option();
1004        self
1005    }
1006
1007    /// Initial session configuration options if supported by the Agent.
1008    #[must_use]
1009    pub fn config_options(
1010        mut self,
1011        config_options: impl IntoOption<Vec<SessionConfigOption>>,
1012    ) -> Self {
1013        self.config_options = config_options.into_option();
1014        self
1015    }
1016
1017    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1018    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1019    /// these keys.
1020    ///
1021    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1022    #[must_use]
1023    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1024        self.meta = meta.into_option();
1025        self
1026    }
1027}
1028
1029// Load session
1030
1031/// Request parameters for loading an existing session.
1032///
1033/// Only available if the Agent supports the `loadSession` capability.
1034///
1035/// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)
1036#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1037#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME))]
1038#[serde(rename_all = "camelCase")]
1039#[non_exhaustive]
1040pub struct LoadSessionRequest {
1041    /// List of MCP servers to connect to for this session.
1042    pub mcp_servers: Vec<McpServer>,
1043    /// The working directory for this session.
1044    pub cwd: PathBuf,
1045    /// The ID of the session to load.
1046    pub session_id: SessionId,
1047    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1048    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1049    /// these keys.
1050    ///
1051    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1052    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1053    pub meta: Option<Meta>,
1054}
1055
1056impl LoadSessionRequest {
1057    #[must_use]
1058    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1059        Self {
1060            mcp_servers: vec![],
1061            cwd: cwd.into(),
1062            session_id: session_id.into(),
1063            meta: None,
1064        }
1065    }
1066
1067    /// List of MCP servers to connect to for this session.
1068    #[must_use]
1069    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1070        self.mcp_servers = mcp_servers;
1071        self
1072    }
1073
1074    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1075    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1076    /// these keys.
1077    ///
1078    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1079    #[must_use]
1080    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1081        self.meta = meta.into_option();
1082        self
1083    }
1084}
1085
1086/// Response from loading an existing session.
1087#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1088#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME))]
1089#[serde(rename_all = "camelCase")]
1090#[non_exhaustive]
1091pub struct LoadSessionResponse {
1092    /// Initial mode state if supported by the Agent
1093    ///
1094    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1095    #[serde(default, skip_serializing_if = "Option::is_none")]
1096    pub modes: Option<SessionModeState>,
1097    /// **UNSTABLE**
1098    ///
1099    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1100    ///
1101    /// Initial model state if supported by the Agent
1102    #[cfg(feature = "unstable_session_model")]
1103    #[serde(default, skip_serializing_if = "Option::is_none")]
1104    pub models: Option<SessionModelState>,
1105    /// Initial session configuration options if supported by the Agent.
1106    #[serde(default, skip_serializing_if = "Option::is_none")]
1107    pub config_options: Option<Vec<SessionConfigOption>>,
1108    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1109    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1110    /// these keys.
1111    ///
1112    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1113    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1114    pub meta: Option<Meta>,
1115}
1116
1117impl LoadSessionResponse {
1118    #[must_use]
1119    pub fn new() -> Self {
1120        Self::default()
1121    }
1122
1123    /// Initial mode state if supported by the Agent
1124    ///
1125    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1126    #[must_use]
1127    pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
1128        self.modes = modes.into_option();
1129        self
1130    }
1131
1132    /// **UNSTABLE**
1133    ///
1134    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1135    ///
1136    /// Initial model state if supported by the Agent
1137    #[cfg(feature = "unstable_session_model")]
1138    #[must_use]
1139    pub fn models(mut self, models: impl IntoOption<SessionModelState>) -> Self {
1140        self.models = models.into_option();
1141        self
1142    }
1143
1144    /// Initial session configuration options if supported by the Agent.
1145    #[must_use]
1146    pub fn config_options(
1147        mut self,
1148        config_options: impl IntoOption<Vec<SessionConfigOption>>,
1149    ) -> Self {
1150        self.config_options = config_options.into_option();
1151        self
1152    }
1153
1154    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1155    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1156    /// these keys.
1157    ///
1158    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1159    #[must_use]
1160    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1161        self.meta = meta.into_option();
1162        self
1163    }
1164}
1165
1166// Fork session
1167
1168/// **UNSTABLE**
1169///
1170/// This capability is not part of the spec yet, and may be removed or changed at any point.
1171///
1172/// Request parameters for forking an existing session.
1173///
1174/// Creates a new session based on the context of an existing one, allowing
1175/// operations like generating summaries without affecting the original session's history.
1176///
1177/// Only available if the Agent supports the `session.fork` capability.
1178#[cfg(feature = "unstable_session_fork")]
1179#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1180#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))]
1181#[serde(rename_all = "camelCase")]
1182#[non_exhaustive]
1183pub struct ForkSessionRequest {
1184    /// The ID of the session to fork.
1185    pub session_id: SessionId,
1186    /// The working directory for this session.
1187    pub cwd: PathBuf,
1188    /// List of MCP servers to connect to for this session.
1189    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1190    pub mcp_servers: Vec<McpServer>,
1191    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1192    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1193    /// these keys.
1194    ///
1195    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1196    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1197    pub meta: Option<Meta>,
1198}
1199
1200#[cfg(feature = "unstable_session_fork")]
1201impl ForkSessionRequest {
1202    #[must_use]
1203    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1204        Self {
1205            session_id: session_id.into(),
1206            cwd: cwd.into(),
1207            mcp_servers: vec![],
1208            meta: None,
1209        }
1210    }
1211
1212    /// List of MCP servers to connect to for this session.
1213    #[must_use]
1214    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1215        self.mcp_servers = mcp_servers;
1216        self
1217    }
1218
1219    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1220    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1221    /// these keys.
1222    ///
1223    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1224    #[must_use]
1225    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1226        self.meta = meta.into_option();
1227        self
1228    }
1229}
1230
1231/// **UNSTABLE**
1232///
1233/// This capability is not part of the spec yet, and may be removed or changed at any point.
1234///
1235/// Response from forking an existing session.
1236#[cfg(feature = "unstable_session_fork")]
1237#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1238#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))]
1239#[serde(rename_all = "camelCase")]
1240#[non_exhaustive]
1241pub struct ForkSessionResponse {
1242    /// Unique identifier for the newly created forked session.
1243    pub session_id: SessionId,
1244    /// Initial mode state if supported by the Agent
1245    ///
1246    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1247    #[serde(skip_serializing_if = "Option::is_none")]
1248    pub modes: Option<SessionModeState>,
1249    /// **UNSTABLE**
1250    ///
1251    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1252    ///
1253    /// Initial model state if supported by the Agent
1254    #[cfg(feature = "unstable_session_model")]
1255    #[serde(skip_serializing_if = "Option::is_none")]
1256    pub models: Option<SessionModelState>,
1257    /// Initial session configuration options if supported by the Agent.
1258    #[serde(skip_serializing_if = "Option::is_none")]
1259    pub config_options: Option<Vec<SessionConfigOption>>,
1260    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1261    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1262    /// these keys.
1263    ///
1264    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1265    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1266    pub meta: Option<Meta>,
1267}
1268
1269#[cfg(feature = "unstable_session_fork")]
1270impl ForkSessionResponse {
1271    #[must_use]
1272    pub fn new(session_id: impl Into<SessionId>) -> Self {
1273        Self {
1274            session_id: session_id.into(),
1275            modes: None,
1276            #[cfg(feature = "unstable_session_model")]
1277            models: None,
1278            config_options: None,
1279            meta: None,
1280        }
1281    }
1282
1283    /// Initial mode state if supported by the Agent
1284    ///
1285    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1286    #[must_use]
1287    pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
1288        self.modes = modes.into_option();
1289        self
1290    }
1291
1292    /// **UNSTABLE**
1293    ///
1294    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1295    ///
1296    /// Initial model state if supported by the Agent
1297    #[cfg(feature = "unstable_session_model")]
1298    #[must_use]
1299    pub fn models(mut self, models: impl IntoOption<SessionModelState>) -> Self {
1300        self.models = models.into_option();
1301        self
1302    }
1303
1304    /// Initial session configuration options if supported by the Agent.
1305    #[must_use]
1306    pub fn config_options(
1307        mut self,
1308        config_options: impl IntoOption<Vec<SessionConfigOption>>,
1309    ) -> Self {
1310        self.config_options = config_options.into_option();
1311        self
1312    }
1313
1314    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1315    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1316    /// these keys.
1317    ///
1318    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1319    #[must_use]
1320    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1321        self.meta = meta.into_option();
1322        self
1323    }
1324}
1325
1326// Resume session
1327
1328/// **UNSTABLE**
1329///
1330/// This capability is not part of the spec yet, and may be removed or changed at any point.
1331///
1332/// Request parameters for resuming an existing session.
1333///
1334/// Resumes an existing session without returning previous messages (unlike `session/load`).
1335/// This is useful for agents that can resume sessions but don't implement full session loading.
1336///
1337/// Only available if the Agent supports the `session.resume` capability.
1338#[cfg(feature = "unstable_session_resume")]
1339#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1340#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))]
1341#[serde(rename_all = "camelCase")]
1342#[non_exhaustive]
1343pub struct ResumeSessionRequest {
1344    /// The ID of the session to resume.
1345    pub session_id: SessionId,
1346    /// The working directory for this session.
1347    pub cwd: PathBuf,
1348    /// List of MCP servers to connect to for this session.
1349    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1350    pub mcp_servers: Vec<McpServer>,
1351    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1352    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1353    /// these keys.
1354    ///
1355    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1356    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1357    pub meta: Option<Meta>,
1358}
1359
1360#[cfg(feature = "unstable_session_resume")]
1361impl ResumeSessionRequest {
1362    #[must_use]
1363    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1364        Self {
1365            session_id: session_id.into(),
1366            cwd: cwd.into(),
1367            mcp_servers: vec![],
1368            meta: None,
1369        }
1370    }
1371
1372    /// List of MCP servers to connect to for this session.
1373    #[must_use]
1374    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1375        self.mcp_servers = mcp_servers;
1376        self
1377    }
1378
1379    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1380    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1381    /// these keys.
1382    ///
1383    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1384    #[must_use]
1385    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1386        self.meta = meta.into_option();
1387        self
1388    }
1389}
1390
1391/// **UNSTABLE**
1392///
1393/// This capability is not part of the spec yet, and may be removed or changed at any point.
1394///
1395/// Response from resuming an existing session.
1396#[cfg(feature = "unstable_session_resume")]
1397#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1398#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))]
1399#[serde(rename_all = "camelCase")]
1400#[non_exhaustive]
1401pub struct ResumeSessionResponse {
1402    /// Initial mode state if supported by the Agent
1403    ///
1404    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1405    #[serde(default, skip_serializing_if = "Option::is_none")]
1406    pub modes: Option<SessionModeState>,
1407    /// **UNSTABLE**
1408    ///
1409    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1410    ///
1411    /// Initial model state if supported by the Agent
1412    #[cfg(feature = "unstable_session_model")]
1413    #[serde(default, skip_serializing_if = "Option::is_none")]
1414    pub models: Option<SessionModelState>,
1415    /// Initial session configuration options if supported by the Agent.
1416    #[serde(default, skip_serializing_if = "Option::is_none")]
1417    pub config_options: Option<Vec<SessionConfigOption>>,
1418    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1419    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1420    /// these keys.
1421    ///
1422    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1423    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1424    pub meta: Option<Meta>,
1425}
1426
1427#[cfg(feature = "unstable_session_resume")]
1428impl ResumeSessionResponse {
1429    #[must_use]
1430    pub fn new() -> Self {
1431        Self::default()
1432    }
1433
1434    /// Initial mode state if supported by the Agent
1435    ///
1436    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1437    #[must_use]
1438    pub fn modes(mut self, modes: impl IntoOption<SessionModeState>) -> Self {
1439        self.modes = modes.into_option();
1440        self
1441    }
1442
1443    /// **UNSTABLE**
1444    ///
1445    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1446    ///
1447    /// Initial model state if supported by the Agent
1448    #[cfg(feature = "unstable_session_model")]
1449    #[must_use]
1450    pub fn models(mut self, models: impl IntoOption<SessionModelState>) -> Self {
1451        self.models = models.into_option();
1452        self
1453    }
1454
1455    /// Initial session configuration options if supported by the Agent.
1456    #[must_use]
1457    pub fn config_options(
1458        mut self,
1459        config_options: impl IntoOption<Vec<SessionConfigOption>>,
1460    ) -> Self {
1461        self.config_options = config_options.into_option();
1462        self
1463    }
1464
1465    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1466    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1467    /// these keys.
1468    ///
1469    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1470    #[must_use]
1471    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1472        self.meta = meta.into_option();
1473        self
1474    }
1475}
1476
1477// Close session
1478
1479/// **UNSTABLE**
1480///
1481/// This capability is not part of the spec yet, and may be removed or changed at any point.
1482///
1483/// Request parameters for closing an active session.
1484///
1485/// If supported, the agent **must** cancel any ongoing work related to the session
1486/// (treat it as if `session/cancel` was called) and then free up any resources
1487/// associated with the session.
1488///
1489/// Only available if the Agent supports the `session.close` capability.
1490#[cfg(feature = "unstable_session_close")]
1491#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1492#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME))]
1493#[serde(rename_all = "camelCase")]
1494#[non_exhaustive]
1495pub struct CloseSessionRequest {
1496    /// The ID of the session to close.
1497    pub session_id: SessionId,
1498    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1499    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1500    /// these keys.
1501    ///
1502    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1503    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1504    pub meta: Option<Meta>,
1505}
1506
1507#[cfg(feature = "unstable_session_close")]
1508impl CloseSessionRequest {
1509    #[must_use]
1510    pub fn new(session_id: impl Into<SessionId>) -> Self {
1511        Self {
1512            session_id: session_id.into(),
1513            meta: None,
1514        }
1515    }
1516
1517    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1518    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1519    /// these keys.
1520    ///
1521    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1522    #[must_use]
1523    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1524        self.meta = meta.into_option();
1525        self
1526    }
1527}
1528
1529/// **UNSTABLE**
1530///
1531/// This capability is not part of the spec yet, and may be removed or changed at any point.
1532///
1533/// Response from closing a session.
1534#[cfg(feature = "unstable_session_close")]
1535#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1536#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME))]
1537#[serde(rename_all = "camelCase")]
1538#[non_exhaustive]
1539pub struct CloseSessionResponse {
1540    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1541    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1542    /// these keys.
1543    ///
1544    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1545    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1546    pub meta: Option<Meta>,
1547}
1548
1549#[cfg(feature = "unstable_session_close")]
1550impl CloseSessionResponse {
1551    #[must_use]
1552    pub fn new() -> Self {
1553        Self::default()
1554    }
1555
1556    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1557    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1558    /// these keys.
1559    ///
1560    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1561    #[must_use]
1562    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1563        self.meta = meta.into_option();
1564        self
1565    }
1566}
1567
1568// List sessions
1569
1570/// Request parameters for listing existing sessions.
1571///
1572/// Only available if the Agent supports the `sessionCapabilities.list` capability.
1573#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1574#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME))]
1575#[serde(rename_all = "camelCase")]
1576#[non_exhaustive]
1577pub struct ListSessionsRequest {
1578    /// Filter sessions by working directory. Must be an absolute path.
1579    #[serde(skip_serializing_if = "Option::is_none")]
1580    pub cwd: Option<PathBuf>,
1581    /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
1582    #[serde(skip_serializing_if = "Option::is_none")]
1583    pub cursor: Option<String>,
1584    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1585    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1586    /// these keys.
1587    ///
1588    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1589    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1590    pub meta: Option<Meta>,
1591}
1592
1593impl ListSessionsRequest {
1594    #[must_use]
1595    pub fn new() -> Self {
1596        Self::default()
1597    }
1598
1599    /// Filter sessions by working directory. Must be an absolute path.
1600    #[must_use]
1601    pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1602        self.cwd = cwd.into_option();
1603        self
1604    }
1605
1606    /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
1607    #[must_use]
1608    pub fn cursor(mut self, cursor: impl IntoOption<String>) -> Self {
1609        self.cursor = cursor.into_option();
1610        self
1611    }
1612
1613    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1614    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1615    /// these keys.
1616    ///
1617    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1618    #[must_use]
1619    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1620        self.meta = meta.into_option();
1621        self
1622    }
1623}
1624
1625/// Response from listing sessions.
1626#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1627#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME))]
1628#[serde(rename_all = "camelCase")]
1629#[non_exhaustive]
1630pub struct ListSessionsResponse {
1631    /// Array of session information objects
1632    pub sessions: Vec<SessionInfo>,
1633    /// Opaque cursor token. If present, pass this in the next request's cursor parameter
1634    /// to fetch the next page. If absent, there are no more results.
1635    #[serde(skip_serializing_if = "Option::is_none")]
1636    pub next_cursor: Option<String>,
1637    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1638    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1639    /// these keys.
1640    ///
1641    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1642    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1643    pub meta: Option<Meta>,
1644}
1645
1646impl ListSessionsResponse {
1647    #[must_use]
1648    pub fn new(sessions: Vec<SessionInfo>) -> Self {
1649        Self {
1650            sessions,
1651            next_cursor: None,
1652            meta: None,
1653        }
1654    }
1655
1656    #[must_use]
1657    pub fn next_cursor(mut self, next_cursor: impl IntoOption<String>) -> Self {
1658        self.next_cursor = next_cursor.into_option();
1659        self
1660    }
1661
1662    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1663    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1664    /// these keys.
1665    ///
1666    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1667    #[must_use]
1668    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1669        self.meta = meta.into_option();
1670        self
1671    }
1672}
1673
1674/// Information about a session returned by session/list
1675#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1676#[serde(rename_all = "camelCase")]
1677#[non_exhaustive]
1678pub struct SessionInfo {
1679    /// Unique identifier for the session
1680    pub session_id: SessionId,
1681    /// The working directory for this session. Must be an absolute path.
1682    pub cwd: PathBuf,
1683    /// Human-readable title for the session
1684    #[serde(skip_serializing_if = "Option::is_none")]
1685    pub title: Option<String>,
1686    /// ISO 8601 timestamp of last activity
1687    #[serde(skip_serializing_if = "Option::is_none")]
1688    pub updated_at: Option<String>,
1689    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1690    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1691    /// these keys.
1692    ///
1693    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1694    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1695    pub meta: Option<Meta>,
1696}
1697
1698impl SessionInfo {
1699    #[must_use]
1700    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<PathBuf>) -> Self {
1701        Self {
1702            session_id: session_id.into(),
1703            cwd: cwd.into(),
1704            title: None,
1705            updated_at: None,
1706            meta: None,
1707        }
1708    }
1709
1710    /// Human-readable title for the session
1711    #[must_use]
1712    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
1713        self.title = title.into_option();
1714        self
1715    }
1716
1717    /// ISO 8601 timestamp of last activity
1718    #[must_use]
1719    pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
1720        self.updated_at = updated_at.into_option();
1721        self
1722    }
1723
1724    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1725    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1726    /// these keys.
1727    ///
1728    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1729    #[must_use]
1730    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1731        self.meta = meta.into_option();
1732        self
1733    }
1734}
1735
1736// Session modes
1737
1738/// The set of modes and the one currently active.
1739#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1740#[serde(rename_all = "camelCase")]
1741#[non_exhaustive]
1742pub struct SessionModeState {
1743    /// The current mode the Agent is in.
1744    pub current_mode_id: SessionModeId,
1745    /// The set of modes that the Agent can operate in
1746    pub available_modes: Vec<SessionMode>,
1747    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1748    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1749    /// these keys.
1750    ///
1751    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1752    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1753    pub meta: Option<Meta>,
1754}
1755
1756impl SessionModeState {
1757    #[must_use]
1758    pub fn new(
1759        current_mode_id: impl Into<SessionModeId>,
1760        available_modes: Vec<SessionMode>,
1761    ) -> Self {
1762        Self {
1763            current_mode_id: current_mode_id.into(),
1764            available_modes,
1765            meta: None,
1766        }
1767    }
1768
1769    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1770    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1771    /// these keys.
1772    ///
1773    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1774    #[must_use]
1775    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1776        self.meta = meta.into_option();
1777        self
1778    }
1779}
1780
1781/// A mode the agent can operate in.
1782///
1783/// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
1784#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1785#[serde(rename_all = "camelCase")]
1786#[non_exhaustive]
1787pub struct SessionMode {
1788    pub id: SessionModeId,
1789    pub name: String,
1790    #[serde(default, skip_serializing_if = "Option::is_none")]
1791    pub description: Option<String>,
1792    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1793    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1794    /// these keys.
1795    ///
1796    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1797    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1798    pub meta: Option<Meta>,
1799}
1800
1801impl SessionMode {
1802    #[must_use]
1803    pub fn new(id: impl Into<SessionModeId>, name: impl Into<String>) -> Self {
1804        Self {
1805            id: id.into(),
1806            name: name.into(),
1807            description: None,
1808            meta: None,
1809        }
1810    }
1811
1812    #[must_use]
1813    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1814        self.description = description.into_option();
1815        self
1816    }
1817
1818    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1819    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1820    /// these keys.
1821    ///
1822    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1823    #[must_use]
1824    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1825        self.meta = meta.into_option();
1826        self
1827    }
1828}
1829
1830/// Unique identifier for a Session Mode.
1831#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
1832#[serde(transparent)]
1833#[from(Arc<str>, String, &'static str)]
1834#[non_exhaustive]
1835pub struct SessionModeId(pub Arc<str>);
1836
1837impl SessionModeId {
1838    #[must_use]
1839    pub fn new(id: impl Into<Arc<str>>) -> Self {
1840        Self(id.into())
1841    }
1842}
1843
1844/// Request parameters for setting a session mode.
1845#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1846#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODE_METHOD_NAME))]
1847#[serde(rename_all = "camelCase")]
1848#[non_exhaustive]
1849pub struct SetSessionModeRequest {
1850    /// The ID of the session to set the mode for.
1851    pub session_id: SessionId,
1852    /// The ID of the mode to set.
1853    pub mode_id: SessionModeId,
1854    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1855    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1856    /// these keys.
1857    ///
1858    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1859    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1860    pub meta: Option<Meta>,
1861}
1862
1863impl SetSessionModeRequest {
1864    #[must_use]
1865    pub fn new(session_id: impl Into<SessionId>, mode_id: impl Into<SessionModeId>) -> Self {
1866        Self {
1867            session_id: session_id.into(),
1868            mode_id: mode_id.into(),
1869            meta: None,
1870        }
1871    }
1872
1873    #[must_use]
1874    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1875        self.meta = meta.into_option();
1876        self
1877    }
1878}
1879
1880/// Response to `session/set_mode` method.
1881#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1882#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODE_METHOD_NAME))]
1883#[serde(rename_all = "camelCase")]
1884#[non_exhaustive]
1885pub struct SetSessionModeResponse {
1886    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1887    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1888    /// these keys.
1889    ///
1890    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1891    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1892    pub meta: Option<Meta>,
1893}
1894
1895impl SetSessionModeResponse {
1896    #[must_use]
1897    pub fn new() -> Self {
1898        Self::default()
1899    }
1900
1901    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1902    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1903    /// these keys.
1904    ///
1905    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1906    #[must_use]
1907    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1908        self.meta = meta.into_option();
1909        self
1910    }
1911}
1912
1913// Session config options
1914
1915/// Unique identifier for a session configuration option.
1916#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
1917#[serde(transparent)]
1918#[from(Arc<str>, String, &'static str)]
1919#[non_exhaustive]
1920pub struct SessionConfigId(pub Arc<str>);
1921
1922impl SessionConfigId {
1923    #[must_use]
1924    pub fn new(id: impl Into<Arc<str>>) -> Self {
1925        Self(id.into())
1926    }
1927}
1928
1929/// Unique identifier for a session configuration option value.
1930#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
1931#[serde(transparent)]
1932#[from(Arc<str>, String, &'static str)]
1933#[non_exhaustive]
1934pub struct SessionConfigValueId(pub Arc<str>);
1935
1936impl SessionConfigValueId {
1937    #[must_use]
1938    pub fn new(id: impl Into<Arc<str>>) -> Self {
1939        Self(id.into())
1940    }
1941}
1942
1943/// Unique identifier for a session configuration option value group.
1944#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, From, Display)]
1945#[serde(transparent)]
1946#[from(Arc<str>, String, &'static str)]
1947#[non_exhaustive]
1948pub struct SessionConfigGroupId(pub Arc<str>);
1949
1950impl SessionConfigGroupId {
1951    #[must_use]
1952    pub fn new(id: impl Into<Arc<str>>) -> Self {
1953        Self(id.into())
1954    }
1955}
1956
1957/// A possible value for a session configuration option.
1958#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1959#[serde(rename_all = "camelCase")]
1960#[non_exhaustive]
1961pub struct SessionConfigSelectOption {
1962    /// Unique identifier for this option value.
1963    pub value: SessionConfigValueId,
1964    /// Human-readable label for this option value.
1965    pub name: String,
1966    /// Optional description for this option value.
1967    #[serde(default, skip_serializing_if = "Option::is_none")]
1968    pub description: Option<String>,
1969    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1970    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1971    /// these keys.
1972    ///
1973    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1974    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1975    pub meta: Option<Meta>,
1976}
1977
1978impl SessionConfigSelectOption {
1979    #[must_use]
1980    pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
1981        Self {
1982            value: value.into(),
1983            name: name.into(),
1984            description: None,
1985            meta: None,
1986        }
1987    }
1988
1989    #[must_use]
1990    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1991        self.description = description.into_option();
1992        self
1993    }
1994
1995    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1996    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1997    /// these keys.
1998    ///
1999    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2000    #[must_use]
2001    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2002        self.meta = meta.into_option();
2003        self
2004    }
2005}
2006
2007/// A group of possible values for a session configuration option.
2008#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2009#[serde(rename_all = "camelCase")]
2010#[non_exhaustive]
2011pub struct SessionConfigSelectGroup {
2012    /// Unique identifier for this group.
2013    pub group: SessionConfigGroupId,
2014    /// Human-readable label for this group.
2015    pub name: String,
2016    /// The set of option values in this group.
2017    pub options: Vec<SessionConfigSelectOption>,
2018    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2019    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2020    /// these keys.
2021    ///
2022    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2023    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2024    pub meta: Option<Meta>,
2025}
2026
2027impl SessionConfigSelectGroup {
2028    #[must_use]
2029    pub fn new(
2030        group: impl Into<SessionConfigGroupId>,
2031        name: impl Into<String>,
2032        options: Vec<SessionConfigSelectOption>,
2033    ) -> Self {
2034        Self {
2035            group: group.into(),
2036            name: name.into(),
2037            options,
2038            meta: None,
2039        }
2040    }
2041
2042    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2043    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2044    /// these keys.
2045    ///
2046    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2047    #[must_use]
2048    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2049        self.meta = meta.into_option();
2050        self
2051    }
2052}
2053
2054/// Possible values for a session configuration option.
2055#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2056#[serde(untagged)]
2057#[non_exhaustive]
2058pub enum SessionConfigSelectOptions {
2059    /// A flat list of options with no grouping.
2060    Ungrouped(Vec<SessionConfigSelectOption>),
2061    /// A list of options grouped under headers.
2062    Grouped(Vec<SessionConfigSelectGroup>),
2063}
2064
2065impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2066    fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2067        SessionConfigSelectOptions::Ungrouped(options)
2068    }
2069}
2070
2071impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2072    fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2073        SessionConfigSelectOptions::Grouped(groups)
2074    }
2075}
2076
2077/// A single-value selector (dropdown) session configuration option payload.
2078#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2079#[serde(rename_all = "camelCase")]
2080#[non_exhaustive]
2081pub struct SessionConfigSelect {
2082    /// The currently selected value.
2083    pub current_value: SessionConfigValueId,
2084    /// The set of selectable options.
2085    pub options: SessionConfigSelectOptions,
2086}
2087
2088impl SessionConfigSelect {
2089    #[must_use]
2090    pub fn new(
2091        current_value: impl Into<SessionConfigValueId>,
2092        options: impl Into<SessionConfigSelectOptions>,
2093    ) -> Self {
2094        Self {
2095            current_value: current_value.into(),
2096            options: options.into(),
2097        }
2098    }
2099}
2100
2101/// **UNSTABLE**
2102///
2103/// This capability is not part of the spec yet, and may be removed or changed at any point.
2104///
2105/// A boolean on/off toggle session configuration option payload.
2106#[cfg(feature = "unstable_boolean_config")]
2107#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2108#[serde(rename_all = "camelCase")]
2109#[non_exhaustive]
2110pub struct SessionConfigBoolean {
2111    /// The current value of the boolean option.
2112    pub current_value: bool,
2113}
2114
2115#[cfg(feature = "unstable_boolean_config")]
2116impl SessionConfigBoolean {
2117    #[must_use]
2118    pub fn new(current_value: bool) -> Self {
2119        Self { current_value }
2120    }
2121}
2122
2123/// Semantic category for a session configuration option.
2124///
2125/// This is intended to help Clients distinguish broadly common selectors (e.g. model selector vs
2126/// session mode selector vs thought/reasoning level) for UX purposes (keyboard shortcuts, icons,
2127/// placement). It MUST NOT be required for correctness. Clients MUST handle missing or unknown
2128/// categories gracefully.
2129///
2130/// Category names beginning with `_` are free for custom use, like other ACP extension methods.
2131/// Category names that do not begin with `_` are reserved for the ACP spec.
2132#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2133#[serde(rename_all = "snake_case")]
2134#[non_exhaustive]
2135pub enum SessionConfigOptionCategory {
2136    /// Session mode selector.
2137    Mode,
2138    /// Model selector.
2139    Model,
2140    /// Thought/reasoning level selector.
2141    ThoughtLevel,
2142    /// Unknown / uncategorized selector.
2143    #[serde(untagged)]
2144    Other(String),
2145}
2146
2147/// Type-specific session configuration option payload.
2148#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2149#[serde(tag = "type", rename_all = "snake_case")]
2150#[schemars(extend("discriminator" = {"propertyName": "type"}))]
2151#[non_exhaustive]
2152pub enum SessionConfigKind {
2153    /// Single-value selector (dropdown).
2154    Select(SessionConfigSelect),
2155    /// **UNSTABLE**
2156    ///
2157    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2158    ///
2159    /// Boolean on/off toggle.
2160    #[cfg(feature = "unstable_boolean_config")]
2161    Boolean(SessionConfigBoolean),
2162}
2163
2164/// A session configuration option selector and its current state.
2165#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2166#[serde(rename_all = "camelCase")]
2167#[non_exhaustive]
2168pub struct SessionConfigOption {
2169    /// Unique identifier for the configuration option.
2170    pub id: SessionConfigId,
2171    /// Human-readable label for the option.
2172    pub name: String,
2173    /// Optional description for the Client to display to the user.
2174    #[serde(default, skip_serializing_if = "Option::is_none")]
2175    pub description: Option<String>,
2176    /// Optional semantic category for this option (UX only).
2177    #[serde(default, skip_serializing_if = "Option::is_none")]
2178    pub category: Option<SessionConfigOptionCategory>,
2179    /// Type-specific fields for this configuration option.
2180    #[serde(flatten)]
2181    pub kind: SessionConfigKind,
2182    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2183    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2184    /// these keys.
2185    ///
2186    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2187    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2188    pub meta: Option<Meta>,
2189}
2190
2191impl SessionConfigOption {
2192    #[must_use]
2193    pub fn new(
2194        id: impl Into<SessionConfigId>,
2195        name: impl Into<String>,
2196        kind: SessionConfigKind,
2197    ) -> Self {
2198        Self {
2199            id: id.into(),
2200            name: name.into(),
2201            description: None,
2202            category: None,
2203            kind,
2204            meta: None,
2205        }
2206    }
2207
2208    #[must_use]
2209    pub fn select(
2210        id: impl Into<SessionConfigId>,
2211        name: impl Into<String>,
2212        current_value: impl Into<SessionConfigValueId>,
2213        options: impl Into<SessionConfigSelectOptions>,
2214    ) -> Self {
2215        Self::new(
2216            id,
2217            name,
2218            SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2219        )
2220    }
2221
2222    /// **UNSTABLE**
2223    ///
2224    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2225    #[cfg(feature = "unstable_boolean_config")]
2226    #[must_use]
2227    pub fn boolean(
2228        id: impl Into<SessionConfigId>,
2229        name: impl Into<String>,
2230        current_value: bool,
2231    ) -> Self {
2232        Self::new(
2233            id,
2234            name,
2235            SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2236        )
2237    }
2238
2239    #[must_use]
2240    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2241        self.description = description.into_option();
2242        self
2243    }
2244
2245    #[must_use]
2246    pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2247        self.category = category.into_option();
2248        self
2249    }
2250
2251    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2252    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2253    /// these keys.
2254    ///
2255    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2256    #[must_use]
2257    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2258        self.meta = meta.into_option();
2259        self
2260    }
2261}
2262
2263/// **UNSTABLE**
2264///
2265/// This capability is not part of the spec yet, and may be removed or changed at any point.
2266///
2267/// The value to set for a session configuration option.
2268///
2269/// The `type` field acts as the discriminator in the serialized JSON form.
2270/// When no `type` is present, the value is treated as a [`SessionConfigValueId`]
2271/// via the [`ValueId`](Self::ValueId) fallback variant.
2272///
2273/// The `type` discriminator describes the *shape* of the value, not the option
2274/// kind. For example every option kind that picks from a list of ids
2275/// (`select`, `radio`, …) would use [`ValueId`](Self::ValueId), while a
2276/// future freeform text option would get its own variant.
2277#[cfg(feature = "unstable_boolean_config")]
2278#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2279#[serde(tag = "type", rename_all = "snake_case")]
2280#[non_exhaustive]
2281pub enum SessionConfigOptionValue {
2282    /// A boolean value (`type: "boolean"`).
2283    Boolean {
2284        /// The boolean value.
2285        value: bool,
2286    },
2287    /// A [`SessionConfigValueId`] string value.
2288    ///
2289    /// This is the default when `type` is absent on the wire. Unknown `type`
2290    /// values with string payloads also gracefully deserialize into this
2291    /// variant.
2292    #[serde(untagged)]
2293    ValueId {
2294        /// The value ID.
2295        value: SessionConfigValueId,
2296    },
2297}
2298
2299#[cfg(feature = "unstable_boolean_config")]
2300impl SessionConfigOptionValue {
2301    /// Create a value-id option value (used by `select` and other id-based option types).
2302    #[must_use]
2303    pub fn value_id(id: impl Into<SessionConfigValueId>) -> Self {
2304        Self::ValueId { value: id.into() }
2305    }
2306
2307    /// Create a boolean option value.
2308    #[must_use]
2309    pub fn boolean(val: bool) -> Self {
2310        Self::Boolean { value: val }
2311    }
2312
2313    /// Return the inner [`SessionConfigValueId`] if this is a
2314    /// [`ValueId`](Self::ValueId) value.
2315    #[must_use]
2316    pub fn as_value_id(&self) -> Option<&SessionConfigValueId> {
2317        match self {
2318            Self::ValueId { value } => Some(value),
2319            _ => None,
2320        }
2321    }
2322
2323    /// Return the inner [`bool`] if this is a [`Boolean`](Self::Boolean) value.
2324    #[must_use]
2325    pub fn as_bool(&self) -> Option<bool> {
2326        match self {
2327            Self::Boolean { value } => Some(*value),
2328            _ => None,
2329        }
2330    }
2331}
2332
2333#[cfg(feature = "unstable_boolean_config")]
2334impl From<SessionConfigValueId> for SessionConfigOptionValue {
2335    fn from(value: SessionConfigValueId) -> Self {
2336        Self::ValueId { value }
2337    }
2338}
2339
2340#[cfg(feature = "unstable_boolean_config")]
2341impl From<bool> for SessionConfigOptionValue {
2342    fn from(value: bool) -> Self {
2343        Self::Boolean { value }
2344    }
2345}
2346
2347#[cfg(feature = "unstable_boolean_config")]
2348impl From<&str> for SessionConfigOptionValue {
2349    fn from(value: &str) -> Self {
2350        Self::ValueId {
2351            value: SessionConfigValueId::new(value),
2352        }
2353    }
2354}
2355
2356/// Request parameters for setting a session configuration option.
2357#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2358#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME))]
2359#[serde(rename_all = "camelCase")]
2360#[non_exhaustive]
2361pub struct SetSessionConfigOptionRequest {
2362    /// The ID of the session to set the configuration option for.
2363    pub session_id: SessionId,
2364    /// The ID of the configuration option to set.
2365    pub config_id: SessionConfigId,
2366    /// The value to set, including a `type` discriminator and the raw `value`.
2367    ///
2368    /// When `type` is absent on the wire, defaults to treating the value as a
2369    /// [`SessionConfigValueId`] for `select` options.
2370    #[cfg(feature = "unstable_boolean_config")]
2371    #[serde(flatten)]
2372    pub value: SessionConfigOptionValue,
2373    /// The ID of the configuration option value to set.
2374    #[cfg(not(feature = "unstable_boolean_config"))]
2375    pub value: SessionConfigValueId,
2376    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2377    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2378    /// these keys.
2379    ///
2380    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2381    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2382    pub meta: Option<Meta>,
2383}
2384
2385impl SetSessionConfigOptionRequest {
2386    #[cfg(feature = "unstable_boolean_config")]
2387    #[must_use]
2388    pub fn new(
2389        session_id: impl Into<SessionId>,
2390        config_id: impl Into<SessionConfigId>,
2391        value: impl Into<SessionConfigOptionValue>,
2392    ) -> Self {
2393        Self {
2394            session_id: session_id.into(),
2395            config_id: config_id.into(),
2396            value: value.into(),
2397            meta: None,
2398        }
2399    }
2400
2401    #[cfg(not(feature = "unstable_boolean_config"))]
2402    #[must_use]
2403    pub fn new(
2404        session_id: impl Into<SessionId>,
2405        config_id: impl Into<SessionConfigId>,
2406        value: impl Into<SessionConfigValueId>,
2407    ) -> Self {
2408        Self {
2409            session_id: session_id.into(),
2410            config_id: config_id.into(),
2411            value: value.into(),
2412            meta: None,
2413        }
2414    }
2415
2416    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2417    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2418    /// these keys.
2419    ///
2420    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2421    #[must_use]
2422    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2423        self.meta = meta.into_option();
2424        self
2425    }
2426}
2427
2428/// Response to `session/set_config_option` method.
2429#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2430#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME))]
2431#[serde(rename_all = "camelCase")]
2432#[non_exhaustive]
2433pub struct SetSessionConfigOptionResponse {
2434    /// The full set of configuration options and their current values.
2435    pub config_options: Vec<SessionConfigOption>,
2436    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2437    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2438    /// these keys.
2439    ///
2440    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2441    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2442    pub meta: Option<Meta>,
2443}
2444
2445impl SetSessionConfigOptionResponse {
2446    #[must_use]
2447    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2448        Self {
2449            config_options,
2450            meta: None,
2451        }
2452    }
2453
2454    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2455    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2456    /// these keys.
2457    ///
2458    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2459    #[must_use]
2460    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2461        self.meta = meta.into_option();
2462        self
2463    }
2464}
2465
2466// MCP
2467
2468/// Configuration for connecting to an MCP (Model Context Protocol) server.
2469///
2470/// MCP servers provide tools and context that the agent can use when
2471/// processing prompts.
2472///
2473/// See protocol docs: [MCP Servers](https://agentclientprotocol.com/protocol/session-setup#mcp-servers)
2474#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2475#[serde(tag = "type", rename_all = "snake_case")]
2476#[non_exhaustive]
2477pub enum McpServer {
2478    /// HTTP transport configuration
2479    ///
2480    /// Only available when the Agent capabilities indicate `mcp_capabilities.http` is `true`.
2481    Http(McpServerHttp),
2482    /// SSE transport configuration
2483    ///
2484    /// Only available when the Agent capabilities indicate `mcp_capabilities.sse` is `true`.
2485    Sse(McpServerSse),
2486    /// Stdio transport configuration
2487    ///
2488    /// All Agents MUST support this transport.
2489    #[serde(untagged)]
2490    Stdio(McpServerStdio),
2491}
2492
2493/// HTTP transport configuration for MCP.
2494#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2495#[serde(rename_all = "camelCase")]
2496#[non_exhaustive]
2497pub struct McpServerHttp {
2498    /// Human-readable name identifying this MCP server.
2499    pub name: String,
2500    /// URL to the MCP server.
2501    pub url: String,
2502    /// HTTP headers to set when making requests to the MCP server.
2503    pub headers: Vec<HttpHeader>,
2504    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2505    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2506    /// these keys.
2507    ///
2508    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2509    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2510    pub meta: Option<Meta>,
2511}
2512
2513impl McpServerHttp {
2514    #[must_use]
2515    pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2516        Self {
2517            name: name.into(),
2518            url: url.into(),
2519            headers: Vec::new(),
2520            meta: None,
2521        }
2522    }
2523
2524    /// HTTP headers to set when making requests to the MCP server.
2525    #[must_use]
2526    pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2527        self.headers = headers;
2528        self
2529    }
2530
2531    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2532    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2533    /// these keys.
2534    ///
2535    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2536    #[must_use]
2537    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2538        self.meta = meta.into_option();
2539        self
2540    }
2541}
2542
2543/// SSE transport configuration for MCP.
2544#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2545#[serde(rename_all = "camelCase")]
2546#[non_exhaustive]
2547pub struct McpServerSse {
2548    /// Human-readable name identifying this MCP server.
2549    pub name: String,
2550    /// URL to the MCP server.
2551    pub url: String,
2552    /// HTTP headers to set when making requests to the MCP server.
2553    pub headers: Vec<HttpHeader>,
2554    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2555    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2556    /// these keys.
2557    ///
2558    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2559    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2560    pub meta: Option<Meta>,
2561}
2562
2563impl McpServerSse {
2564    #[must_use]
2565    pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2566        Self {
2567            name: name.into(),
2568            url: url.into(),
2569            headers: Vec::new(),
2570            meta: None,
2571        }
2572    }
2573
2574    /// HTTP headers to set when making requests to the MCP server.
2575    #[must_use]
2576    pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2577        self.headers = headers;
2578        self
2579    }
2580
2581    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2582    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2583    /// these keys.
2584    ///
2585    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2586    #[must_use]
2587    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2588        self.meta = meta.into_option();
2589        self
2590    }
2591}
2592
2593/// Stdio transport configuration for MCP.
2594#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2595#[serde(rename_all = "camelCase")]
2596#[non_exhaustive]
2597pub struct McpServerStdio {
2598    /// Human-readable name identifying this MCP server.
2599    pub name: String,
2600    /// Path to the MCP server executable.
2601    pub command: PathBuf,
2602    /// Command-line arguments to pass to the MCP server.
2603    pub args: Vec<String>,
2604    /// Environment variables to set when launching the MCP server.
2605    pub env: Vec<EnvVariable>,
2606    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2607    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2608    /// these keys.
2609    ///
2610    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2611    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2612    pub meta: Option<Meta>,
2613}
2614
2615impl McpServerStdio {
2616    #[must_use]
2617    pub fn new(name: impl Into<String>, command: impl Into<PathBuf>) -> Self {
2618        Self {
2619            name: name.into(),
2620            command: command.into(),
2621            args: Vec::new(),
2622            env: Vec::new(),
2623            meta: None,
2624        }
2625    }
2626
2627    /// Command-line arguments to pass to the MCP server.
2628    #[must_use]
2629    pub fn args(mut self, args: Vec<String>) -> Self {
2630        self.args = args;
2631        self
2632    }
2633
2634    /// Environment variables to set when launching the MCP server.
2635    #[must_use]
2636    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
2637        self.env = env;
2638        self
2639    }
2640
2641    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2642    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2643    /// these keys.
2644    ///
2645    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2646    #[must_use]
2647    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2648        self.meta = meta.into_option();
2649        self
2650    }
2651}
2652
2653/// An environment variable to set when launching an MCP server.
2654#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2655#[serde(rename_all = "camelCase")]
2656#[non_exhaustive]
2657pub struct EnvVariable {
2658    /// The name of the environment variable.
2659    pub name: String,
2660    /// The value to set for the environment variable.
2661    pub value: String,
2662    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2663    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2664    /// these keys.
2665    ///
2666    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2667    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2668    pub meta: Option<Meta>,
2669}
2670
2671impl EnvVariable {
2672    #[must_use]
2673    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
2674        Self {
2675            name: name.into(),
2676            value: value.into(),
2677            meta: None,
2678        }
2679    }
2680
2681    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2682    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2683    /// these keys.
2684    ///
2685    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2686    #[must_use]
2687    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2688        self.meta = meta.into_option();
2689        self
2690    }
2691}
2692
2693/// An HTTP header to set when making requests to the MCP server.
2694#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2695#[serde(rename_all = "camelCase")]
2696#[non_exhaustive]
2697pub struct HttpHeader {
2698    /// The name of the HTTP header.
2699    pub name: String,
2700    /// The value to set for the HTTP header.
2701    pub value: String,
2702    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2703    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2704    /// these keys.
2705    ///
2706    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2707    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2708    pub meta: Option<Meta>,
2709}
2710
2711impl HttpHeader {
2712    #[must_use]
2713    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
2714        Self {
2715            name: name.into(),
2716            value: value.into(),
2717            meta: None,
2718        }
2719    }
2720
2721    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2722    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2723    /// these keys.
2724    ///
2725    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2726    #[must_use]
2727    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2728        self.meta = meta.into_option();
2729        self
2730    }
2731}
2732
2733// Prompt
2734
2735/// Request parameters for sending a user prompt to the agent.
2736///
2737/// Contains the user's message and any additional context.
2738///
2739/// See protocol docs: [User Message](https://agentclientprotocol.com/protocol/prompt-turn#1-user-message)
2740#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
2741#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
2742#[serde(rename_all = "camelCase")]
2743#[non_exhaustive]
2744pub struct PromptRequest {
2745    /// The ID of the session to send this user message to
2746    pub session_id: SessionId,
2747    /// **UNSTABLE**
2748    ///
2749    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2750    ///
2751    /// A client-generated unique identifier for this user message.
2752    ///
2753    /// If provided, the Agent SHOULD echo this value as `userMessageId` in the
2754    /// [`PromptResponse`] to confirm it was recorded.
2755    /// Both clients and agents MUST use UUID format for message IDs.
2756    #[cfg(feature = "unstable_message_id")]
2757    #[serde(skip_serializing_if = "Option::is_none")]
2758    pub message_id: Option<String>,
2759    /// The blocks of content that compose the user's message.
2760    ///
2761    /// As a baseline, the Agent MUST support [`ContentBlock::Text`] and [`ContentBlock::ResourceLink`],
2762    /// while other variants are optionally enabled via [`PromptCapabilities`].
2763    ///
2764    /// The Client MUST adapt its interface according to [`PromptCapabilities`].
2765    ///
2766    /// The client MAY include referenced pieces of context as either
2767    /// [`ContentBlock::Resource`] or [`ContentBlock::ResourceLink`].
2768    ///
2769    /// When available, [`ContentBlock::Resource`] is preferred
2770    /// as it avoids extra round-trips and allows the message to include
2771    /// pieces of context from sources the agent may not have access to.
2772    pub prompt: Vec<ContentBlock>,
2773    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2774    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2775    /// these keys.
2776    ///
2777    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2778    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2779    pub meta: Option<Meta>,
2780}
2781
2782impl PromptRequest {
2783    #[must_use]
2784    pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
2785        Self {
2786            session_id: session_id.into(),
2787            #[cfg(feature = "unstable_message_id")]
2788            message_id: None,
2789            prompt,
2790            meta: None,
2791        }
2792    }
2793
2794    /// **UNSTABLE**
2795    ///
2796    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2797    ///
2798    /// A client-generated unique identifier for this user message.
2799    ///
2800    /// If provided, the Agent SHOULD echo this value as `userMessageId` in the
2801    /// [`PromptResponse`] to confirm it was recorded.
2802    /// Both clients and agents MUST use UUID format for message IDs.
2803    #[cfg(feature = "unstable_message_id")]
2804    #[must_use]
2805    pub fn message_id(mut self, message_id: impl IntoOption<String>) -> Self {
2806        self.message_id = message_id.into_option();
2807        self
2808    }
2809
2810    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2811    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2812    /// these keys.
2813    ///
2814    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2815    #[must_use]
2816    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2817        self.meta = meta.into_option();
2818        self
2819    }
2820}
2821
2822/// Response from processing a user prompt.
2823///
2824/// See protocol docs: [Check for Completion](https://agentclientprotocol.com/protocol/prompt-turn#4-check-for-completion)
2825#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2826#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
2827#[serde(rename_all = "camelCase")]
2828#[non_exhaustive]
2829pub struct PromptResponse {
2830    /// **UNSTABLE**
2831    ///
2832    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2833    ///
2834    /// The acknowledged user message ID.
2835    ///
2836    /// If the client provided a `messageId` in the [`PromptRequest`], the agent echoes it here
2837    /// to confirm it was recorded. If the client did not provide one, the agent MAY assign one
2838    /// and return it here. Absence of this field indicates the agent did not record a message ID.
2839    #[cfg(feature = "unstable_message_id")]
2840    #[serde(skip_serializing_if = "Option::is_none")]
2841    pub user_message_id: Option<String>,
2842    /// Indicates why the agent stopped processing the turn.
2843    pub stop_reason: StopReason,
2844    /// **UNSTABLE**
2845    ///
2846    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2847    ///
2848    /// Token usage for this turn (optional).
2849    #[cfg(feature = "unstable_session_usage")]
2850    #[serde(skip_serializing_if = "Option::is_none")]
2851    pub usage: Option<Usage>,
2852    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2853    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2854    /// these keys.
2855    ///
2856    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2857    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
2858    pub meta: Option<Meta>,
2859}
2860
2861impl PromptResponse {
2862    #[must_use]
2863    pub fn new(stop_reason: StopReason) -> Self {
2864        Self {
2865            #[cfg(feature = "unstable_message_id")]
2866            user_message_id: None,
2867            stop_reason,
2868            #[cfg(feature = "unstable_session_usage")]
2869            usage: None,
2870            meta: None,
2871        }
2872    }
2873
2874    /// **UNSTABLE**
2875    ///
2876    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2877    ///
2878    /// The acknowledged user message ID.
2879    ///
2880    /// If the client provided a `messageId` in the [`PromptRequest`], the agent echoes it here
2881    /// to confirm it was recorded. If the client did not provide one, the agent MAY assign one
2882    /// and return it here. Absence of this field indicates the agent did not record a message ID.
2883    #[cfg(feature = "unstable_message_id")]
2884    #[must_use]
2885    pub fn user_message_id(mut self, user_message_id: impl IntoOption<String>) -> Self {
2886        self.user_message_id = user_message_id.into_option();
2887        self
2888    }
2889
2890    /// **UNSTABLE**
2891    ///
2892    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2893    ///
2894    /// Token usage for this turn.
2895    #[cfg(feature = "unstable_session_usage")]
2896    #[must_use]
2897    pub fn usage(mut self, usage: impl IntoOption<Usage>) -> Self {
2898        self.usage = usage.into_option();
2899        self
2900    }
2901
2902    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2903    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2904    /// these keys.
2905    ///
2906    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2907    #[must_use]
2908    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2909        self.meta = meta.into_option();
2910        self
2911    }
2912}
2913
2914/// Reasons why an agent stops processing a prompt turn.
2915///
2916/// See protocol docs: [Stop Reasons](https://agentclientprotocol.com/protocol/prompt-turn#stop-reasons)
2917#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
2918#[serde(rename_all = "snake_case")]
2919#[non_exhaustive]
2920pub enum StopReason {
2921    /// The turn ended successfully.
2922    EndTurn,
2923    /// The turn ended because the agent reached the maximum number of tokens.
2924    MaxTokens,
2925    /// The turn ended because the agent reached the maximum number of allowed
2926    /// agent requests between user turns.
2927    MaxTurnRequests,
2928    /// The turn ended because the agent refused to continue. The user prompt
2929    /// and everything that comes after it won't be included in the next
2930    /// prompt, so this should be reflected in the UI.
2931    Refusal,
2932    /// The turn was cancelled by the client via `session/cancel`.
2933    ///
2934    /// This stop reason MUST be returned when the client sends a `session/cancel`
2935    /// notification, even if the cancellation causes exceptions in underlying operations.
2936    /// Agents should catch these exceptions and return this semantically meaningful
2937    /// response to confirm successful cancellation.
2938    Cancelled,
2939}
2940
2941/// **UNSTABLE**
2942///
2943/// This capability is not part of the spec yet, and may be removed or changed at any point.
2944///
2945/// Token usage information for a prompt turn.
2946#[cfg(feature = "unstable_session_usage")]
2947#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2948#[serde(rename_all = "camelCase")]
2949#[non_exhaustive]
2950pub struct Usage {
2951    /// Sum of all token types across session.
2952    pub total_tokens: u64,
2953    /// Total input tokens across all turns.
2954    pub input_tokens: u64,
2955    /// Total output tokens across all turns.
2956    pub output_tokens: u64,
2957    /// Total thought/reasoning tokens
2958    #[serde(skip_serializing_if = "Option::is_none")]
2959    pub thought_tokens: Option<u64>,
2960    /// Total cache read tokens.
2961    #[serde(skip_serializing_if = "Option::is_none")]
2962    pub cached_read_tokens: Option<u64>,
2963    /// Total cache write tokens.
2964    #[serde(skip_serializing_if = "Option::is_none")]
2965    pub cached_write_tokens: Option<u64>,
2966}
2967
2968#[cfg(feature = "unstable_session_usage")]
2969impl Usage {
2970    #[must_use]
2971    pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
2972        Self {
2973            total_tokens,
2974            input_tokens,
2975            output_tokens,
2976            thought_tokens: None,
2977            cached_read_tokens: None,
2978            cached_write_tokens: None,
2979        }
2980    }
2981
2982    /// Total thought/reasoning tokens
2983    #[must_use]
2984    pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
2985        self.thought_tokens = thought_tokens.into_option();
2986        self
2987    }
2988
2989    /// Total cache read tokens.
2990    #[must_use]
2991    pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
2992        self.cached_read_tokens = cached_read_tokens.into_option();
2993        self
2994    }
2995
2996    /// Total cache write tokens.
2997    #[must_use]
2998    pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
2999        self.cached_write_tokens = cached_write_tokens.into_option();
3000        self
3001    }
3002}
3003
3004// Model
3005
3006/// **UNSTABLE**
3007///
3008/// This capability is not part of the spec yet, and may be removed or changed at any point.
3009///
3010/// The set of models and the one currently active.
3011#[cfg(feature = "unstable_session_model")]
3012#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3013#[serde(rename_all = "camelCase")]
3014#[non_exhaustive]
3015pub struct SessionModelState {
3016    /// The current model the Agent is in.
3017    pub current_model_id: ModelId,
3018    /// The set of models that the Agent can use
3019    pub available_models: Vec<ModelInfo>,
3020    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3021    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3022    /// these keys.
3023    ///
3024    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3025    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3026    pub meta: Option<Meta>,
3027}
3028
3029#[cfg(feature = "unstable_session_model")]
3030impl SessionModelState {
3031    #[must_use]
3032    pub fn new(current_model_id: impl Into<ModelId>, available_models: Vec<ModelInfo>) -> Self {
3033        Self {
3034            current_model_id: current_model_id.into(),
3035            available_models,
3036            meta: None,
3037        }
3038    }
3039
3040    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3041    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3042    /// these keys.
3043    ///
3044    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3045    #[must_use]
3046    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3047        self.meta = meta.into_option();
3048        self
3049    }
3050}
3051
3052/// **UNSTABLE**
3053///
3054/// This capability is not part of the spec yet, and may be removed or changed at any point.
3055///
3056/// A unique identifier for a model.
3057#[cfg(feature = "unstable_session_model")]
3058#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
3059#[serde(transparent)]
3060#[from(Arc<str>, String, &'static str)]
3061#[non_exhaustive]
3062pub struct ModelId(pub Arc<str>);
3063
3064#[cfg(feature = "unstable_session_model")]
3065impl ModelId {
3066    #[must_use]
3067    pub fn new(id: impl Into<Arc<str>>) -> Self {
3068        Self(id.into())
3069    }
3070}
3071
3072/// **UNSTABLE**
3073///
3074/// This capability is not part of the spec yet, and may be removed or changed at any point.
3075///
3076/// Information about a selectable model.
3077#[cfg(feature = "unstable_session_model")]
3078#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3079#[serde(rename_all = "camelCase")]
3080#[non_exhaustive]
3081pub struct ModelInfo {
3082    /// Unique identifier for the model.
3083    pub model_id: ModelId,
3084    /// Human-readable name of the model.
3085    pub name: String,
3086    /// Optional description of the model.
3087    #[serde(default, skip_serializing_if = "Option::is_none")]
3088    pub description: Option<String>,
3089    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3090    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3091    /// these keys.
3092    ///
3093    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3094    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3095    pub meta: Option<Meta>,
3096}
3097
3098#[cfg(feature = "unstable_session_model")]
3099impl ModelInfo {
3100    #[must_use]
3101    pub fn new(model_id: impl Into<ModelId>, name: impl Into<String>) -> Self {
3102        Self {
3103            model_id: model_id.into(),
3104            name: name.into(),
3105            description: None,
3106            meta: None,
3107        }
3108    }
3109
3110    /// Optional description of the model.
3111    #[must_use]
3112    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3113        self.description = description.into_option();
3114        self
3115    }
3116
3117    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3118    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3119    /// these keys.
3120    ///
3121    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3122    #[must_use]
3123    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3124        self.meta = meta.into_option();
3125        self
3126    }
3127}
3128
3129/// **UNSTABLE**
3130///
3131/// This capability is not part of the spec yet, and may be removed or changed at any point.
3132///
3133/// Request parameters for setting a session model.
3134#[cfg(feature = "unstable_session_model")]
3135#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3136#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODEL_METHOD_NAME))]
3137#[serde(rename_all = "camelCase")]
3138#[non_exhaustive]
3139pub struct SetSessionModelRequest {
3140    /// The ID of the session to set the model for.
3141    pub session_id: SessionId,
3142    /// The ID of the model to set.
3143    pub model_id: ModelId,
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(skip_serializing_if = "Option::is_none", rename = "_meta")]
3150    pub meta: Option<Meta>,
3151}
3152
3153#[cfg(feature = "unstable_session_model")]
3154impl SetSessionModelRequest {
3155    #[must_use]
3156    pub fn new(session_id: impl Into<SessionId>, model_id: impl Into<ModelId>) -> Self {
3157        Self {
3158            session_id: session_id.into(),
3159            model_id: model_id.into(),
3160            meta: None,
3161        }
3162    }
3163
3164    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3165    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3166    /// these keys.
3167    ///
3168    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3169    #[must_use]
3170    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3171        self.meta = meta.into_option();
3172        self
3173    }
3174}
3175
3176/// **UNSTABLE**
3177///
3178/// This capability is not part of the spec yet, and may be removed or changed at any point.
3179///
3180/// Response to `session/set_model` method.
3181#[cfg(feature = "unstable_session_model")]
3182#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3183#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODEL_METHOD_NAME))]
3184#[serde(rename_all = "camelCase")]
3185#[non_exhaustive]
3186pub struct SetSessionModelResponse {
3187    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3188    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3189    /// these keys.
3190    ///
3191    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3192    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3193    pub meta: Option<Meta>,
3194}
3195
3196#[cfg(feature = "unstable_session_model")]
3197impl SetSessionModelResponse {
3198    #[must_use]
3199    pub fn new() -> Self {
3200        Self::default()
3201    }
3202
3203    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3204    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3205    /// these keys.
3206    ///
3207    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3208    #[must_use]
3209    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3210        self.meta = meta.into_option();
3211        self
3212    }
3213}
3214
3215// Capabilities
3216
3217/// Capabilities supported by the agent.
3218///
3219/// Advertised during initialization to inform the client about
3220/// available features and content types.
3221///
3222/// See protocol docs: [Agent Capabilities](https://agentclientprotocol.com/protocol/initialization#agent-capabilities)
3223#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3224#[serde(rename_all = "camelCase")]
3225#[non_exhaustive]
3226pub struct AgentCapabilities {
3227    /// Whether the agent supports `session/load`.
3228    #[serde(default)]
3229    pub load_session: bool,
3230    /// Prompt capabilities supported by the agent.
3231    #[serde(default)]
3232    pub prompt_capabilities: PromptCapabilities,
3233    /// MCP capabilities supported by the agent.
3234    #[serde(default)]
3235    pub mcp_capabilities: McpCapabilities,
3236    #[serde(default)]
3237    pub session_capabilities: SessionCapabilities,
3238    /// **UNSTABLE**
3239    ///
3240    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3241    ///
3242    /// Authentication-related capabilities supported by the agent.
3243    #[cfg(feature = "unstable_logout")]
3244    #[serde(default)]
3245    pub auth: AgentAuthCapabilities,
3246    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3247    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3248    /// these keys.
3249    ///
3250    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3251    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3252    pub meta: Option<Meta>,
3253}
3254
3255impl AgentCapabilities {
3256    #[must_use]
3257    pub fn new() -> Self {
3258        Self::default()
3259    }
3260
3261    /// Whether the agent supports `session/load`.
3262    #[must_use]
3263    pub fn load_session(mut self, load_session: bool) -> Self {
3264        self.load_session = load_session;
3265        self
3266    }
3267
3268    /// Prompt capabilities supported by the agent.
3269    #[must_use]
3270    pub fn prompt_capabilities(mut self, prompt_capabilities: PromptCapabilities) -> Self {
3271        self.prompt_capabilities = prompt_capabilities;
3272        self
3273    }
3274
3275    /// MCP capabilities supported by the agent.
3276    #[must_use]
3277    pub fn mcp_capabilities(mut self, mcp_capabilities: McpCapabilities) -> Self {
3278        self.mcp_capabilities = mcp_capabilities;
3279        self
3280    }
3281
3282    /// Session capabilities supported by the agent.
3283    #[must_use]
3284    pub fn session_capabilities(mut self, session_capabilities: SessionCapabilities) -> Self {
3285        self.session_capabilities = session_capabilities;
3286        self
3287    }
3288
3289    /// **UNSTABLE**
3290    ///
3291    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3292    ///
3293    /// Authentication-related capabilities supported by the agent.
3294    #[cfg(feature = "unstable_logout")]
3295    #[must_use]
3296    pub fn auth(mut self, auth: AgentAuthCapabilities) -> Self {
3297        self.auth = auth;
3298        self
3299    }
3300
3301    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3302    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3303    /// these keys.
3304    ///
3305    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3306    #[must_use]
3307    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3308        self.meta = meta.into_option();
3309        self
3310    }
3311}
3312
3313/// Session capabilities supported by the agent.
3314///
3315/// As a baseline, all Agents **MUST** support `session/new`, `session/prompt`, `session/cancel`, and `session/update`.
3316///
3317/// Optionally, they **MAY** support other session methods and notifications by specifying additional capabilities.
3318///
3319/// Note: `session/load` is still handled by the top-level `load_session` capability. This will be unified in future versions of the protocol.
3320///
3321/// See protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/initialization#session-capabilities)
3322#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3323#[non_exhaustive]
3324pub struct SessionCapabilities {
3325    /// Whether the agent supports `session/list`.
3326    #[serde(skip_serializing_if = "Option::is_none")]
3327    pub list: Option<SessionListCapabilities>,
3328    /// **UNSTABLE**
3329    ///
3330    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3331    ///
3332    /// Whether the agent supports `session/fork`.
3333    #[cfg(feature = "unstable_session_fork")]
3334    #[serde(skip_serializing_if = "Option::is_none")]
3335    pub fork: Option<SessionForkCapabilities>,
3336    /// **UNSTABLE**
3337    ///
3338    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3339    ///
3340    /// Whether the agent supports `session/resume`.
3341    #[cfg(feature = "unstable_session_resume")]
3342    #[serde(skip_serializing_if = "Option::is_none")]
3343    pub resume: Option<SessionResumeCapabilities>,
3344    /// **UNSTABLE**
3345    ///
3346    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3347    ///
3348    /// Whether the agent supports `session/close`.
3349    #[cfg(feature = "unstable_session_close")]
3350    #[serde(skip_serializing_if = "Option::is_none")]
3351    pub close: Option<SessionCloseCapabilities>,
3352    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3353    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3354    /// these keys.
3355    ///
3356    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3357    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3358    pub meta: Option<Meta>,
3359}
3360
3361impl SessionCapabilities {
3362    #[must_use]
3363    pub fn new() -> Self {
3364        Self::default()
3365    }
3366
3367    /// Whether the agent supports `session/list`.
3368    #[must_use]
3369    pub fn list(mut self, list: impl IntoOption<SessionListCapabilities>) -> Self {
3370        self.list = list.into_option();
3371        self
3372    }
3373
3374    #[cfg(feature = "unstable_session_fork")]
3375    /// Whether the agent supports `session/fork`.
3376    #[must_use]
3377    pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
3378        self.fork = fork.into_option();
3379        self
3380    }
3381
3382    #[cfg(feature = "unstable_session_resume")]
3383    /// Whether the agent supports `session/resume`.
3384    #[must_use]
3385    pub fn resume(mut self, resume: impl IntoOption<SessionResumeCapabilities>) -> Self {
3386        self.resume = resume.into_option();
3387        self
3388    }
3389
3390    #[cfg(feature = "unstable_session_close")]
3391    /// Whether the agent supports `session/close`.
3392    #[must_use]
3393    pub fn close(mut self, close: impl IntoOption<SessionCloseCapabilities>) -> Self {
3394        self.close = close.into_option();
3395        self
3396    }
3397
3398    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3399    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3400    /// these keys.
3401    ///
3402    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3403    #[must_use]
3404    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3405        self.meta = meta.into_option();
3406        self
3407    }
3408}
3409
3410/// Capabilities for the `session/list` method.
3411///
3412/// By supplying `{}` it means that the agent supports listing of sessions.
3413#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3414#[non_exhaustive]
3415pub struct SessionListCapabilities {
3416    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3417    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3418    /// these keys.
3419    ///
3420    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3421    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3422    pub meta: Option<Meta>,
3423}
3424
3425impl SessionListCapabilities {
3426    #[must_use]
3427    pub fn new() -> Self {
3428        Self::default()
3429    }
3430    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3431    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3432    /// these keys.
3433    ///
3434    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3435    #[must_use]
3436    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3437        self.meta = meta.into_option();
3438        self
3439    }
3440}
3441
3442/// **UNSTABLE**
3443///
3444/// This capability is not part of the spec yet, and may be removed or changed at any point.
3445///
3446/// Capabilities for the `session/fork` method.
3447///
3448/// By supplying `{}` it means that the agent supports forking of sessions.
3449#[cfg(feature = "unstable_session_fork")]
3450#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3451#[non_exhaustive]
3452pub struct SessionForkCapabilities {
3453    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3454    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3455    /// these keys.
3456    ///
3457    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3458    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3459    pub meta: Option<Meta>,
3460}
3461
3462#[cfg(feature = "unstable_session_fork")]
3463impl SessionForkCapabilities {
3464    #[must_use]
3465    pub fn new() -> Self {
3466        Self::default()
3467    }
3468
3469    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3470    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3471    /// these keys.
3472    ///
3473    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3474    #[must_use]
3475    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3476        self.meta = meta.into_option();
3477        self
3478    }
3479}
3480
3481/// **UNSTABLE**
3482///
3483/// This capability is not part of the spec yet, and may be removed or changed at any point.
3484///
3485/// Capabilities for the `session/resume` method.
3486///
3487/// By supplying `{}` it means that the agent supports resuming of sessions.
3488#[cfg(feature = "unstable_session_resume")]
3489#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3490#[non_exhaustive]
3491pub struct SessionResumeCapabilities {
3492    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3493    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3494    /// these keys.
3495    ///
3496    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3497    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3498    pub meta: Option<Meta>,
3499}
3500
3501#[cfg(feature = "unstable_session_resume")]
3502impl SessionResumeCapabilities {
3503    #[must_use]
3504    pub fn new() -> Self {
3505        Self::default()
3506    }
3507
3508    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3509    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3510    /// these keys.
3511    ///
3512    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3513    #[must_use]
3514    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3515        self.meta = meta.into_option();
3516        self
3517    }
3518}
3519
3520/// **UNSTABLE**
3521///
3522/// This capability is not part of the spec yet, and may be removed or changed at any point.
3523///
3524/// Capabilities for the `session/close` method.
3525///
3526/// By supplying `{}` it means that the agent supports closing of sessions.
3527#[cfg(feature = "unstable_session_close")]
3528#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3529#[non_exhaustive]
3530pub struct SessionCloseCapabilities {
3531    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3532    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3533    /// these keys.
3534    ///
3535    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3536    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3537    pub meta: Option<Meta>,
3538}
3539
3540#[cfg(feature = "unstable_session_close")]
3541impl SessionCloseCapabilities {
3542    #[must_use]
3543    pub fn new() -> Self {
3544        Self::default()
3545    }
3546
3547    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3548    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3549    /// these keys.
3550    ///
3551    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3552    #[must_use]
3553    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3554        self.meta = meta.into_option();
3555        self
3556    }
3557}
3558
3559/// Prompt capabilities supported by the agent in `session/prompt` requests.
3560///
3561/// Baseline agent functionality requires support for [`ContentBlock::Text`]
3562/// and [`ContentBlock::ResourceLink`] in prompt requests.
3563///
3564/// Other variants must be explicitly opted in to.
3565/// Capabilities for different types of content in prompt requests.
3566///
3567/// Indicates which content types beyond the baseline (text and resource links)
3568/// the agent can process.
3569///
3570/// See protocol docs: [Prompt Capabilities](https://agentclientprotocol.com/protocol/initialization#prompt-capabilities)
3571#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3572#[serde(rename_all = "camelCase")]
3573#[non_exhaustive]
3574pub struct PromptCapabilities {
3575    /// Agent supports [`ContentBlock::Image`].
3576    #[serde(default)]
3577    pub image: bool,
3578    /// Agent supports [`ContentBlock::Audio`].
3579    #[serde(default)]
3580    pub audio: bool,
3581    /// Agent supports embedded context in `session/prompt` requests.
3582    ///
3583    /// When enabled, the Client is allowed to include [`ContentBlock::Resource`]
3584    /// in prompt requests for pieces of context that are referenced in the message.
3585    #[serde(default)]
3586    pub embedded_context: bool,
3587    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3588    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3589    /// these keys.
3590    ///
3591    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3592    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3593    pub meta: Option<Meta>,
3594}
3595
3596impl PromptCapabilities {
3597    #[must_use]
3598    pub fn new() -> Self {
3599        Self::default()
3600    }
3601
3602    /// Agent supports [`ContentBlock::Image`].
3603    #[must_use]
3604    pub fn image(mut self, image: bool) -> Self {
3605        self.image = image;
3606        self
3607    }
3608
3609    /// Agent supports [`ContentBlock::Audio`].
3610    #[must_use]
3611    pub fn audio(mut self, audio: bool) -> Self {
3612        self.audio = audio;
3613        self
3614    }
3615
3616    /// Agent supports embedded context in `session/prompt` requests.
3617    ///
3618    /// When enabled, the Client is allowed to include [`ContentBlock::Resource`]
3619    /// in prompt requests for pieces of context that are referenced in the message.
3620    #[must_use]
3621    pub fn embedded_context(mut self, embedded_context: bool) -> Self {
3622        self.embedded_context = embedded_context;
3623        self
3624    }
3625
3626    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3627    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3628    /// these keys.
3629    ///
3630    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3631    #[must_use]
3632    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3633        self.meta = meta.into_option();
3634        self
3635    }
3636}
3637
3638/// MCP capabilities supported by the agent
3639#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
3640#[serde(rename_all = "camelCase")]
3641#[non_exhaustive]
3642pub struct McpCapabilities {
3643    /// Agent supports [`McpServer::Http`].
3644    #[serde(default)]
3645    pub http: bool,
3646    /// Agent supports [`McpServer::Sse`].
3647    #[serde(default)]
3648    pub sse: bool,
3649    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3650    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3651    /// these keys.
3652    ///
3653    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3654    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
3655    pub meta: Option<Meta>,
3656}
3657
3658impl McpCapabilities {
3659    #[must_use]
3660    pub fn new() -> Self {
3661        Self::default()
3662    }
3663
3664    /// Agent supports [`McpServer::Http`].
3665    #[must_use]
3666    pub fn http(mut self, http: bool) -> Self {
3667        self.http = http;
3668        self
3669    }
3670
3671    /// Agent supports [`McpServer::Sse`].
3672    #[must_use]
3673    pub fn sse(mut self, sse: bool) -> Self {
3674        self.sse = sse;
3675        self
3676    }
3677
3678    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3679    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3680    /// these keys.
3681    ///
3682    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3683    #[must_use]
3684    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3685        self.meta = meta.into_option();
3686        self
3687    }
3688}
3689
3690// Method schema
3691
3692/// Names of all methods that agents handle.
3693///
3694/// Provides a centralized definition of method names used in the protocol.
3695#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3696#[non_exhaustive]
3697pub struct AgentMethodNames {
3698    /// Method for initializing the connection.
3699    pub initialize: &'static str,
3700    /// Method for authenticating with the agent.
3701    pub authenticate: &'static str,
3702    /// Method for creating a new session.
3703    pub session_new: &'static str,
3704    /// Method for loading an existing session.
3705    pub session_load: &'static str,
3706    /// Method for setting the mode for a session.
3707    pub session_set_mode: &'static str,
3708    /// Method for setting a configuration option for a session.
3709    pub session_set_config_option: &'static str,
3710    /// Method for sending a prompt to the agent.
3711    pub session_prompt: &'static str,
3712    /// Notification for cancelling operations.
3713    pub session_cancel: &'static str,
3714    /// Method for selecting a model for a given session.
3715    #[cfg(feature = "unstable_session_model")]
3716    pub session_set_model: &'static str,
3717    /// Method for listing existing sessions.
3718    pub session_list: &'static str,
3719    /// Method for forking an existing session.
3720    #[cfg(feature = "unstable_session_fork")]
3721    pub session_fork: &'static str,
3722    /// Method for resuming an existing session.
3723    #[cfg(feature = "unstable_session_resume")]
3724    pub session_resume: &'static str,
3725    /// Method for closing an active session.
3726    #[cfg(feature = "unstable_session_close")]
3727    pub session_close: &'static str,
3728    /// Method for logging out of an authenticated session.
3729    #[cfg(feature = "unstable_logout")]
3730    pub logout: &'static str,
3731}
3732
3733/// Constant containing all agent method names.
3734pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
3735    initialize: INITIALIZE_METHOD_NAME,
3736    authenticate: AUTHENTICATE_METHOD_NAME,
3737    session_new: SESSION_NEW_METHOD_NAME,
3738    session_load: SESSION_LOAD_METHOD_NAME,
3739    session_set_mode: SESSION_SET_MODE_METHOD_NAME,
3740    session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
3741    session_prompt: SESSION_PROMPT_METHOD_NAME,
3742    session_cancel: SESSION_CANCEL_METHOD_NAME,
3743    #[cfg(feature = "unstable_session_model")]
3744    session_set_model: SESSION_SET_MODEL_METHOD_NAME,
3745    session_list: SESSION_LIST_METHOD_NAME,
3746    #[cfg(feature = "unstable_session_fork")]
3747    session_fork: SESSION_FORK_METHOD_NAME,
3748    #[cfg(feature = "unstable_session_resume")]
3749    session_resume: SESSION_RESUME_METHOD_NAME,
3750    #[cfg(feature = "unstable_session_close")]
3751    session_close: SESSION_CLOSE_METHOD_NAME,
3752    #[cfg(feature = "unstable_logout")]
3753    logout: LOGOUT_METHOD_NAME,
3754};
3755
3756/// Method name for the initialize request.
3757pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
3758/// Method name for the authenticate request.
3759pub(crate) const AUTHENTICATE_METHOD_NAME: &str = "authenticate";
3760/// Method name for creating a new session.
3761pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
3762/// Method name for loading an existing session.
3763pub(crate) const SESSION_LOAD_METHOD_NAME: &str = "session/load";
3764/// Method name for setting the mode for a session.
3765pub(crate) const SESSION_SET_MODE_METHOD_NAME: &str = "session/set_mode";
3766/// Method name for setting a configuration option for a session.
3767pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
3768/// Method name for sending a prompt.
3769pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
3770/// Method name for the cancel notification.
3771pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
3772/// Method name for selecting a model for a given session.
3773#[cfg(feature = "unstable_session_model")]
3774pub(crate) const SESSION_SET_MODEL_METHOD_NAME: &str = "session/set_model";
3775/// Method name for listing existing sessions.
3776pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
3777/// Method name for forking an existing session.
3778#[cfg(feature = "unstable_session_fork")]
3779pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
3780/// Method name for resuming an existing session.
3781#[cfg(feature = "unstable_session_resume")]
3782pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
3783/// Method name for closing an active session.
3784#[cfg(feature = "unstable_session_close")]
3785pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
3786/// Method name for logging out of an authenticated session.
3787#[cfg(feature = "unstable_logout")]
3788pub(crate) const LOGOUT_METHOD_NAME: &str = "logout";
3789
3790/// All possible requests that a client can send to an agent.
3791///
3792/// This enum is used internally for routing RPC requests. You typically won't need
3793/// to use this directly - instead, use the methods on the [`Agent`] trait.
3794///
3795/// This enum encompasses all method calls from client to agent.
3796#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
3797#[serde(untagged)]
3798#[schemars(inline)]
3799#[non_exhaustive]
3800#[allow(clippy::large_enum_variant)]
3801pub enum ClientRequest {
3802    /// Establishes the connection with a client and negotiates protocol capabilities.
3803    ///
3804    /// This method is called once at the beginning of the connection to:
3805    /// - Negotiate the protocol version to use
3806    /// - Exchange capability information between client and agent
3807    /// - Determine available authentication methods
3808    ///
3809    /// The agent should respond with its supported protocol version and capabilities.
3810    ///
3811    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
3812    InitializeRequest(InitializeRequest),
3813    /// Authenticates the client using the specified authentication method.
3814    ///
3815    /// Called when the agent requires authentication before allowing session creation.
3816    /// The client provides the authentication method ID that was advertised during initialization.
3817    ///
3818    /// After successful authentication, the client can proceed to create sessions with
3819    /// `new_session` without receiving an `auth_required` error.
3820    ///
3821    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
3822    AuthenticateRequest(AuthenticateRequest),
3823    /// **UNSTABLE**
3824    ///
3825    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3826    ///
3827    /// Logs out of the current authenticated state.
3828    ///
3829    /// After a successful logout, all new sessions will require authentication.
3830    /// There is no guarantee about the behavior of already running sessions.
3831    #[cfg(feature = "unstable_logout")]
3832    LogoutRequest(LogoutRequest),
3833    /// Creates a new conversation session with the agent.
3834    ///
3835    /// Sessions represent independent conversation contexts with their own history and state.
3836    ///
3837    /// The agent should:
3838    /// - Create a new session context
3839    /// - Connect to any specified MCP servers
3840    /// - Return a unique session ID for future requests
3841    ///
3842    /// May return an `auth_required` error if the agent requires authentication.
3843    ///
3844    /// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/session-setup)
3845    NewSessionRequest(NewSessionRequest),
3846    /// Loads an existing session to resume a previous conversation.
3847    ///
3848    /// This method is only available if the agent advertises the `loadSession` capability.
3849    ///
3850    /// The agent should:
3851    /// - Restore the session context and conversation history
3852    /// - Connect to the specified MCP servers
3853    /// - Stream the entire conversation history back to the client via notifications
3854    ///
3855    /// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)
3856    LoadSessionRequest(LoadSessionRequest),
3857    /// Lists existing sessions known to the agent.
3858    ///
3859    /// This method is only available if the agent advertises the `sessionCapabilities.list` capability.
3860    ///
3861    /// The agent should return metadata about sessions with optional filtering and pagination support.
3862    ListSessionsRequest(ListSessionsRequest),
3863    #[cfg(feature = "unstable_session_fork")]
3864    /// **UNSTABLE**
3865    ///
3866    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3867    ///
3868    /// Forks an existing session to create a new independent session.
3869    ///
3870    /// This method is only available if the agent advertises the `session.fork` capability.
3871    ///
3872    /// The agent should create a new session with the same conversation context as the
3873    /// original, allowing operations like generating summaries without affecting the
3874    /// original session's history.
3875    ForkSessionRequest(ForkSessionRequest),
3876    #[cfg(feature = "unstable_session_resume")]
3877    /// **UNSTABLE**
3878    ///
3879    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3880    ///
3881    /// Resumes an existing session without returning previous messages.
3882    ///
3883    /// This method is only available if the agent advertises the `session.resume` capability.
3884    ///
3885    /// The agent should resume the session context, allowing the conversation to continue
3886    /// without replaying the message history (unlike `session/load`).
3887    ResumeSessionRequest(ResumeSessionRequest),
3888    #[cfg(feature = "unstable_session_close")]
3889    /// **UNSTABLE**
3890    ///
3891    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3892    ///
3893    /// Closes an active session and frees up any resources associated with it.
3894    ///
3895    /// This method is only available if the agent advertises the `session.close` capability.
3896    ///
3897    /// The agent must cancel any ongoing work (as if `session/cancel` was called)
3898    /// and then free up any resources associated with the session.
3899    CloseSessionRequest(CloseSessionRequest),
3900    /// Sets the current mode for a session.
3901    ///
3902    /// Allows switching between different agent modes (e.g., "ask", "architect", "code")
3903    /// that affect system prompts, tool availability, and permission behaviors.
3904    ///
3905    /// The mode must be one of the modes advertised in `availableModes` during session
3906    /// creation or loading. Agents may also change modes autonomously and notify the
3907    /// client via `current_mode_update` notifications.
3908    ///
3909    /// This method can be called at any time during a session, whether the Agent is
3910    /// idle or actively generating a response.
3911    ///
3912    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
3913    SetSessionModeRequest(SetSessionModeRequest),
3914    /// Sets the current value for a session configuration option.
3915    SetSessionConfigOptionRequest(SetSessionConfigOptionRequest),
3916    /// Processes a user prompt within a session.
3917    ///
3918    /// This method handles the whole lifecycle of a prompt:
3919    /// - Receives user messages with optional context (files, images, etc.)
3920    /// - Processes the prompt using language models
3921    /// - Reports language model content and tool calls to the Clients
3922    /// - Requests permission to run tools
3923    /// - Executes any requested tool calls
3924    /// - Returns when the turn is complete with a stop reason
3925    ///
3926    /// See protocol docs: [Prompt Turn](https://agentclientprotocol.com/protocol/prompt-turn)
3927    PromptRequest(PromptRequest),
3928    #[cfg(feature = "unstable_session_model")]
3929    /// **UNSTABLE**
3930    ///
3931    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3932    ///
3933    /// Select a model for a given session.
3934    SetSessionModelRequest(SetSessionModelRequest),
3935    /// Handles extension method requests from the client.
3936    ///
3937    /// Extension methods provide a way to add custom functionality while maintaining
3938    /// protocol compatibility.
3939    ///
3940    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3941    ExtMethodRequest(ExtRequest),
3942}
3943
3944impl ClientRequest {
3945    /// Returns the corresponding method name of the request.
3946    #[must_use]
3947    pub fn method(&self) -> &str {
3948        match self {
3949            Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
3950            Self::AuthenticateRequest(_) => AGENT_METHOD_NAMES.authenticate,
3951            #[cfg(feature = "unstable_logout")]
3952            Self::LogoutRequest(_) => AGENT_METHOD_NAMES.logout,
3953            Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
3954            Self::LoadSessionRequest(_) => AGENT_METHOD_NAMES.session_load,
3955            Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
3956            #[cfg(feature = "unstable_session_fork")]
3957            Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
3958            #[cfg(feature = "unstable_session_resume")]
3959            Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
3960            #[cfg(feature = "unstable_session_close")]
3961            Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
3962            Self::SetSessionModeRequest(_) => AGENT_METHOD_NAMES.session_set_mode,
3963            Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
3964            Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
3965            #[cfg(feature = "unstable_session_model")]
3966            Self::SetSessionModelRequest(_) => AGENT_METHOD_NAMES.session_set_model,
3967            Self::ExtMethodRequest(ext_request) => &ext_request.method,
3968        }
3969    }
3970}
3971
3972/// All possible responses that an agent can send to a client.
3973///
3974/// This enum is used internally for routing RPC responses. You typically won't need
3975/// to use this directly - the responses are handled automatically by the connection.
3976///
3977/// These are responses to the corresponding `ClientRequest` variants.
3978#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
3979#[serde(untagged)]
3980#[schemars(inline)]
3981#[non_exhaustive]
3982#[allow(clippy::large_enum_variant)]
3983pub enum AgentResponse {
3984    InitializeResponse(InitializeResponse),
3985    AuthenticateResponse(#[serde(default)] AuthenticateResponse),
3986    #[cfg(feature = "unstable_logout")]
3987    LogoutResponse(#[serde(default)] LogoutResponse),
3988    NewSessionResponse(NewSessionResponse),
3989    LoadSessionResponse(#[serde(default)] LoadSessionResponse),
3990    ListSessionsResponse(ListSessionsResponse),
3991    #[cfg(feature = "unstable_session_fork")]
3992    ForkSessionResponse(ForkSessionResponse),
3993    #[cfg(feature = "unstable_session_resume")]
3994    ResumeSessionResponse(#[serde(default)] ResumeSessionResponse),
3995    #[cfg(feature = "unstable_session_close")]
3996    CloseSessionResponse(#[serde(default)] CloseSessionResponse),
3997    SetSessionModeResponse(#[serde(default)] SetSessionModeResponse),
3998    SetSessionConfigOptionResponse(SetSessionConfigOptionResponse),
3999    PromptResponse(PromptResponse),
4000    #[cfg(feature = "unstable_session_model")]
4001    SetSessionModelResponse(#[serde(default)] SetSessionModelResponse),
4002    ExtMethodResponse(ExtResponse),
4003}
4004
4005/// All possible notifications that a client can send to an agent.
4006///
4007/// This enum is used internally for routing RPC notifications. You typically won't need
4008/// to use this directly - use the notification methods on the [`Agent`] trait instead.
4009///
4010/// Notifications do not expect a response.
4011#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
4012#[serde(untagged)]
4013#[schemars(inline)]
4014#[non_exhaustive]
4015pub enum ClientNotification {
4016    /// Cancels ongoing operations for a session.
4017    ///
4018    /// This is a notification sent by the client to cancel an ongoing prompt turn.
4019    ///
4020    /// Upon receiving this notification, the Agent SHOULD:
4021    /// - Stop all language model requests as soon as possible
4022    /// - Abort all tool call invocations in progress
4023    /// - Send any pending `session/update` notifications
4024    /// - Respond to the original `session/prompt` request with `StopReason::Cancelled`
4025    ///
4026    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
4027    CancelNotification(CancelNotification),
4028    /// Handles extension notifications from the client.
4029    ///
4030    /// Extension notifications provide a way to send one-way messages for custom functionality
4031    /// while maintaining protocol compatibility.
4032    ///
4033    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4034    ExtNotification(ExtNotification),
4035}
4036
4037impl ClientNotification {
4038    /// Returns the corresponding method name of the notification.
4039    #[must_use]
4040    pub fn method(&self) -> &str {
4041        match self {
4042            Self::CancelNotification(_) => AGENT_METHOD_NAMES.session_cancel,
4043            Self::ExtNotification(ext_notification) => &ext_notification.method,
4044        }
4045    }
4046}
4047
4048/// Notification to cancel ongoing operations for a session.
4049///
4050/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
4051#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
4052#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME))]
4053#[serde(rename_all = "camelCase")]
4054#[non_exhaustive]
4055pub struct CancelNotification {
4056    /// The ID of the session to cancel operations for.
4057    pub session_id: SessionId,
4058    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4059    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4060    /// these keys.
4061    ///
4062    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4063    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
4064    pub meta: Option<Meta>,
4065}
4066
4067impl CancelNotification {
4068    #[must_use]
4069    pub fn new(session_id: impl Into<SessionId>) -> Self {
4070        Self {
4071            session_id: session_id.into(),
4072            meta: None,
4073        }
4074    }
4075
4076    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4077    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4078    /// these keys.
4079    ///
4080    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4081    #[must_use]
4082    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4083        self.meta = meta.into_option();
4084        self
4085    }
4086}
4087
4088#[cfg(test)]
4089mod test_serialization {
4090    use super::*;
4091    use serde_json::json;
4092
4093    #[test]
4094    fn test_mcp_server_stdio_serialization() {
4095        let server = McpServer::Stdio(
4096            McpServerStdio::new("test-server", "/usr/bin/server")
4097                .args(vec!["--port".to_string(), "3000".to_string()])
4098                .env(vec![EnvVariable::new("API_KEY", "secret123")]),
4099        );
4100
4101        let json = serde_json::to_value(&server).unwrap();
4102        assert_eq!(
4103            json,
4104            json!({
4105                "name": "test-server",
4106                "command": "/usr/bin/server",
4107                "args": ["--port", "3000"],
4108                "env": [
4109                    {
4110                        "name": "API_KEY",
4111                        "value": "secret123"
4112                    }
4113                ]
4114            })
4115        );
4116
4117        let deserialized: McpServer = serde_json::from_value(json).unwrap();
4118        match deserialized {
4119            McpServer::Stdio(McpServerStdio {
4120                name,
4121                command,
4122                args,
4123                env,
4124                meta: _,
4125            }) => {
4126                assert_eq!(name, "test-server");
4127                assert_eq!(command, PathBuf::from("/usr/bin/server"));
4128                assert_eq!(args, vec!["--port", "3000"]);
4129                assert_eq!(env.len(), 1);
4130                assert_eq!(env[0].name, "API_KEY");
4131                assert_eq!(env[0].value, "secret123");
4132            }
4133            _ => panic!("Expected Stdio variant"),
4134        }
4135    }
4136
4137    #[test]
4138    fn test_mcp_server_http_serialization() {
4139        let server = McpServer::Http(
4140            McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
4141                HttpHeader::new("Authorization", "Bearer token123"),
4142                HttpHeader::new("Content-Type", "application/json"),
4143            ]),
4144        );
4145
4146        let json = serde_json::to_value(&server).unwrap();
4147        assert_eq!(
4148            json,
4149            json!({
4150                "type": "http",
4151                "name": "http-server",
4152                "url": "https://api.example.com",
4153                "headers": [
4154                    {
4155                        "name": "Authorization",
4156                        "value": "Bearer token123"
4157                    },
4158                    {
4159                        "name": "Content-Type",
4160                        "value": "application/json"
4161                    }
4162                ]
4163            })
4164        );
4165
4166        let deserialized: McpServer = serde_json::from_value(json).unwrap();
4167        match deserialized {
4168            McpServer::Http(McpServerHttp {
4169                name,
4170                url,
4171                headers,
4172                meta: _,
4173            }) => {
4174                assert_eq!(name, "http-server");
4175                assert_eq!(url, "https://api.example.com");
4176                assert_eq!(headers.len(), 2);
4177                assert_eq!(headers[0].name, "Authorization");
4178                assert_eq!(headers[0].value, "Bearer token123");
4179                assert_eq!(headers[1].name, "Content-Type");
4180                assert_eq!(headers[1].value, "application/json");
4181            }
4182            _ => panic!("Expected Http variant"),
4183        }
4184    }
4185
4186    #[test]
4187    fn test_mcp_server_sse_serialization() {
4188        let server = McpServer::Sse(
4189            McpServerSse::new("sse-server", "https://sse.example.com/events")
4190                .headers(vec![HttpHeader::new("X-API-Key", "apikey456")]),
4191        );
4192
4193        let json = serde_json::to_value(&server).unwrap();
4194        assert_eq!(
4195            json,
4196            json!({
4197                "type": "sse",
4198                "name": "sse-server",
4199                "url": "https://sse.example.com/events",
4200                "headers": [
4201                    {
4202                        "name": "X-API-Key",
4203                        "value": "apikey456"
4204                    }
4205                ]
4206            })
4207        );
4208
4209        let deserialized: McpServer = serde_json::from_value(json).unwrap();
4210        match deserialized {
4211            McpServer::Sse(McpServerSse {
4212                name,
4213                url,
4214                headers,
4215                meta: _,
4216            }) => {
4217                assert_eq!(name, "sse-server");
4218                assert_eq!(url, "https://sse.example.com/events");
4219                assert_eq!(headers.len(), 1);
4220                assert_eq!(headers[0].name, "X-API-Key");
4221                assert_eq!(headers[0].value, "apikey456");
4222            }
4223            _ => panic!("Expected Sse variant"),
4224        }
4225    }
4226
4227    #[test]
4228    fn test_session_config_option_category_known_variants() {
4229        // Test serialization of known variants
4230        assert_eq!(
4231            serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
4232            json!("mode")
4233        );
4234        assert_eq!(
4235            serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
4236            json!("model")
4237        );
4238        assert_eq!(
4239            serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
4240            json!("thought_level")
4241        );
4242
4243        // Test deserialization of known variants
4244        assert_eq!(
4245            serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
4246            SessionConfigOptionCategory::Mode
4247        );
4248        assert_eq!(
4249            serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
4250            SessionConfigOptionCategory::Model
4251        );
4252        assert_eq!(
4253            serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
4254            SessionConfigOptionCategory::ThoughtLevel
4255        );
4256    }
4257
4258    #[test]
4259    fn test_session_config_option_category_unknown_variants() {
4260        // Test that unknown strings are captured in Other variant
4261        let unknown: SessionConfigOptionCategory =
4262            serde_json::from_str("\"some_future_category\"").unwrap();
4263        assert_eq!(
4264            unknown,
4265            SessionConfigOptionCategory::Other("some_future_category".to_string())
4266        );
4267
4268        // Test round-trip of unknown category
4269        let json = serde_json::to_value(&unknown).unwrap();
4270        assert_eq!(json, json!("some_future_category"));
4271    }
4272
4273    #[test]
4274    fn test_session_config_option_category_custom_categories() {
4275        // Category names beginning with `_` are free for custom use
4276        let custom: SessionConfigOptionCategory =
4277            serde_json::from_str("\"_my_custom_category\"").unwrap();
4278        assert_eq!(
4279            custom,
4280            SessionConfigOptionCategory::Other("_my_custom_category".to_string())
4281        );
4282
4283        // Test round-trip preserves the custom category name
4284        let json = serde_json::to_value(&custom).unwrap();
4285        assert_eq!(json, json!("_my_custom_category"));
4286
4287        // Deserialize back and verify
4288        let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
4289        assert_eq!(
4290            deserialized,
4291            SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
4292        );
4293    }
4294
4295    #[test]
4296    fn test_auth_method_agent_serialization() {
4297        let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
4298
4299        let json = serde_json::to_value(&method).unwrap();
4300        assert_eq!(
4301            json,
4302            json!({
4303                "id": "default-auth",
4304                "name": "Default Auth"
4305            })
4306        );
4307        // description should be omitted when None
4308        assert!(!json.as_object().unwrap().contains_key("description"));
4309        // Agent variant should not emit a `type` field (backward compat)
4310        assert!(!json.as_object().unwrap().contains_key("type"));
4311
4312        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
4313        match deserialized {
4314            AuthMethod::Agent(AuthMethodAgent { id, name, .. }) => {
4315                assert_eq!(id.0.as_ref(), "default-auth");
4316                assert_eq!(name, "Default Auth");
4317            }
4318            #[cfg(feature = "unstable_auth_methods")]
4319            _ => panic!("Expected Agent variant"),
4320        }
4321    }
4322
4323    #[test]
4324    fn test_auth_method_explicit_agent_deserialization() {
4325        // An explicit `"type": "agent"` should also deserialize to Agent
4326        let json = json!({
4327            "id": "agent-auth",
4328            "name": "Agent Auth",
4329            "type": "agent"
4330        });
4331
4332        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
4333        assert!(matches!(deserialized, AuthMethod::Agent(_)));
4334    }
4335
4336    #[cfg(feature = "unstable_auth_methods")]
4337    #[test]
4338    fn test_auth_method_env_var_serialization() {
4339        let method = AuthMethod::EnvVar(AuthMethodEnvVar::new(
4340            "api-key",
4341            "API Key",
4342            vec![AuthEnvVar::new("API_KEY")],
4343        ));
4344
4345        let json = serde_json::to_value(&method).unwrap();
4346        assert_eq!(
4347            json,
4348            json!({
4349                "id": "api-key",
4350                "name": "API Key",
4351                "type": "env_var",
4352                "vars": [{"name": "API_KEY"}]
4353            })
4354        );
4355        // secret defaults to true and should be omitted; optional defaults to false and should be omitted
4356        assert!(!json["vars"][0].as_object().unwrap().contains_key("secret"));
4357        assert!(
4358            !json["vars"][0]
4359                .as_object()
4360                .unwrap()
4361                .contains_key("optional")
4362        );
4363
4364        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
4365        match deserialized {
4366            AuthMethod::EnvVar(AuthMethodEnvVar {
4367                id,
4368                name: method_name,
4369                vars,
4370                link,
4371                ..
4372            }) => {
4373                assert_eq!(id.0.as_ref(), "api-key");
4374                assert_eq!(method_name, "API Key");
4375                assert_eq!(vars.len(), 1);
4376                assert_eq!(vars[0].name, "API_KEY");
4377                assert!(vars[0].secret);
4378                assert!(!vars[0].optional);
4379                assert!(link.is_none());
4380            }
4381            _ => panic!("Expected EnvVar variant"),
4382        }
4383    }
4384
4385    #[cfg(feature = "unstable_auth_methods")]
4386    #[test]
4387    fn test_auth_method_env_var_with_link_serialization() {
4388        let method = AuthMethod::EnvVar(
4389            AuthMethodEnvVar::new("api-key", "API Key", vec![AuthEnvVar::new("API_KEY")])
4390                .link("https://example.com/keys"),
4391        );
4392
4393        let json = serde_json::to_value(&method).unwrap();
4394        assert_eq!(
4395            json,
4396            json!({
4397                "id": "api-key",
4398                "name": "API Key",
4399                "type": "env_var",
4400                "vars": [{"name": "API_KEY"}],
4401                "link": "https://example.com/keys"
4402            })
4403        );
4404
4405        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
4406        match deserialized {
4407            AuthMethod::EnvVar(AuthMethodEnvVar { link, .. }) => {
4408                assert_eq!(link.as_deref(), Some("https://example.com/keys"));
4409            }
4410            _ => panic!("Expected EnvVar variant"),
4411        }
4412    }
4413
4414    #[cfg(feature = "unstable_auth_methods")]
4415    #[test]
4416    fn test_auth_method_env_var_multiple_vars() {
4417        let method = AuthMethod::EnvVar(AuthMethodEnvVar::new(
4418            "azure-openai",
4419            "Azure OpenAI",
4420            vec![
4421                AuthEnvVar::new("AZURE_OPENAI_API_KEY").label("API Key"),
4422                AuthEnvVar::new("AZURE_OPENAI_ENDPOINT")
4423                    .label("Endpoint URL")
4424                    .secret(false),
4425                AuthEnvVar::new("AZURE_OPENAI_API_VERSION")
4426                    .label("API Version")
4427                    .secret(false)
4428                    .optional(true),
4429            ],
4430        ));
4431
4432        let json = serde_json::to_value(&method).unwrap();
4433        assert_eq!(
4434            json,
4435            json!({
4436                "id": "azure-openai",
4437                "name": "Azure OpenAI",
4438                "type": "env_var",
4439                "vars": [
4440                    {"name": "AZURE_OPENAI_API_KEY", "label": "API Key"},
4441                    {"name": "AZURE_OPENAI_ENDPOINT", "label": "Endpoint URL", "secret": false},
4442                    {"name": "AZURE_OPENAI_API_VERSION", "label": "API Version", "secret": false, "optional": true}
4443                ]
4444            })
4445        );
4446
4447        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
4448        match deserialized {
4449            AuthMethod::EnvVar(AuthMethodEnvVar { vars, .. }) => {
4450                assert_eq!(vars.len(), 3);
4451                // First var: secret (default true), not optional (default false)
4452                assert_eq!(vars[0].name, "AZURE_OPENAI_API_KEY");
4453                assert_eq!(vars[0].label.as_deref(), Some("API Key"));
4454                assert!(vars[0].secret);
4455                assert!(!vars[0].optional);
4456                // Second var: not a secret, not optional
4457                assert_eq!(vars[1].name, "AZURE_OPENAI_ENDPOINT");
4458                assert!(!vars[1].secret);
4459                assert!(!vars[1].optional);
4460                // Third var: not a secret, optional
4461                assert_eq!(vars[2].name, "AZURE_OPENAI_API_VERSION");
4462                assert!(!vars[2].secret);
4463                assert!(vars[2].optional);
4464            }
4465            _ => panic!("Expected EnvVar variant"),
4466        }
4467    }
4468
4469    #[cfg(feature = "unstable_auth_methods")]
4470    #[test]
4471    fn test_auth_method_terminal_serialization() {
4472        let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
4473
4474        let json = serde_json::to_value(&method).unwrap();
4475        assert_eq!(
4476            json,
4477            json!({
4478                "id": "tui-auth",
4479                "name": "Terminal Auth",
4480                "type": "terminal"
4481            })
4482        );
4483        // args and env should be omitted when empty
4484        assert!(!json.as_object().unwrap().contains_key("args"));
4485        assert!(!json.as_object().unwrap().contains_key("env"));
4486
4487        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
4488        match deserialized {
4489            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
4490                assert!(args.is_empty());
4491                assert!(env.is_empty());
4492            }
4493            _ => panic!("Expected Terminal variant"),
4494        }
4495    }
4496
4497    #[cfg(feature = "unstable_auth_methods")]
4498    #[test]
4499    fn test_auth_method_terminal_with_args_and_env_serialization() {
4500        use std::collections::HashMap;
4501
4502        let mut env = HashMap::new();
4503        env.insert("TERM".to_string(), "xterm-256color".to_string());
4504
4505        let method = AuthMethod::Terminal(
4506            AuthMethodTerminal::new("tui-auth", "Terminal Auth")
4507                .args(vec!["--interactive".to_string(), "--color".to_string()])
4508                .env(env),
4509        );
4510
4511        let json = serde_json::to_value(&method).unwrap();
4512        assert_eq!(
4513            json,
4514            json!({
4515                "id": "tui-auth",
4516                "name": "Terminal Auth",
4517                "type": "terminal",
4518                "args": ["--interactive", "--color"],
4519                "env": {
4520                    "TERM": "xterm-256color"
4521                }
4522            })
4523        );
4524
4525        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
4526        match deserialized {
4527            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
4528                assert_eq!(args, vec!["--interactive", "--color"]);
4529                assert_eq!(env.len(), 1);
4530                assert_eq!(env.get("TERM").unwrap(), "xterm-256color");
4531            }
4532            _ => panic!("Expected Terminal variant"),
4533        }
4534    }
4535
4536    #[cfg(feature = "unstable_boolean_config")]
4537    #[test]
4538    fn test_session_config_option_value_id_serialize() {
4539        let val = SessionConfigOptionValue::value_id("model-1");
4540        let json = serde_json::to_value(&val).unwrap();
4541        // ValueId omits the "type" field (it's the default)
4542        assert_eq!(json, json!({ "value": "model-1" }));
4543        assert!(!json.as_object().unwrap().contains_key("type"));
4544    }
4545
4546    #[cfg(feature = "unstable_boolean_config")]
4547    #[test]
4548    fn test_session_config_option_value_boolean_serialize() {
4549        let val = SessionConfigOptionValue::boolean(true);
4550        let json = serde_json::to_value(&val).unwrap();
4551        assert_eq!(json, json!({ "type": "boolean", "value": true }));
4552    }
4553
4554    #[cfg(feature = "unstable_boolean_config")]
4555    #[test]
4556    fn test_session_config_option_value_deserialize_no_type() {
4557        // Missing "type" should default to ValueId
4558        let json = json!({ "value": "model-1" });
4559        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
4560        assert_eq!(val, SessionConfigOptionValue::value_id("model-1"));
4561        assert_eq!(val.as_value_id().unwrap().to_string(), "model-1");
4562    }
4563
4564    #[cfg(feature = "unstable_boolean_config")]
4565    #[test]
4566    fn test_session_config_option_value_deserialize_boolean() {
4567        let json = json!({ "type": "boolean", "value": true });
4568        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
4569        assert_eq!(val, SessionConfigOptionValue::boolean(true));
4570        assert_eq!(val.as_bool(), Some(true));
4571    }
4572
4573    #[cfg(feature = "unstable_boolean_config")]
4574    #[test]
4575    fn test_session_config_option_value_deserialize_boolean_false() {
4576        let json = json!({ "type": "boolean", "value": false });
4577        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
4578        assert_eq!(val, SessionConfigOptionValue::boolean(false));
4579        assert_eq!(val.as_bool(), Some(false));
4580    }
4581
4582    #[cfg(feature = "unstable_boolean_config")]
4583    #[test]
4584    fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
4585        // Unknown type with a string value gracefully falls back to ValueId
4586        let json = json!({ "type": "text", "value": "freeform input" });
4587        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
4588        assert_eq!(val.as_value_id().unwrap().to_string(), "freeform input");
4589    }
4590
4591    #[cfg(feature = "unstable_boolean_config")]
4592    #[test]
4593    fn test_session_config_option_value_roundtrip_value_id() {
4594        let original = SessionConfigOptionValue::value_id("option-a");
4595        let json = serde_json::to_value(&original).unwrap();
4596        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
4597        assert_eq!(original, roundtripped);
4598    }
4599
4600    #[cfg(feature = "unstable_boolean_config")]
4601    #[test]
4602    fn test_session_config_option_value_roundtrip_boolean() {
4603        let original = SessionConfigOptionValue::boolean(false);
4604        let json = serde_json::to_value(&original).unwrap();
4605        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
4606        assert_eq!(original, roundtripped);
4607    }
4608
4609    #[cfg(feature = "unstable_boolean_config")]
4610    #[test]
4611    fn test_session_config_option_value_type_mismatch_boolean_with_string() {
4612        // type says "boolean" but value is a string — falls to untagged ValueId
4613        let json = json!({ "type": "boolean", "value": "not a bool" });
4614        let result = serde_json::from_value::<SessionConfigOptionValue>(json);
4615        // serde tries Boolean first (fails), then falls to untagged ValueId (succeeds)
4616        assert!(result.is_ok());
4617        assert_eq!(
4618            result.unwrap().as_value_id().unwrap().to_string(),
4619            "not a bool"
4620        );
4621    }
4622
4623    #[cfg(feature = "unstable_boolean_config")]
4624    #[test]
4625    fn test_session_config_option_value_from_impls() {
4626        let from_str: SessionConfigOptionValue = "model-1".into();
4627        assert_eq!(from_str.as_value_id().unwrap().to_string(), "model-1");
4628
4629        let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
4630        assert_eq!(from_id.as_value_id().unwrap().to_string(), "model-2");
4631
4632        let from_bool: SessionConfigOptionValue = true.into();
4633        assert_eq!(from_bool.as_bool(), Some(true));
4634    }
4635
4636    #[cfg(feature = "unstable_boolean_config")]
4637    #[test]
4638    fn test_set_session_config_option_request_value_id() {
4639        let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
4640        let json = serde_json::to_value(&req).unwrap();
4641        assert_eq!(
4642            json,
4643            json!({
4644                "sessionId": "sess_1",
4645                "configId": "model",
4646                "value": "model-1"
4647            })
4648        );
4649        // No "type" field for value_id
4650        assert!(!json.as_object().unwrap().contains_key("type"));
4651    }
4652
4653    #[cfg(feature = "unstable_boolean_config")]
4654    #[test]
4655    fn test_set_session_config_option_request_boolean() {
4656        let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
4657        let json = serde_json::to_value(&req).unwrap();
4658        assert_eq!(
4659            json,
4660            json!({
4661                "sessionId": "sess_1",
4662                "configId": "brave_mode",
4663                "type": "boolean",
4664                "value": true
4665            })
4666        );
4667    }
4668
4669    #[cfg(feature = "unstable_boolean_config")]
4670    #[test]
4671    fn test_set_session_config_option_request_deserialize_no_type() {
4672        // Backwards-compatible: no "type" field → value_id
4673        let json = json!({
4674            "sessionId": "sess_1",
4675            "configId": "model",
4676            "value": "model-1"
4677        });
4678        let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
4679        assert_eq!(req.session_id.to_string(), "sess_1");
4680        assert_eq!(req.config_id.to_string(), "model");
4681        assert_eq!(req.value.as_value_id().unwrap().to_string(), "model-1");
4682    }
4683
4684    #[cfg(feature = "unstable_boolean_config")]
4685    #[test]
4686    fn test_set_session_config_option_request_deserialize_boolean() {
4687        let json = json!({
4688            "sessionId": "sess_1",
4689            "configId": "brave_mode",
4690            "type": "boolean",
4691            "value": true
4692        });
4693        let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
4694        assert_eq!(req.value.as_bool(), Some(true));
4695    }
4696
4697    #[cfg(feature = "unstable_boolean_config")]
4698    #[test]
4699    fn test_set_session_config_option_request_roundtrip_value_id() {
4700        let original = SetSessionConfigOptionRequest::new("s", "c", "v");
4701        let json = serde_json::to_value(&original).unwrap();
4702        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
4703        assert_eq!(original, roundtripped);
4704    }
4705
4706    #[cfg(feature = "unstable_boolean_config")]
4707    #[test]
4708    fn test_set_session_config_option_request_roundtrip_boolean() {
4709        let original = SetSessionConfigOptionRequest::new("s", "c", false);
4710        let json = serde_json::to_value(&original).unwrap();
4711        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
4712        assert_eq!(original, roundtripped);
4713    }
4714
4715    #[cfg(feature = "unstable_boolean_config")]
4716    #[test]
4717    fn test_session_config_boolean_serialization() {
4718        let cfg = SessionConfigBoolean::new(true);
4719        let json = serde_json::to_value(&cfg).unwrap();
4720        assert_eq!(json, json!({ "currentValue": true }));
4721
4722        let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
4723        assert!(deserialized.current_value);
4724    }
4725
4726    #[cfg(feature = "unstable_boolean_config")]
4727    #[test]
4728    fn test_session_config_option_boolean_variant() {
4729        let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
4730            .description("Skip confirmation prompts");
4731        let json = serde_json::to_value(&opt).unwrap();
4732        assert_eq!(
4733            json,
4734            json!({
4735                "id": "brave_mode",
4736                "name": "Brave Mode",
4737                "description": "Skip confirmation prompts",
4738                "type": "boolean",
4739                "currentValue": false
4740            })
4741        );
4742
4743        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
4744        assert_eq!(deserialized.id.to_string(), "brave_mode");
4745        assert_eq!(deserialized.name, "Brave Mode");
4746        match deserialized.kind {
4747            SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
4748            _ => panic!("Expected Boolean kind"),
4749        }
4750    }
4751
4752    #[cfg(feature = "unstable_boolean_config")]
4753    #[test]
4754    fn test_session_config_option_select_still_works() {
4755        // Make sure existing select options are unaffected
4756        let opt = SessionConfigOption::select(
4757            "model",
4758            "Model",
4759            "model-1",
4760            vec![
4761                SessionConfigSelectOption::new("model-1", "Model 1"),
4762                SessionConfigSelectOption::new("model-2", "Model 2"),
4763            ],
4764        );
4765        let json = serde_json::to_value(&opt).unwrap();
4766        assert_eq!(json["type"], "select");
4767        assert_eq!(json["currentValue"], "model-1");
4768        assert_eq!(json["options"].as_array().unwrap().len(), 2);
4769
4770        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
4771        match deserialized.kind {
4772            SessionConfigKind::Select(ref s) => {
4773                assert_eq!(s.current_value.to_string(), "model-1");
4774            }
4775            _ => panic!("Expected Select kind"),
4776        }
4777    }
4778}