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