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