agent_client_protocol/
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
15/// Content blocks represent displayable information in the Agent Client Protocol.
16///
17/// They provide a structured way to handle various types of user-facing content—whether
18/// it's text from language models, images for analysis, or embedded resources for context.
19///
20/// Content blocks appear in:
21/// - User prompts sent via `session/prompt`
22/// - Language model output streamed through `session/update` notifications
23/// - Progress updates and results from tool calls
24///
25/// This structure is compatible with the Model Context Protocol (MCP), enabling
26/// agents to seamlessly forward content from MCP tool outputs without transformation.
27///
28/// See protocol docs: [Content](https://agentclientprotocol.com/protocol/content)
29#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
30#[serde(tag = "type", rename_all = "snake_case")]
31pub enum ContentBlock {
32    /// Plain text content
33    ///
34    /// All agents MUST support text content blocks in prompts.
35    Text(TextContent),
36    /// Images for visual context or analysis.
37    ///
38    /// Requires the `image` prompt capability when included in prompts.
39    Image(ImageContent),
40    /// Audio data for transcription or analysis.
41    ///
42    /// Requires the `audio` prompt capability when included in prompts.
43    Audio(AudioContent),
44    /// References to resources that the agent can access.
45    ///
46    /// All agents MUST support resource links in prompts.
47    ResourceLink(ResourceLink),
48    /// Complete resource contents embedded directly in the message.
49    ///
50    /// Preferred for including context as it avoids extra round-trips.
51    ///
52    /// Requires the `embeddedContext` prompt capability when included in prompts.
53    Resource(EmbeddedResource),
54}
55
56/// Text provided to or from an LLM.
57#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
58pub struct TextContent {
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub annotations: Option<Annotations>,
61    pub text: String,
62    /// Extension point for implementations
63    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
64    pub meta: Option<serde_json::Value>,
65}
66
67impl<T: Into<String>> From<T> for ContentBlock {
68    fn from(value: T) -> Self {
69        Self::Text(TextContent {
70            annotations: None,
71            text: value.into(),
72            meta: None,
73        })
74    }
75}
76
77/// An image provided to or from an LLM.
78#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
79pub struct ImageContent {
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    pub annotations: Option<Annotations>,
82    pub data: String,
83    #[serde(rename = "mimeType")]
84    pub mime_type: String,
85    #[serde(default, skip_serializing_if = "Option::is_none")]
86    pub uri: Option<String>,
87    /// Extension point for implementations
88    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
89    pub meta: Option<serde_json::Value>,
90}
91
92/// Audio provided to or from an LLM.
93#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
94pub struct AudioContent {
95    #[serde(default, skip_serializing_if = "Option::is_none")]
96    pub annotations: Option<Annotations>,
97    pub data: String,
98    #[serde(rename = "mimeType")]
99    pub mime_type: String,
100    /// Extension point for implementations
101    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
102    pub meta: Option<serde_json::Value>,
103}
104
105/// The contents of a resource, embedded into a prompt or tool call result.
106#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
107pub struct EmbeddedResource {
108    #[serde(default, skip_serializing_if = "Option::is_none")]
109    pub annotations: Option<Annotations>,
110    pub resource: EmbeddedResourceResource,
111    /// Extension point for implementations
112    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
113    pub meta: Option<serde_json::Value>,
114}
115
116/// Resource content that can be embedded in a message.
117#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
118#[serde(untagged)]
119pub enum EmbeddedResourceResource {
120    TextResourceContents(TextResourceContents),
121    BlobResourceContents(BlobResourceContents),
122}
123
124/// Text-based resource contents.
125#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
126pub struct TextResourceContents {
127    #[serde(rename = "mimeType", default, skip_serializing_if = "Option::is_none")]
128    pub mime_type: Option<String>,
129    pub text: String,
130    pub uri: String,
131    /// Extension point for implementations
132    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
133    pub meta: Option<serde_json::Value>,
134}
135
136/// Binary resource contents.
137#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
138pub struct BlobResourceContents {
139    pub blob: String,
140    #[serde(rename = "mimeType", default, skip_serializing_if = "Option::is_none")]
141    pub mime_type: Option<String>,
142    pub uri: String,
143    /// Extension point for implementations
144    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
145    pub meta: Option<serde_json::Value>,
146}
147
148/// A resource that the server is capable of reading, included in a prompt or tool call result.
149#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
150pub struct ResourceLink {
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub annotations: Option<Annotations>,
153    #[serde(default, skip_serializing_if = "Option::is_none")]
154    pub description: Option<String>,
155    #[serde(rename = "mimeType", default, skip_serializing_if = "Option::is_none")]
156    pub mime_type: Option<String>,
157    pub name: String,
158    #[serde(default, skip_serializing_if = "Option::is_none")]
159    pub size: Option<i64>,
160    #[serde(default, skip_serializing_if = "Option::is_none")]
161    pub title: Option<String>,
162    pub uri: String,
163    /// Extension point for implementations
164    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
165    pub meta: Option<serde_json::Value>,
166}
167
168/// Optional annotations for the client. The client can use annotations to inform how objects are used or displayed
169#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
170pub struct Annotations {
171    #[serde(default, skip_serializing_if = "Option::is_none")]
172    pub audience: Option<Vec<Role>>,
173    #[serde(
174        rename = "lastModified",
175        default,
176        skip_serializing_if = "Option::is_none"
177    )]
178    pub last_modified: Option<String>,
179    #[serde(default, skip_serializing_if = "Option::is_none")]
180    pub priority: Option<f64>,
181    /// Extension point for implementations
182    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
183    pub meta: Option<serde_json::Value>,
184}
185
186/// The sender or recipient of messages and data in a conversation.
187#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
188pub enum Role {
189    #[serde(rename = "assistant")]
190    Assistant,
191    #[serde(rename = "user")]
192    User,
193}