agent_client_protocol_schema/
tool_call.rs

1//! Tool calls represent actions that language models request agents to perform.
2//!
3//! When an LLM determines it needs to interact with external systems—like reading files,
4//! running code, or fetching data—it generates tool calls that the agent executes on its behalf.
5//!
6/// See protocol docs: [Tool Calls](https://agentclientprotocol.com/protocol/tool-calls)
7use std::{path::PathBuf, sync::Arc};
8
9use derive_more::{Display, From};
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13use crate::{ContentBlock, Error};
14
15/// Represents a tool call that the language model has requested.
16///
17/// Tool calls are actions that the agent executes on behalf of the language model,
18/// such as reading files, executing code, or fetching data from external sources.
19///
20/// See protocol docs: [Tool Calls](https://agentclientprotocol.com/protocol/tool-calls)
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
22#[serde(rename_all = "camelCase")]
23pub struct ToolCall {
24    /// Unique identifier for this tool call within the session.
25    #[serde(rename = "toolCallId")]
26    pub id: ToolCallId,
27    /// Human-readable title describing what the tool is doing.
28    pub title: String,
29    /// The category of tool being invoked.
30    /// Helps clients choose appropriate icons and UI treatment.
31    #[serde(default, skip_serializing_if = "ToolKind::is_default")]
32    pub kind: ToolKind,
33    /// Current execution status of the tool call.
34    #[serde(default, skip_serializing_if = "ToolCallStatus::is_default")]
35    pub status: ToolCallStatus,
36    /// Content produced by the tool call.
37    #[serde(default, skip_serializing_if = "Vec::is_empty")]
38    pub content: Vec<ToolCallContent>,
39    /// File locations affected by this tool call.
40    /// Enables "follow-along" features in clients.
41    #[serde(default, skip_serializing_if = "Vec::is_empty")]
42    pub locations: Vec<ToolCallLocation>,
43    /// Raw input parameters sent to the tool.
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub raw_input: Option<serde_json::Value>,
46    /// Raw output returned by the tool.
47    #[serde(default, skip_serializing_if = "Option::is_none")]
48    pub raw_output: Option<serde_json::Value>,
49    /// Extension point for implementations
50    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
51    pub meta: Option<serde_json::Value>,
52}
53
54impl ToolCall {
55    /// Update an existing tool call with the values in the provided update
56    /// fields. Fields with collections of values are overwritten, not extended.
57    pub fn update(&mut self, fields: ToolCallUpdateFields) {
58        if let Some(title) = fields.title {
59            self.title = title;
60        }
61        if let Some(kind) = fields.kind {
62            self.kind = kind;
63        }
64        if let Some(status) = fields.status {
65            self.status = status;
66        }
67        if let Some(content) = fields.content {
68            self.content = content;
69        }
70        if let Some(locations) = fields.locations {
71            self.locations = locations;
72        }
73        if let Some(raw_input) = fields.raw_input {
74            self.raw_input = Some(raw_input);
75        }
76        if let Some(raw_output) = fields.raw_output {
77            self.raw_output = Some(raw_output);
78        }
79    }
80}
81
82/// An update to an existing tool call.
83///
84/// Used to report progress and results as tools execute. All fields except
85/// the tool call ID are optional - only changed fields need to be included.
86///
87/// See protocol docs: [Updating](https://agentclientprotocol.com/protocol/tool-calls#updating)
88#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
89#[serde(rename_all = "camelCase")]
90pub struct ToolCallUpdate {
91    /// The ID of the tool call being updated.
92    #[serde(rename = "toolCallId")]
93    pub id: ToolCallId,
94    /// Fields being updated.
95    #[serde(flatten)]
96    pub fields: ToolCallUpdateFields,
97    /// Extension point for implementations
98    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
99    pub meta: Option<serde_json::Value>,
100}
101
102/// Optional fields that can be updated in a tool call.
103///
104/// All fields are optional - only include the ones being changed.
105/// Collections (content, locations) are overwritten, not extended.
106///
107/// See protocol docs: [Updating](https://agentclientprotocol.com/protocol/tool-calls#updating)
108#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
109#[serde(rename_all = "camelCase")]
110pub struct ToolCallUpdateFields {
111    /// Update the tool kind.
112    #[serde(default, skip_serializing_if = "Option::is_none")]
113    pub kind: Option<ToolKind>,
114    /// Update the execution status.
115    #[serde(default, skip_serializing_if = "Option::is_none")]
116    pub status: Option<ToolCallStatus>,
117    /// Update the human-readable title.
118    #[serde(default, skip_serializing_if = "Option::is_none")]
119    pub title: Option<String>,
120    /// Replace the content collection.
121    #[serde(default, skip_serializing_if = "Option::is_none")]
122    pub content: Option<Vec<ToolCallContent>>,
123    /// Replace the locations collection.
124    #[serde(default, skip_serializing_if = "Option::is_none")]
125    pub locations: Option<Vec<ToolCallLocation>>,
126    /// Update the raw input.
127    #[serde(default, skip_serializing_if = "Option::is_none")]
128    pub raw_input: Option<serde_json::Value>,
129    /// Update the raw output.
130    #[serde(default, skip_serializing_if = "Option::is_none")]
131    pub raw_output: Option<serde_json::Value>,
132}
133
134/// If a given tool call doesn't exist yet, allows for attempting to construct
135/// one from a tool call update if possible.
136impl TryFrom<ToolCallUpdate> for ToolCall {
137    type Error = Error;
138
139    fn try_from(update: ToolCallUpdate) -> Result<Self, Self::Error> {
140        let ToolCallUpdate {
141            id,
142            fields:
143                ToolCallUpdateFields {
144                    kind,
145                    status,
146                    title,
147                    content,
148                    locations,
149                    raw_input,
150                    raw_output,
151                },
152            meta: _,
153        } = update;
154
155        Ok(Self {
156            id,
157            title: title.ok_or_else(|| {
158                Error::invalid_params()
159                    .with_data(serde_json::json!("title is required for a tool call"))
160            })?,
161            kind: kind.unwrap_or_default(),
162            status: status.unwrap_or_default(),
163            content: content.unwrap_or_default(),
164            locations: locations.unwrap_or_default(),
165            raw_input,
166            raw_output,
167            meta: None,
168        })
169    }
170}
171
172impl From<ToolCall> for ToolCallUpdate {
173    fn from(value: ToolCall) -> Self {
174        let ToolCall {
175            id,
176            title,
177            kind,
178            status,
179            content,
180            locations,
181            raw_input,
182            raw_output,
183            meta: _,
184        } = value;
185        Self {
186            id,
187            fields: ToolCallUpdateFields {
188                kind: Some(kind),
189                status: Some(status),
190                title: Some(title),
191                content: Some(content),
192                locations: Some(locations),
193                raw_input,
194                raw_output,
195            },
196            meta: None,
197        }
198    }
199}
200
201/// Unique identifier for a tool call within a session.
202#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
203#[serde(transparent)]
204#[from(Arc<str>, String, &'static str)]
205pub struct ToolCallId(pub Arc<str>);
206
207/// Categories of tools that can be invoked.
208///
209/// Tool kinds help clients choose appropriate icons and optimize how they
210/// display tool execution progress.
211///
212/// See protocol docs: [Creating](https://agentclientprotocol.com/protocol/tool-calls#creating)
213#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
214#[serde(rename_all = "snake_case")]
215pub enum ToolKind {
216    /// Reading files or data.
217    Read,
218    /// Modifying files or content.
219    Edit,
220    /// Removing files or data.
221    Delete,
222    /// Moving or renaming files.
223    Move,
224    /// Searching for information.
225    Search,
226    /// Running commands or code.
227    Execute,
228    /// Internal reasoning or planning.
229    Think,
230    /// Retrieving external data.
231    Fetch,
232    /// Switching the current session mode.
233    SwitchMode,
234    /// Other tool types (default).
235    #[default]
236    #[serde(other)]
237    Other,
238}
239
240impl ToolKind {
241    fn is_default(&self) -> bool {
242        matches!(self, ToolKind::Other)
243    }
244}
245
246/// Execution status of a tool call.
247///
248/// Tool calls progress through different statuses during their lifecycle.
249///
250/// See protocol docs: [Status](https://agentclientprotocol.com/protocol/tool-calls#status)
251#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
252#[serde(rename_all = "snake_case")]
253pub enum ToolCallStatus {
254    /// The tool call hasn't started running yet because the input is either
255    /// streaming or we're awaiting approval.
256    #[default]
257    Pending,
258    /// The tool call is currently running.
259    InProgress,
260    /// The tool call completed successfully.
261    Completed,
262    /// The tool call failed with an error.
263    Failed,
264}
265
266impl ToolCallStatus {
267    fn is_default(&self) -> bool {
268        matches!(self, ToolCallStatus::Pending)
269    }
270}
271
272/// Content produced by a tool call.
273///
274/// Tool calls can produce different types of content including
275/// standard content blocks (text, images) or file diffs.
276///
277/// See protocol docs: [Content](https://agentclientprotocol.com/protocol/tool-calls#content)
278#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
279#[serde(tag = "type", rename_all = "snake_case")]
280pub enum ToolCallContent {
281    /// Standard content block (text, images, resources).
282    Content {
283        /// The actual content block.
284        content: ContentBlock,
285    },
286    /// File modification shown as a diff.
287    Diff {
288        /// The diff details.
289        #[serde(flatten)]
290        diff: Diff,
291    },
292    /// Embed a terminal created with `terminal/create` by its id.
293    ///
294    /// The terminal must be added before calling `terminal/release`.
295    ///
296    /// See protocol docs: [Terminal](https://agentclientprotocol.com/protocol/terminal)
297    #[serde(rename_all = "camelCase")]
298    Terminal { terminal_id: crate::TerminalId },
299}
300
301impl<T: Into<ContentBlock>> From<T> for ToolCallContent {
302    fn from(content: T) -> Self {
303        ToolCallContent::Content {
304            content: content.into(),
305        }
306    }
307}
308
309impl From<Diff> for ToolCallContent {
310    fn from(diff: Diff) -> Self {
311        ToolCallContent::Diff { diff }
312    }
313}
314
315/// A diff representing file modifications.
316///
317/// Shows changes to files in a format suitable for display in the client UI.
318///
319/// See protocol docs: [Content](https://agentclientprotocol.com/protocol/tool-calls#content)
320#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
321#[serde(rename_all = "camelCase")]
322pub struct Diff {
323    /// The file path being modified.
324    pub path: PathBuf,
325    /// The original content (None for new files).
326    pub old_text: Option<String>,
327    /// The new content after modification.
328    pub new_text: String,
329    /// Extension point for implementations
330    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
331    pub meta: Option<serde_json::Value>,
332}
333
334/// A file location being accessed or modified by a tool.
335///
336/// Enables clients to implement "follow-along" features that track
337/// which files the agent is working with in real-time.
338///
339/// See protocol docs: [Following the Agent](https://agentclientprotocol.com/protocol/tool-calls#following-the-agent)
340#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
341#[serde(rename_all = "camelCase")]
342pub struct ToolCallLocation {
343    /// The file path being accessed or modified.
344    pub path: PathBuf,
345    /// Optional line number within the file.
346    #[serde(default, skip_serializing_if = "Option::is_none")]
347    pub line: Option<u32>,
348    /// Extension point for implementations
349    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
350    pub meta: Option<serde_json::Value>,
351}