agent_client_protocol/
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::{fmt, path::PathBuf, sync::Arc};
7
8use anyhow::Result;
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11
12use crate::{ContentBlock, Error, Plan, SessionId, ToolCall, ToolCallUpdate};
13
14/// The Client trait defines the interface that ACP-compliant clients must implement.
15///
16/// Clients are typically code editors (IDEs, text editors) that provide the interface
17/// between users and AI agents. They manage the environment, handle user interactions,
18/// and control access to resources.
19///
20/// See: <https://agentclientprotocol.com/protocol/prompt-turn#cancellation>
21pub trait Client {
22    /// Requests permission from the user for a tool call operation.
23    ///
24    /// Called by the agent when it needs user authorization before executing
25    /// a potentially sensitive operation. The client should present the options
26    /// to the user and return their decision.
27    ///
28    /// If the client cancels the prompt turn via `session/cancel`, it MUST
29    /// respond to this request with `RequestPermissionOutcome::Cancelled`.
30    ///
31    /// See: <https://agentclientprotocol.com/protocol/tool-calls#requesting-permission>
32    fn request_permission(
33        &self,
34        args: RequestPermissionRequest,
35    ) -> impl Future<Output = Result<RequestPermissionResponse, Error>>;
36
37    /// Writes content to a text file in the client's file system.
38    ///
39    /// Only available if the client advertises the `fs.writeTextFile` capability.
40    /// Allows the agent to create or modify files within the client's environment.
41    ///
42    /// See: <https://agentclientprotocol.com/protocol/overview#client>
43    fn write_text_file(
44        &self,
45        args: WriteTextFileRequest,
46    ) -> impl Future<Output = Result<(), Error>>;
47
48    /// Reads content from a text file in the client's file system.
49    ///
50    /// Only available if the client advertises the `fs.readTextFile` capability.
51    /// Allows the agent to access file contents within the client's environment.
52    ///
53    /// See: <https://agentclientprotocol.com/protocol/overview#client>
54    fn read_text_file(
55        &self,
56        args: ReadTextFileRequest,
57    ) -> impl Future<Output = Result<ReadTextFileResponse, Error>>;
58
59    /// Handles session update notifications from the agent.
60    ///
61    /// This is a notification endpoint (no response expected) that receives
62    /// real-time updates about session progress, including message chunks,
63    /// tool calls, and execution plans.
64    ///
65    /// Note: Clients SHOULD continue accepting tool call updates even after
66    /// sending a `session/cancel` notification, as the agent may send final
67    /// updates before responding with the cancelled stop reason.
68    ///
69    /// See: <https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output>
70    fn session_notification(
71        &self,
72        args: SessionNotification,
73    ) -> impl Future<Output = Result<(), Error>>;
74}
75
76// Session updates
77
78/// Notification containing a session update from the agent.
79///
80/// Used to stream real-time progress and results during prompt processing.
81///
82/// See: <https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output>
83#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
84#[serde(rename_all = "camelCase")]
85pub struct SessionNotification {
86    /// The ID of the session this update pertains to.
87    pub session_id: SessionId,
88    /// The actual update content.
89    pub update: SessionUpdate,
90}
91
92/// Different types of updates that can be sent during session processing.
93///
94/// These updates provide real-time feedback about the agent's progress.
95///
96/// See: <https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output>
97#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
98#[serde(tag = "sessionUpdate", rename_all = "snake_case")]
99pub enum SessionUpdate {
100    /// A chunk of the user's message being streamed.
101    UserMessageChunk { content: ContentBlock },
102    /// A chunk of the agent's response being streamed.
103    AgentMessageChunk { content: ContentBlock },
104    /// A chunk of the agent's internal reasoning being streamed.
105    AgentThoughtChunk { content: ContentBlock },
106    /// Notification that a new tool call has been initiated.
107    ToolCall(ToolCall),
108    /// Update on the status or results of a tool call.
109    ToolCallUpdate(ToolCallUpdate),
110    /// The agent's execution plan for complex tasks.
111    /// See: <https://agentclientprotocol.com/protocol/agent-plan>
112    Plan(Plan),
113}
114
115// Permission
116
117/// Request for user permission to execute a tool call.
118///
119/// Sent when the agent needs authorization before performing a sensitive operation.
120///
121/// See: <https://agentclientprotocol.com/protocol/tool-calls#requesting-permission>
122#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
123#[serde(rename_all = "camelCase")]
124pub struct RequestPermissionRequest {
125    /// The session ID for this request.
126    pub session_id: SessionId,
127    /// Details about the tool call requiring permission.
128    pub tool_call: ToolCallUpdate,
129    /// Available permission options for the user to choose from.
130    pub options: Vec<PermissionOption>,
131}
132
133/// An option presented to the user when requesting permission.
134#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
135pub struct PermissionOption {
136    /// Unique identifier for this permission option.
137    #[serde(rename = "optionId")]
138    pub id: PermissionOptionId,
139    /// Human-readable label to display to the user.
140    pub name: String,
141    /// Hint about the nature of this permission option.
142    pub kind: PermissionOptionKind,
143}
144
145/// Unique identifier for a permission option.
146#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
147#[serde(transparent)]
148pub struct PermissionOptionId(pub Arc<str>);
149
150impl fmt::Display for PermissionOptionId {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        self.0.fmt(f)
153    }
154}
155
156/// The type of permission option being presented to the user.
157///
158/// Helps clients choose appropriate icons and UI treatment.
159#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
160#[serde(rename_all = "snake_case")]
161pub enum PermissionOptionKind {
162    /// Allow this operation only this time.
163    AllowOnce,
164    /// Allow this operation and remember the choice.
165    AllowAlways,
166    /// Reject this operation only this time.
167    RejectOnce,
168    /// Reject this operation and remember the choice.
169    RejectAlways,
170}
171
172/// Response to a permission request.
173#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
174#[serde(rename_all = "camelCase")]
175pub struct RequestPermissionResponse {
176    /// The user's decision on the permission request.
177    // This extra-level is unfortunately needed because the output must be an object
178    pub outcome: RequestPermissionOutcome,
179}
180
181/// The outcome of a permission request.
182#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
183#[serde(tag = "outcome", rename_all = "snake_case")]
184pub enum RequestPermissionOutcome {
185    /// The prompt turn was cancelled before the user responded.
186    ///
187    /// When a client sends a `session/cancel` notification to cancel an ongoing
188    /// prompt turn, it MUST respond to all pending `session/request_permission`
189    /// requests with this `Cancelled` outcome.
190    ///
191    /// See: <https://agentclientprotocol.com/protocol/prompt-turn#cancellation>
192    Cancelled,
193    /// The user selected one of the provided options.
194    #[serde(rename_all = "camelCase")]
195    Selected {
196        /// The ID of the option the user selected.
197        option_id: PermissionOptionId,
198    },
199}
200
201// Write text file
202
203/// Request to write content to a text file.
204///
205/// Only available if the client supports the `fs.writeTextFile` capability.
206#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
207#[serde(rename_all = "camelCase")]
208pub struct WriteTextFileRequest {
209    /// The session ID for this request.
210    pub session_id: SessionId,
211    /// Path to the file to write (relative to the session's working directory).
212    pub path: PathBuf,
213    /// The text content to write to the file.
214    /// The text content read from the file.
215    pub content: String,
216}
217
218// Read text file
219
220/// Request to read content from a text file.
221///
222/// Only available if the client supports the `fs.readTextFile` capability.
223#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
224#[serde(rename_all = "camelCase")]
225pub struct ReadTextFileRequest {
226    /// The session ID for this request.
227    pub session_id: SessionId,
228    /// Path to the file to read (relative to the session's working directory).
229    pub path: PathBuf,
230    /// Optional line number to start reading from (1-based).
231    #[serde(default, skip_serializing_if = "Option::is_none")]
232    pub line: Option<u32>,
233    /// Optional maximum number of lines to read.
234    #[serde(default, skip_serializing_if = "Option::is_none")]
235    pub limit: Option<u32>,
236}
237
238/// Response containing the contents of a text file.
239#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
240#[serde(rename_all = "camelCase")]
241pub struct ReadTextFileResponse {
242    pub content: String,
243}
244
245// Capabilities
246
247/// Capabilities supported by the client.
248///
249/// Advertised during initialization to inform the agent about
250/// available features and methods.
251///
252/// See: <https://agentclientprotocol.com/protocol/initialization#client-capabilities>
253#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
254#[serde(rename_all = "camelCase")]
255pub struct ClientCapabilities {
256    /// File system capabilities supported by the client.
257    /// Determines which file operations the agent can request.
258    #[serde(default)]
259    pub fs: FileSystemCapability,
260}
261
262/// File system capabilities that a client may support.
263///
264/// See: <https://agentclientprotocol.com/protocol/initialization#filesystem>
265#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
266#[serde(rename_all = "camelCase")]
267pub struct FileSystemCapability {
268    /// Whether the Client supports `fs/read_text_file` requests.
269    #[serde(default)]
270    pub read_text_file: bool,
271    /// Whether the Client supports `fs/write_text_file` requests.
272    #[serde(default)]
273    pub write_text_file: bool,
274}
275
276// Method schema
277
278/// Names of all methods that clients handle.
279///
280/// Provides a centralized definition of method names used in the protocol.
281#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct ClientMethodNames {
283    /// Method for requesting permission from the user.
284    pub session_request_permission: &'static str,
285    /// Notification for session updates.
286    pub session_update: &'static str,
287    /// Method for writing text files.
288    pub fs_write_text_file: &'static str,
289    /// Method for reading text files.
290    pub fs_read_text_file: &'static str,
291}
292
293/// Constant containing all client method names.
294pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
295    session_update: SESSION_UPDATE_NOTIFICATION,
296    session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
297    fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
298    fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
299};
300
301/// Notification name for session updates.
302pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
303/// Method name for requesting user permission.
304pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
305/// Method name for writing text files.
306pub(crate) const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
307/// Method name for reading text files.
308pub(crate) const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
309
310/// All possible requests that an agent can send to a client.
311///
312/// This enum is used internally for routing RPC requests. You typically won't need
313/// to use this directly - instead, use the methods on the [`Client`] trait.
314///
315/// This enum encompasses all method calls from agent to client.
316#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
317#[serde(untagged)]
318pub enum AgentRequest {
319    WriteTextFileRequest(WriteTextFileRequest),
320    ReadTextFileRequest(ReadTextFileRequest),
321    RequestPermissionRequest(RequestPermissionRequest),
322}
323
324/// All possible responses that a client can send to an agent.
325///
326/// This enum is used internally for routing RPC responses. You typically won't need
327/// to use this directly - the responses are handled automatically by the connection.
328///
329/// These are responses to the corresponding AgentRequest variants.
330#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
331#[serde(untagged)]
332pub enum ClientResponse {
333    WriteTextFileResponse,
334    ReadTextFileResponse(ReadTextFileResponse),
335    RequestPermissionResponse(RequestPermissionResponse),
336}
337
338/// All possible notifications that an agent can send to a client.
339///
340/// This enum is used internally for routing RPC notifications. You typically won't need
341/// to use this directly - use the notification methods on the [`Client`] trait instead.
342///
343/// Notifications do not expect a response.
344#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
345#[serde(untagged)]
346pub enum AgentNotification {
347    SessionNotification(SessionNotification),
348}