do_memory_mcp/server/tools/
episode_complete.rs1use crate::server::MemoryMCPServer;
7use anyhow::{Result, anyhow};
8use do_memory_core::TaskOutcome;
9use serde_json::{Value, json};
10use tracing::debug;
11use tracing::info;
12use uuid::Uuid;
13
14impl MemoryMCPServer {
15 pub async fn complete_episode_tool(&self, args: Value) -> Result<Value> {
32 debug!("Completing episode with args: {}", args);
33
34 let episode_id_str = args
36 .get("episode_id")
37 .and_then(|v| v.as_str())
38 .ok_or_else(|| anyhow!("Missing required field: episode_id"))?;
39
40 let episode_id = Uuid::parse_str(episode_id_str)
41 .map_err(|e| anyhow!("Invalid episode_id format: {}", e))?;
42
43 let outcome_type = args
44 .get("outcome_type")
45 .and_then(|v| v.as_str())
46 .ok_or_else(|| anyhow!("Missing required field: outcome_type"))?;
47
48 let outcome = match outcome_type {
50 "success" => {
51 let verdict = args
52 .get("verdict")
53 .and_then(|v| v.as_str())
54 .ok_or_else(|| anyhow!("Missing required field for success: verdict"))?
55 .to_string();
56
57 let artifacts = args
58 .get("artifacts")
59 .and_then(|v| v.as_array())
60 .map(|arr| {
61 arr.iter()
62 .filter_map(|v| v.as_str().map(|s| s.to_string()))
63 .collect()
64 })
65 .unwrap_or_default();
66
67 TaskOutcome::Success { verdict, artifacts }
68 }
69 "partial_success" => {
70 let verdict = args
71 .get("verdict")
72 .and_then(|v| v.as_str())
73 .ok_or_else(|| anyhow!("Missing required field for partial_success: verdict"))?
74 .to_string();
75
76 let completed = args
77 .get("completed")
78 .and_then(|v| v.as_array())
79 .ok_or_else(|| {
80 anyhow!("Missing required field for partial_success: completed")
81 })?
82 .iter()
83 .filter_map(|v| v.as_str().map(|s| s.to_string()))
84 .collect();
85
86 let failed = args
87 .get("failed")
88 .and_then(|v| v.as_array())
89 .ok_or_else(|| anyhow!("Missing required field for partial_success: failed"))?
90 .iter()
91 .filter_map(|v| v.as_str().map(|s| s.to_string()))
92 .collect();
93
94 TaskOutcome::PartialSuccess {
95 verdict,
96 completed,
97 failed,
98 }
99 }
100 "failure" => {
101 let reason = args
102 .get("reason")
103 .and_then(|v| v.as_str())
104 .ok_or_else(|| anyhow!("Missing required field for failure: reason"))?
105 .to_string();
106
107 let error_details = args
108 .get("error_details")
109 .and_then(|v| v.as_str())
110 .map(|s| s.to_string());
111
112 TaskOutcome::Failure {
113 reason,
114 error_details,
115 }
116 }
117 _ => {
118 return Err(anyhow!(
119 "Invalid outcome_type: {}. Must be one of: success, partial_success, failure",
120 outcome_type
121 ));
122 }
123 };
124
125 self.memory
127 .complete_episode(episode_id, outcome.clone())
128 .await
129 .map_err(|e| anyhow!("Failed to complete episode: {}", e))?;
130
131 info!(
132 episode_id = %episode_id,
133 outcome_type = %outcome_type,
134 "Completed episode via MCP"
135 );
136
137 Ok(json!({
138 "success": true,
139 "episode_id": episode_id.to_string(),
140 "outcome_type": outcome_type,
141 "message": "Episode completed successfully. Learning cycle triggered (reward, reflection, patterns)."
142 }))
143 }
144}