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