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