do_memory_mcp/server/tools/
episode_timeline.rs1use crate::server::MemoryMCPServer;
7use anyhow::{Result, anyhow};
8use do_memory_core::{ExecutionResult, TaskOutcome};
9use serde_json::{Value, json};
10use tracing::debug;
11use uuid::Uuid;
12
13impl MemoryMCPServer {
14 pub async fn get_episode_timeline_tool(&self, args: Value) -> Result<Value> {
23 debug!("Getting episode timeline with args: {}", args);
24
25 let episode_id_str = args
26 .get("episode_id")
27 .and_then(|v| v.as_str())
28 .ok_or_else(|| anyhow!("Missing required field: episode_id"))?;
29
30 let episode_id = Uuid::parse_str(episode_id_str)
31 .map_err(|e| anyhow!("Invalid episode_id format: {}", e))?;
32
33 let episode = self
34 .memory
35 .get_episode(episode_id)
36 .await
37 .map_err(|e| anyhow!("Failed to get episode: {}", e))?;
38
39 let timeline: Vec<Value> = episode
41 .steps
42 .iter()
43 .map(|step| {
44 json!({
45 "step_number": step.step_number,
46 "timestamp": step.timestamp.to_rfc3339(),
47 "tool": step.tool,
48 "action": step.action,
49 "result_type": match &step.result {
50 Some(ExecutionResult::Success { .. }) => "success",
51 Some(ExecutionResult::Error { .. }) => "error",
52 Some(ExecutionResult::Timeout) => "timeout",
53 None => "pending",
54 },
55 "latency_ms": step.latency_ms,
56 })
57 })
58 .collect();
59
60 let duration_seconds = episode
61 .end_time
62 .map(|end| (end.timestamp() - episode.start_time.timestamp()) as f64);
63
64 Ok(json!({
65 "success": true,
66 "episode_id": episode_id.to_string(),
67 "task_description": episode.task_description,
68 "start_time": episode.start_time.to_rfc3339(),
69 "end_time": episode.end_time.map(|t| t.to_rfc3339()),
70 "duration_seconds": duration_seconds,
71 "step_count": episode.steps.len(),
72 "timeline": timeline,
73 "outcome": episode.outcome.as_ref().map(|o| match o {
74 TaskOutcome::Success { .. } => "success",
75 TaskOutcome::PartialSuccess { .. } => "partial_success",
76 TaskOutcome::Failure { .. } => "failure",
77 }),
78 }))
79 }
80}