feat(cli): add mcp prompt support via slash commands (#1323)
This commit is contained in:
@@ -10,6 +10,8 @@ use std::collections::HashSet;
|
||||
use chrono::Utc;
|
||||
use mcp_core::content::{Content, ImageContent, TextContent};
|
||||
use mcp_core::handler::ToolResult;
|
||||
use mcp_core::prompt::{PromptMessage, PromptMessageContent, PromptMessageRole};
|
||||
use mcp_core::resource::ResourceContents;
|
||||
use mcp_core::role::Role;
|
||||
use mcp_core::tool::ToolCall;
|
||||
use serde_json::Value;
|
||||
@@ -156,6 +158,37 @@ impl From<Content> for MessageContent {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PromptMessage> for Message {
|
||||
fn from(prompt_message: PromptMessage) -> Self {
|
||||
// Create a new message with the appropriate role
|
||||
let message = match prompt_message.role {
|
||||
PromptMessageRole::User => Message::user(),
|
||||
PromptMessageRole::Assistant => Message::assistant(),
|
||||
};
|
||||
|
||||
// Convert and add the content
|
||||
let content = match prompt_message.content {
|
||||
PromptMessageContent::Text { text } => MessageContent::text(text),
|
||||
PromptMessageContent::Image { image } => {
|
||||
MessageContent::image(image.data, image.mime_type)
|
||||
}
|
||||
PromptMessageContent::Resource { resource } => {
|
||||
// For resources, convert to text content with the resource text
|
||||
match resource.resource {
|
||||
ResourceContents::TextResourceContents { text, .. } => {
|
||||
MessageContent::text(text)
|
||||
}
|
||||
ResourceContents::BlobResourceContents { blob, .. } => {
|
||||
MessageContent::text(format!("[Binary content: {}]", blob))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
message.with_content(content)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
/// A message to or from an LLM
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -305,7 +338,10 @@ impl Message {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use mcp_core::content::EmbeddedResource;
|
||||
use mcp_core::handler::ToolError;
|
||||
use mcp_core::prompt::PromptMessageContent;
|
||||
use mcp_core::resource::ResourceContents;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
#[test]
|
||||
@@ -420,4 +456,158 @@ mod tests {
|
||||
panic!("Expected ToolRequest content");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_prompt_message_text() {
|
||||
let prompt_content = PromptMessageContent::Text {
|
||||
text: "Hello, world!".to_string(),
|
||||
};
|
||||
|
||||
let prompt_message = PromptMessage {
|
||||
role: PromptMessageRole::User,
|
||||
content: prompt_content,
|
||||
};
|
||||
|
||||
let message = Message::from(prompt_message);
|
||||
|
||||
if let MessageContent::Text(text_content) = &message.content[0] {
|
||||
assert_eq!(text_content.text, "Hello, world!");
|
||||
} else {
|
||||
panic!("Expected MessageContent::Text");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_prompt_message_image() {
|
||||
let prompt_content = PromptMessageContent::Image {
|
||||
image: ImageContent {
|
||||
data: "base64data".to_string(),
|
||||
mime_type: "image/jpeg".to_string(),
|
||||
annotations: None,
|
||||
},
|
||||
};
|
||||
|
||||
let prompt_message = PromptMessage {
|
||||
role: PromptMessageRole::User,
|
||||
content: prompt_content,
|
||||
};
|
||||
|
||||
let message = Message::from(prompt_message);
|
||||
|
||||
if let MessageContent::Image(image_content) = &message.content[0] {
|
||||
assert_eq!(image_content.data, "base64data");
|
||||
assert_eq!(image_content.mime_type, "image/jpeg");
|
||||
} else {
|
||||
panic!("Expected MessageContent::Image");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_prompt_message_text_resource() {
|
||||
let resource = ResourceContents::TextResourceContents {
|
||||
uri: "file:///test.txt".to_string(),
|
||||
mime_type: Some("text/plain".to_string()),
|
||||
text: "Resource content".to_string(),
|
||||
};
|
||||
|
||||
let prompt_content = PromptMessageContent::Resource {
|
||||
resource: EmbeddedResource {
|
||||
resource,
|
||||
annotations: None,
|
||||
},
|
||||
};
|
||||
|
||||
let prompt_message = PromptMessage {
|
||||
role: PromptMessageRole::User,
|
||||
content: prompt_content,
|
||||
};
|
||||
|
||||
let message = Message::from(prompt_message);
|
||||
|
||||
if let MessageContent::Text(text_content) = &message.content[0] {
|
||||
assert_eq!(text_content.text, "Resource content");
|
||||
} else {
|
||||
panic!("Expected MessageContent::Text");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_prompt_message_blob_resource() {
|
||||
let resource = ResourceContents::BlobResourceContents {
|
||||
uri: "file:///test.bin".to_string(),
|
||||
mime_type: Some("application/octet-stream".to_string()),
|
||||
blob: "binary_data".to_string(),
|
||||
};
|
||||
|
||||
let prompt_content = PromptMessageContent::Resource {
|
||||
resource: EmbeddedResource {
|
||||
resource,
|
||||
annotations: None,
|
||||
},
|
||||
};
|
||||
|
||||
let prompt_message = PromptMessage {
|
||||
role: PromptMessageRole::User,
|
||||
content: prompt_content,
|
||||
};
|
||||
|
||||
let message = Message::from(prompt_message);
|
||||
|
||||
if let MessageContent::Text(text_content) = &message.content[0] {
|
||||
assert_eq!(text_content.text, "[Binary content: binary_data]");
|
||||
} else {
|
||||
panic!("Expected MessageContent::Text");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_prompt_message() {
|
||||
// Test user message conversion
|
||||
let prompt_message = PromptMessage {
|
||||
role: PromptMessageRole::User,
|
||||
content: PromptMessageContent::Text {
|
||||
text: "Hello, world!".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
let message = Message::from(prompt_message);
|
||||
assert_eq!(message.role, Role::User);
|
||||
assert_eq!(message.content.len(), 1);
|
||||
assert_eq!(message.as_concat_text(), "Hello, world!");
|
||||
|
||||
// Test assistant message conversion
|
||||
let prompt_message = PromptMessage {
|
||||
role: PromptMessageRole::Assistant,
|
||||
content: PromptMessageContent::Text {
|
||||
text: "I can help with that.".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
let message = Message::from(prompt_message);
|
||||
assert_eq!(message.role, Role::Assistant);
|
||||
assert_eq!(message.content.len(), 1);
|
||||
assert_eq!(message.as_concat_text(), "I can help with that.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_with_text() {
|
||||
let message = Message::user().with_text("Hello");
|
||||
assert_eq!(message.as_concat_text(), "Hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_with_tool_request() {
|
||||
let tool_call = Ok(ToolCall {
|
||||
name: "test_tool".to_string(),
|
||||
arguments: serde_json::json!({}),
|
||||
});
|
||||
|
||||
let message = Message::assistant().with_tool_request("req1", tool_call);
|
||||
assert!(message.is_tool_call());
|
||||
assert!(!message.is_tool_response());
|
||||
|
||||
let ids = message.get_tool_ids();
|
||||
assert_eq!(ids.len(), 1);
|
||||
assert!(ids.contains("req1"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user