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