use async_trait::async_trait;
use pmcp::error::Result;
use pmcp::server::cancellation::RequestHandlerExtra;
use pmcp::server::{PromptHandler, Server};
use pmcp::types::{
Content, GetPromptRequest, GetPromptResult, ProgressToken, PromptMessage, RequestMeta,
};
use std::collections::HashMap;
use std::time::Duration;
struct AnalysisWorkflowPrompt;
#[async_trait]
impl PromptHandler for AnalysisWorkflowPrompt {
async fn handle(
&self,
args: HashMap<String, String>,
extra: RequestHandlerExtra,
) -> Result<GetPromptResult> {
let topic = args
.get("topic")
.cloned()
.unwrap_or_else(|| "general analysis".to_string());
tracing::info!("Starting analysis workflow for topic: {}", topic);
let steps = [
("gather", "Gathering information and context"),
("analyze", "Analyzing data and patterns"),
("synthesize", "Synthesizing insights"),
("validate", "Validating conclusions"),
("format", "Formatting final report"),
];
let mut results = Vec::new();
for (i, (step_name, step_description)) in steps.iter().enumerate() {
if extra.is_cancelled() {
tracing::warn!("Workflow cancelled at step: {}", step_name);
return Err(pmcp::error::Error::internal(format!(
"Analysis workflow cancelled during {} step",
step_name
)));
}
extra
.report_count(
i + 1,
steps.len(),
Some(format!(
"Step {}/{}: {}",
i + 1,
steps.len(),
step_description
)),
)
.await?;
tracing::info!(
"Executing step {}/{}: {} - {}",
i + 1,
steps.len(),
step_name,
step_description
);
tokio::time::sleep(Duration::from_secs(1)).await;
results.push(format!(
"✓ {} - {} (completed)",
step_name, step_description
));
}
let workflow_summary = results.join("\n");
Ok(GetPromptResult::new(
vec![PromptMessage::user(Content::text(format!(
"Analysis Workflow Complete\n\nTopic: {}\n\nWorkflow Steps:\n{}\n\nAll {} steps completed successfully. Ready for review.",
topic, workflow_summary, steps.len()
)))],
Some(format!(
"Multi-step analysis workflow for: {}",
topic
)),
))
}
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_target(false)
.with_level(true)
.init();
println!("=== Prompt Workflow Progress Example ===\n");
let _server = Server::builder()
.name("workflow-server")
.version("1.0.0")
.prompt("analysis_workflow", AnalysisWorkflowPrompt)
.build()?;
println!("Server created with 'analysis_workflow' prompt");
println!("Prompt arguments:");
println!(" topic (optional) - Topic to analyze (default: 'general analysis')\n");
println!("--- Example 1: Complete workflow with progress tracking ---\n");
let request = GetPromptRequest {
name: "analysis_workflow".to_string(),
arguments: HashMap::from([("topic".to_string(), "Machine Learning".to_string())]),
_meta: Some(
RequestMeta::new().with_progress_token(ProgressToken::String("workflow-1".to_string())),
),
};
println!("Executing workflow with progress token 'workflow-1'...\n");
let prompt = AnalysisWorkflowPrompt;
let extra = RequestHandlerExtra::new(
"test-request-1".to_string(),
tokio_util::sync::CancellationToken::new(),
);
let result = prompt.handle(request.arguments, extra).await?;
println!("\n✅ Workflow completed!");
println!("Description: {}", result.description.unwrap_or_default());
println!("\nMessages generated: {}", result.messages.len());
println!("\n--- Example 2: Workflow with mid-execution cancellation ---\n");
let request = GetPromptRequest {
name: "analysis_workflow".to_string(),
arguments: HashMap::from([("topic".to_string(), "Data Science".to_string())]),
_meta: Some(
RequestMeta::new().with_progress_token(ProgressToken::String("workflow-2".to_string())),
),
};
println!("Executing workflow with cancellation after 2.5 seconds...\n");
let cancellation_token = tokio_util::sync::CancellationToken::new();
let extra = RequestHandlerExtra::new("test-request-2".to_string(), cancellation_token.clone());
let cancel_handle = tokio::spawn({
let token = cancellation_token.clone();
async move {
tokio::time::sleep(Duration::from_millis(2500)).await;
println!("\n🛑 Cancelling workflow...\n");
token.cancel();
}
});
let result = prompt.handle(request.arguments, extra).await;
match result {
Ok(v) => println!("Unexpected success: {}", v.description.unwrap_or_default()),
Err(e) => println!("❌ Workflow cancelled as expected: {}\n", e),
}
cancel_handle.await.unwrap();
println!("--- Best Practices Demonstrated ---\n");
println!("1. ✅ Multi-step workflow with clear progress tracking");
println!("2. ✅ Progress reported at each workflow step");
println!("3. ✅ Meaningful status messages for each step");
println!("4. ✅ Cancellation checked between steps");
println!("5. ✅ Same API as tools - extra.report_count()");
println!("6. ✅ Automatic progress notification handling\n");
println!("--- When to Use Progress in Prompts ---\n");
println!("✓ Multi-step workflows (analysis → planning → execution)");
println!("✓ Long-running data processing or generation");
println!("✓ Prompts with multiple external API calls");
println!("✓ Complex reasoning chains with distinct phases");
println!("✗ Simple single-step prompts (no progress needed)\n");
println!("=== Example Complete ===");
Ok(())
}