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