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-06-18: 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/// Backward compatibility alias for `ContentBlock`.
65///
66/// The MCP specification originally named this type `Content`, but later renamed it to
67/// `ContentBlock` for clarity. This alias exists to maintain backward compatibility with
68/// code written against earlier versions of the TurboMCP SDK.
69///
70/// **For new code**, prefer using `ContentBlock` directly as it matches the current
71/// MCP specification terminology.
72///
73/// # Example
74///
75/// ```rust
76/// use turbomcp_protocol::types::{Content, ContentBlock, TextContent};
77///
78/// // Both are equivalent:
79/// let content_old: Content = ContentBlock::Text(TextContent {
80///     text: "Hello".to_string(),
81///     annotations: None,
82///     meta: None,
83/// });
84///
85/// let content_new: ContentBlock = ContentBlock::Text(TextContent {
86///     text: "Hello".to_string(),
87///     annotations: None,
88///     meta: None,
89/// });
90/// ```
91pub type Content = ContentBlock;
92
93/// Text content per MCP 2025-06-18 specification
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct TextContent {
96    /// The text content of the message
97    pub text: String,
98    /// Optional annotations for the client
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub annotations: Option<Annotations>,
101    /// General metadata field for extensions and custom data
102    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
103    pub meta: Option<HashMap<String, serde_json::Value>>,
104}
105
106/// Image content per MCP 2025-06-18 specification
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct ImageContent {
109    /// The base64-encoded image data
110    pub data: Base64String,
111    /// The MIME type of the image. Different providers may support different image types
112    #[serde(rename = "mimeType")]
113    pub mime_type: MimeType,
114    /// Optional annotations for the client
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub annotations: Option<Annotations>,
117    /// General metadata field for extensions and custom data
118    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
119    pub meta: Option<HashMap<String, serde_json::Value>>,
120}
121
122/// Audio content per MCP 2025-06-18 specification
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct AudioContent {
125    /// The base64-encoded audio data
126    pub data: Base64String,
127    /// The MIME type of the audio. Different providers may support different audio types
128    #[serde(rename = "mimeType")]
129    pub mime_type: MimeType,
130    /// Optional annotations for the client
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub annotations: Option<Annotations>,
133    /// General metadata field for extensions and custom data
134    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
135    pub meta: Option<HashMap<String, serde_json::Value>>,
136}
137
138/// Resource link per MCP 2025-06-18 specification
139#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct ResourceLink {
141    /// Resource name (programmatic identifier)
142    pub name: String,
143    /// Display title for UI contexts (optional, falls back to name if not provided)
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub title: Option<String>,
146    /// The URI of this resource
147    pub uri: Uri,
148    /// A description of what this resource represents
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub description: Option<String>,
151    /// The MIME type of this resource, if known
152    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
153    pub mime_type: Option<MimeType>,
154    /// Optional annotations for the client
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub annotations: Option<Annotations>,
157    /// The size of the raw resource content, if known
158    #[serde(skip_serializing_if = "Option::is_none")]
159    pub size: Option<u64>,
160    /// General metadata field for extensions and custom data
161    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
162    pub meta: Option<HashMap<String, serde_json::Value>>,
163}
164
165/// Embedded resource content per MCP 2025-06-18 specification
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct EmbeddedResource {
168    /// The embedded resource content (text or binary)
169    pub resource: ResourceContent,
170    /// Optional annotations for the client
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub annotations: Option<Annotations>,
173    /// General metadata field for extensions and custom data
174    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
175    pub meta: Option<HashMap<String, serde_json::Value>>,
176}
177
178/// Text resource contents
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct TextResourceContents {
181    /// The URI of this resource
182    pub uri: Uri,
183    /// The MIME type of this resource, if known
184    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
185    pub mime_type: Option<MimeType>,
186    /// The text content (must only be set for text-representable data)
187    pub text: String,
188    /// General metadata field for extensions and custom data
189    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
190    pub meta: Option<HashMap<String, serde_json::Value>>,
191}
192
193/// Binary resource contents
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct BlobResourceContents {
196    /// The URI of this resource
197    pub uri: Uri,
198    /// The MIME type of this resource, if known
199    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
200    pub mime_type: Option<MimeType>,
201    /// Base64-encoded binary data
202    pub blob: Base64String,
203    /// General metadata field for extensions and custom data
204    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
205    pub meta: Option<HashMap<String, serde_json::Value>>,
206}
207
208/// Union type for resource contents (text or binary)
209#[derive(Debug, Clone, Serialize, Deserialize)]
210#[serde(untagged)]
211pub enum ResourceContent {
212    /// Text resource content
213    Text(TextResourceContents),
214    /// Binary resource content
215    Blob(BlobResourceContents),
216}
217
218/// Tool use content (MCP 2025-11-25 draft, SEP-1577)
219///
220/// Represents a request from the LLM to call a tool during sampling.
221/// The model wants to execute a function and receive its results.
222#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct ToolUseContent {
224    /// A unique identifier for this tool use
225    /// This ID is used to match tool results to their corresponding tool uses
226    pub id: String,
227
228    /// The name of the tool to call
229    pub name: String,
230
231    /// The arguments to pass to the tool, conforming to the tool's input schema
232    pub input: serde_json::Value,
233
234    /// Optional metadata about the tool use
235    /// Clients SHOULD preserve this field when including tool uses in subsequent
236    /// sampling requests to enable caching optimizations
237    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
238    pub meta: Option<HashMap<String, serde_json::Value>>,
239}
240
241/// Tool result content (MCP 2025-11-25 draft, SEP-1577)
242///
243/// Represents the result of executing a tool that was requested by the LLM.
244/// The server provides the tool execution results back to the model.
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct ToolResultContent {
247    /// The ID of the tool use this result corresponds to
248    /// This MUST match the ID from a previous ToolUseContent
249    #[serde(rename = "toolUseId")]
250    pub tool_use_id: String,
251
252    /// The unstructured result content of the tool use
253    /// Can include text, images, audio, resource links, and embedded resources
254    pub content: Vec<ContentBlock>,
255
256    /// An optional structured result object
257    /// If the tool defined an outputSchema, this SHOULD conform to that schema
258    #[serde(rename = "structuredContent", skip_serializing_if = "Option::is_none")]
259    pub structured_content: Option<serde_json::Value>,
260
261    /// Whether the tool use resulted in an error
262    /// If true, the content typically describes the error that occurred
263    /// Default: false
264    #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
265    pub is_error: Option<bool>,
266
267    /// Optional metadata about the tool result
268    /// Clients SHOULD preserve this field when including tool results in subsequent
269    /// sampling requests to enable caching optimizations
270    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
271    pub meta: Option<HashMap<String, serde_json::Value>>,
272}