agent_client_protocol_schema/content.rs
1//! Content blocks for representing various types of information in the Agent Client Protocol.
2//!
3//! This module defines the core content types used throughout the protocol for communication
4//! between agents and clients. Content blocks provide a flexible, extensible way to represent
5//! text, images, audio, and other resources in prompts, responses, and tool call results.
6//!
7//! The content block structure is designed to be compatible with the Model Context Protocol (MCP),
8//! allowing seamless integration between ACP and MCP-based tools.
9//!
10//! See: [Content](https://agentclientprotocol.com/protocol/content)
11
12use schemars::JsonSchema;
13use serde::{Deserialize, Serialize};
14
15use crate::Meta;
16
17/// Content blocks represent displayable information in the Agent Client Protocol.
18///
19/// They provide a structured way to handle various types of user-facing content—whether
20/// it's text from language models, images for analysis, or embedded resources for context.
21///
22/// Content blocks appear in:
23/// - User prompts sent via `session/prompt`
24/// - Language model output streamed through `session/update` notifications
25/// - Progress updates and results from tool calls
26///
27/// This structure is compatible with the Model Context Protocol (MCP), enabling
28/// agents to seamlessly forward content from MCP tool outputs without transformation.
29///
30/// See protocol docs: [Content](https://agentclientprotocol.com/protocol/content)
31#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
32#[serde(tag = "type", rename_all = "snake_case")]
33#[schemars(extend("discriminator" = {"propertyName": "type"}))]
34#[non_exhaustive]
35pub enum ContentBlock {
36 /// Text content. May be plain text or formatted with Markdown.
37 ///
38 /// All agents MUST support text content blocks in prompts.
39 /// Clients SHOULD render this text as Markdown.
40 Text(TextContent),
41 /// Images for visual context or analysis.
42 ///
43 /// Requires the `image` prompt capability when included in prompts.
44 Image(ImageContent),
45 /// Audio data for transcription or analysis.
46 ///
47 /// Requires the `audio` prompt capability when included in prompts.
48 Audio(AudioContent),
49 /// References to resources that the agent can access.
50 ///
51 /// All agents MUST support resource links in prompts.
52 ResourceLink(ResourceLink),
53 /// Complete resource contents embedded directly in the message.
54 ///
55 /// Preferred for including context as it avoids extra round-trips.
56 ///
57 /// Requires the `embeddedContext` prompt capability when included in prompts.
58 Resource(EmbeddedResource),
59}
60
61/// Text provided to or from an LLM.
62#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
63#[non_exhaustive]
64pub struct TextContent {
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub annotations: Option<Annotations>,
67 pub text: String,
68 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
69 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
70 /// these keys.
71 ///
72 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
73 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
74 pub meta: Option<Meta>,
75}
76
77impl TextContent {
78 pub fn new(text: impl Into<String>) -> Self {
79 Self {
80 annotations: None,
81 text: text.into(),
82 meta: None,
83 }
84 }
85
86 #[must_use]
87 pub fn annotations(mut self, annotations: Annotations) -> Self {
88 self.annotations = Some(annotations);
89 self
90 }
91
92 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
93 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
94 /// these keys.
95 ///
96 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
97 #[must_use]
98 pub fn meta(mut self, meta: Meta) -> Self {
99 self.meta = Some(meta);
100 self
101 }
102}
103
104impl<T: Into<String>> From<T> for ContentBlock {
105 fn from(value: T) -> Self {
106 Self::Text(TextContent::new(value))
107 }
108}
109
110/// An image provided to or from an LLM.
111#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
112#[serde(rename_all = "camelCase")]
113#[non_exhaustive]
114pub struct ImageContent {
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub annotations: Option<Annotations>,
117 pub data: String,
118 pub mime_type: String,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub uri: Option<String>,
121 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
122 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
123 /// these keys.
124 ///
125 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
126 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
127 pub meta: Option<Meta>,
128}
129
130impl ImageContent {
131 pub fn new(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
132 Self {
133 annotations: None,
134 data: data.into(),
135 mime_type: mime_type.into(),
136 uri: None,
137 meta: None,
138 }
139 }
140
141 #[must_use]
142 pub fn annotations(mut self, annotations: Annotations) -> Self {
143 self.annotations = Some(annotations);
144 self
145 }
146
147 #[must_use]
148 pub fn uri(mut self, uri: impl Into<String>) -> Self {
149 self.uri = Some(uri.into());
150 self
151 }
152
153 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
154 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
155 /// these keys.
156 ///
157 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
158 #[must_use]
159 pub fn meta(mut self, meta: Meta) -> Self {
160 self.meta = Some(meta);
161 self
162 }
163}
164
165/// Audio provided to or from an LLM.
166#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
167#[serde(rename_all = "camelCase")]
168#[non_exhaustive]
169pub struct AudioContent {
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub annotations: Option<Annotations>,
172 pub data: String,
173 pub mime_type: String,
174 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
175 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
176 /// these keys.
177 ///
178 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
179 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
180 pub meta: Option<Meta>,
181}
182
183impl AudioContent {
184 pub fn new(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
185 Self {
186 annotations: None,
187 data: data.into(),
188 mime_type: mime_type.into(),
189 meta: None,
190 }
191 }
192
193 #[must_use]
194 pub fn annotations(mut self, annotations: Annotations) -> Self {
195 self.annotations = Some(annotations);
196 self
197 }
198
199 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
200 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
201 /// these keys.
202 ///
203 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
204 #[must_use]
205 pub fn meta(mut self, meta: Meta) -> Self {
206 self.meta = Some(meta);
207 self
208 }
209}
210
211/// The contents of a resource, embedded into a prompt or tool call result.
212#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
213#[non_exhaustive]
214pub struct EmbeddedResource {
215 #[serde(skip_serializing_if = "Option::is_none")]
216 pub annotations: Option<Annotations>,
217 pub resource: EmbeddedResourceResource,
218 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
219 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
220 /// these keys.
221 ///
222 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
223 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
224 pub meta: Option<Meta>,
225}
226
227impl EmbeddedResource {
228 #[must_use]
229 pub fn new(resource: EmbeddedResourceResource) -> Self {
230 Self {
231 annotations: None,
232 resource,
233 meta: None,
234 }
235 }
236
237 #[must_use]
238 pub fn annotations(mut self, annotations: Annotations) -> Self {
239 self.annotations = Some(annotations);
240 self
241 }
242
243 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
244 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
245 /// these keys.
246 ///
247 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
248 #[must_use]
249 pub fn meta(mut self, meta: Meta) -> Self {
250 self.meta = Some(meta);
251 self
252 }
253}
254
255/// Resource content that can be embedded in a message.
256#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
257#[serde(untagged)]
258#[non_exhaustive]
259pub enum EmbeddedResourceResource {
260 TextResourceContents(TextResourceContents),
261 BlobResourceContents(BlobResourceContents),
262}
263
264/// Text-based resource contents.
265#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
266#[serde(rename_all = "camelCase")]
267#[non_exhaustive]
268pub struct TextResourceContents {
269 #[serde(skip_serializing_if = "Option::is_none")]
270 pub mime_type: Option<String>,
271 pub text: String,
272 pub uri: String,
273 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
274 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
275 /// these keys.
276 ///
277 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
278 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
279 pub meta: Option<Meta>,
280}
281
282impl TextResourceContents {
283 pub fn new(text: impl Into<String>, uri: impl Into<String>) -> Self {
284 Self {
285 mime_type: None,
286 text: text.into(),
287 uri: uri.into(),
288 meta: None,
289 }
290 }
291
292 #[must_use]
293 pub fn mime_type(mut self, mime_type: impl Into<String>) -> Self {
294 self.mime_type = Some(mime_type.into());
295 self
296 }
297
298 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
299 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
300 /// these keys.
301 ///
302 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
303 #[must_use]
304 pub fn meta(mut self, meta: Meta) -> Self {
305 self.meta = Some(meta);
306 self
307 }
308}
309
310/// Binary resource contents.
311#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
312#[serde(rename_all = "camelCase")]
313#[non_exhaustive]
314pub struct BlobResourceContents {
315 pub blob: String,
316 #[serde(skip_serializing_if = "Option::is_none")]
317 pub mime_type: Option<String>,
318 pub uri: String,
319 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
320 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
321 /// these keys.
322 ///
323 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
324 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
325 pub meta: Option<Meta>,
326}
327
328impl BlobResourceContents {
329 pub fn new(blob: impl Into<String>, uri: impl Into<String>) -> Self {
330 Self {
331 blob: blob.into(),
332 mime_type: None,
333 uri: uri.into(),
334 meta: None,
335 }
336 }
337
338 #[must_use]
339 pub fn mime_type(mut self, mime_type: impl Into<String>) -> Self {
340 self.mime_type = Some(mime_type.into());
341 self
342 }
343
344 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
345 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
346 /// these keys.
347 ///
348 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
349 #[must_use]
350 pub fn meta(mut self, meta: Meta) -> Self {
351 self.meta = Some(meta);
352 self
353 }
354}
355
356/// A resource that the server is capable of reading, included in a prompt or tool call result.
357#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
358#[serde(rename_all = "camelCase")]
359#[non_exhaustive]
360pub struct ResourceLink {
361 #[serde(skip_serializing_if = "Option::is_none")]
362 pub annotations: Option<Annotations>,
363 #[serde(skip_serializing_if = "Option::is_none")]
364 pub description: Option<String>,
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub mime_type: Option<String>,
367 pub name: String,
368 #[serde(skip_serializing_if = "Option::is_none")]
369 pub size: Option<i64>,
370 #[serde(skip_serializing_if = "Option::is_none")]
371 pub title: Option<String>,
372 pub uri: String,
373 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
374 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
375 /// these keys.
376 ///
377 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
378 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
379 pub meta: Option<Meta>,
380}
381
382impl ResourceLink {
383 pub fn new(name: impl Into<String>, uri: impl Into<String>) -> Self {
384 Self {
385 annotations: None,
386 description: None,
387 mime_type: None,
388 name: name.into(),
389 size: None,
390 title: None,
391 uri: uri.into(),
392 meta: None,
393 }
394 }
395
396 #[must_use]
397 pub fn annotations(mut self, annotations: Annotations) -> Self {
398 self.annotations = Some(annotations);
399 self
400 }
401
402 #[must_use]
403 pub fn description(mut self, description: impl Into<String>) -> Self {
404 self.description = Some(description.into());
405 self
406 }
407
408 #[must_use]
409 pub fn mime_type(mut self, mime_type: impl Into<String>) -> Self {
410 self.mime_type = Some(mime_type.into());
411 self
412 }
413
414 #[must_use]
415 pub fn size(mut self, size: i64) -> Self {
416 self.size = Some(size);
417 self
418 }
419
420 #[must_use]
421 pub fn title(mut self, title: impl Into<String>) -> Self {
422 self.title = Some(title.into());
423 self
424 }
425
426 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
427 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
428 /// these keys.
429 ///
430 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
431 #[must_use]
432 pub fn meta(mut self, meta: Meta) -> Self {
433 self.meta = Some(meta);
434 self
435 }
436}
437
438/// Optional annotations for the client. The client can use annotations to inform how objects are used or displayed
439#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, Default)]
440#[serde(rename_all = "camelCase")]
441#[non_exhaustive]
442pub struct Annotations {
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub audience: Option<Vec<Role>>,
445 #[serde(skip_serializing_if = "Option::is_none")]
446 pub last_modified: Option<String>,
447 #[serde(skip_serializing_if = "Option::is_none")]
448 pub priority: Option<f64>,
449 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
450 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
451 /// these keys.
452 ///
453 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
454 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
455 pub meta: Option<Meta>,
456}
457
458impl Annotations {
459 #[must_use]
460 pub fn new() -> Self {
461 Self::default()
462 }
463
464 #[must_use]
465 pub fn audience(mut self, audience: Vec<Role>) -> Self {
466 self.audience = Some(audience);
467 self
468 }
469
470 #[must_use]
471 pub fn last_modified(mut self, last_modified: impl Into<String>) -> Self {
472 self.last_modified = Some(last_modified.into());
473 self
474 }
475
476 #[must_use]
477 pub fn priority(mut self, priority: f64) -> Self {
478 self.priority = Some(priority);
479 self
480 }
481
482 /// The _meta property is reserved by ACP to allow clients and agents to attach additional
483 /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
484 /// these keys.
485 ///
486 /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
487 #[must_use]
488 pub fn meta(mut self, meta: Meta) -> Self {
489 self.meta = Some(meta);
490 self
491 }
492}
493
494/// The sender or recipient of messages and data in a conversation.
495#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
496#[serde(rename_all = "camelCase")]
497#[non_exhaustive]
498pub enum Role {
499 Assistant,
500 User,
501}