refactor: drop Manage prefix from consolidated smart MCP tools

This commit is contained in:
Riz Ashraf committed 2026-10-05 11:11:41 +01:00
1 parent e45db33297
commit 961abb01e7
8 files changed
+914 -100

No files matched your search

+68 -94
View File
@@ -7,23 +7,23 @@ use serde_json::Value;
use std::collections::HashSet;
use std::sync::Arc;
pub struct ManageStickyNotesHandler;
pub struct StickyNotesHandler;
#[async_trait]
impl McpTool for ManageStickyNotesHandler {
impl McpTool for StickyNotesHandler {
fn name(&self) -> &'static str {
"manage_sticky_notes"
"sticky_notes"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ManageStickyNotesTool>(
"manage_sticky_notes",
"Manage ephemeral sticky notes with TTL (add, read, delete, clear)",
crate::mcp::tool_def::<StickyNotesTool>(
"sticky_notes",
"Ephemeral sticky notes with TTL (add, read, delete, clear)",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ManageStickyNotesTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let req: StickyNotesTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
match req.action {
StickyNoteAction::Add => {
let content = req.content.ok_or_else(|| {
@@ -216,97 +216,72 @@ impl McpTool for ClearStickyNotesHandler {
}
}
pub struct LeaveHandoffMemoHandler;
pub struct HandoffMemosHandler;
#[async_trait]
impl McpTool for LeaveHandoffMemoHandler {
impl McpTool for HandoffMemosHandler {
fn name(&self) -> &'static str {
"leave_handoff_memo"
"handoff_memos"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<LeaveHandoffMemoTool>(
"leave_handoff_memo",
"Execute leave_handoff_memo",
crate::mcp::tool_def::<HandoffMemosTool>(
"handoff_memos",
"Handoff memos across sessions (leave, read, clear)",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: LeaveHandoffMemoTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.telemetry.handoff_memos.modify(|memos| {
memos.push(crate::models::HandoffMemo {
id: uuid::Uuid::new_v4().to_string(),
author: "agy".to_string(),
content: req.content,
namespace: req.namespace,
timestamp: crate::handlers::utils::now_secs(),
expires_at: None,
});
if memos.len() > 200 {
memos.remove(0);
}
});
Ok("Handoff memo left".to_string())
}
}
pub struct ReadHandoffMemosHandler;
#[async_trait]
impl McpTool for ReadHandoffMemosHandler {
fn name(&self) -> &'static str {
"read_handoff_memos"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ReadHandoffMemosTool>(
"read_handoff_memos",
"Execute read_handoff_memos",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ReadHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.telemetry.handoff_memos.read_with(|items| {
let filtered: Vec<_> = items
.iter()
.filter(|i| {
if let Some(ns) = &req.namespace {
&i.namespace == ns
} else {
true
let req: HandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
match req.action {
HandoffMemoAction::Leave => {
let content = req.content.ok_or_else(|| {
crate::error::AppError::Internal("content is required for 'leave' action".to_string())
})?;
let ns = req.namespace.unwrap_or_else(|| crate::models::default_namespace());
state.telemetry.handoff_memos.modify(|memos| {
memos.push(crate::models::HandoffMemo {
id: uuid::Uuid::new_v4().to_string(),
author: "agy".to_string(),
content,
namespace: ns,
timestamp: crate::handlers::utils::now_secs(),
expires_at: None,
});
if memos.len() > 200 {
memos.remove(0);
}
})
.collect();
Ok::<String, crate::error::AppError>(serde_json::to_string(&filtered)?)
})?;
Ok(data)
}
}
pub struct ClearHandoffMemosHandler;
#[async_trait]
impl McpTool for ClearHandoffMemosHandler {
fn name(&self) -> &'static str {
"clear_handoff_memos"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ClearHandoffMemosTool>(
"clear_handoff_memos",
"Execute clear_handoff_memos",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ClearHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let ids: HashSet<_> = req.ids.into_iter().collect();
state
.telemetry
.handoff_memos
.modify(|memos| memos.retain(|m| !ids.contains(&m.id)));
Ok("Handoff memos cleared".to_string())
});
Ok("Handoff memo left.".to_string())
}
HandoffMemoAction::Read => {
let data = state.telemetry.handoff_memos.read_with(|items| {
let filtered: Vec<_> = items
.iter()
.filter(|i| {
if let Some(ns) = &req.namespace {
&i.namespace == ns
} else {
true
}
})
.collect();
Ok::<String, crate::error::AppError>(serde_json::to_string(&filtered)?)
})?;
Ok(data)
}
HandoffMemoAction::Clear => {
let ids: HashSet<_> = req.ids.unwrap_or_default().into_iter().collect();
state.telemetry.handoff_memos.modify(|memos| {
if ids.is_empty() {
memos.clear();
} else {
memos.retain(|m| !ids.contains(&m.id));
}
});
Ok("Handoff memos cleared.".to_string())
}
}
}
}
@@ -481,10 +456,10 @@ mod tests {
let dir = tempdir().unwrap();
let state = Arc::new(MemoryState::new(dir.path().to_str().unwrap()));
let handoff_handler = LeaveHandoffMemoHandler;
let handoff_handler = HandoffMemosHandler;
let args = json!({
"action": "leave",
"content": "Finished implementing graph tests",
"author": "Antigravity",
"namespace": "global"
});
@@ -493,11 +468,10 @@ mod tests {
.await
.map_err(|e| crate::error::AppError::Internal(e.to_string()))
.unwrap();
assert_eq!(res, "Handoff memo left");
assert_eq!(res, "Handoff memo left.");
let read_handoff = ReadHandoffMemosHandler;
let res2 = read_handoff
.execute(json!({"namespace": "global"}), state.clone())
let res2 = handoff_handler
.execute(json!({"action": "read", "namespace": "global"}), state.clone())
.await
.map_err(|e| crate::error::AppError::Internal(e.to_string()))
.unwrap();
@@ -532,7 +506,7 @@ mod tests {
let dir = tempfile::tempdir().unwrap();
let state = Arc::new(MemoryState::new(dir.path().to_str().unwrap()));
let handler = ManageStickyNotesHandler;
let handler = StickyNotesHandler;
let invalid_args = serde_json::json!({
"action": "invalid_action"
});