Skip to main content

agentic_memory_mcp/tools/
memory_temporal.rs

1//! Tool: memory_temporal — Compare knowledge across time periods.
2
3use std::sync::Arc;
4use tokio::sync::Mutex;
5
6use serde::Deserialize;
7use serde_json::{json, Value};
8
9use agentic_memory::{TemporalParams, TimeRange};
10
11use crate::session::SessionManager;
12use crate::types::{McpError, McpResult, ToolCallResult, ToolDefinition};
13
14#[derive(Debug, Deserialize)]
15struct TemporalInputParams {
16    range_a: RangeSpec,
17    range_b: RangeSpec,
18}
19
20#[derive(Debug, Deserialize)]
21#[serde(tag = "type")]
22enum RangeSpec {
23    #[serde(rename = "time_window")]
24    TimeWindow { start: u64, end: u64 },
25    #[serde(rename = "session")]
26    Session { session_id: u32 },
27    #[serde(rename = "sessions")]
28    Sessions { session_ids: Vec<u32> },
29}
30
31impl RangeSpec {
32    fn to_time_range(&self) -> TimeRange {
33        match self {
34            RangeSpec::TimeWindow { start, end } => TimeRange::TimeWindow {
35                start: *start,
36                end: *end,
37            },
38            RangeSpec::Session { session_id } => TimeRange::Session(*session_id),
39            RangeSpec::Sessions { session_ids } => TimeRange::Sessions(session_ids.clone()),
40        }
41    }
42}
43
44/// Return the tool definition for memory_temporal.
45pub fn definition() -> ToolDefinition {
46    ToolDefinition {
47        name: "memory_temporal".to_string(),
48        description: Some("Compare knowledge across two time periods".to_string()),
49        input_schema: json!({
50            "type": "object",
51            "properties": {
52                "range_a": {
53                    "oneOf": [
54                        { "type": "object", "properties": { "type": { "const": "time_window" }, "start": { "type": "integer" }, "end": { "type": "integer" } } },
55                        { "type": "object", "properties": { "type": { "const": "session" }, "session_id": { "type": "integer" } } },
56                        { "type": "object", "properties": { "type": { "const": "sessions" }, "session_ids": { "type": "array" } } }
57                    ]
58                },
59                "range_b": { "description": "Same structure as range_a" }
60            },
61            "required": ["range_a", "range_b"]
62        }),
63    }
64}
65
66/// Execute the memory_temporal tool.
67pub async fn execute(
68    args: Value,
69    session: &Arc<Mutex<SessionManager>>,
70) -> McpResult<ToolCallResult> {
71    let params: TemporalInputParams =
72        serde_json::from_value(args).map_err(|e| McpError::InvalidParams(e.to_string()))?;
73
74    let temporal_params = TemporalParams {
75        range_a: params.range_a.to_time_range(),
76        range_b: params.range_b.to_time_range(),
77    };
78
79    let session = session.lock().await;
80
81    let result = session
82        .query_engine()
83        .temporal(session.graph(), temporal_params)
84        .map_err(|e| McpError::AgenticMemory(format!("Temporal comparison failed: {e}")))?;
85
86    Ok(ToolCallResult::json(&json!({
87        "added": result.added,
88        "corrected": result.corrected,
89        "unchanged": result.unchanged,
90        "potentially_stale": result.potentially_stale,
91        "summary": {
92            "added_count": result.added.len(),
93            "corrected_count": result.corrected.len(),
94            "unchanged_count": result.unchanged.len(),
95            "stale_count": result.potentially_stale.len(),
96        }
97    })))
98}