use crate::mcp::{Annotations, ResourceContents, Role}; use serde::{Deserialize, Serialize};
use serde_with::base64::Base64;
use serde_with::{serde_as, skip_serializing_none};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PromptMessage {
pub role: Role,
pub content: MessageContent,
}
impl PromptMessage {
pub fn new(role: Role, content: MessageContent) -> Self {
Self { role, content }
}
}
#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum MessageContent {
#[serde(rename_all = "camelCase")]
Text {
text: String,
#[serde(skip_serializing_if = "Option::is_none")]
annotations: Option<Annotations>,
},
#[serde(rename_all = "camelCase")]
Image {
#[serde_as(as = "Base64")]
data: Vec<u8>,
mime_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
annotations: Option<Annotations>,
},
#[serde(rename_all = "camelCase")]
Audio {
#[serde_as(as = "Base64")]
data: Vec<u8>,
mime_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
annotations: Option<Annotations>,
},
#[serde(rename_all = "camelCase")]
Resource {
resource: ResourceContents,
#[serde(skip_serializing_if = "Option::is_none")]
annotations: Option<Annotations>,
},
}
impl MessageContent {
pub fn new_text(text: impl Into<String>) -> Self {
Self::Text {
text: text.into(),
annotations: None,
}
}
pub fn new_image(data: Vec<u8>, mime_type: impl Into<String>) -> Self {
Self::Image {
data,
mime_type: mime_type.into(),
annotations: None,
}
}
pub fn new_audio(data: Vec<u8>, mime_type: impl Into<String>) -> Self {
Self::Audio {
data,
mime_type: mime_type.into(),
annotations: None,
}
}
pub fn new_resource(resource: ResourceContents) -> Self {
Self::Resource {
resource,
annotations: None,
}
}
pub fn with_annotations(mut self, annotations: Annotations) -> Self {
match &mut self {
Self::Text { annotations: a, .. } => *a = Some(annotations),
Self::Image { annotations: a, .. } => *a = Some(annotations),
Self::Audio { annotations: a, .. } => *a = Some(annotations),
Self::Resource { annotations: a, .. } => *a = Some(annotations),
}
self
}
}
impl MessageContent {
pub fn as_text(&self) -> Option<&str> {
if let Self::Text { text, .. } = self {
Some(text.as_str())
} else {
None
}
}
}
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PromptArgument {
pub name: String,
pub description: Option<String>,
pub required: Option<bool>,
}
impl PromptArgument {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
description: None,
required: None,
}
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn with_required(mut self, required: bool) -> Self {
self.required = Some(required);
self
}
}
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Prompt {
pub name: String,
pub description: Option<String>,
pub arguments: Option<Vec<PromptArgument>>,
}
impl Prompt {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
description: None,
arguments: None,
}
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn with_arguments(mut self, arguments: Vec<PromptArgument>) -> Self {
self.arguments = Some(arguments);
self
}
pub fn append_argument(mut self, argument: PromptArgument) -> Self {
self.arguments.get_or_insert_with(Vec::new).push(argument);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PromptReference {
pub name: String,
}
impl PromptReference {
pub fn new(name: impl Into<String>) -> Self {
Self { name: name.into() }
}
}