agent_client_protocol/
client.rs

1use std::{rc::Rc, sync::Arc};
2
3use serde_json::value::RawValue;
4
5use agent_client_protocol_schema::{
6    CreateTerminalRequest, CreateTerminalResponse, Error, ExtNotification, ExtRequest, ExtResponse,
7    KillTerminalCommandRequest, KillTerminalCommandResponse, ReadTextFileRequest,
8    ReadTextFileResponse, ReleaseTerminalRequest, ReleaseTerminalResponse,
9    RequestPermissionRequest, RequestPermissionResponse, SessionNotification,
10    TerminalOutputRequest, TerminalOutputResponse, WaitForTerminalExitRequest,
11    WaitForTerminalExitResponse, WriteTextFileRequest, WriteTextFileResponse,
12};
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.
19#[async_trait::async_trait(?Send)]
20pub trait Client {
21    /// Requests permission from the user for a tool call operation.
22    ///
23    /// Called by the agent when it needs user authorization before executing
24    /// a potentially sensitive operation. The client should present the options
25    /// to the user and return their decision.
26    ///
27    /// If the client cancels the prompt turn via `session/cancel`, it MUST
28    /// respond to this request with `RequestPermissionOutcome::Cancelled`.
29    ///
30    /// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
31    async fn request_permission(
32        &self,
33        args: RequestPermissionRequest,
34    ) -> Result<RequestPermissionResponse, Error>;
35
36    /// Handles session update notifications from the agent.
37    ///
38    /// This is a notification endpoint (no response expected) that receives
39    /// real-time updates about session progress, including message chunks,
40    /// tool calls, and execution plans.
41    ///
42    /// Note: Clients SHOULD continue accepting tool call updates even after
43    /// sending a `session/cancel` notification, as the agent may send final
44    /// updates before responding with the cancelled stop reason.
45    ///
46    /// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
47    async fn session_notification(&self, args: SessionNotification) -> Result<(), Error>;
48
49    /// Writes content to a text file in the client's file system.
50    ///
51    /// Only available if the client advertises the `fs.writeTextFile` capability.
52    /// Allows the agent to create or modify files within the client's environment.
53    ///
54    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
55    async fn write_text_file(
56        &self,
57        _args: WriteTextFileRequest,
58    ) -> Result<WriteTextFileResponse, Error> {
59        Err(Error::method_not_found())
60    }
61
62    /// Reads content from a text file in the client's file system.
63    ///
64    /// Only available if the client advertises the `fs.readTextFile` capability.
65    /// Allows the agent to access file contents within the client's environment.
66    ///
67    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
68    async fn read_text_file(
69        &self,
70        _args: ReadTextFileRequest,
71    ) -> Result<ReadTextFileResponse, Error> {
72        Err(Error::method_not_found())
73    }
74
75    /// Executes a command in a new terminal
76    ///
77    /// Only available if the `terminal` Client capability is set to `true`.
78    ///
79    /// Returns a `TerminalId` that can be used with other terminal methods
80    /// to get the current output, wait for exit, and kill the command.
81    ///
82    /// The `TerminalId` can also be used to embed the terminal in a tool call
83    /// by using the `ToolCallContent::Terminal` variant.
84    ///
85    /// The Agent is responsible for releasing the terminal by using the `terminal/release`
86    /// method.
87    ///
88    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
89    async fn create_terminal(
90        &self,
91        _args: CreateTerminalRequest,
92    ) -> Result<CreateTerminalResponse, Error> {
93        Err(Error::method_not_found())
94    }
95
96    /// Gets the terminal output and exit status
97    ///
98    /// Returns the current content in the terminal without waiting for the command to exit.
99    /// If the command has already exited, the exit status is included.
100    ///
101    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
102    async fn terminal_output(
103        &self,
104        _args: TerminalOutputRequest,
105    ) -> Result<TerminalOutputResponse, Error> {
106        Err(Error::method_not_found())
107    }
108
109    /// Releases a terminal
110    ///
111    /// The command is killed if it hasn't exited yet. Use `terminal/wait_for_exit`
112    /// to wait for the command to exit before releasing the terminal.
113    ///
114    /// After release, the `TerminalId` can no longer be used with other `terminal/*` methods,
115    /// but tool calls that already contain it, continue to display its output.
116    ///
117    /// The `terminal/kill` method can be used to terminate the command without releasing
118    /// the terminal, allowing the Agent to call `terminal/output` and other methods.
119    ///
120    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
121    async fn release_terminal(
122        &self,
123        _args: ReleaseTerminalRequest,
124    ) -> Result<ReleaseTerminalResponse, Error> {
125        Err(Error::method_not_found())
126    }
127
128    /// Waits for the terminal command to exit and return its exit status
129    ///
130    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
131    async fn wait_for_terminal_exit(
132        &self,
133        _args: WaitForTerminalExitRequest,
134    ) -> Result<WaitForTerminalExitResponse, Error> {
135        Err(Error::method_not_found())
136    }
137
138    /// Kills the terminal command without releasing the terminal
139    ///
140    /// While `terminal/release` will also kill the command, this method will keep
141    /// the `TerminalId` valid so it can be used with other methods.
142    ///
143    /// This method can be helpful when implementing command timeouts which terminate
144    /// the command as soon as elapsed, and then get the final output so it can be sent
145    /// to the model.
146    ///
147    /// Note: `terminal/release` when `TerminalId` is no longer needed.
148    ///
149    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
150    async fn kill_terminal_command(
151        &self,
152        _args: KillTerminalCommandRequest,
153    ) -> Result<KillTerminalCommandResponse, Error> {
154        Err(Error::method_not_found())
155    }
156
157    /// Handles extension method requests from the agent.
158    ///
159    /// Allows the Agent to send an arbitrary request that is not part of the ACP spec.
160    /// Extension methods provide a way to add custom functionality while maintaining
161    /// protocol compatibility.
162    ///
163    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
164    async fn ext_method(&self, _args: ExtRequest) -> Result<ExtResponse, Error> {
165        Ok(RawValue::NULL.to_owned().into())
166    }
167
168    /// Handles extension notifications from the agent.
169    ///
170    /// Allows the Agent to send an arbitrary notification that is not part of the ACP spec.
171    /// Extension notifications provide a way to send one-way messages for custom functionality
172    /// while maintaining protocol compatibility.
173    ///
174    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
175    async fn ext_notification(&self, _args: ExtNotification) -> Result<(), Error> {
176        Ok(())
177    }
178}
179
180#[async_trait::async_trait(?Send)]
181impl<T: Client> Client for Rc<T> {
182    async fn request_permission(
183        &self,
184        args: RequestPermissionRequest,
185    ) -> Result<RequestPermissionResponse, Error> {
186        self.as_ref().request_permission(args).await
187    }
188    async fn write_text_file(
189        &self,
190        args: WriteTextFileRequest,
191    ) -> Result<WriteTextFileResponse, Error> {
192        self.as_ref().write_text_file(args).await
193    }
194    async fn read_text_file(
195        &self,
196        args: ReadTextFileRequest,
197    ) -> Result<ReadTextFileResponse, Error> {
198        self.as_ref().read_text_file(args).await
199    }
200    async fn session_notification(&self, args: SessionNotification) -> Result<(), Error> {
201        self.as_ref().session_notification(args).await
202    }
203    async fn create_terminal(
204        &self,
205        args: CreateTerminalRequest,
206    ) -> Result<CreateTerminalResponse, Error> {
207        self.as_ref().create_terminal(args).await
208    }
209    async fn terminal_output(
210        &self,
211        args: TerminalOutputRequest,
212    ) -> Result<TerminalOutputResponse, Error> {
213        self.as_ref().terminal_output(args).await
214    }
215    async fn release_terminal(
216        &self,
217        args: ReleaseTerminalRequest,
218    ) -> Result<ReleaseTerminalResponse, Error> {
219        self.as_ref().release_terminal(args).await
220    }
221    async fn wait_for_terminal_exit(
222        &self,
223        args: WaitForTerminalExitRequest,
224    ) -> Result<WaitForTerminalExitResponse, Error> {
225        self.as_ref().wait_for_terminal_exit(args).await
226    }
227    async fn kill_terminal_command(
228        &self,
229        args: KillTerminalCommandRequest,
230    ) -> Result<KillTerminalCommandResponse, Error> {
231        self.as_ref().kill_terminal_command(args).await
232    }
233    async fn ext_method(&self, args: ExtRequest) -> Result<ExtResponse, Error> {
234        self.as_ref().ext_method(args).await
235    }
236    async fn ext_notification(&self, args: ExtNotification) -> Result<(), Error> {
237        self.as_ref().ext_notification(args).await
238    }
239}
240
241#[async_trait::async_trait(?Send)]
242impl<T: Client> Client for Arc<T> {
243    async fn request_permission(
244        &self,
245        args: RequestPermissionRequest,
246    ) -> Result<RequestPermissionResponse, Error> {
247        self.as_ref().request_permission(args).await
248    }
249    async fn write_text_file(
250        &self,
251        args: WriteTextFileRequest,
252    ) -> Result<WriteTextFileResponse, Error> {
253        self.as_ref().write_text_file(args).await
254    }
255    async fn read_text_file(
256        &self,
257        args: ReadTextFileRequest,
258    ) -> Result<ReadTextFileResponse, Error> {
259        self.as_ref().read_text_file(args).await
260    }
261    async fn session_notification(&self, args: SessionNotification) -> Result<(), Error> {
262        self.as_ref().session_notification(args).await
263    }
264    async fn create_terminal(
265        &self,
266        args: CreateTerminalRequest,
267    ) -> Result<CreateTerminalResponse, Error> {
268        self.as_ref().create_terminal(args).await
269    }
270    async fn terminal_output(
271        &self,
272        args: TerminalOutputRequest,
273    ) -> Result<TerminalOutputResponse, Error> {
274        self.as_ref().terminal_output(args).await
275    }
276    async fn release_terminal(
277        &self,
278        args: ReleaseTerminalRequest,
279    ) -> Result<ReleaseTerminalResponse, Error> {
280        self.as_ref().release_terminal(args).await
281    }
282    async fn wait_for_terminal_exit(
283        &self,
284        args: WaitForTerminalExitRequest,
285    ) -> Result<WaitForTerminalExitResponse, Error> {
286        self.as_ref().wait_for_terminal_exit(args).await
287    }
288    async fn kill_terminal_command(
289        &self,
290        args: KillTerminalCommandRequest,
291    ) -> Result<KillTerminalCommandResponse, Error> {
292        self.as_ref().kill_terminal_command(args).await
293    }
294    async fn ext_method(&self, args: ExtRequest) -> Result<ExtResponse, Error> {
295        self.as_ref().ext_method(args).await
296    }
297    async fn ext_notification(&self, args: ExtNotification) -> Result<(), Error> {
298        self.as_ref().ext_notification(args).await
299    }
300}