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