cortex_mcp/tools/
health.rs1use std::sync::{Arc, Mutex};
11
12use serde_json::{json, Value};
13
14use crate::{GateId, ToolError, ToolHandler};
15
16#[derive(Debug)]
29pub struct CortexMemoryHealthTool {
30 pool: Arc<Mutex<cortex_store::Pool>>,
31}
32
33impl CortexMemoryHealthTool {
34 #[must_use]
36 pub fn new(pool: Arc<Mutex<cortex_store::Pool>>) -> Self {
37 Self { pool }
38 }
39}
40
41impl ToolHandler for CortexMemoryHealthTool {
42 fn name(&self) -> &'static str {
43 "cortex_memory_health"
44 }
45
46 fn gate_set(&self) -> &'static [GateId] {
47 &[GateId::HealthRead]
48 }
49
50 fn call(&self, _params: Value) -> Result<Value, ToolError> {
51 let pool = self
52 .pool
53 .lock()
54 .map_err(|err| ToolError::Internal(format!("failed to acquire store lock: {err}")))?;
55
56 let cutoff_dt = chrono::Utc::now() - chrono::Duration::days(30);
57 let cutoff_rfc = cutoff_dt.to_rfc3339();
58
59 let mut stmt = pool
61 .prepare(
62 "SELECT created_at, validation_epoch \
63 FROM memories WHERE status = 'active';",
64 )
65 .map_err(|err| ToolError::Internal(format!("failed to prepare health query: {err}")))?;
66
67 let rows = stmt
68 .query_map([], |row| {
69 Ok((row.get::<_, String>(0)?, row.get::<_, Option<i64>>(1)?))
70 })
71 .map_err(|err| {
72 ToolError::Internal(format!("failed to query active memories: {err}"))
73 })?;
74
75 let mut total: i64 = 0;
76 let mut stale: i64 = 0;
77 let mut unvalidated: i64 = 0;
78
79 for row_result in rows {
80 let (created_at, validation_epoch) = row_result
81 .map_err(|err| ToolError::Internal(format!("failed to read health row: {err}")))?;
82
83 total += 1;
84
85 if created_at.as_str() < cutoff_rfc.as_str() {
86 stale += 1;
87 }
88
89 if validation_epoch.unwrap_or(0) == 0 {
90 unvalidated += 1;
91 }
92 }
93
94 let quarantined: i64 = pool
96 .query_row(
97 "SELECT COUNT(*) FROM memories WHERE status = 'quarantined';",
98 [],
99 |row| row.get(0),
100 )
101 .map_err(|err| {
102 ToolError::Internal(format!("failed to query quarantined count: {err}"))
103 })?;
104
105 Ok(json!({
106 "total": total,
107 "stale": stale,
108 "unvalidated": unvalidated,
109 "quarantined": quarantined,
110 }))
111 }
112}