Skip to main content

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