task_graph_mcp/tools/
gates.rs1use super::{get_string, make_tool_with_prompts};
6use crate::config::Prompts;
7use crate::config::workflows::WorkflowsConfig;
8use crate::db::Database;
9use crate::error::ToolError;
10use crate::gates::{GateResult, evaluate_gates};
11use anyhow::Result;
12use rmcp::model::Tool;
13use serde_json::{Value, json};
14
15pub fn get_tools(prompts: &Prompts) -> Vec<Tool> {
17 vec![make_tool_with_prompts(
18 "check_gates",
19 "Check gate requirements for a task before attempting a status/phase transition. Returns unsatisfied gates with overall status (pass/warn/fail).",
20 json!({
21 "task": {
22 "type": "string",
23 "description": "Task ID to check gates for"
24 }
25 }),
26 vec!["task"],
27 prompts,
28 )]
29}
30
31pub fn check_gates(db: &Database, workflows: &WorkflowsConfig, args: Value) -> Result<Value> {
56 let task_id = get_string(&args, "task").ok_or_else(|| ToolError::missing_field("task"))?;
57
58 let task = db
60 .get_task(&task_id)?
61 .ok_or_else(|| ToolError::new(crate::error::ErrorCode::TaskNotFound, "Task not found"))?;
62
63 let mut all_gates = Vec::new();
65
66 let status_gates = workflows.get_status_exit_gates(&task.status);
68 all_gates.extend(status_gates.into_iter().cloned());
69
70 if let Some(ref phase) = task.phase {
72 let phase_gates = workflows.get_phase_exit_gates(phase);
73 all_gates.extend(phase_gates.into_iter().cloned());
74 }
75
76 let result = evaluate_gates(db, &task_id, &all_gates)?;
78
79 let gates: Vec<Value> = result
81 .unsatisfied_gates
82 .iter()
83 .map(gate_result_to_json)
84 .collect();
85
86 Ok(json!({
87 "status": result.status,
88 "gates": gates
89 }))
90}
91
92fn gate_result_to_json(gate: &GateResult) -> Value {
94 json!({
95 "type": gate.gate_type,
96 "enforcement": gate.enforcement,
97 "description": gate.description,
98 "satisfied": gate.satisfied
99 })
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn test_gate_result_to_json() {
108 use crate::config::GateEnforcement;
109
110 let gate = GateResult {
111 gate_type: "gate/tests".to_string(),
112 enforcement: GateEnforcement::Reject,
113 description: "Attach test results".to_string(),
114 satisfied: false,
115 };
116
117 let json = gate_result_to_json(&gate);
118 assert_eq!(json["type"], "gate/tests");
119 assert_eq!(json["enforcement"], "reject");
120 assert_eq!(json["description"], "Attach test results");
121 assert_eq!(json["satisfied"], false);
122 }
123}