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, Meta, TerminalId};
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")]
23#[non_exhaustive]
24pub struct ToolCall {
25 /// Unique identifier for this tool call within the session.
26 pub tool_call_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(skip_serializing_if = "Option::is_none")]
45 pub raw_input: Option<serde_json::Value>,
46 /// Raw output returned by the tool.
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub raw_output: Option<serde_json::Value>,
49 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
50 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
51 /// these keys.
52 ///
53 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
54 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
55 pub meta: Option<Meta>,
56}
57
58impl ToolCall {
59 pub fn new(tool_call_id: impl Into<ToolCallId>, title: impl Into<String>) -> Self {
60 Self {
61 tool_call_id: tool_call_id.into(),
62 title: title.into(),
63 kind: ToolKind::default(),
64 status: ToolCallStatus::default(),
65 content: Vec::default(),
66 locations: Vec::default(),
67 raw_input: None,
68 raw_output: None,
69 meta: None,
70 }
71 }
72
73 /// The category of tool being invoked.
74 /// Helps clients choose appropriate icons and UI treatment.
75 #[must_use]
76 pub fn kind(mut self, kind: ToolKind) -> Self {
77 self.kind = kind;
78 self
79 }
80
81 /// Current execution status of the tool call.
82 #[must_use]
83 pub fn status(mut self, status: ToolCallStatus) -> Self {
84 self.status = status;
85 self
86 }
87
88 /// Content produced by the tool call.
89 #[must_use]
90 pub fn content(mut self, content: Vec<ToolCallContent>) -> Self {
91 self.content = content;
92 self
93 }
94
95 /// File locations affected by this tool call.
96 /// Enables "follow-along" features in clients.
97 #[must_use]
98 pub fn locations(mut self, locations: Vec<ToolCallLocation>) -> Self {
99 self.locations = locations;
100 self
101 }
102
103 /// Raw input parameters sent to the tool.
104 #[must_use]
105 pub fn raw_input(mut self, raw_input: serde_json::Value) -> Self {
106 self.raw_input = Some(raw_input);
107 self
108 }
109
110 /// Raw output returned by the tool.
111 #[must_use]
112 pub fn raw_output(mut self, raw_output: serde_json::Value) -> Self {
113 self.raw_output = Some(raw_output);
114 self
115 }
116
117 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
118 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
119 /// these keys.
120 ///
121 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
122 #[must_use]
123 pub fn meta(mut self, meta: Meta) -> Self {
124 self.meta = Some(meta);
125 self
126 }
127
128 /// Update an existing tool call with the values in the provided update
129 /// fields. Fields with collections of values are overwritten, not extended.
130 pub fn update(&mut self, fields: ToolCallUpdateFields) {
131 if let Some(title) = fields.title {
132 self.title = title;
133 }
134 if let Some(kind) = fields.kind {
135 self.kind = kind;
136 }
137 if let Some(status) = fields.status {
138 self.status = status;
139 }
140 if let Some(content) = fields.content {
141 self.content = content;
142 }
143 if let Some(locations) = fields.locations {
144 self.locations = locations;
145 }
146 if let Some(raw_input) = fields.raw_input {
147 self.raw_input = Some(raw_input);
148 }
149 if let Some(raw_output) = fields.raw_output {
150 self.raw_output = Some(raw_output);
151 }
152 }
153}
154
155/// An update to an existing tool call.
156///
157/// Used to report progress and results as tools execute. All fields except
158/// the tool call ID are optional - only changed fields need to be included.
159///
160/// See protocol docs: [Updating](https://agentclientprotocol.com/protocol/tool-calls#updating)
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
162#[serde(rename_all = "camelCase")]
163#[non_exhaustive]
164pub struct ToolCallUpdate {
165 /// The ID of the tool call being updated.
166 pub tool_call_id: ToolCallId,
167 /// Fields being updated.
168 #[serde(flatten)]
169 pub fields: ToolCallUpdateFields,
170 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
171 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
172 /// these keys.
173 ///
174 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
175 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
176 pub meta: Option<Meta>,
177}
178
179impl ToolCallUpdate {
180 #[must_use]
181 pub fn new(tool_call_id: impl Into<ToolCallId>, fields: ToolCallUpdateFields) -> Self {
182 Self {
183 tool_call_id: tool_call_id.into(),
184 fields,
185 meta: None,
186 }
187 }
188
189 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
190 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
191 /// these keys.
192 ///
193 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
194 #[must_use]
195 pub fn meta(mut self, meta: Meta) -> Self {
196 self.meta = Some(meta);
197 self
198 }
199}
200
201/// Optional fields that can be updated in a tool call.
202///
203/// All fields are optional - only include the ones being changed.
204/// Collections (content, locations) are overwritten, not extended.
205///
206/// See protocol docs: [Updating](https://agentclientprotocol.com/protocol/tool-calls#updating)
207#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
208#[serde(rename_all = "camelCase")]
209#[non_exhaustive]
210pub struct ToolCallUpdateFields {
211 /// Update the tool kind.
212 #[serde(skip_serializing_if = "Option::is_none")]
213 pub kind: Option<ToolKind>,
214 /// Update the execution status.
215 #[serde(skip_serializing_if = "Option::is_none")]
216 pub status: Option<ToolCallStatus>,
217 /// Update the human-readable title.
218 #[serde(skip_serializing_if = "Option::is_none")]
219 pub title: Option<String>,
220 /// Replace the content collection.
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub content: Option<Vec<ToolCallContent>>,
223 /// Replace the locations collection.
224 #[serde(skip_serializing_if = "Option::is_none")]
225 pub locations: Option<Vec<ToolCallLocation>>,
226 /// Update the raw input.
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub raw_input: Option<serde_json::Value>,
229 /// Update the raw output.
230 #[serde(skip_serializing_if = "Option::is_none")]
231 pub raw_output: Option<serde_json::Value>,
232}
233
234impl ToolCallUpdateFields {
235 #[must_use]
236 pub fn new() -> Self {
237 Self::default()
238 }
239
240 /// Update the tool kind.
241 #[must_use]
242 pub fn kind(mut self, kind: ToolKind) -> Self {
243 self.kind = Some(kind);
244 self
245 }
246
247 /// Update the execution status.
248 #[must_use]
249 pub fn status(mut self, status: ToolCallStatus) -> Self {
250 self.status = Some(status);
251 self
252 }
253
254 /// Update the human-readable title.
255 #[must_use]
256 pub fn title(mut self, title: impl Into<String>) -> Self {
257 self.title = Some(title.into());
258 self
259 }
260
261 /// Replace the content collection.
262 #[must_use]
263 pub fn content(mut self, content: Vec<ToolCallContent>) -> Self {
264 self.content = Some(content);
265 self
266 }
267
268 /// Replace the locations collection.
269 #[must_use]
270 pub fn locations(mut self, locations: Vec<ToolCallLocation>) -> Self {
271 self.locations = Some(locations);
272 self
273 }
274
275 /// Update the raw input.
276 #[must_use]
277 pub fn raw_input(mut self, raw_input: serde_json::Value) -> Self {
278 self.raw_input = Some(raw_input);
279 self
280 }
281
282 /// Update the raw output.
283 #[must_use]
284 pub fn raw_output(mut self, raw_output: serde_json::Value) -> Self {
285 self.raw_output = Some(raw_output);
286 self
287 }
288}
289
290/// If a given tool call doesn't exist yet, allows for attempting to construct
291/// one from a tool call update if possible.
292impl TryFrom<ToolCallUpdate> for ToolCall {
293 type Error = Error;
294
295 fn try_from(update: ToolCallUpdate) -> Result<Self, Self::Error> {
296 let ToolCallUpdate {
297 tool_call_id,
298 fields:
299 ToolCallUpdateFields {
300 kind,
301 status,
302 title,
303 content,
304 locations,
305 raw_input,
306 raw_output,
307 },
308 meta,
309 } = update;
310
311 Ok(Self {
312 tool_call_id,
313 title: title.ok_or_else(|| {
314 Error::invalid_params().data(serde_json::json!("title is required for a tool call"))
315 })?,
316 kind: kind.unwrap_or_default(),
317 status: status.unwrap_or_default(),
318 content: content.unwrap_or_default(),
319 locations: locations.unwrap_or_default(),
320 raw_input,
321 raw_output,
322 meta,
323 })
324 }
325}
326
327impl From<ToolCall> for ToolCallUpdate {
328 fn from(value: ToolCall) -> Self {
329 let ToolCall {
330 tool_call_id,
331 title,
332 kind,
333 status,
334 content,
335 locations,
336 raw_input,
337 raw_output,
338 meta,
339 } = value;
340 Self {
341 tool_call_id,
342 fields: ToolCallUpdateFields {
343 kind: Some(kind),
344 status: Some(status),
345 title: Some(title),
346 content: Some(content),
347 locations: Some(locations),
348 raw_input,
349 raw_output,
350 },
351 meta,
352 }
353 }
354}
355
356/// Unique identifier for a tool call within a session.
357#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
358#[serde(transparent)]
359#[from(Arc<str>, String, &'static str)]
360#[non_exhaustive]
361pub struct ToolCallId(pub Arc<str>);
362
363impl ToolCallId {
364 pub fn new(id: impl Into<Arc<str>>) -> Self {
365 Self(id.into())
366 }
367}
368
369/// Categories of tools that can be invoked.
370///
371/// Tool kinds help clients choose appropriate icons and optimize how they
372/// display tool execution progress.
373///
374/// See protocol docs: [Creating](https://agentclientprotocol.com/protocol/tool-calls#creating)
375#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
376#[serde(rename_all = "snake_case")]
377#[non_exhaustive]
378pub enum ToolKind {
379 /// Reading files or data.
380 Read,
381 /// Modifying files or content.
382 Edit,
383 /// Removing files or data.
384 Delete,
385 /// Moving or renaming files.
386 Move,
387 /// Searching for information.
388 Search,
389 /// Running commands or code.
390 Execute,
391 /// Internal reasoning or planning.
392 Think,
393 /// Retrieving external data.
394 Fetch,
395 /// Switching the current session mode.
396 SwitchMode,
397 /// Other tool types (default).
398 #[default]
399 #[serde(other)]
400 Other,
401}
402
403impl ToolKind {
404 #[expect(clippy::trivially_copy_pass_by_ref, reason = "Required by serde")]
405 fn is_default(&self) -> bool {
406 matches!(self, ToolKind::Other)
407 }
408}
409
410/// Execution status of a tool call.
411///
412/// Tool calls progress through different statuses during their lifecycle.
413///
414/// See protocol docs: [Status](https://agentclientprotocol.com/protocol/tool-calls#status)
415#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
416#[serde(rename_all = "snake_case")]
417#[non_exhaustive]
418pub enum ToolCallStatus {
419 /// The tool call hasn't started running yet because the input is either
420 /// streaming or we're awaiting approval.
421 #[default]
422 Pending,
423 /// The tool call is currently running.
424 InProgress,
425 /// The tool call completed successfully.
426 Completed,
427 /// The tool call failed with an error.
428 Failed,
429}
430
431impl ToolCallStatus {
432 #[expect(clippy::trivially_copy_pass_by_ref, reason = "Required by serde")]
433 fn is_default(&self) -> bool {
434 matches!(self, ToolCallStatus::Pending)
435 }
436}
437
438/// Content produced by a tool call.
439///
440/// Tool calls can produce different types of content including
441/// standard content blocks (text, images) or file diffs.
442///
443/// See protocol docs: [Content](https://agentclientprotocol.com/protocol/tool-calls#content)
444#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
445#[serde(tag = "type", rename_all = "snake_case")]
446#[schemars(extend("discriminator" = {"propertyName": "type"}))]
447#[non_exhaustive]
448pub enum ToolCallContent {
449 /// Standard content block (text, images, resources).
450 Content(Content),
451 /// File modification shown as a diff.
452 Diff(Diff),
453 /// Embed a terminal created with `terminal/create` by its id.
454 ///
455 /// The terminal must be added before calling `terminal/release`.
456 ///
457 /// See protocol docs: [Terminal](https://agentclientprotocol.com/protocol/terminals)
458 Terminal(Terminal),
459}
460
461impl<T: Into<ContentBlock>> From<T> for ToolCallContent {
462 fn from(content: T) -> Self {
463 ToolCallContent::Content(Content::new(content))
464 }
465}
466
467impl From<Diff> for ToolCallContent {
468 fn from(diff: Diff) -> Self {
469 ToolCallContent::Diff(diff)
470 }
471}
472
473/// Standard content block (text, images, resources).
474#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
475#[serde(rename_all = "camelCase")]
476#[non_exhaustive]
477pub struct Content {
478 /// The actual content block.
479 pub content: ContentBlock,
480 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
481 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
482 /// these keys.
483 ///
484 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
485 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
486 pub meta: Option<Meta>,
487}
488
489impl Content {
490 pub fn new(content: impl Into<ContentBlock>) -> Self {
491 Self {
492 content: content.into(),
493 meta: None,
494 }
495 }
496
497 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
498 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
499 /// these keys.
500 ///
501 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
502 #[must_use]
503 pub fn meta(mut self, meta: Meta) -> Self {
504 self.meta = Some(meta);
505 self
506 }
507}
508
509/// Embed a terminal created with `terminal/create` by its id.
510///
511/// The terminal must be added before calling `terminal/release`.
512///
513/// See protocol docs: [Terminal](https://agentclientprotocol.com/protocol/terminals)
514#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
515#[serde(rename_all = "camelCase")]
516#[non_exhaustive]
517pub struct Terminal {
518 pub terminal_id: TerminalId,
519 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
520 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
521 /// these keys.
522 ///
523 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
524 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
525 pub meta: Option<Meta>,
526}
527
528impl Terminal {
529 #[must_use]
530 pub fn new(terminal_id: impl Into<TerminalId>) -> Self {
531 Self {
532 terminal_id: terminal_id.into(),
533 meta: None,
534 }
535 }
536
537 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
538 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
539 /// these keys.
540 ///
541 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
542 #[must_use]
543 pub fn meta(mut self, meta: Meta) -> Self {
544 self.meta = Some(meta);
545 self
546 }
547}
548
549/// A diff representing file modifications.
550///
551/// Shows changes to files in a format suitable for display in the client UI.
552///
553/// See protocol docs: [Content](https://agentclientprotocol.com/protocol/tool-calls#content)
554#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
555#[serde(rename_all = "camelCase")]
556#[non_exhaustive]
557pub struct Diff {
558 /// The file path being modified.
559 pub path: PathBuf,
560 /// The original content (None for new files).
561 pub old_text: Option<String>,
562 /// The new content after modification.
563 pub new_text: String,
564 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
565 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
566 /// these keys.
567 ///
568 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
569 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
570 pub meta: Option<Meta>,
571}
572
573impl Diff {
574 pub fn new(path: impl Into<PathBuf>, new_text: impl Into<String>) -> Self {
575 Self {
576 path: path.into(),
577 old_text: None,
578 new_text: new_text.into(),
579 meta: None,
580 }
581 }
582
583 /// The original content (None for new files).
584 #[must_use]
585 pub fn old_text(mut self, old_text: impl Into<String>) -> Self {
586 self.old_text = Some(old_text.into());
587 self
588 }
589
590 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
591 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
592 /// these keys.
593 ///
594 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
595 #[must_use]
596 pub fn meta(mut self, meta: Meta) -> Self {
597 self.meta = Some(meta);
598 self
599 }
600}
601
602/// A file location being accessed or modified by a tool.
603///
604/// Enables clients to implement "follow-along" features that track
605/// which files the agent is working with in real-time.
606///
607/// See protocol docs: [Following the Agent](https://agentclientprotocol.com/protocol/tool-calls#following-the-agent)
608#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
609#[serde(rename_all = "camelCase")]
610#[non_exhaustive]
611pub struct ToolCallLocation {
612 /// The file path being accessed or modified.
613 pub path: PathBuf,
614 /// Optional line number within the file.
615 #[serde(default, skip_serializing_if = "Option::is_none")]
616 pub line: Option<u32>,
617 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
618 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
619 /// these keys.
620 ///
621 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
622 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
623 pub meta: Option<Meta>,
624}
625
626impl ToolCallLocation {
627 pub fn new(path: impl Into<PathBuf>) -> Self {
628 Self {
629 path: path.into(),
630 line: None,
631 meta: None,
632 }
633 }
634
635 /// Optional line number within the file.
636 #[must_use]
637 pub fn line(mut self, line: u32) -> Self {
638 self.line = Some(line);
639 self
640 }
641
642 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
643 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
644 /// these keys.
645 ///
646 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
647 #[must_use]
648 pub fn meta(mut self, meta: Meta) -> Self {
649 self.meta = Some(meta);
650 self
651 }
652}