Skip to main content

agent_client_protocol_schema/v1/
client.rs

1//! Methods and notifications the client handles/receives.
2//!
3//! This module defines the Client trait and all associated types for implementing
4//! a client that interacts with AI coding agents via the Agent Client Protocol (ACP).
5
6use std::{path::PathBuf, sync::Arc};
7
8use derive_more::{Display, From};
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
12
13#[cfg(feature = "unstable_elicitation")]
14use crate::{
15    CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse,
16    ElicitationCapabilities,
17};
18use crate::{
19    ContentBlock, EnvVariable, ExtNotification, ExtRequest, ExtResponse, IntoMaybeUndefined,
20    IntoOption, MaybeUndefined, Meta, Plan, SessionConfigOption, SessionId, SessionModeId,
21    SkipListener, ToolCall, ToolCallUpdate,
22};
23#[cfg(feature = "unstable_plan_operations")]
24use crate::{PlanCapabilities, PlanRemoved, PlanUpdate};
25
26#[cfg(feature = "unstable_mcp_over_acp")]
27use super::mcp::{
28    ConnectMcpRequest, ConnectMcpResponse, DisconnectMcpRequest, DisconnectMcpResponse,
29    MCP_CONNECT_METHOD_NAME, MCP_DISCONNECT_METHOD_NAME, MCP_MESSAGE_METHOD_NAME,
30    MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
31};
32
33#[cfg(feature = "unstable_nes")]
34use crate::{ClientNesCapabilities, PositionEncodingKind};
35
36// Session updates
37
38/// Notification containing a session update from the agent.
39///
40/// Used to stream real-time progress and results during prompt processing.
41///
42/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
43#[skip_serializing_none]
44#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
45#[schemars(extend("x-side" = "client", "x-method" = SESSION_UPDATE_NOTIFICATION))]
46#[serde(rename_all = "camelCase")]
47#[non_exhaustive]
48pub struct SessionNotification {
49    /// The ID of the session this update pertains to.
50    pub session_id: SessionId,
51    /// The actual update content.
52    pub update: SessionUpdate,
53    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
54    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
55    /// these keys.
56    ///
57    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
58    #[serde(rename = "_meta")]
59    pub meta: Option<Meta>,
60}
61
62impl SessionNotification {
63    #[must_use]
64    pub fn new(session_id: impl Into<SessionId>, update: SessionUpdate) -> Self {
65        Self {
66            session_id: session_id.into(),
67            update,
68            meta: None,
69        }
70    }
71
72    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
73    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
74    /// these keys.
75    ///
76    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
77    #[must_use]
78    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
79        self.meta = meta.into_option();
80        self
81    }
82}
83
84/// Different types of updates that can be sent during session processing.
85///
86/// These updates provide real-time feedback about the agent's progress.
87///
88/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
89#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
90#[serde(tag = "sessionUpdate", rename_all = "snake_case")]
91#[schemars(extend("discriminator" = {"propertyName": "sessionUpdate"}))]
92#[non_exhaustive]
93pub enum SessionUpdate {
94    /// A chunk of the user's message being streamed.
95    UserMessageChunk(ContentChunk),
96    /// A chunk of the agent's response being streamed.
97    AgentMessageChunk(ContentChunk),
98    /// A chunk of the agent's internal reasoning being streamed.
99    AgentThoughtChunk(ContentChunk),
100    /// Notification that a new tool call has been initiated.
101    ToolCall(ToolCall),
102    /// Update on the status or results of a tool call.
103    ToolCallUpdate(ToolCallUpdate),
104    /// The agent's execution plan for complex tasks.
105    /// See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan)
106    Plan(Plan),
107    /// **UNSTABLE**
108    ///
109    /// This capability is not part of the spec yet, and may be removed or changed at any point.
110    ///
111    /// A content update for a plan identified by ID.
112    #[cfg(feature = "unstable_plan_operations")]
113    PlanUpdate(PlanUpdate),
114    /// **UNSTABLE**
115    ///
116    /// This capability is not part of the spec yet, and may be removed or changed at any point.
117    ///
118    /// Removal notice for a plan identified by ID.
119    #[cfg(feature = "unstable_plan_operations")]
120    PlanRemoved(PlanRemoved),
121    /// Available commands are ready or have changed
122    AvailableCommandsUpdate(AvailableCommandsUpdate),
123    /// The current mode of the session has changed
124    ///
125    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
126    CurrentModeUpdate(CurrentModeUpdate),
127    /// Session configuration options have been updated.
128    ConfigOptionUpdate(ConfigOptionUpdate),
129    /// Session metadata has been updated (title, timestamps, custom metadata)
130    SessionInfoUpdate(SessionInfoUpdate),
131    /// Context window and cost update for the session.
132    UsageUpdate(UsageUpdate),
133}
134
135/// The current mode of the session has changed
136///
137/// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
138#[skip_serializing_none]
139#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
140#[serde(rename_all = "camelCase")]
141#[non_exhaustive]
142pub struct CurrentModeUpdate {
143    /// The ID of the current mode
144    pub current_mode_id: SessionModeId,
145    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
146    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
147    /// these keys.
148    ///
149    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
150    #[serde(rename = "_meta")]
151    pub meta: Option<Meta>,
152}
153
154impl CurrentModeUpdate {
155    #[must_use]
156    pub fn new(current_mode_id: impl Into<SessionModeId>) -> Self {
157        Self {
158            current_mode_id: current_mode_id.into(),
159            meta: None,
160        }
161    }
162
163    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
164    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
165    /// these keys.
166    ///
167    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
168    #[must_use]
169    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
170        self.meta = meta.into_option();
171        self
172    }
173}
174
175/// Session configuration options have been updated.
176#[serde_as]
177#[skip_serializing_none]
178#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
179#[serde(rename_all = "camelCase")]
180#[non_exhaustive]
181pub struct ConfigOptionUpdate {
182    /// The full set of configuration options and their current values.
183    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
184    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
185    pub config_options: Vec<SessionConfigOption>,
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(rename = "_meta")]
192    pub meta: Option<Meta>,
193}
194
195impl ConfigOptionUpdate {
196    #[must_use]
197    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
198        Self {
199            config_options,
200            meta: None,
201        }
202    }
203
204    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
205    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
206    /// these keys.
207    ///
208    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
209    #[must_use]
210    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
211        self.meta = meta.into_option();
212        self
213    }
214}
215
216/// Update to session metadata. All fields are optional to support partial updates.
217///
218/// Agents send this notification to update session information like title or custom metadata.
219/// This allows clients to display dynamic session names and track session state changes.
220#[skip_serializing_none]
221#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
222#[serde(rename_all = "camelCase")]
223#[non_exhaustive]
224pub struct SessionInfoUpdate {
225    /// Human-readable title for the session. Set to null to clear.
226    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
227    pub title: MaybeUndefined<String>,
228    /// ISO 8601 timestamp of last activity. Set to null to clear.
229    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
230    pub updated_at: MaybeUndefined<String>,
231    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
232    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
233    /// these keys.
234    ///
235    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
236    #[serde(rename = "_meta")]
237    pub meta: Option<Meta>,
238}
239
240impl SessionInfoUpdate {
241    #[must_use]
242    pub fn new() -> Self {
243        Self::default()
244    }
245
246    /// Human-readable title for the session. Set to null to clear.
247    #[must_use]
248    pub fn title(mut self, title: impl IntoMaybeUndefined<String>) -> Self {
249        self.title = title.into_maybe_undefined();
250        self
251    }
252
253    /// ISO 8601 timestamp of last activity. Set to null to clear.
254    #[must_use]
255    pub fn updated_at(mut self, updated_at: impl IntoMaybeUndefined<String>) -> Self {
256        self.updated_at = updated_at.into_maybe_undefined();
257        self
258    }
259
260    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
261    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
262    /// these keys.
263    ///
264    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
265    #[must_use]
266    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
267        self.meta = meta.into_option();
268        self
269    }
270}
271
272/// Context window and cost update for a session.
273#[serde_as]
274#[skip_serializing_none]
275#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
276#[serde(rename_all = "camelCase")]
277#[non_exhaustive]
278pub struct UsageUpdate {
279    /// Tokens currently in context.
280    pub used: u64,
281    /// Total context window size in tokens.
282    pub size: u64,
283    /// Cumulative session cost (optional).
284    #[serde_as(deserialize_as = "DefaultOnError")]
285    #[schemars(extend("x-deserialize-default-on-error" = true))]
286    #[serde(default)]
287    pub cost: Option<Cost>,
288    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
289    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
290    /// these keys.
291    ///
292    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
293    #[serde(rename = "_meta")]
294    pub meta: Option<Meta>,
295}
296
297impl UsageUpdate {
298    #[must_use]
299    pub fn new(used: u64, size: u64) -> Self {
300        Self {
301            used,
302            size,
303            cost: None,
304            meta: None,
305        }
306    }
307
308    /// Cumulative session cost (optional).
309    #[must_use]
310    pub fn cost(mut self, cost: impl IntoOption<Cost>) -> Self {
311        self.cost = cost.into_option();
312        self
313    }
314
315    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
316    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
317    /// these keys.
318    ///
319    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
320    #[must_use]
321    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
322        self.meta = meta.into_option();
323        self
324    }
325}
326
327/// Cost information for a session.
328#[skip_serializing_none]
329#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
330#[serde(rename_all = "camelCase")]
331#[non_exhaustive]
332pub struct Cost {
333    /// Total cumulative cost for session.
334    pub amount: f64,
335    /// ISO 4217 currency code (e.g., "USD", "EUR").
336    pub currency: String,
337    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
338    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
339    /// these keys.
340    ///
341    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
342    #[serde(rename = "_meta")]
343    pub meta: Option<Meta>,
344}
345
346impl Cost {
347    #[must_use]
348    pub fn new(amount: f64, currency: impl Into<String>) -> Self {
349        Self {
350            amount,
351            currency: currency.into(),
352            meta: None,
353        }
354    }
355
356    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
357    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
358    /// these keys.
359    ///
360    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
361    #[must_use]
362    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
363        self.meta = meta.into_option();
364        self
365    }
366}
367
368/// A streamed item of content
369#[skip_serializing_none]
370#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
371#[serde(rename_all = "camelCase")]
372#[non_exhaustive]
373pub struct ContentChunk {
374    /// A single item of content
375    pub content: ContentBlock,
376    /// A unique identifier for the message this chunk belongs to.
377    ///
378    /// All chunks belonging to the same message share the same `messageId`.
379    /// A change in `messageId` indicates a new message has started.
380    pub message_id: Option<MessageId>,
381    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
382    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
383    /// these keys.
384    ///
385    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
386    #[serde(rename = "_meta")]
387    pub meta: Option<Meta>,
388}
389
390impl ContentChunk {
391    #[must_use]
392    pub fn new(content: ContentBlock) -> Self {
393        Self {
394            content,
395            message_id: None,
396            meta: None,
397        }
398    }
399
400    /// A unique identifier for the message this chunk belongs to.
401    ///
402    /// All chunks belonging to the same message share the same `messageId`.
403    /// A change in `messageId` indicates a new message has started.
404    #[must_use]
405    pub fn message_id(mut self, message_id: impl IntoOption<MessageId>) -> Self {
406        self.message_id = message_id.into_option();
407        self
408    }
409
410    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
411    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
412    /// these keys.
413    ///
414    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
415    #[must_use]
416    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
417        self.meta = meta.into_option();
418        self
419    }
420}
421
422/// Unique identifier for a message within a session.
423#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
424#[serde(transparent)]
425#[from(Arc<str>, String, &'static str)]
426#[non_exhaustive]
427pub struct MessageId(pub Arc<str>);
428
429impl MessageId {
430    #[must_use]
431    pub fn new(id: impl Into<Arc<str>>) -> Self {
432        Self(id.into())
433    }
434}
435
436impl IntoOption<MessageId> for &str {
437    fn into_option(self) -> Option<MessageId> {
438        Some(MessageId::new(self))
439    }
440}
441
442/// Available commands are ready or have changed
443#[serde_as]
444#[skip_serializing_none]
445#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
446#[serde(rename_all = "camelCase")]
447#[non_exhaustive]
448pub struct AvailableCommandsUpdate {
449    /// Commands the agent can execute
450    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
451    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
452    pub available_commands: Vec<AvailableCommand>,
453    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
454    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
455    /// these keys.
456    ///
457    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
458    #[serde(rename = "_meta")]
459    pub meta: Option<Meta>,
460}
461
462impl AvailableCommandsUpdate {
463    #[must_use]
464    pub fn new(available_commands: Vec<AvailableCommand>) -> Self {
465        Self {
466            available_commands,
467            meta: None,
468        }
469    }
470
471    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
472    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
473    /// these keys.
474    ///
475    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
476    #[must_use]
477    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
478        self.meta = meta.into_option();
479        self
480    }
481}
482
483/// Information about a command.
484#[serde_as]
485#[skip_serializing_none]
486#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
487#[serde(rename_all = "camelCase")]
488#[non_exhaustive]
489pub struct AvailableCommand {
490    /// Command name (e.g., `create_plan`, `research_codebase`).
491    pub name: String,
492    /// Human-readable description of what the command does.
493    pub description: String,
494    /// Input for the command if required
495    #[serde_as(deserialize_as = "DefaultOnError")]
496    #[schemars(extend("x-deserialize-default-on-error" = true))]
497    #[serde(default)]
498    pub input: Option<AvailableCommandInput>,
499    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
500    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
501    /// these keys.
502    ///
503    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
504    #[serde(rename = "_meta")]
505    pub meta: Option<Meta>,
506}
507
508impl AvailableCommand {
509    #[must_use]
510    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
511        Self {
512            name: name.into(),
513            description: description.into(),
514            input: None,
515            meta: None,
516        }
517    }
518
519    /// Input for the command if required
520    #[must_use]
521    pub fn input(mut self, input: impl IntoOption<AvailableCommandInput>) -> Self {
522        self.input = input.into_option();
523        self
524    }
525
526    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
527    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
528    /// these keys.
529    ///
530    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
531    #[must_use]
532    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
533        self.meta = meta.into_option();
534        self
535    }
536}
537
538/// The input specification for a command.
539#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
540#[serde(untagged, rename_all = "camelCase")]
541#[non_exhaustive]
542pub enum AvailableCommandInput {
543    /// All text that was typed after the command name is provided as input.
544    Unstructured(UnstructuredCommandInput),
545}
546
547/// All text that was typed after the command name is provided as input.
548#[skip_serializing_none]
549#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
550#[serde(rename_all = "camelCase")]
551#[non_exhaustive]
552pub struct UnstructuredCommandInput {
553    /// A hint to display when the input hasn't been provided yet
554    pub hint: String,
555    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
556    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
557    /// these keys.
558    ///
559    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
560    #[serde(rename = "_meta")]
561    pub meta: Option<Meta>,
562}
563
564impl UnstructuredCommandInput {
565    #[must_use]
566    pub fn new(hint: impl Into<String>) -> Self {
567        Self {
568            hint: hint.into(),
569            meta: None,
570        }
571    }
572
573    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
574    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
575    /// these keys.
576    ///
577    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
578    #[must_use]
579    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
580        self.meta = meta.into_option();
581        self
582    }
583}
584
585// Permission
586
587/// Request for user permission to execute a tool call.
588///
589/// Sent when the agent needs authorization before performing a sensitive operation.
590///
591/// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
592#[skip_serializing_none]
593#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
594#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
595#[serde(rename_all = "camelCase")]
596#[non_exhaustive]
597pub struct RequestPermissionRequest {
598    /// The session ID for this request.
599    pub session_id: SessionId,
600    /// Details about the tool call requiring permission.
601    pub tool_call: ToolCallUpdate,
602    /// Available permission options for the user to choose from.
603    pub options: Vec<PermissionOption>,
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    #[serde(rename = "_meta")]
610    pub meta: Option<Meta>,
611}
612
613impl RequestPermissionRequest {
614    #[must_use]
615    pub fn new(
616        session_id: impl Into<SessionId>,
617        tool_call: ToolCallUpdate,
618        options: Vec<PermissionOption>,
619    ) -> Self {
620        Self {
621            session_id: session_id.into(),
622            tool_call,
623            options,
624            meta: None,
625        }
626    }
627
628    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
629    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
630    /// these keys.
631    ///
632    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
633    #[must_use]
634    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
635        self.meta = meta.into_option();
636        self
637    }
638}
639
640/// An option presented to the user when requesting permission.
641#[skip_serializing_none]
642#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
643#[serde(rename_all = "camelCase")]
644#[non_exhaustive]
645pub struct PermissionOption {
646    /// Unique identifier for this permission option.
647    pub option_id: PermissionOptionId,
648    /// Human-readable label to display to the user.
649    pub name: String,
650    /// Hint about the nature of this permission option.
651    pub kind: PermissionOptionKind,
652    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
653    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
654    /// these keys.
655    ///
656    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
657    #[serde(rename = "_meta")]
658    pub meta: Option<Meta>,
659}
660
661impl PermissionOption {
662    #[must_use]
663    pub fn new(
664        option_id: impl Into<PermissionOptionId>,
665        name: impl Into<String>,
666        kind: PermissionOptionKind,
667    ) -> Self {
668        Self {
669            option_id: option_id.into(),
670            name: name.into(),
671            kind,
672            meta: None,
673        }
674    }
675
676    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
677    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
678    /// these keys.
679    ///
680    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
681    #[must_use]
682    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
683        self.meta = meta.into_option();
684        self
685    }
686}
687
688/// Unique identifier for a permission option.
689#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
690#[serde(transparent)]
691#[from(Arc<str>, String, &'static str)]
692#[non_exhaustive]
693pub struct PermissionOptionId(pub Arc<str>);
694
695impl PermissionOptionId {
696    #[must_use]
697    pub fn new(id: impl Into<Arc<str>>) -> Self {
698        Self(id.into())
699    }
700}
701
702/// The type of permission option being presented to the user.
703///
704/// Helps clients choose appropriate icons and UI treatment.
705#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
706#[serde(rename_all = "snake_case")]
707#[non_exhaustive]
708pub enum PermissionOptionKind {
709    /// Allow this operation only this time.
710    AllowOnce,
711    /// Allow this operation and remember the choice.
712    AllowAlways,
713    /// Reject this operation only this time.
714    RejectOnce,
715    /// Reject this operation and remember the choice.
716    RejectAlways,
717}
718
719/// Response to a permission request.
720#[skip_serializing_none]
721#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
722#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
723#[serde(rename_all = "camelCase")]
724#[non_exhaustive]
725pub struct RequestPermissionResponse {
726    /// The user's decision on the permission request.
727    // This extra-level is unfortunately needed because the output must be an object
728    pub outcome: RequestPermissionOutcome,
729    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
730    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
731    /// these keys.
732    ///
733    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
734    #[serde(rename = "_meta")]
735    pub meta: Option<Meta>,
736}
737
738impl RequestPermissionResponse {
739    #[must_use]
740    pub fn new(outcome: RequestPermissionOutcome) -> Self {
741        Self {
742            outcome,
743            meta: None,
744        }
745    }
746
747    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
748    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
749    /// these keys.
750    ///
751    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
752    #[must_use]
753    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
754        self.meta = meta.into_option();
755        self
756    }
757}
758
759/// The outcome of a permission request.
760#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
761#[serde(tag = "outcome", rename_all = "snake_case")]
762#[schemars(extend("discriminator" = {"propertyName": "outcome"}))]
763#[non_exhaustive]
764pub enum RequestPermissionOutcome {
765    /// The prompt turn was cancelled before the user responded.
766    ///
767    /// When a client sends a `session/cancel` notification to cancel an ongoing
768    /// prompt turn, it MUST respond to all pending `session/request_permission`
769    /// requests with this `Cancelled` outcome.
770    ///
771    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
772    Cancelled,
773    /// The user selected one of the provided options.
774    #[serde(rename_all = "camelCase")]
775    Selected(SelectedPermissionOutcome),
776}
777
778/// The user selected one of the provided options.
779#[skip_serializing_none]
780#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
781#[serde(rename_all = "camelCase")]
782#[non_exhaustive]
783pub struct SelectedPermissionOutcome {
784    /// The ID of the option the user selected.
785    pub option_id: PermissionOptionId,
786    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
787    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
788    /// these keys.
789    ///
790    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
791    #[serde(rename = "_meta")]
792    pub meta: Option<Meta>,
793}
794
795impl SelectedPermissionOutcome {
796    #[must_use]
797    pub fn new(option_id: impl Into<PermissionOptionId>) -> Self {
798        Self {
799            option_id: option_id.into(),
800            meta: None,
801        }
802    }
803
804    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
805    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
806    /// these keys.
807    ///
808    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
809    #[must_use]
810    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
811        self.meta = meta.into_option();
812        self
813    }
814}
815
816// Write text file
817
818/// Request to write content to a text file.
819///
820/// Only available if the client supports the `fs.writeTextFile` capability.
821#[skip_serializing_none]
822#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
823#[schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME))]
824#[serde(rename_all = "camelCase")]
825#[non_exhaustive]
826pub struct WriteTextFileRequest {
827    /// The session ID for this request.
828    pub session_id: SessionId,
829    /// Absolute path to the file to write.
830    pub path: PathBuf,
831    /// The text content to write to the file.
832    pub content: String,
833    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
834    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
835    /// these keys.
836    ///
837    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
838    #[serde(rename = "_meta")]
839    pub meta: Option<Meta>,
840}
841
842impl WriteTextFileRequest {
843    #[must_use]
844    pub fn new(
845        session_id: impl Into<SessionId>,
846        path: impl Into<PathBuf>,
847        content: impl Into<String>,
848    ) -> Self {
849        Self {
850            session_id: session_id.into(),
851            path: path.into(),
852            content: content.into(),
853            meta: None,
854        }
855    }
856
857    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
858    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
859    /// these keys.
860    ///
861    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
862    #[must_use]
863    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
864        self.meta = meta.into_option();
865        self
866    }
867}
868
869/// Response to `fs/write_text_file`
870#[skip_serializing_none]
871#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
872#[serde(rename_all = "camelCase")]
873#[schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME))]
874#[non_exhaustive]
875pub struct WriteTextFileResponse {
876    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
877    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
878    /// these keys.
879    ///
880    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
881    #[serde(rename = "_meta")]
882    pub meta: Option<Meta>,
883}
884
885impl WriteTextFileResponse {
886    #[must_use]
887    pub fn new() -> Self {
888        Self::default()
889    }
890
891    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
892    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
893    /// these keys.
894    ///
895    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
896    #[must_use]
897    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
898        self.meta = meta.into_option();
899        self
900    }
901}
902
903// Read text file
904
905/// Request to read content from a text file.
906///
907/// Only available if the client supports the `fs.readTextFile` capability.
908#[skip_serializing_none]
909#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
910#[schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME))]
911#[serde(rename_all = "camelCase")]
912#[non_exhaustive]
913pub struct ReadTextFileRequest {
914    /// The session ID for this request.
915    pub session_id: SessionId,
916    /// Absolute path to the file to read.
917    pub path: PathBuf,
918    /// Line number to start reading from (1-based).
919    pub line: Option<u32>,
920    /// Maximum number of lines to read.
921    pub limit: Option<u32>,
922    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
923    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
924    /// these keys.
925    ///
926    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
927    #[serde(rename = "_meta")]
928    pub meta: Option<Meta>,
929}
930
931impl ReadTextFileRequest {
932    #[must_use]
933    pub fn new(session_id: impl Into<SessionId>, path: impl Into<PathBuf>) -> Self {
934        Self {
935            session_id: session_id.into(),
936            path: path.into(),
937            line: None,
938            limit: None,
939            meta: None,
940        }
941    }
942
943    /// Line number to start reading from (1-based).
944    #[must_use]
945    pub fn line(mut self, line: impl IntoOption<u32>) -> Self {
946        self.line = line.into_option();
947        self
948    }
949
950    /// Maximum number of lines to read.
951    #[must_use]
952    pub fn limit(mut self, limit: impl IntoOption<u32>) -> Self {
953        self.limit = limit.into_option();
954        self
955    }
956
957    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
958    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
959    /// these keys.
960    ///
961    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
962    #[must_use]
963    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
964        self.meta = meta.into_option();
965        self
966    }
967}
968
969/// Response containing the contents of a text file.
970#[skip_serializing_none]
971#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
972#[schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME))]
973#[serde(rename_all = "camelCase")]
974#[non_exhaustive]
975pub struct ReadTextFileResponse {
976    pub content: String,
977    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
978    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
979    /// these keys.
980    ///
981    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
982    #[serde(rename = "_meta")]
983    pub meta: Option<Meta>,
984}
985
986impl ReadTextFileResponse {
987    #[must_use]
988    pub fn new(content: impl Into<String>) -> Self {
989        Self {
990            content: content.into(),
991            meta: None,
992        }
993    }
994
995    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
996    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
997    /// these keys.
998    ///
999    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1000    #[must_use]
1001    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1002        self.meta = meta.into_option();
1003        self
1004    }
1005}
1006
1007// Terminals
1008
1009#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
1010#[serde(transparent)]
1011#[from(Arc<str>, String, &'static str)]
1012#[non_exhaustive]
1013pub struct TerminalId(pub Arc<str>);
1014
1015impl TerminalId {
1016    #[must_use]
1017    pub fn new(id: impl Into<Arc<str>>) -> Self {
1018        Self(id.into())
1019    }
1020}
1021
1022/// Request to create a new terminal and execute a command.
1023#[skip_serializing_none]
1024#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1025#[serde(rename_all = "camelCase")]
1026#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME))]
1027#[non_exhaustive]
1028pub struct CreateTerminalRequest {
1029    /// The session ID for this request.
1030    pub session_id: SessionId,
1031    /// The command to execute.
1032    pub command: String,
1033    /// Array of command arguments.
1034    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1035    pub args: Vec<String>,
1036    /// Environment variables for the command.
1037    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1038    pub env: Vec<EnvVariable>,
1039    /// Working directory for the command (absolute path).
1040    pub cwd: Option<PathBuf>,
1041    /// Maximum number of output bytes to retain.
1042    ///
1043    /// When the limit is exceeded, the Client truncates from the beginning of the output
1044    /// to stay within the limit.
1045    ///
1046    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
1047    /// string output, even if this means the retained output is slightly less than the
1048    /// specified limit.
1049    pub output_byte_limit: Option<u64>,
1050    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1051    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1052    /// these keys.
1053    ///
1054    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1055    #[serde(rename = "_meta")]
1056    pub meta: Option<Meta>,
1057}
1058
1059impl CreateTerminalRequest {
1060    #[must_use]
1061    pub fn new(session_id: impl Into<SessionId>, command: impl Into<String>) -> Self {
1062        Self {
1063            session_id: session_id.into(),
1064            command: command.into(),
1065            args: Vec::new(),
1066            env: Vec::new(),
1067            cwd: None,
1068            output_byte_limit: None,
1069            meta: None,
1070        }
1071    }
1072
1073    /// Array of command arguments.
1074    #[must_use]
1075    pub fn args(mut self, args: Vec<String>) -> Self {
1076        self.args = args;
1077        self
1078    }
1079
1080    /// Environment variables for the command.
1081    #[must_use]
1082    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1083        self.env = env;
1084        self
1085    }
1086
1087    /// Working directory for the command (absolute path).
1088    #[must_use]
1089    pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1090        self.cwd = cwd.into_option();
1091        self
1092    }
1093
1094    /// Maximum number of output bytes to retain.
1095    ///
1096    /// When the limit is exceeded, the Client truncates from the beginning of the output
1097    /// to stay within the limit.
1098    ///
1099    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
1100    /// string output, even if this means the retained output is slightly less than the
1101    /// specified limit.
1102    #[must_use]
1103    pub fn output_byte_limit(mut self, output_byte_limit: impl IntoOption<u64>) -> Self {
1104        self.output_byte_limit = output_byte_limit.into_option();
1105        self
1106    }
1107
1108    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1109    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1110    /// these keys.
1111    ///
1112    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1113    #[must_use]
1114    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1115        self.meta = meta.into_option();
1116        self
1117    }
1118}
1119
1120/// Response containing the ID of the created terminal.
1121#[skip_serializing_none]
1122#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1123#[serde(rename_all = "camelCase")]
1124#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME))]
1125#[non_exhaustive]
1126pub struct CreateTerminalResponse {
1127    /// The unique identifier for the created terminal.
1128    pub terminal_id: TerminalId,
1129    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1130    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1131    /// these keys.
1132    ///
1133    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1134    #[serde(rename = "_meta")]
1135    pub meta: Option<Meta>,
1136}
1137
1138impl CreateTerminalResponse {
1139    #[must_use]
1140    pub fn new(terminal_id: impl Into<TerminalId>) -> Self {
1141        Self {
1142            terminal_id: terminal_id.into(),
1143            meta: None,
1144        }
1145    }
1146
1147    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1148    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1149    /// these keys.
1150    ///
1151    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1152    #[must_use]
1153    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1154        self.meta = meta.into_option();
1155        self
1156    }
1157}
1158
1159/// Request to get the current output and status of a terminal.
1160#[skip_serializing_none]
1161#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1162#[serde(rename_all = "camelCase")]
1163#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME))]
1164#[non_exhaustive]
1165pub struct TerminalOutputRequest {
1166    /// The session ID for this request.
1167    pub session_id: SessionId,
1168    /// The ID of the terminal to get output from.
1169    pub terminal_id: TerminalId,
1170    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1171    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1172    /// these keys.
1173    ///
1174    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1175    #[serde(rename = "_meta")]
1176    pub meta: Option<Meta>,
1177}
1178
1179impl TerminalOutputRequest {
1180    #[must_use]
1181    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1182        Self {
1183            session_id: session_id.into(),
1184            terminal_id: terminal_id.into(),
1185            meta: None,
1186        }
1187    }
1188
1189    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1190    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1191    /// these keys.
1192    ///
1193    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1194    #[must_use]
1195    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1196        self.meta = meta.into_option();
1197        self
1198    }
1199}
1200
1201/// Response containing the terminal output and exit status.
1202#[skip_serializing_none]
1203#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1204#[serde(rename_all = "camelCase")]
1205#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME))]
1206#[non_exhaustive]
1207pub struct TerminalOutputResponse {
1208    /// The terminal output captured so far.
1209    pub output: String,
1210    /// Whether the output was truncated due to byte limits.
1211    pub truncated: bool,
1212    /// Exit status if the command has completed.
1213    pub exit_status: Option<TerminalExitStatus>,
1214    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1215    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1216    /// these keys.
1217    ///
1218    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1219    #[serde(rename = "_meta")]
1220    pub meta: Option<Meta>,
1221}
1222
1223impl TerminalOutputResponse {
1224    #[must_use]
1225    pub fn new(output: impl Into<String>, truncated: bool) -> Self {
1226        Self {
1227            output: output.into(),
1228            truncated,
1229            exit_status: None,
1230            meta: None,
1231        }
1232    }
1233
1234    /// Exit status if the command has completed.
1235    #[must_use]
1236    pub fn exit_status(mut self, exit_status: impl IntoOption<TerminalExitStatus>) -> Self {
1237        self.exit_status = exit_status.into_option();
1238        self
1239    }
1240
1241    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1242    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1243    /// these keys.
1244    ///
1245    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1246    #[must_use]
1247    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1248        self.meta = meta.into_option();
1249        self
1250    }
1251}
1252
1253/// Request to release a terminal and free its resources.
1254#[skip_serializing_none]
1255#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1256#[serde(rename_all = "camelCase")]
1257#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME))]
1258#[non_exhaustive]
1259pub struct ReleaseTerminalRequest {
1260    /// The session ID for this request.
1261    pub session_id: SessionId,
1262    /// The ID of the terminal to release.
1263    pub terminal_id: TerminalId,
1264    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1265    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1266    /// these keys.
1267    ///
1268    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1269    #[serde(rename = "_meta")]
1270    pub meta: Option<Meta>,
1271}
1272
1273impl ReleaseTerminalRequest {
1274    #[must_use]
1275    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1276        Self {
1277            session_id: session_id.into(),
1278            terminal_id: terminal_id.into(),
1279            meta: None,
1280        }
1281    }
1282
1283    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1284    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1285    /// these keys.
1286    ///
1287    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1288    #[must_use]
1289    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1290        self.meta = meta.into_option();
1291        self
1292    }
1293}
1294
1295/// Response to terminal/release method
1296#[skip_serializing_none]
1297#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1298#[serde(rename_all = "camelCase")]
1299#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME))]
1300#[non_exhaustive]
1301pub struct ReleaseTerminalResponse {
1302    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1303    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1304    /// these keys.
1305    ///
1306    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1307    #[serde(rename = "_meta")]
1308    pub meta: Option<Meta>,
1309}
1310
1311impl ReleaseTerminalResponse {
1312    #[must_use]
1313    pub fn new() -> Self {
1314        Self::default()
1315    }
1316
1317    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1318    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1319    /// these keys.
1320    ///
1321    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1322    #[must_use]
1323    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1324        self.meta = meta.into_option();
1325        self
1326    }
1327}
1328
1329/// Request to kill a terminal without releasing it.
1330#[skip_serializing_none]
1331#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1332#[serde(rename_all = "camelCase")]
1333#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME))]
1334#[non_exhaustive]
1335pub struct KillTerminalRequest {
1336    /// The session ID for this request.
1337    pub session_id: SessionId,
1338    /// The ID of the terminal to kill.
1339    pub terminal_id: TerminalId,
1340    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1341    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1342    /// these keys.
1343    ///
1344    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1345    #[serde(rename = "_meta")]
1346    pub meta: Option<Meta>,
1347}
1348
1349impl KillTerminalRequest {
1350    #[must_use]
1351    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1352        Self {
1353            session_id: session_id.into(),
1354            terminal_id: terminal_id.into(),
1355            meta: None,
1356        }
1357    }
1358
1359    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1360    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1361    /// these keys.
1362    ///
1363    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1364    #[must_use]
1365    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1366        self.meta = meta.into_option();
1367        self
1368    }
1369}
1370
1371/// Response to `terminal/kill` method
1372#[skip_serializing_none]
1373#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1374#[serde(rename_all = "camelCase")]
1375#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME))]
1376#[non_exhaustive]
1377pub struct KillTerminalResponse {
1378    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1379    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1380    /// these keys.
1381    ///
1382    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1383    #[serde(rename = "_meta")]
1384    pub meta: Option<Meta>,
1385}
1386
1387impl KillTerminalResponse {
1388    #[must_use]
1389    pub fn new() -> Self {
1390        Self::default()
1391    }
1392
1393    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1394    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1395    /// these keys.
1396    ///
1397    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1398    #[must_use]
1399    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1400        self.meta = meta.into_option();
1401        self
1402    }
1403}
1404
1405/// Request to wait for a terminal command to exit.
1406#[skip_serializing_none]
1407#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1408#[serde(rename_all = "camelCase")]
1409#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME))]
1410#[non_exhaustive]
1411pub struct WaitForTerminalExitRequest {
1412    /// The session ID for this request.
1413    pub session_id: SessionId,
1414    /// The ID of the terminal to wait for.
1415    pub terminal_id: TerminalId,
1416    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1417    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1418    /// these keys.
1419    ///
1420    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1421    #[serde(rename = "_meta")]
1422    pub meta: Option<Meta>,
1423}
1424
1425impl WaitForTerminalExitRequest {
1426    #[must_use]
1427    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1428        Self {
1429            session_id: session_id.into(),
1430            terminal_id: terminal_id.into(),
1431            meta: None,
1432        }
1433    }
1434
1435    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1436    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1437    /// these keys.
1438    ///
1439    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1440    #[must_use]
1441    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1442        self.meta = meta.into_option();
1443        self
1444    }
1445}
1446
1447/// Response containing the exit status of a terminal command.
1448#[skip_serializing_none]
1449#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1450#[serde(rename_all = "camelCase")]
1451#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME))]
1452#[non_exhaustive]
1453pub struct WaitForTerminalExitResponse {
1454    /// The exit status of the terminal command.
1455    #[serde(flatten)]
1456    pub exit_status: TerminalExitStatus,
1457    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1458    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1459    /// these keys.
1460    ///
1461    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1462    #[serde(rename = "_meta")]
1463    pub meta: Option<Meta>,
1464}
1465
1466impl WaitForTerminalExitResponse {
1467    #[must_use]
1468    pub fn new(exit_status: TerminalExitStatus) -> Self {
1469        Self {
1470            exit_status,
1471            meta: None,
1472        }
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/// Exit status of a terminal command.
1488#[skip_serializing_none]
1489#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1490#[serde(rename_all = "camelCase")]
1491#[non_exhaustive]
1492pub struct TerminalExitStatus {
1493    /// The process exit code (may be null if terminated by signal).
1494    pub exit_code: Option<u32>,
1495    /// The signal that terminated the process (may be null if exited normally).
1496    pub signal: Option<String>,
1497    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1498    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1499    /// these keys.
1500    ///
1501    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1502    #[serde(rename = "_meta")]
1503    pub meta: Option<Meta>,
1504}
1505
1506impl TerminalExitStatus {
1507    #[must_use]
1508    pub fn new() -> Self {
1509        Self::default()
1510    }
1511
1512    /// The process exit code (may be null if terminated by signal).
1513    #[must_use]
1514    pub fn exit_code(mut self, exit_code: impl IntoOption<u32>) -> Self {
1515        self.exit_code = exit_code.into_option();
1516        self
1517    }
1518
1519    /// The signal that terminated the process (may be null if exited normally).
1520    #[must_use]
1521    pub fn signal(mut self, signal: impl IntoOption<String>) -> Self {
1522        self.signal = signal.into_option();
1523        self
1524    }
1525
1526    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1527    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1528    /// these keys.
1529    ///
1530    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1531    #[must_use]
1532    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1533        self.meta = meta.into_option();
1534        self
1535    }
1536}
1537
1538// Capabilities
1539
1540/// Capabilities supported by the client.
1541///
1542/// Advertised during initialization to inform the agent about
1543/// available features and methods.
1544///
1545/// See protocol docs: [Client Capabilities](https://agentclientprotocol.com/protocol/initialization#client-capabilities)
1546#[serde_as]
1547#[skip_serializing_none]
1548#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1549#[serde(rename_all = "camelCase")]
1550#[non_exhaustive]
1551pub struct ClientCapabilities {
1552    /// File system capabilities supported by the client.
1553    /// Determines which file operations the agent can request.
1554    #[serde(default)]
1555    pub fs: FileSystemCapabilities,
1556    /// Whether the Client support all `terminal/*` methods.
1557    #[serde(default)]
1558    pub terminal: bool,
1559    /// **UNSTABLE**
1560    ///
1561    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1562    ///
1563    /// Whether the client supports `plan_update` and `plan_removed` session updates.
1564    ///
1565    /// Optional. Omitted means the client does not advertise support.
1566    /// Supplying `{}` means the client can receive both update types.
1567    #[cfg(feature = "unstable_plan_operations")]
1568    #[serde_as(deserialize_as = "DefaultOnError")]
1569    #[schemars(extend("x-deserialize-default-on-error" = true))]
1570    #[serde(default)]
1571    pub plan: Option<PlanCapabilities>,
1572    /// **UNSTABLE**
1573    ///
1574    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1575    ///
1576    /// Authentication capabilities supported by the client.
1577    /// Determines which authentication method types the agent may include
1578    /// in its `InitializeResponse`.
1579    #[cfg(feature = "unstable_auth_methods")]
1580    #[serde(default)]
1581    pub auth: AuthCapabilities,
1582    /// **UNSTABLE**
1583    ///
1584    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1585    ///
1586    /// Elicitation capabilities supported by the client.
1587    /// Determines which elicitation modes the agent may use.
1588    #[cfg(feature = "unstable_elicitation")]
1589    #[serde_as(deserialize_as = "DefaultOnError")]
1590    #[schemars(extend("x-deserialize-default-on-error" = true))]
1591    #[serde(default)]
1592    pub elicitation: Option<ElicitationCapabilities>,
1593    /// **UNSTABLE**
1594    ///
1595    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1596    ///
1597    /// NES (Next Edit Suggestions) capabilities supported by the client.
1598    #[cfg(feature = "unstable_nes")]
1599    #[serde_as(deserialize_as = "DefaultOnError")]
1600    #[schemars(extend("x-deserialize-default-on-error" = true))]
1601    #[serde(default)]
1602    pub nes: Option<ClientNesCapabilities>,
1603    /// **UNSTABLE**
1604    ///
1605    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1606    ///
1607    /// The position encodings supported by the client, in order of preference.
1608    #[cfg(feature = "unstable_nes")]
1609    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1610    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1611    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1612    pub position_encodings: Vec<PositionEncodingKind>,
1613
1614    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1615    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1616    /// these keys.
1617    ///
1618    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1619    #[serde(rename = "_meta")]
1620    pub meta: Option<Meta>,
1621}
1622
1623impl ClientCapabilities {
1624    #[must_use]
1625    pub fn new() -> Self {
1626        Self::default()
1627    }
1628
1629    /// File system capabilities supported by the client.
1630    /// Determines which file operations the agent can request.
1631    #[must_use]
1632    pub fn fs(mut self, fs: FileSystemCapabilities) -> Self {
1633        self.fs = fs;
1634        self
1635    }
1636
1637    /// Whether the Client support all `terminal/*` methods.
1638    #[must_use]
1639    pub fn terminal(mut self, terminal: bool) -> Self {
1640        self.terminal = terminal;
1641        self
1642    }
1643
1644    /// **UNSTABLE**
1645    ///
1646    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1647    ///
1648    /// Whether the client supports `plan_update` and `plan_removed` session updates.
1649    ///
1650    /// Omitted means the client does not advertise support.
1651    /// Supplying `{}` means the client can receive both update types.
1652    #[cfg(feature = "unstable_plan_operations")]
1653    #[must_use]
1654    pub fn plan(mut self, plan: impl IntoOption<PlanCapabilities>) -> Self {
1655        self.plan = plan.into_option();
1656        self
1657    }
1658
1659    /// **UNSTABLE**
1660    ///
1661    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1662    ///
1663    /// Authentication capabilities supported by the client.
1664    /// Determines which authentication method types the agent may include
1665    /// in its `InitializeResponse`.
1666    #[cfg(feature = "unstable_auth_methods")]
1667    #[must_use]
1668    pub fn auth(mut self, auth: AuthCapabilities) -> Self {
1669        self.auth = auth;
1670        self
1671    }
1672
1673    /// **UNSTABLE**
1674    ///
1675    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1676    ///
1677    /// Elicitation capabilities supported by the client.
1678    /// Determines which elicitation modes the agent may use.
1679    #[cfg(feature = "unstable_elicitation")]
1680    #[must_use]
1681    pub fn elicitation(mut self, elicitation: impl IntoOption<ElicitationCapabilities>) -> Self {
1682        self.elicitation = elicitation.into_option();
1683        self
1684    }
1685
1686    /// **UNSTABLE**
1687    ///
1688    /// NES (Next Edit Suggestions) capabilities supported by the client.
1689    #[cfg(feature = "unstable_nes")]
1690    #[must_use]
1691    pub fn nes(mut self, nes: impl IntoOption<ClientNesCapabilities>) -> Self {
1692        self.nes = nes.into_option();
1693        self
1694    }
1695
1696    /// **UNSTABLE**
1697    ///
1698    /// The position encodings supported by the client, in order of preference.
1699    #[cfg(feature = "unstable_nes")]
1700    #[must_use]
1701    pub fn position_encodings(mut self, position_encodings: Vec<PositionEncodingKind>) -> Self {
1702        self.position_encodings = position_encodings;
1703        self
1704    }
1705
1706    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1707    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1708    /// these keys.
1709    ///
1710    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1711    #[must_use]
1712    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1713        self.meta = meta.into_option();
1714        self
1715    }
1716}
1717
1718/// **UNSTABLE**
1719///
1720/// This capability is not part of the spec yet, and may be removed or changed at any point.
1721///
1722/// Authentication capabilities supported by the client.
1723///
1724/// Advertised during initialization to inform the agent which authentication
1725/// method types the client can handle. This governs opt-in types that require
1726/// additional client-side support.
1727#[cfg(feature = "unstable_auth_methods")]
1728#[skip_serializing_none]
1729#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1730#[serde(rename_all = "camelCase")]
1731#[non_exhaustive]
1732pub struct AuthCapabilities {
1733    /// Whether the client supports `terminal` authentication methods.
1734    ///
1735    /// When `true`, the agent may include `terminal` entries in its authentication methods.
1736    #[serde(default)]
1737    pub terminal: bool,
1738    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1739    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1740    /// these keys.
1741    ///
1742    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1743    #[serde(rename = "_meta")]
1744    pub meta: Option<Meta>,
1745}
1746
1747#[cfg(feature = "unstable_auth_methods")]
1748impl AuthCapabilities {
1749    #[must_use]
1750    pub fn new() -> Self {
1751        Self::default()
1752    }
1753
1754    /// Whether the client supports `terminal` authentication methods.
1755    ///
1756    /// When `true`, the agent may include `AuthMethod::Terminal`
1757    /// entries in its authentication methods.
1758    #[must_use]
1759    pub fn terminal(mut self, terminal: bool) -> Self {
1760        self.terminal = terminal;
1761        self
1762    }
1763
1764    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1765    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1766    /// these keys.
1767    ///
1768    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1769    #[must_use]
1770    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1771        self.meta = meta.into_option();
1772        self
1773    }
1774}
1775
1776/// File system capabilities that a client may support.
1777///
1778/// See protocol docs: [FileSystem](https://agentclientprotocol.com/protocol/initialization#filesystem)
1779#[skip_serializing_none]
1780#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1781#[serde(rename_all = "camelCase")]
1782#[non_exhaustive]
1783pub struct FileSystemCapabilities {
1784    /// Whether the Client supports `fs/read_text_file` requests.
1785    #[serde(default)]
1786    pub read_text_file: bool,
1787    /// Whether the Client supports `fs/write_text_file` requests.
1788    #[serde(default)]
1789    pub write_text_file: bool,
1790    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1791    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1792    /// these keys.
1793    ///
1794    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1795    #[serde(rename = "_meta")]
1796    pub meta: Option<Meta>,
1797}
1798
1799impl FileSystemCapabilities {
1800    #[must_use]
1801    pub fn new() -> Self {
1802        Self::default()
1803    }
1804
1805    /// Whether the Client supports `fs/read_text_file` requests.
1806    #[must_use]
1807    pub fn read_text_file(mut self, read_text_file: bool) -> Self {
1808        self.read_text_file = read_text_file;
1809        self
1810    }
1811
1812    /// Whether the Client supports `fs/write_text_file` requests.
1813    #[must_use]
1814    pub fn write_text_file(mut self, write_text_file: bool) -> Self {
1815        self.write_text_file = write_text_file;
1816        self
1817    }
1818
1819    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1820    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1821    /// these keys.
1822    ///
1823    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1824    #[must_use]
1825    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1826        self.meta = meta.into_option();
1827        self
1828    }
1829}
1830
1831// Method schema
1832
1833/// Names of all methods that clients handle.
1834///
1835/// Provides a centralized definition of method names used in the protocol.
1836#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1837#[non_exhaustive]
1838pub struct ClientMethodNames {
1839    /// Method for requesting permission from the user.
1840    pub session_request_permission: &'static str,
1841    /// Notification for session updates.
1842    pub session_update: &'static str,
1843    /// Method for writing text files.
1844    pub fs_write_text_file: &'static str,
1845    /// Method for reading text files.
1846    pub fs_read_text_file: &'static str,
1847    /// Method for creating new terminals.
1848    pub terminal_create: &'static str,
1849    /// Method for getting terminals output.
1850    pub terminal_output: &'static str,
1851    /// Method for releasing a terminal.
1852    pub terminal_release: &'static str,
1853    /// Method for waiting for a terminal to finish.
1854    pub terminal_wait_for_exit: &'static str,
1855    /// Method for killing a terminal.
1856    pub terminal_kill: &'static str,
1857    /// Method for opening an MCP-over-ACP connection.
1858    #[cfg(feature = "unstable_mcp_over_acp")]
1859    pub mcp_connect: &'static str,
1860    /// Method for exchanging MCP-over-ACP messages.
1861    #[cfg(feature = "unstable_mcp_over_acp")]
1862    pub mcp_message: &'static str,
1863    /// Method for closing an MCP-over-ACP connection.
1864    #[cfg(feature = "unstable_mcp_over_acp")]
1865    pub mcp_disconnect: &'static str,
1866    /// Method for elicitation.
1867    #[cfg(feature = "unstable_elicitation")]
1868    pub elicitation_create: &'static str,
1869    /// Notification for elicitation completion.
1870    #[cfg(feature = "unstable_elicitation")]
1871    pub elicitation_complete: &'static str,
1872}
1873
1874/// Constant containing all client method names.
1875pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
1876    session_update: SESSION_UPDATE_NOTIFICATION,
1877    session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
1878    fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
1879    fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
1880    terminal_create: TERMINAL_CREATE_METHOD_NAME,
1881    terminal_output: TERMINAL_OUTPUT_METHOD_NAME,
1882    terminal_release: TERMINAL_RELEASE_METHOD_NAME,
1883    terminal_wait_for_exit: TERMINAL_WAIT_FOR_EXIT_METHOD_NAME,
1884    terminal_kill: TERMINAL_KILL_METHOD_NAME,
1885    #[cfg(feature = "unstable_mcp_over_acp")]
1886    mcp_connect: MCP_CONNECT_METHOD_NAME,
1887    #[cfg(feature = "unstable_mcp_over_acp")]
1888    mcp_message: MCP_MESSAGE_METHOD_NAME,
1889    #[cfg(feature = "unstable_mcp_over_acp")]
1890    mcp_disconnect: MCP_DISCONNECT_METHOD_NAME,
1891    #[cfg(feature = "unstable_elicitation")]
1892    elicitation_create: ELICITATION_CREATE_METHOD_NAME,
1893    #[cfg(feature = "unstable_elicitation")]
1894    elicitation_complete: ELICITATION_COMPLETE_NOTIFICATION,
1895};
1896
1897/// Notification name for session updates.
1898pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
1899/// Method name for requesting user permission.
1900pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
1901/// Method name for writing text files.
1902pub(crate) const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
1903/// Method name for reading text files.
1904pub(crate) const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
1905/// Method name for creating a new terminal.
1906pub(crate) const TERMINAL_CREATE_METHOD_NAME: &str = "terminal/create";
1907/// Method for getting terminals output.
1908pub(crate) const TERMINAL_OUTPUT_METHOD_NAME: &str = "terminal/output";
1909/// Method for releasing a terminal.
1910pub(crate) const TERMINAL_RELEASE_METHOD_NAME: &str = "terminal/release";
1911/// Method for waiting for a terminal to finish.
1912pub(crate) const TERMINAL_WAIT_FOR_EXIT_METHOD_NAME: &str = "terminal/wait_for_exit";
1913/// Method for killing a terminal.
1914pub(crate) const TERMINAL_KILL_METHOD_NAME: &str = "terminal/kill";
1915/// Method name for elicitation.
1916#[cfg(feature = "unstable_elicitation")]
1917pub(crate) const ELICITATION_CREATE_METHOD_NAME: &str = "elicitation/create";
1918/// Notification name for elicitation completion.
1919#[cfg(feature = "unstable_elicitation")]
1920pub(crate) const ELICITATION_COMPLETE_NOTIFICATION: &str = "elicitation/complete";
1921
1922/// All possible requests that an agent can send to a client.
1923///
1924/// This enum is used internally for routing RPC requests. You typically won't need
1925/// to use this directly.
1926///
1927/// This enum encompasses all method calls from agent to client.
1928#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1929#[serde(untagged)]
1930#[schemars(inline)]
1931#[non_exhaustive]
1932#[allow(clippy::large_enum_variant)]
1933pub enum AgentRequest {
1934    /// Writes content to a text file in the client's file system.
1935    ///
1936    /// Only available if the client advertises the `fs.writeTextFile` capability.
1937    /// Allows the agent to create or modify files within the client's environment.
1938    ///
1939    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
1940    WriteTextFileRequest(WriteTextFileRequest),
1941    /// Reads content from a text file in the client's file system.
1942    ///
1943    /// Only available if the client advertises the `fs.readTextFile` capability.
1944    /// Allows the agent to access file contents within the client's environment.
1945    ///
1946    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
1947    ReadTextFileRequest(ReadTextFileRequest),
1948    /// Requests permission from the user for a tool call operation.
1949    ///
1950    /// Called by the agent when it needs user authorization before executing
1951    /// a potentially sensitive operation. The client should present the options
1952    /// to the user and return their decision.
1953    ///
1954    /// If the client cancels the prompt turn via `session/cancel`, it MUST
1955    /// respond to this request with `RequestPermissionOutcome::Cancelled`.
1956    ///
1957    /// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
1958    RequestPermissionRequest(RequestPermissionRequest),
1959    /// Executes a command in a new terminal
1960    ///
1961    /// Only available if the `terminal` Client capability is set to `true`.
1962    ///
1963    /// Returns a `TerminalId` that can be used with other terminal methods
1964    /// to get the current output, wait for exit, and kill the command.
1965    ///
1966    /// The `TerminalId` can also be used to embed the terminal in a tool call
1967    /// by using the `ToolCallContent::Terminal` variant.
1968    ///
1969    /// The Agent is responsible for releasing the terminal by using the `terminal/release`
1970    /// method.
1971    ///
1972    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
1973    CreateTerminalRequest(CreateTerminalRequest),
1974    /// Gets the terminal output and exit status
1975    ///
1976    /// Returns the current content in the terminal without waiting for the command to exit.
1977    /// If the command has already exited, the exit status is included.
1978    ///
1979    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
1980    TerminalOutputRequest(TerminalOutputRequest),
1981    /// Releases a terminal
1982    ///
1983    /// The command is killed if it hasn't exited yet. Use `terminal/wait_for_exit`
1984    /// to wait for the command to exit before releasing the terminal.
1985    ///
1986    /// After release, the `TerminalId` can no longer be used with other `terminal/*` methods,
1987    /// but tool calls that already contain it, continue to display its output.
1988    ///
1989    /// The `terminal/kill` method can be used to terminate the command without releasing
1990    /// the terminal, allowing the Agent to call `terminal/output` and other methods.
1991    ///
1992    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
1993    ReleaseTerminalRequest(ReleaseTerminalRequest),
1994    /// Waits for the terminal command to exit and return its exit status
1995    ///
1996    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
1997    WaitForTerminalExitRequest(WaitForTerminalExitRequest),
1998    /// Kills the terminal command without releasing the terminal
1999    ///
2000    /// While `terminal/release` will also kill the command, this method will keep
2001    /// the `TerminalId` valid so it can be used with other methods.
2002    ///
2003    /// This method can be helpful when implementing command timeouts which terminate
2004    /// the command as soon as elapsed, and then get the final output so it can be sent
2005    /// to the model.
2006    ///
2007    /// Note: Call `terminal/release` when `TerminalId` is no longer needed.
2008    ///
2009    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2010    KillTerminalRequest(KillTerminalRequest),
2011    /// **UNSTABLE**
2012    ///
2013    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2014    ///
2015    /// Requests structured user input via a form or URL.
2016    #[cfg(feature = "unstable_elicitation")]
2017    CreateElicitationRequest(CreateElicitationRequest),
2018    /// **UNSTABLE**
2019    ///
2020    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2021    ///
2022    /// Opens an MCP-over-ACP connection.
2023    #[cfg(feature = "unstable_mcp_over_acp")]
2024    ConnectMcpRequest(ConnectMcpRequest),
2025    /// **UNSTABLE**
2026    ///
2027    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2028    ///
2029    /// Exchanges an MCP-over-ACP message.
2030    #[cfg(feature = "unstable_mcp_over_acp")]
2031    MessageMcpRequest(MessageMcpRequest),
2032    /// **UNSTABLE**
2033    ///
2034    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2035    ///
2036    /// Closes an MCP-over-ACP connection.
2037    #[cfg(feature = "unstable_mcp_over_acp")]
2038    DisconnectMcpRequest(DisconnectMcpRequest),
2039    /// Handles extension method requests from the agent.
2040    ///
2041    /// Allows the Agent to send an arbitrary request that is not part of the ACP spec.
2042    /// Extension methods provide a way to add custom functionality while maintaining
2043    /// protocol compatibility.
2044    ///
2045    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2046    ExtMethodRequest(ExtRequest),
2047}
2048
2049impl AgentRequest {
2050    /// Returns the corresponding method name of the request.
2051    #[must_use]
2052    pub fn method(&self) -> &str {
2053        match self {
2054            Self::WriteTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_write_text_file,
2055            Self::ReadTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_read_text_file,
2056            Self::RequestPermissionRequest(_) => CLIENT_METHOD_NAMES.session_request_permission,
2057            Self::CreateTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_create,
2058            Self::TerminalOutputRequest(_) => CLIENT_METHOD_NAMES.terminal_output,
2059            Self::ReleaseTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_release,
2060            Self::WaitForTerminalExitRequest(_) => CLIENT_METHOD_NAMES.terminal_wait_for_exit,
2061            Self::KillTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_kill,
2062            #[cfg(feature = "unstable_elicitation")]
2063            Self::CreateElicitationRequest(_) => CLIENT_METHOD_NAMES.elicitation_create,
2064            #[cfg(feature = "unstable_mcp_over_acp")]
2065            Self::ConnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_connect,
2066            #[cfg(feature = "unstable_mcp_over_acp")]
2067            Self::MessageMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_message,
2068            #[cfg(feature = "unstable_mcp_over_acp")]
2069            Self::DisconnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_disconnect,
2070            Self::ExtMethodRequest(ext_request) => &ext_request.method,
2071        }
2072    }
2073}
2074
2075/// All possible responses that a client can send to an agent.
2076///
2077/// This enum is used internally for routing RPC responses. You typically won't need
2078/// to use this directly - the responses are handled automatically by the connection.
2079///
2080/// These are responses to the corresponding `AgentRequest` variants.
2081#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
2082#[serde(untagged)]
2083#[schemars(inline)]
2084#[non_exhaustive]
2085pub enum ClientResponse {
2086    WriteTextFileResponse(#[serde(default)] WriteTextFileResponse),
2087    ReadTextFileResponse(ReadTextFileResponse),
2088    RequestPermissionResponse(RequestPermissionResponse),
2089    CreateTerminalResponse(CreateTerminalResponse),
2090    TerminalOutputResponse(TerminalOutputResponse),
2091    ReleaseTerminalResponse(#[serde(default)] ReleaseTerminalResponse),
2092    WaitForTerminalExitResponse(WaitForTerminalExitResponse),
2093    KillTerminalResponse(#[serde(default)] KillTerminalResponse),
2094    #[cfg(feature = "unstable_elicitation")]
2095    CreateElicitationResponse(CreateElicitationResponse),
2096    #[cfg(feature = "unstable_mcp_over_acp")]
2097    ConnectMcpResponse(ConnectMcpResponse),
2098    #[cfg(feature = "unstable_mcp_over_acp")]
2099    DisconnectMcpResponse(#[serde(default)] DisconnectMcpResponse),
2100    ExtMethodResponse(ExtResponse),
2101    #[cfg(feature = "unstable_mcp_over_acp")]
2102    MessageMcpResponse(MessageMcpResponse),
2103}
2104
2105/// All possible notifications that an agent can send to a client.
2106///
2107/// This enum is used internally for routing RPC notifications. You typically won't need
2108/// to use this directly.
2109///
2110/// Notifications do not expect a response.
2111#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
2112#[serde(untagged)]
2113#[expect(clippy::large_enum_variant)]
2114#[schemars(inline)]
2115#[non_exhaustive]
2116pub enum AgentNotification {
2117    /// Handles session update notifications from the agent.
2118    ///
2119    /// This is a notification endpoint (no response expected) that receives
2120    /// real-time updates about session progress, including message chunks,
2121    /// tool calls, and execution plans.
2122    ///
2123    /// Note: Clients SHOULD continue accepting tool call updates even after
2124    /// sending a `session/cancel` notification, as the agent may send final
2125    /// updates before responding with the cancelled stop reason.
2126    ///
2127    /// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
2128    SessionNotification(SessionNotification),
2129    /// **UNSTABLE**
2130    ///
2131    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2132    ///
2133    /// Notification that a URL-based elicitation has completed.
2134    #[cfg(feature = "unstable_elicitation")]
2135    CompleteElicitationNotification(CompleteElicitationNotification),
2136    /// **UNSTABLE**
2137    ///
2138    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2139    ///
2140    /// Receives an MCP-over-ACP notification.
2141    #[cfg(feature = "unstable_mcp_over_acp")]
2142    MessageMcpNotification(MessageMcpNotification),
2143    /// Handles extension notifications from the agent.
2144    ///
2145    /// Allows the Agent to send an arbitrary notification that is not part of the ACP spec.
2146    /// Extension notifications provide a way to send one-way messages for custom functionality
2147    /// while maintaining protocol compatibility.
2148    ///
2149    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2150    ExtNotification(ExtNotification),
2151}
2152
2153impl AgentNotification {
2154    /// Returns the corresponding method name of the notification.
2155    #[must_use]
2156    pub fn method(&self) -> &str {
2157        match self {
2158            Self::SessionNotification(_) => CLIENT_METHOD_NAMES.session_update,
2159            #[cfg(feature = "unstable_elicitation")]
2160            Self::CompleteElicitationNotification(_) => CLIENT_METHOD_NAMES.elicitation_complete,
2161            #[cfg(feature = "unstable_mcp_over_acp")]
2162            Self::MessageMcpNotification(_) => CLIENT_METHOD_NAMES.mcp_message,
2163            Self::ExtNotification(ext_notification) => &ext_notification.method,
2164        }
2165    }
2166}
2167
2168#[cfg(test)]
2169mod tests {
2170    use super::*;
2171
2172    #[test]
2173    fn test_serialization_behavior() {
2174        use serde_json::json;
2175
2176        assert_eq!(
2177            serde_json::from_value::<SessionInfoUpdate>(json!({})).unwrap(),
2178            SessionInfoUpdate {
2179                title: MaybeUndefined::Undefined,
2180                updated_at: MaybeUndefined::Undefined,
2181                meta: None
2182            }
2183        );
2184        assert_eq!(
2185            serde_json::from_value::<SessionInfoUpdate>(json!({"title": null, "updatedAt": null}))
2186                .unwrap(),
2187            SessionInfoUpdate {
2188                title: MaybeUndefined::Null,
2189                updated_at: MaybeUndefined::Null,
2190                meta: None
2191            }
2192        );
2193        assert_eq!(
2194            serde_json::from_value::<SessionInfoUpdate>(
2195                json!({"title": "title", "updatedAt": "timestamp"})
2196            )
2197            .unwrap(),
2198            SessionInfoUpdate {
2199                title: MaybeUndefined::Value("title".to_string()),
2200                updated_at: MaybeUndefined::Value("timestamp".to_string()),
2201                meta: None
2202            }
2203        );
2204
2205        assert_eq!(
2206            serde_json::to_value(SessionInfoUpdate::new()).unwrap(),
2207            json!({})
2208        );
2209        assert_eq!(
2210            serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(),
2211            json!({"title": "title"})
2212        );
2213        assert_eq!(
2214            serde_json::to_value(SessionInfoUpdate::new().title(None)).unwrap(),
2215            json!({"title": null})
2216        );
2217        assert_eq!(
2218            serde_json::to_value(
2219                SessionInfoUpdate::new()
2220                    .title("title")
2221                    .title(MaybeUndefined::Undefined)
2222            )
2223            .unwrap(),
2224            json!({})
2225        );
2226    }
2227
2228    #[test]
2229    fn test_content_chunk_message_id_serialization() {
2230        use serde_json::json;
2231
2232        assert_eq!(
2233            serde_json::to_value(SessionUpdate::AgentMessageChunk(ContentChunk::new(
2234                ContentBlock::Text(crate::TextContent::new("Hello"))
2235            )))
2236            .unwrap(),
2237            json!({
2238                "sessionUpdate": "agent_message_chunk",
2239                "content": {
2240                    "type": "text",
2241                    "text": "Hello"
2242                }
2243            })
2244        );
2245
2246        assert_eq!(
2247            serde_json::to_value(SessionUpdate::AgentMessageChunk(
2248                ContentChunk::new(ContentBlock::Text(crate::TextContent::new("Hello")))
2249                    .message_id("msg_agent_c42b9")
2250            ))
2251            .unwrap(),
2252            json!({
2253                "sessionUpdate": "agent_message_chunk",
2254                "messageId": "msg_agent_c42b9",
2255                "content": {
2256                    "type": "text",
2257                    "text": "Hello"
2258                }
2259            })
2260        );
2261
2262        let SessionUpdate::AgentMessageChunk(chunk) = serde_json::from_value(json!({
2263            "sessionUpdate": "agent_message_chunk",
2264            "messageId": null,
2265            "content": {
2266                "type": "text",
2267                "text": "Hello"
2268            }
2269        }))
2270        .unwrap() else {
2271            panic!("expected agent message chunk");
2272        };
2273
2274        assert_eq!(chunk.message_id, None);
2275    }
2276
2277    #[test]
2278    fn test_usage_update_serialization() {
2279        use serde_json::json;
2280
2281        assert_eq!(
2282            serde_json::to_value(SessionUpdate::UsageUpdate(UsageUpdate::new(
2283                53_000, 200_000
2284            )))
2285            .unwrap(),
2286            json!({
2287                "sessionUpdate": "usage_update",
2288                "used": 53000,
2289                "size": 200_000
2290            })
2291        );
2292
2293        assert_eq!(
2294            serde_json::to_value(SessionUpdate::UsageUpdate(
2295                UsageUpdate::new(53_000, 200_000).cost(Cost::new(0.045, "USD"))
2296            ))
2297            .unwrap(),
2298            json!({
2299                "sessionUpdate": "usage_update",
2300                "used": 53000,
2301                "size": 200_000,
2302                "cost": {
2303                    "amount": 0.045,
2304                    "currency": "USD"
2305                }
2306            })
2307        );
2308
2309        let SessionUpdate::UsageUpdate(update) = serde_json::from_value(json!({
2310            "sessionUpdate": "usage_update",
2311            "used": 53000,
2312            "size": 200_000,
2313            "cost": null
2314        }))
2315        .unwrap() else {
2316            panic!("expected usage update");
2317        };
2318
2319        assert_eq!(update.cost, None);
2320    }
2321
2322    #[cfg(feature = "unstable_nes")]
2323    #[test]
2324    fn test_client_capabilities_position_encodings_serialization() {
2325        use serde_json::json;
2326
2327        let capabilities = ClientCapabilities::new().position_encodings(vec![
2328            PositionEncodingKind::Utf32,
2329            PositionEncodingKind::Utf16,
2330        ]);
2331        let json = serde_json::to_value(&capabilities).unwrap();
2332
2333        assert_eq!(json["positionEncodings"], json!(["utf-32", "utf-16"]));
2334    }
2335
2336    #[cfg(feature = "unstable_plan_operations")]
2337    #[test]
2338    fn test_plan_operations_serialization() {
2339        use serde_json::json;
2340
2341        let plan_update =
2342            SessionUpdate::PlanUpdate(PlanUpdate::new(crate::PlanUpdateContent::items(
2343                "plan-1",
2344                vec![crate::PlanEntry::new(
2345                    "Step 1",
2346                    crate::PlanEntryPriority::High,
2347                    crate::PlanEntryStatus::Pending,
2348                )],
2349            )));
2350
2351        assert_eq!(
2352            serde_json::to_value(plan_update).unwrap(),
2353            json!({
2354                "sessionUpdate": "plan_update",
2355                "plan": {
2356                    "type": "items",
2357                    "id": "plan-1",
2358                    "entries": [
2359                        {
2360                            "content": "Step 1",
2361                            "priority": "high",
2362                            "status": "pending"
2363                        }
2364                    ]
2365                }
2366            })
2367        );
2368
2369        assert_eq!(
2370            serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(),
2371            json!({
2372                "sessionUpdate": "plan_removed",
2373                "id": "plan-1"
2374            })
2375        );
2376
2377        let capabilities = ClientCapabilities::new().plan(PlanCapabilities::new());
2378        let json = serde_json::to_value(&capabilities).unwrap();
2379        assert_eq!(json["plan"], json!({}));
2380
2381        assert_eq!(
2382            serde_json::from_value::<ClientCapabilities>(json!({ "plan": null }))
2383                .unwrap()
2384                .plan,
2385            None
2386        );
2387    }
2388
2389    #[cfg(feature = "unstable_mcp_over_acp")]
2390    #[test]
2391    fn test_agent_mcp_request_method_names() {
2392        use serde_json::json;
2393
2394        let params: serde_json::Map<String, serde_json::Value> =
2395            [("cursor".to_string(), json!("abc"))].into_iter().collect();
2396
2397        assert_eq!(CLIENT_METHOD_NAMES.mcp_connect, "mcp/connect");
2398        assert_eq!(CLIENT_METHOD_NAMES.mcp_message, "mcp/message");
2399        assert_eq!(CLIENT_METHOD_NAMES.mcp_disconnect, "mcp/disconnect");
2400
2401        assert_eq!(
2402            AgentRequest::ConnectMcpRequest(ConnectMcpRequest::new("server-1")).method(),
2403            "mcp/connect"
2404        );
2405        assert_eq!(
2406            AgentRequest::MessageMcpRequest(MessageMcpRequest::new("conn-1", "tools/list"))
2407                .method(),
2408            "mcp/message"
2409        );
2410        assert_eq!(
2411            AgentRequest::DisconnectMcpRequest(DisconnectMcpRequest::new("conn-1")).method(),
2412            "mcp/disconnect"
2413        );
2414        assert_eq!(
2415            AgentNotification::MessageMcpNotification(MessageMcpNotification::new(
2416                "conn-1",
2417                "notifications/progress"
2418            ))
2419            .method(),
2420            "mcp/message"
2421        );
2422
2423        assert_eq!(
2424            serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(),
2425            json!({ "acpId": "server-1" })
2426        );
2427        assert_eq!(
2428            serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(),
2429            json!({ "connectionId": "conn-1" })
2430        );
2431        assert_eq!(
2432            serde_json::to_value(MessageMcpRequest::new("conn-1", "tools/list").params(params))
2433                .unwrap(),
2434            json!({
2435                "connectionId": "conn-1",
2436                "method": "tools/list",
2437                "params": { "cursor": "abc" }
2438            })
2439        );
2440        assert_eq!(
2441            serde_json::to_value(DisconnectMcpRequest::new("conn-1")).unwrap(),
2442            json!({ "connectionId": "conn-1" })
2443        );
2444        assert_eq!(
2445            serde_json::to_value(MessageMcpNotification::new(
2446                "conn-1",
2447                "notifications/progress"
2448            ))
2449            .unwrap(),
2450            json!({
2451                "connectionId": "conn-1",
2452                "method": "notifications/progress"
2453            })
2454        );
2455
2456        let request_with_null_params: MessageMcpRequest = serde_json::from_value(json!({
2457            "connectionId": "conn-1",
2458            "method": "tools/list",
2459            "params": null
2460        }))
2461        .unwrap();
2462        assert_eq!(request_with_null_params.params, None);
2463    }
2464}