use async_trait::async_trait;
use crate::error::Result;
use super::{Prompt, PromptArgument, PromptDefinition};
#[async_trait]
pub trait McpPrompt: Send + Sync + std::fmt::Debug {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn arguments(&self) -> Vec<PromptArgument>;
async fn execute(&self, arguments: serde_json::Value) -> Result<Prompt>;
fn to_definition(&self) -> PromptDefinition {
let args = self.arguments();
PromptDefinition {
name: self.name().to_string(),
description: self.description().to_string(),
arguments: if args.is_empty() { None } else { Some(args) },
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Result;
use async_trait::async_trait;
#[derive(Debug)]
struct DummyPrompt;
#[async_trait]
impl McpPrompt for DummyPrompt {
fn name(&self) -> &str {
"dummy"
}
fn description(&self) -> &str {
"A dummy prompt"
}
fn arguments(&self) -> Vec<PromptArgument> {
vec![PromptArgument {
name: "input".to_string(),
description: None,
required: Some(true),
}]
}
async fn execute(&self, _arguments: serde_json::Value) -> Result<Prompt> {
Ok(Prompt::user("dummy output"))
}
}
#[test]
fn test_to_definition_default_impl() {
let prompt = DummyPrompt;
let def = prompt.to_definition();
assert_eq!(def.name, "dummy");
assert_eq!(def.description, "A dummy prompt");
let args = def.arguments.unwrap();
assert_eq!(args.len(), 1);
assert_eq!(args[0].name, "input");
assert_eq!(args[0].required, Some(true));
}
#[test]
fn test_to_definition_no_arguments_returns_none() {
#[derive(Debug)]
struct NoArgPrompt;
#[async_trait]
impl McpPrompt for NoArgPrompt {
fn name(&self) -> &str {
"no_args"
}
fn description(&self) -> &str {
"No args"
}
fn arguments(&self) -> Vec<PromptArgument> {
vec![]
}
async fn execute(&self, _: serde_json::Value) -> Result<Prompt> {
Ok(Prompt::user("ok"))
}
}
let def = NoArgPrompt.to_definition();
assert!(def.arguments.is_none());
}
}