cortex_mcp/tools/
decay_status.rs1use std::sync::{Arc, Mutex};
10
11use cortex_store::repo::DecayJobRepo;
12use cortex_store::Pool;
13use serde_json::{json, Value};
14
15use crate::tool_handler::{GateId, ToolError, ToolHandler};
16
17const VALID_STATES: &[&str] = &["pending", "in_progress", "completed", "failed", "cancelled"];
19
20#[derive(Debug)]
32pub struct CortexDecayStatusTool {
33 pool: Arc<Mutex<Pool>>,
34}
35
36impl CortexDecayStatusTool {
37 #[must_use]
39 pub fn new(pool: Arc<Mutex<Pool>>) -> Self {
40 Self { pool }
41 }
42}
43
44impl ToolHandler for CortexDecayStatusTool {
45 fn name(&self) -> &'static str {
46 "cortex_decay_status"
47 }
48
49 fn gate_set(&self) -> &'static [GateId] {
50 &[GateId::HealthRead]
51 }
52
53 fn call(&self, params: Value) -> Result<Value, ToolError> {
54 let state_wire_owned: Option<String> = match params["state"].as_str() {
56 Some("") | None => None,
57 Some(s) => {
58 let wire = if s == "in-progress" { "in_progress" } else { s };
61 if !VALID_STATES.contains(&wire) {
62 return Err(ToolError::InvalidParams(format!(
63 "state must be one of {VALID_STATES:?}, got `{s}`"
64 )));
65 }
66 Some(wire.to_owned())
67 }
68 };
69
70 let pool = self
71 .pool
72 .lock()
73 .map_err(|err| ToolError::Internal(format!("pool lock poisoned: {err}")))?;
74 let repo = DecayJobRepo::new(&pool);
75
76 let mut jobs: Vec<Value> = Vec::new();
77 let states_to_query: Vec<&str> = match state_wire_owned.as_deref() {
78 Some(s) => vec![s],
79 None => VALID_STATES.to_vec(),
80 };
81 for wire in &states_to_query {
82 let records = repo.list_by_state(wire).map_err(|err| {
83 ToolError::Internal(format!("failed to read decay jobs (state={wire}): {err}"))
84 })?;
85 for record in records {
86 jobs.push(json!({
87 "id": record.id.to_string(),
88 "kind": record.kind_wire,
89 "state": record.state_wire,
90 "scheduled_for": record.scheduled_for.to_rfc3339(),
91 "created_at": record.created_at.to_rfc3339(),
92 }));
93 }
94 }
95
96 let total = jobs.len();
97 Ok(json!({ "jobs": jobs, "total": total }))
98 }
99}