Skip to main content

do_memory_mcp/server/tools/
episode_steps.rs

1//! Episode step logging tool for MCP server
2//!
3//! This module provides the tool for logging execution steps to track
4//! progress within an episode.
5
6use crate::server::MemoryMCPServer;
7use anyhow::{Result, anyhow};
8use do_memory_core::ExecutionStep;
9use serde_json::{Value, json};
10use tracing::debug;
11use tracing::info;
12use uuid::Uuid;
13
14impl MemoryMCPServer {
15    /// Add a step to an existing episode
16    ///
17    /// This tool allows logging execution steps to track progress
18    /// within an episode.
19    ///
20    /// # Arguments (from JSON)
21    ///
22    /// * `episode_id` - UUID of the episode
23    /// * `step_number` - Sequential step number
24    /// * `tool` - Name of the tool/component performing the action
25    /// * `action` - Description of the action taken
26    /// * `parameters` - Optional JSON object of parameters
27    /// * `result` - Optional result object with type and details
28    /// * `latency_ms` - Optional execution time in milliseconds
29    pub async fn add_episode_step_tool(&self, args: Value) -> Result<Value> {
30        debug!("Adding episode step with args: {}", args);
31
32        // Extract required fields
33        let episode_id_str = args
34            .get("episode_id")
35            .and_then(|v| v.as_str())
36            .ok_or_else(|| anyhow!("Missing required field: episode_id"))?;
37
38        let episode_id = Uuid::parse_str(episode_id_str)
39            .map_err(|e| anyhow!("Invalid episode_id format: {}", e))?;
40
41        let step_number = args
42            .get("step_number")
43            .and_then(|v| v.as_u64())
44            .ok_or_else(|| anyhow!("Missing required field: step_number"))?
45            as usize;
46
47        let tool = args
48            .get("tool")
49            .and_then(|v| v.as_str())
50            .ok_or_else(|| anyhow!("Missing required field: tool"))?
51            .to_string();
52
53        let action = args
54            .get("action")
55            .and_then(|v| v.as_str())
56            .ok_or_else(|| anyhow!("Missing required field: action"))?
57            .to_string();
58
59        // Create execution step
60        let mut step = ExecutionStep::new(step_number, tool.clone(), action.clone());
61
62        // Add optional parameters
63        if let Some(params) = args.get("parameters") {
64            step.parameters = params.clone();
65        }
66
67        // Add optional result
68        if let Some(result) = args.get("result") {
69            let result_type = result
70                .get("type")
71                .and_then(|v| v.as_str())
72                .unwrap_or("success");
73
74            step.result = Some(match result_type {
75                "success" => do_memory_core::ExecutionResult::Success {
76                    output: result
77                        .get("output")
78                        .and_then(|v| v.as_str())
79                        .unwrap_or("")
80                        .to_string(),
81                },
82                "error" => do_memory_core::ExecutionResult::Error {
83                    message: result
84                        .get("message")
85                        .and_then(|v| v.as_str())
86                        .unwrap_or("Unknown error")
87                        .to_string(),
88                },
89                "timeout" => do_memory_core::ExecutionResult::Timeout,
90                _ => {
91                    return Err(anyhow!(
92                        "Invalid result type: {}. Must be one of: success, error, timeout",
93                        result_type
94                    ));
95                }
96            });
97        }
98
99        // Add optional latency
100        if let Some(latency) = args.get("latency_ms").and_then(|v| v.as_u64()) {
101            step.latency_ms = latency;
102        }
103
104        // Log the step
105        self.memory.log_step(episode_id, step).await;
106
107        info!(
108            episode_id = %episode_id,
109            step_number = step_number,
110            tool = %tool,
111            "Added step to episode via MCP"
112        );
113
114        Ok(json!({
115            "success": true,
116            "episode_id": episode_id.to_string(),
117            "step_number": step_number,
118            "message": "Step added successfully"
119        }))
120    }
121}