use pmcp::server::workflow::{
dsl::{from_step, prompt_arg},
SequentialWorkflow, ToolHandle, WorkflowStep,
};
fn main() {
println!("=== Workflow Validation Error Messages ===\n");
println!("This example demonstrates common workflow errors and their messages.\n");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!("❌ Error Case 1: Unknown Binding");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
let workflow_unknown_binding = SequentialWorkflow::new(
"broken_workflow_1",
"Workflow with unknown binding reference",
)
.argument("topic", "The topic to write about", true)
.step(
WorkflowStep::new("create", ToolHandle::new("create_content"))
.arg("topic", prompt_arg("topic"))
.bind("content"), )
.step(
WorkflowStep::new("review", ToolHandle::new("review_content"))
.arg("text", from_step("draft")), );
println!("Problem:");
println!(" Step 'review' references binding 'draft' which doesn't exist.");
println!(" The previous step binds to 'content', not 'draft'.\n");
println!("Code:");
println!(" .step(");
println!(" WorkflowStep::new(\"create\", ToolHandle::new(\"create_content\"))");
println!(" .bind(\"content\") // ← Binds as 'content'");
println!(" )");
println!(" .step(");
println!(" WorkflowStep::new(\"review\", ToolHandle::new(\"review_content\"))");
println!(" .arg(\"text\", from_step(\"draft\")) // ← ERROR: 'draft' doesn't exist");
println!(" )\n");
match workflow_unknown_binding.validate() {
Ok(_) => println!(" ✓ Validation passed (unexpected!)"),
Err(e) => {
println!("Error Message:");
println!(" {}\n", e);
println!("Fix:");
println!(" Change from_step(\"draft\") to from_step(\"content\")");
println!(" OR change .bind(\"content\") to .bind(\"draft\")\n");
},
}
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!("❌ Error Case 2: Undefined Prompt Argument");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
let workflow_undefined_arg = SequentialWorkflow::new(
"broken_workflow_2",
"Workflow with undefined prompt argument",
)
.argument("topic", "The topic to write about", true)
.step(
WorkflowStep::new("create", ToolHandle::new("create_content"))
.arg("topic", prompt_arg("topic"))
.arg("style", prompt_arg("writing_style")) .bind("content"),
);
println!("Problem:");
println!(" Step 'create' uses prompt_arg(\"writing_style\") but the workflow");
println!(" only defines argument 'topic'. 'writing_style' was never declared.\n");
println!("Code:");
println!(" SequentialWorkflow::new(...)");
println!(" .argument(\"topic\", \"The topic to write about\", true)");
println!(" // ← Missing: .argument(\"writing_style\", ..., ...)");
println!(" .step(");
println!(" WorkflowStep::new(\"create\", ...)");
println!(" .arg(\"style\", prompt_arg(\"writing_style\")) // ← ERROR");
println!(" )\n");
match workflow_undefined_arg.validate() {
Ok(_) => println!(" ✓ Validation passed (unexpected!)"),
Err(e) => {
println!("Error Message:");
println!(" {}\n", e);
println!("Fix:");
println!(" Add .argument(\"writing_style\", \"Writing style\", false)");
println!(" before the .step() call.\n");
},
}
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!("❌ Error Case 3: Step Without .bind() Cannot Be Referenced");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
let workflow_no_binding = SequentialWorkflow::new(
"broken_workflow_3",
"Workflow with step that has no binding",
)
.argument("topic", "The topic", true)
.step(
WorkflowStep::new("create", ToolHandle::new("create_content"))
.arg("topic", prompt_arg("topic")),
)
.step(
WorkflowStep::new("review", ToolHandle::new("review_content"))
.arg("text", from_step("create")), );
println!("Problem:");
println!(" Step 'create' doesn't have a .bind() call, so its output");
println!(" cannot be referenced by later steps. Step 'review' tries to");
println!(" reference it anyway.\n");
println!("Code:");
println!(" .step(");
println!(" WorkflowStep::new(\"create\", ToolHandle::new(\"create_content\"))");
println!(" .arg(\"topic\", prompt_arg(\"topic\"))");
println!(" // ← Missing: .bind(\"content\")");
println!(" )");
println!(" .step(");
println!(" WorkflowStep::new(\"review\", ToolHandle::new(\"review_content\"))");
println!(" .arg(\"text\", from_step(\"create\")) // ← ERROR");
println!(" )\n");
match workflow_no_binding.validate() {
Ok(_) => println!(" ✓ Validation passed (unexpected!)"),
Err(e) => {
println!("Error Message:");
println!(" {}\n", e);
println!("Fix:");
println!(" Add .bind(\"content\") to the first step:");
println!(" WorkflowStep::new(\"create\", ...)");
println!(" .arg(\"topic\", prompt_arg(\"topic\"))");
println!(" .bind(\"content\") // ← Add this");
println!();
println!(" Then update the reference:");
println!(" .arg(\"text\", from_step(\"content\")) // ← Use binding name\n");
},
}
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!("❌ Bonus: Workflow with Multiple Errors");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
let workflow_multiple_errors = SequentialWorkflow::new(
"broken_workflow_4",
"Workflow with multiple validation errors",
)
.argument("topic", "The topic", true)
.step(
WorkflowStep::new("create", ToolHandle::new("create_content"))
.arg("topic", prompt_arg("topic"))
.arg("style", prompt_arg("writing_style")) .bind("content"),
)
.step(
WorkflowStep::new("enhance", ToolHandle::new("enhance_content"))
.arg("text", from_step("content")),
)
.step(
WorkflowStep::new("review", ToolHandle::new("review_content"))
.arg("text", from_step("enhanced")), );
println!("Problem:");
println!(" This workflow has THREE errors:");
println!(" 1. References undefined prompt argument 'writing_style'");
println!(" 2. Step 'enhance' has no .bind() but is referenced later");
println!(" 3. Step 'review' references 'enhanced' instead of 'content'\n");
match workflow_multiple_errors.validate() {
Ok(_) => println!(" ✓ Validation passed (unexpected!)"),
Err(e) => {
println!("Error Message:");
println!(" {}\n", e);
println!("Note: Validation stops at the first error encountered.");
println!("Fix errors one at a time and re-validate.\n");
},
}
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!("✅ Success Case: Properly Validated Workflow");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
let workflow_correct = SequentialWorkflow::new(
"correct_workflow",
"A properly constructed workflow",
)
.argument("topic", "The topic to write about", true)
.argument("style", "Writing style", false) .step(
WorkflowStep::new("create", ToolHandle::new("create_content"))
.arg("topic", prompt_arg("topic"))
.arg("style", prompt_arg("style"))
.bind("content"), )
.step(
WorkflowStep::new("review", ToolHandle::new("review_content"))
.arg("text", from_step("content")) .bind("review_result"), )
.step(
WorkflowStep::new("publish", ToolHandle::new("publish_content"))
.arg("content", from_step("content")) .arg("review", from_step("review_result")), );
println!("Code:");
println!(" SequentialWorkflow::new(...)");
println!(" .argument(\"topic\", \"The topic to write about\", true)");
println!(" .argument(\"style\", \"Writing style\", false)");
println!(" .step(");
println!(" WorkflowStep::new(\"create\", ToolHandle::new(\"create_content\"))");
println!(" .arg(\"topic\", prompt_arg(\"topic\"))");
println!(" .arg(\"style\", prompt_arg(\"style\"))");
println!(" .bind(\"content\")");
println!(" )");
println!(" .step(");
println!(" WorkflowStep::new(\"review\", ToolHandle::new(\"review_content\"))");
println!(" .arg(\"text\", from_step(\"content\"))");
println!(" .bind(\"review_result\")");
println!(" )");
println!(" .step(");
println!(" WorkflowStep::new(\"publish\", ToolHandle::new(\"publish_content\"))");
println!(" .arg(\"content\", from_step(\"content\"))");
println!(" .arg(\"review\", from_step(\"review_result\"))");
println!(" )\n");
match workflow_correct.validate() {
Ok(_) => {
println!("✅ Validation passed!\n");
println!(
"Output bindings: {:?}\n",
workflow_correct.output_bindings()
);
},
Err(e) => println!(" ✗ Validation failed: {}\n", e),
}
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!("📋 Summary: Common Workflow Validation Errors");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
println!("1. UnknownBinding:");
println!(" - Cause: from_step(\"X\") where no step has .bind(\"X\")");
println!(" - Fix: Ensure binding name matches .bind() call\n");
println!("2. InvalidMapping (undefined prompt arg):");
println!(" - Cause: prompt_arg(\"X\") where no .argument(\"X\", ...) exists");
println!(" - Fix: Add .argument(\"X\", description, required) to workflow\n");
println!("3. UnknownBinding (step without .bind()):");
println!(" - Cause: Referencing a step that has no .bind() call");
println!(" - Fix: Add .bind(\"name\") to the step being referenced\n");
println!("💡 Best Practices:");
println!(" - Always call .bind() on steps whose output will be used");
println!(" - Use descriptive binding names (not step names)");
println!(" - Declare all prompt arguments before using them");
println!(" - Call .validate() early to catch errors before runtime");
println!(" - Fix errors one at a time - validation stops at first error\n");
println!("✨ Error messages are designed to be actionable:");
println!(" - They tell you exactly which step/binding/argument is the problem");
println!(" - They include context to help you locate the issue");
println!(" - They suggest the type of fix needed\n");
}