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
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)]
58#[schemars(inline)]
59pub struct TextContent {
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub annotations: Option<Annotations>,
62    pub text: String,
63    /// Extension point for implementations
64    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
65    pub meta: Option<serde_json::Value>,
66}
67
68impl<T: Into<String>> From<T> for ContentBlock {
69    fn from(value: T) -> Self {
70        Self::Text(TextContent {
71            annotations: None,
72            text: value.into(),
73            meta: None,
74        })
75    }
76}
77
78/// An image provided to or from an LLM.
79#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
80#[schemars(inline)]
81pub struct ImageContent {
82    #[serde(default, skip_serializing_if = "Option::is_none")]
83    pub annotations: Option<Annotations>,
84    pub data: String,
85    #[serde(rename = "mimeType")]
86    pub mime_type: String,
87    #[serde(default, skip_serializing_if = "Option::is_none")]
88    pub uri: Option<String>,
89    /// Extension point for implementations
90    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
91    pub meta: Option<serde_json::Value>,
92}
93
94/// Audio provided to or from an LLM.
95#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
96#[schemars(inline)]
97pub struct AudioContent {
98    #[serde(default, skip_serializing_if = "Option::is_none")]
99    pub annotations: Option<Annotations>,
100    pub data: String,
101    #[serde(rename = "mimeType")]
102    pub mime_type: String,
103    /// Extension point for implementations
104    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
105    pub meta: Option<serde_json::Value>,
106}
107
108/// The contents of a resource, embedded into a prompt or tool call result.
109#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
110#[schemars(inline)]
111pub struct EmbeddedResource {
112    #[serde(default, skip_serializing_if = "Option::is_none")]
113    pub annotations: Option<Annotations>,
114    pub resource: EmbeddedResourceResource,
115    /// Extension point for implementations
116    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
117    pub meta: Option<serde_json::Value>,
118}
119
120/// Resource content that can be embedded in a message.
121#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
122#[serde(untagged)]
123pub enum EmbeddedResourceResource {
124    TextResourceContents(TextResourceContents),
125    BlobResourceContents(BlobResourceContents),
126}
127
128/// Text-based resource contents.
129#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
130pub struct TextResourceContents {
131    #[serde(rename = "mimeType", default, skip_serializing_if = "Option::is_none")]
132    pub mime_type: Option<String>,
133    pub text: String,
134    pub uri: String,
135    /// Extension point for implementations
136    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
137    pub meta: Option<serde_json::Value>,
138}
139
140/// Binary resource contents.
141#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
142pub struct BlobResourceContents {
143    pub blob: String,
144    #[serde(rename = "mimeType", default, skip_serializing_if = "Option::is_none")]
145    pub mime_type: Option<String>,
146    pub uri: String,
147    /// Extension point for implementations
148    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
149    pub meta: Option<serde_json::Value>,
150}
151
152/// A resource that the server is capable of reading, included in a prompt or tool call result.
153#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
154#[schemars(inline)]
155pub struct ResourceLink {
156    #[serde(default, skip_serializing_if = "Option::is_none")]
157    pub annotations: Option<Annotations>,
158    #[serde(default, skip_serializing_if = "Option::is_none")]
159    pub description: Option<String>,
160    #[serde(rename = "mimeType", default, skip_serializing_if = "Option::is_none")]
161    pub mime_type: Option<String>,
162    pub name: String,
163    #[serde(default, skip_serializing_if = "Option::is_none")]
164    pub size: Option<i64>,
165    #[serde(default, skip_serializing_if = "Option::is_none")]
166    pub title: Option<String>,
167    pub uri: String,
168    /// Extension point for implementations
169    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
170    pub meta: Option<serde_json::Value>,
171}
172
173/// Optional annotations for the client. The client can use annotations to inform how objects are used or displayed
174#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
175pub struct Annotations {
176    #[serde(default, skip_serializing_if = "Option::is_none")]
177    pub audience: Option<Vec<Role>>,
178    #[serde(
179        rename = "lastModified",
180        default,
181        skip_serializing_if = "Option::is_none"
182    )]
183    pub last_modified: Option<String>,
184    #[serde(default, skip_serializing_if = "Option::is_none")]
185    pub priority: Option<f64>,
186    /// Extension point for implementations
187    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
188    pub meta: Option<serde_json::Value>,
189}
190
191/// The sender or recipient of messages and data in a conversation.
192#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
193pub enum Role {
194    #[serde(rename = "assistant")]
195    Assistant,
196    #[serde(rename = "user")]
197    User,
198}