Skip to main content

turbomcp_protocol/types/
content.rs

1//! Message content types
2//!
3//! This module contains all content block types used in MCP messages.
4//! Content blocks allow rich message composition with text, images, audio,
5//! and resource references.
6//!
7//! # Content Types
8//!
9//! - [`ContentBlock`] - Content block enum (text, image, audio, resource link, embedded resource)
10//! - [`TextContent`] - Plain text content with annotations
11//! - [`ImageContent`] - Base64-encoded image content
12//! - [`AudioContent`] - Base64-encoded audio content
13//! - [`ResourceLink`] - Reference to external resource
14//! - [`EmbeddedResource`] - Embedded resource content
15//! - [`ContentType`] - Content type enumeration (JSON/Binary/Text)
16
17use serde::{Deserialize, Serialize};
18use std::collections::HashMap;
19
20use super::core::{Annotations, Base64String, MimeType, Uri};
21
22/// Content type enumeration
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
24#[serde(rename_all = "lowercase")]
25pub enum ContentType {
26    /// JSON content
27    Json,
28    /// Binary content
29    Binary,
30    /// Plain text content
31    Text,
32}
33
34/// Content block union type
35///
36/// - MCP 2025-11-25: text, image, audio, resource_link, resource
37/// - MCP 2025-11-25 draft (SEP-1577): + tool_use, tool_result
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[serde(tag = "type")]
40pub enum ContentBlock {
41    /// Text content
42    #[serde(rename = "text")]
43    Text(TextContent),
44    /// Image content
45    #[serde(rename = "image")]
46    Image(ImageContent),
47    /// Audio content
48    #[serde(rename = "audio")]
49    Audio(AudioContent),
50    /// Resource link
51    #[serde(rename = "resource_link")]
52    ResourceLink(ResourceLink),
53    /// Embedded resource
54    #[serde(rename = "resource")]
55    Resource(EmbeddedResource),
56    /// Tool use (MCP 2025-11-25 draft, SEP-1577)
57    #[serde(rename = "tool_use")]
58    ToolUse(ToolUseContent),
59    /// Tool result (MCP 2025-11-25 draft, SEP-1577)
60    #[serde(rename = "tool_result")]
61    ToolResult(ToolResultContent),
62}
63
64/// Text content per the current MCP specification
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct TextContent {
67    /// The text content of the message
68    pub text: String,
69    /// Optional annotations for the client
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub annotations: Option<Annotations>,
72    /// General metadata field for extensions and custom data
73    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
74    pub meta: Option<HashMap<String, serde_json::Value>>,
75}
76
77/// Image content per the current MCP specification
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct ImageContent {
80    /// The base64-encoded image data
81    pub data: Base64String,
82    /// The MIME type of the image. Different providers may support different image types
83    #[serde(rename = "mimeType")]
84    pub mime_type: MimeType,
85    /// Optional annotations for the client
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub annotations: Option<Annotations>,
88    /// General metadata field for extensions and custom data
89    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
90    pub meta: Option<HashMap<String, serde_json::Value>>,
91}
92
93/// Audio content per the current MCP specification
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct AudioContent {
96    /// The base64-encoded audio data
97    pub data: Base64String,
98    /// The MIME type of the audio. Different providers may support different audio types
99    #[serde(rename = "mimeType")]
100    pub mime_type: MimeType,
101    /// Optional annotations for the client
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub annotations: Option<Annotations>,
104    /// General metadata field for extensions and custom data
105    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
106    pub meta: Option<HashMap<String, serde_json::Value>>,
107}
108
109/// Resource link per the current MCP specification
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct ResourceLink {
112    /// Resource name (programmatic identifier)
113    pub name: String,
114    /// Display title for UI contexts (optional, falls back to name if not provided)
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub title: Option<String>,
117    /// The URI of this resource
118    pub uri: Uri,
119    /// A description of what this resource represents
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub description: Option<String>,
122    /// The MIME type of this resource, if known
123    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
124    pub mime_type: Option<MimeType>,
125    /// Optional annotations for the client
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub annotations: Option<Annotations>,
128    /// The size of the raw resource content, if known
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub size: Option<u64>,
131    /// General metadata field for extensions and custom data
132    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
133    pub meta: Option<HashMap<String, serde_json::Value>>,
134}
135
136/// Embedded resource content per the current MCP specification
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct EmbeddedResource {
139    /// The embedded resource content (text or binary)
140    pub resource: ResourceContent,
141    /// Optional annotations for the client
142    #[serde(skip_serializing_if = "Option::is_none")]
143    pub annotations: Option<Annotations>,
144    /// General metadata field for extensions and custom data
145    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
146    pub meta: Option<HashMap<String, serde_json::Value>>,
147}
148
149/// Text resource contents
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct TextResourceContents {
152    /// The URI of this resource
153    pub uri: Uri,
154    /// The MIME type of this resource, if known
155    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
156    pub mime_type: Option<MimeType>,
157    /// The text content (must only be set for text-representable data)
158    pub text: String,
159    /// General metadata field for extensions and custom data
160    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
161    pub meta: Option<HashMap<String, serde_json::Value>>,
162}
163
164/// Binary resource contents
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct BlobResourceContents {
167    /// The URI of this resource
168    pub uri: Uri,
169    /// The MIME type of this resource, if known
170    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
171    pub mime_type: Option<MimeType>,
172    /// Base64-encoded binary data
173    pub blob: Base64String,
174    /// General metadata field for extensions and custom data
175    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
176    pub meta: Option<HashMap<String, serde_json::Value>>,
177}
178
179/// Union type for resource contents (text or binary)
180#[derive(Debug, Clone, Serialize, Deserialize)]
181#[serde(untagged)]
182pub enum ResourceContent {
183    /// Text resource content
184    Text(TextResourceContents),
185    /// Binary resource content
186    Blob(BlobResourceContents),
187}
188
189/// Tool use content (MCP 2025-11-25 draft, SEP-1577)
190///
191/// Represents a request from the LLM to call a tool during sampling.
192/// The model wants to execute a function and receive its results.
193#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct ToolUseContent {
195    /// A unique identifier for this tool use
196    /// This ID is used to match tool results to their corresponding tool uses
197    pub id: String,
198
199    /// The name of the tool to call
200    pub name: String,
201
202    /// The arguments to pass to the tool, conforming to the tool's input schema
203    pub input: serde_json::Value,
204
205    /// Optional metadata about the tool use
206    /// Clients SHOULD preserve this field when including tool uses in subsequent
207    /// sampling requests to enable caching optimizations
208    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
209    pub meta: Option<HashMap<String, serde_json::Value>>,
210}
211
212/// Tool result content (MCP 2025-11-25 draft, SEP-1577)
213///
214/// Represents the result of executing a tool that was requested by the LLM.
215/// The server provides the tool execution results back to the model.
216#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct ToolResultContent {
218    /// The ID of the tool use this result corresponds to
219    /// This MUST match the ID from a previous ToolUseContent
220    #[serde(rename = "toolUseId")]
221    pub tool_use_id: String,
222
223    /// The unstructured result content of the tool use
224    /// Can include text, images, audio, resource links, and embedded resources
225    pub content: Vec<ContentBlock>,
226
227    /// An optional structured result object
228    /// If the tool defined an outputSchema, this SHOULD conform to that schema
229    #[serde(rename = "structuredContent", skip_serializing_if = "Option::is_none")]
230    pub structured_content: Option<serde_json::Value>,
231
232    /// Whether the tool use resulted in an error
233    /// If true, the content typically describes the error that occurred
234    /// Default: false
235    #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
236    pub is_error: Option<bool>,
237
238    /// Optional metadata about the tool result
239    /// Clients SHOULD preserve this field when including tool results in subsequent
240    /// sampling requests to enable caching optimizations
241    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
242    pub meta: Option<HashMap<String, serde_json::Value>>,
243}