Refactor: Migrate unwrap calls to AppError in MCP handlers

This commit is contained in:
Riz Ashraf committed 2026-09-30 21:02:50 +01:00
1 parent 4e1a633dbd
commit 0e866f2465
12 files changed
+276 -326

No files matched your search

+32 -43
View File
@@ -18,7 +18,7 @@ impl McpTool for AddTaskHandler {
crate::mcp::tool_def::<AddTaskTool>("add_task", "Execute add_task")
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: AddTaskTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let now = crate::handlers::utils::now_secs();
let task_id = uuid::Uuid::new_v4().to_string();
@@ -59,7 +59,7 @@ impl McpTool for DeleteTaskHandler {
crate::mcp::tool_def::<DeleteTaskTool>("delete_task", "Execute delete_task")
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: DeleteTaskTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut deleted_count = 0;
let mut actually_deleted = Vec::new();
@@ -114,7 +114,7 @@ impl McpTool for DeleteTaskHandler {
][0]
.clone())
} else {
Err("Task not found. Please use the list_active_tasks tool to verify the correct task ID.".to_string())
Err(crate::error::AppError::Internal("Task not found. Please use the list_active_tasks tool to verify the correct task ID.".to_string()))
}
}
}
@@ -134,7 +134,7 @@ impl McpTool for UpdateTaskStatusHandler {
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: UpdateTaskStatusTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut found = false;
let mut blocked = false;
@@ -250,14 +250,14 @@ impl McpTool for UpdateTaskStatusHandler {
});
if blocked {
Err(format!(
Err(crate::error::AppError::Internal(format!(
"Error: Cannot transition task. {}",
blocker_details
))
)))
} else if found {
Ok("Task status updated.".to_string())
} else {
Err("Task not found. Please use the list_active_tasks tool to verify the correct task ID.".to_string())
Err(crate::error::AppError::Internal("Task not found. Please use the list_active_tasks tool to verify the correct task ID.".to_string()))
}
}
}
@@ -277,7 +277,7 @@ impl McpTool for ListActiveTasksHandler {
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ListActiveTasksTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.tasks.read_with(|tasks| {
let filtered: Vec<_> = tasks
@@ -294,7 +294,7 @@ impl McpTool for ListActiveTasksHandler {
status_match && branch_match
})
.collect();
serde_json::to_string(&filtered).map_err(|e| e.to_string())
Ok::<String, crate::error::AppError>(serde_json::to_string(&filtered)?)
})?;
Ok(data)
}
@@ -315,7 +315,7 @@ impl McpTool for SetAcceptanceCriteriaHandler {
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: SetAcceptanceCriteriaTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut success = false;
@@ -337,7 +337,7 @@ impl McpTool for SetAcceptanceCriteriaHandler {
if success {
Ok("Acceptance criteria set successfully.".to_string())
} else {
Err("Task not found. Please use the list_active_tasks tool to verify the correct task ID.".to_string())
Err(crate::error::AppError::Internal("Task not found. Please use the list_active_tasks tool to verify the correct task ID.".to_string()))
}
}
}
@@ -357,7 +357,7 @@ impl McpTool for VerifyAcceptanceCriteriaHandler {
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: VerifyAcceptanceCriteriaTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut success = false;
@@ -386,7 +386,7 @@ impl McpTool for VerifyAcceptanceCriteriaHandler {
} else if already_met {
Ok("Acceptance criteria was already met.".to_string())
} else {
Err("Acceptance criteria or task not found.".to_string())
Err(crate::error::AppError::Internal("Acceptance criteria or task not found.".to_string()))
}
}
}
@@ -403,7 +403,7 @@ impl McpTool for AddMilestoneHandler {
crate::mcp::tool_def::<AddMilestoneTool>("add_milestone", "Execute add_milestone")
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: AddMilestoneTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.milestones.modify(|ms| {
ms.push(crate::models::Milestone {
@@ -430,7 +430,7 @@ impl McpTool for UpdateMilestoneHandler {
crate::mcp::tool_def::<UpdateMilestoneTool>("update_milestone", "Execute update_milestone")
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: UpdateMilestoneTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut found = false;
state.milestones.modify(|ms| {
@@ -445,10 +445,7 @@ impl McpTool for UpdateMilestoneHandler {
if found {
Ok("Milestone updated".to_string())
} else {
Err(
"Milestone not found. Please verify the milestone ID using list_milestones."
.to_string(),
)
Err(crate::error::AppError::Internal("Milestone not found. Please verify the milestone ID using list_milestones.".to_string()))
}
}
}
@@ -465,7 +462,7 @@ impl McpTool for ListMilestonesHandler {
crate::mcp::tool_def::<ListMilestonesTool>("list_milestones", "Execute list_milestones")
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ListMilestonesTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.milestones.read_with(|items| {
let filtered: Vec<_> = items
@@ -478,7 +475,7 @@ impl McpTool for ListMilestonesHandler {
}
})
.collect();
serde_json::to_string(&filtered).map_err(|e| e.to_string())
Ok::<String, crate::error::AppError>(serde_json::to_string(&filtered)?)
})?;
Ok(data)
}
@@ -502,14 +499,13 @@ mod tests {
"acceptance_criteria": ["Stop the noise", "Reach lightspeed"],
});
let res = add_handler.execute(args, state.clone()).await.unwrap();
let res = add_handler.execute(args, state.clone()).await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
assert!(res.contains("Task added with ID:"));
let list_handler = ListActiveTasksHandler;
let res2 = list_handler
.execute(json!({}), state.clone())
.await
.unwrap();
.await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
assert!(res2.contains("Fix the hyperdrive"));
}
@@ -524,8 +520,7 @@ mod tests {
json!({"title": "Test", "description": "test"}),
state.clone(),
)
.await
.unwrap();
.await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
let id_start = res.find("ID: ").unwrap() + 4;
let task_id = res[id_start..].trim();
@@ -535,14 +530,13 @@ mod tests {
"id": task_id,
"status": "done"
});
let res3 = update_handler.execute(args, state.clone()).await.unwrap();
let res3 = update_handler.execute(args, state.clone()).await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
assert_eq!(res3, "Task status updated.");
let list_handler = ListActiveTasksHandler;
let res4 = list_handler
.execute(json!({}), state.clone())
.await
.unwrap();
.await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
assert!(!res4.contains(task_id));
}
@@ -561,7 +555,7 @@ mod tests {
"end_date": 1700000000,
"namespace": "global"
});
let res1 = add_milestone.execute(args_ms, state.clone()).await.unwrap();
let res1 = add_milestone.execute(args_ms, state.clone()).await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
assert!(res1.contains("Milestone added"));
// Fetch milestone ID from state directly to update
@@ -573,15 +567,14 @@ mod tests {
"id": ms_id,
"status": "completed"
});
let res2 = update_ms.execute(args_ums, state.clone()).await.unwrap();
let res2 = update_ms.execute(args_ums, state.clone()).await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
assert_eq!(res2, "Milestone updated");
// List Milestones
let list_ms = ListMilestonesHandler;
let res3 = list_ms
.execute(json!({"namespace": "global"}), state.clone())
.await
.unwrap();
.await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
assert!(res3.contains("completed"));
assert!(res3.contains("Release 1.0"));
@@ -592,8 +585,7 @@ mod tests {
json!({"title": "Test", "description": "desc"}),
state.clone(),
)
.await
.unwrap();
.await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
let task_id = res_task[res_task.find("ID: ").unwrap() + 4..].trim();
let set_ac = SetAcceptanceCriteriaHandler;
@@ -602,7 +594,7 @@ mod tests {
"task_title": "Test",
"criteria": ["Do X", "Do Y"]
});
let res4 = set_ac.execute(args_ac, state.clone()).await.unwrap();
let res4 = set_ac.execute(args_ac, state.clone()).await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
assert_eq!(res4, "Acceptance criteria set successfully.");
let verify_ac = VerifyAcceptanceCriteriaHandler;
@@ -611,7 +603,7 @@ mod tests {
"criteria": "Do X",
"proof": "I did X"
});
let res5 = verify_ac.execute(args_vac, state.clone()).await.unwrap();
let res5 = verify_ac.execute(args_vac, state.clone()).await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
assert!(res5.contains("Acceptance criteria verified"));
}
@@ -626,8 +618,7 @@ mod tests {
json!({"title": "Parent", "description": "p"}),
state.clone(),
)
.await
.unwrap();
.await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
let parent_id = parent[parent.find("ID: ").unwrap() + 4..]
.trim()
.to_string();
@@ -637,15 +628,13 @@ mod tests {
json!({"title": "Child", "description": "c", "parent_id": parent_id}),
state.clone(),
)
.await
.unwrap();
.await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
let _child_id = child[child.find("ID: ").unwrap() + 4..].trim().to_string();
let del_task = DeleteTaskHandler;
let res_del = del_task
.execute(json!({"id": parent_id}), state.clone())
.await
.unwrap();
.await.map_err(|e| crate::error::AppError::Internal(e.to_string()))?;
assert!(res_del.contains("Deleted task and its children (2 total)."));
}
}