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}