use std::collections::HashMap;
use tower_mcp::{GetPromptResult, Prompt, PromptBuilder, PromptMessage, PromptRole};
pub fn build() -> Prompt {
PromptBuilder::new("evaluate_dependencies")
.description("Evaluate a project's dependencies for health, security, and maintenance")
.required_arg("crates", "Comma-separated list of dependency names")
.optional_arg("use_case", "Project context (e.g. 'production web service')")
.handler(|args: HashMap<String, String>| async move {
let crates = args.get("crates").map(|s| s.as_str()).unwrap_or("unknown");
let use_case = args.get("use_case");
let mut prompt = format!(
"Please evaluate the following Rust dependencies: {}\n\n\
For each crate, run `get_crate_health` to gather comprehensive data, then assess:\n\n\
1. **Staleness**: Flag crates with no recent releases or inactive maintenance\n\
2. **Security**: Identify any known vulnerabilities or audit concerns\n\
3. **Adoption**: Flag low-download or low-ecosystem-usage crates\n\
4. **MSRV**: Note crates missing a declared minimum supported Rust version\n\
5. **Alternatives**: Suggest better-maintained or more popular alternatives for any problematic dependencies\n\n\
After evaluating each dependency individually, provide a summary of overall dependency health \
and prioritized recommendations for action.",
crates
);
if let Some(uc) = use_case {
prompt.push_str(&format!(
"\n\nProject context: {}\n\
Please tailor your evaluation and recommendations to this use case.",
uc
));
}
Ok(GetPromptResult {
description: Some("Evaluate dependency health, security, and maintenance".to_string()),
messages: vec![PromptMessage {
role: PromptRole::User,
content: tower_mcp::protocol::Content::Text {
text: prompt,
annotations: None,
meta: None,
},
meta: None,
}],
meta: None,
})
})
.build()
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn required_args_appear_in_text() {
let prompt = build();
let mut args = HashMap::new();
args.insert("crates".to_string(), "serde, anyhow".to_string());
let result = prompt.get(args).await.unwrap();
let text = result.first_message_text().expect("expected message text");
assert!(text.contains("serde"));
assert!(text.contains("anyhow"));
}
#[tokio::test]
async fn optional_use_case_appears_in_text() {
let prompt = build();
let mut args = HashMap::new();
args.insert("crates".to_string(), "reqwest".to_string());
args.insert("use_case".to_string(), "production web service".to_string());
let result = prompt.get(args).await.unwrap();
let text = result.first_message_text().expect("expected message text");
assert!(text.contains("reqwest"));
assert!(text.contains("production web service"));
}
#[tokio::test]
async fn description_is_set() {
let prompt = build();
let mut args = HashMap::new();
args.insert("crates".to_string(), "serde".to_string());
let result = prompt.get(args).await.unwrap();
assert!(result.description.is_some());
}
}