Skip to main content

mcp_kit/types/
content.rs

1use serde::{Deserialize, Serialize};
2
3/// Priority/audience annotation for content
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct Annotations {
6    #[serde(skip_serializing_if = "Option::is_none")]
7    pub audience: Option<Vec<Role>>,
8    /// Priority from 0.0 (lowest) to 1.0 (highest)
9    #[serde(skip_serializing_if = "Option::is_none")]
10    pub priority: Option<f64>,
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15pub enum Role {
16    User,
17    Assistant,
18}
19
20/// Plain text content
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct TextContent {
23    pub text: String,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub annotations: Option<Annotations>,
26}
27
28impl TextContent {
29    pub fn new(text: impl Into<String>) -> Self {
30        Self {
31            text: text.into(),
32            annotations: None,
33        }
34    }
35}
36
37/// Base64-encoded image content
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[serde(rename_all = "camelCase")]
40pub struct ImageContent {
41    /// Base64-encoded image data
42    pub data: String,
43    pub mime_type: String,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub annotations: Option<Annotations>,
46}
47
48impl ImageContent {
49    pub fn new(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
50        Self {
51            data: data.into(),
52            mime_type: mime_type.into(),
53            annotations: None,
54        }
55    }
56
57    pub fn png(data: impl Into<String>) -> Self {
58        Self::new(data, "image/png")
59    }
60
61    pub fn jpeg(data: impl Into<String>) -> Self {
62        Self::new(data, "image/jpeg")
63    }
64}
65
66/// Base64-encoded audio content
67#[derive(Debug, Clone, Serialize, Deserialize)]
68#[serde(rename_all = "camelCase")]
69pub struct AudioContent {
70    pub data: String,
71    pub mime_type: String,
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub annotations: Option<Annotations>,
74}
75
76/// An embedded resource (text or blob)
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct EmbeddedResource {
79    pub resource: ResourceContents,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub annotations: Option<Annotations>,
82}
83
84/// The contents of a resource, which can be text or binary
85#[derive(Debug, Clone, Serialize, Deserialize)]
86#[serde(untagged)]
87pub enum ResourceContents {
88    Text(TextResourceContents),
89    Blob(BlobResourceContents),
90}
91
92impl ResourceContents {
93    pub fn text(uri: impl Into<String>, text: impl Into<String>) -> Self {
94        Self::Text(TextResourceContents {
95            uri: uri.into(),
96            mime_type: Some("text/plain".to_owned()),
97            text: text.into(),
98        })
99    }
100
101    pub fn blob(
102        uri: impl Into<String>,
103        blob: impl Into<String>,
104        mime_type: impl Into<String>,
105    ) -> Self {
106        Self::Blob(BlobResourceContents {
107            uri: uri.into(),
108            mime_type: Some(mime_type.into()),
109            blob: blob.into(),
110        })
111    }
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub struct TextResourceContents {
117    pub uri: String,
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub mime_type: Option<String>,
120    pub text: String,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
124#[serde(rename_all = "camelCase")]
125pub struct BlobResourceContents {
126    pub uri: String,
127    #[serde(skip_serializing_if = "Option::is_none")]
128    pub mime_type: Option<String>,
129    /// Base64-encoded binary data
130    pub blob: String,
131}
132
133/// Polymorphic content — used in tool results and sampling messages
134#[derive(Debug, Clone, Serialize, Deserialize)]
135#[serde(tag = "type", rename_all = "lowercase")]
136pub enum Content {
137    Text(TextContent),
138    Image(ImageContent),
139    Audio(AudioContent),
140    Resource(EmbeddedResource),
141}
142
143impl Content {
144    pub fn text(text: impl Into<String>) -> Self {
145        Self::Text(TextContent::new(text))
146    }
147
148    pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
149        Self::Image(ImageContent::new(data, mime_type))
150    }
151
152    pub fn resource(resource: ResourceContents) -> Self {
153        Self::Resource(EmbeddedResource {
154            resource,
155            annotations: None,
156        })
157    }
158}
159
160impl From<TextContent> for Content {
161    fn from(t: TextContent) -> Self {
162        Self::Text(t)
163    }
164}
165
166impl From<ImageContent> for Content {
167    fn from(i: ImageContent) -> Self {
168        Self::Image(i)
169    }
170}