280 lines
8.3 KiB
Rust
280 lines
8.3 KiB
Rust
use crate::models::*;
|
|
use crate::router::McpTool;
|
|
use crate::state::MemoryState;
|
|
use crate::tools::*;
|
|
use async_trait::async_trait;
|
|
use serde_json::Value;
|
|
use std::collections::HashSet;
|
|
use std::sync::Arc;
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
pub struct AddStickyNoteHandler;
|
|
|
|
#[async_trait]
|
|
impl McpTool for AddStickyNoteHandler {
|
|
fn name(&self) -> &'static str {
|
|
"add_sticky_note"
|
|
}
|
|
|
|
fn schema(&self) -> Value {
|
|
crate::mcp::tool_def::<AddStickyNoteTool>("add_sticky_note", "Execute add_sticky_note")
|
|
}
|
|
|
|
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
|
|
let req: AddStickyNoteTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
state.sticky.modify(|notes| {
|
|
notes.push(StickyNote {
|
|
timestamp: SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_secs(),
|
|
content: req.content,
|
|
});
|
|
});
|
|
Ok("Sticky note added.".to_string())
|
|
}
|
|
}
|
|
|
|
pub struct ReadStickyNotesHandler;
|
|
|
|
#[async_trait]
|
|
impl McpTool for ReadStickyNotesHandler {
|
|
fn name(&self) -> &'static str {
|
|
"read_sticky_notes"
|
|
}
|
|
|
|
fn schema(&self) -> Value {
|
|
crate::mcp::tool_def::<ReadStickyNotesTool>(
|
|
"read_sticky_notes",
|
|
"Execute read_sticky_notes",
|
|
)
|
|
}
|
|
|
|
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
|
|
let data = state.sticky.read_with(|s| serde_json::to_string(s).unwrap_or_else(|_| "[]".to_string()));
|
|
Ok(data.to_string())
|
|
}
|
|
}
|
|
|
|
pub struct DeleteStickyNoteHandler;
|
|
|
|
#[async_trait]
|
|
impl McpTool for DeleteStickyNoteHandler {
|
|
fn name(&self) -> &'static str {
|
|
"delete_sticky_note"
|
|
}
|
|
|
|
fn schema(&self) -> Value {
|
|
crate::mcp::tool_def::<DeleteStickyNoteTool>(
|
|
"delete_sticky_note",
|
|
"Execute delete_sticky_note",
|
|
)
|
|
}
|
|
|
|
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
|
|
let req: DeleteStickyNoteTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
let mut success = false;
|
|
state.sticky.modify(|notes| {
|
|
if req.index > 0 && req.index <= notes.len() {
|
|
notes.remove(req.index - 1);
|
|
success = true;
|
|
}
|
|
});
|
|
if success {
|
|
Ok("Sticky note deleted.".to_string())
|
|
} else {
|
|
Err("Invalid sticky note index.".to_string())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct ClearStickyNotesHandler;
|
|
|
|
#[async_trait]
|
|
impl McpTool for ClearStickyNotesHandler {
|
|
fn name(&self) -> &'static str {
|
|
"clear_sticky_notes"
|
|
}
|
|
|
|
fn schema(&self) -> Value {
|
|
crate::mcp::tool_def::<ClearStickyNotesTool>(
|
|
"clear_sticky_notes",
|
|
"Execute clear_sticky_notes",
|
|
)
|
|
}
|
|
|
|
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
|
|
state.sticky.modify(|notes| {
|
|
notes.clear();
|
|
});
|
|
Ok("All sticky notes cleared.".to_string())
|
|
}
|
|
}
|
|
|
|
pub struct LeaveHandoffMemoHandler;
|
|
|
|
#[async_trait]
|
|
impl McpTool for LeaveHandoffMemoHandler {
|
|
fn name(&self) -> &'static str {
|
|
"leave_handoff_memo"
|
|
}
|
|
|
|
fn schema(&self) -> Value {
|
|
crate::mcp::tool_def::<LeaveHandoffMemoTool>(
|
|
"leave_handoff_memo",
|
|
"Execute leave_handoff_memo",
|
|
)
|
|
}
|
|
|
|
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
|
|
let req: LeaveHandoffMemoTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
state.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: SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_secs(),
|
|
})
|
|
});
|
|
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>) -> Result<String, String> {
|
|
let req: ReadHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
let data = state.handoff_memos.read_with(|items| {
|
|
let filtered: Vec<_> = items.iter().filter(|i| {
|
|
if let Some(ns) = &req.namespace {
|
|
&i.namespace == ns
|
|
} else {
|
|
true
|
|
}
|
|
}).collect();
|
|
serde_json::to_string(&filtered).unwrap_or_default()
|
|
});
|
|
Ok(data.to_string())
|
|
}
|
|
}
|
|
|
|
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>) -> Result<String, String> {
|
|
let req: ClearHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
let ids: HashSet<_> = req.ids.into_iter().collect();
|
|
state
|
|
.handoff_memos
|
|
.modify(|memos| memos.retain(|m| !ids.contains(&m.id)));
|
|
Ok("Handoff memos cleared".to_string())
|
|
}
|
|
}
|
|
|
|
pub struct AddSessionSummaryHandler;
|
|
|
|
#[async_trait]
|
|
impl McpTool for AddSessionSummaryHandler {
|
|
fn name(&self) -> &'static str {
|
|
"add_session_summary"
|
|
}
|
|
|
|
fn schema(&self) -> Value {
|
|
crate::mcp::tool_def::<AddSessionSummaryTool>(
|
|
"add_session_summary",
|
|
"Execute add_session_summary",
|
|
)
|
|
}
|
|
|
|
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
|
|
let req: AddSessionSummaryTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
state.session_summaries.modify(|summaries| {
|
|
summaries.push(crate::models::SessionSummary {
|
|
summary: req.summary,
|
|
namespace: req.namespace,
|
|
timestamp: SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_secs(),
|
|
})
|
|
});
|
|
Ok("Session summary added".to_string())
|
|
}
|
|
}
|
|
|
|
pub struct GenerateStandupReportHandler;
|
|
|
|
#[async_trait]
|
|
impl McpTool for GenerateStandupReportHandler {
|
|
fn name(&self) -> &'static str {
|
|
"generate_standup_report"
|
|
}
|
|
|
|
fn schema(&self) -> Value {
|
|
crate::mcp::tool_def::<GenerateStandupReportTool>(
|
|
"generate_standup_report",
|
|
"Execute generate_standup_report",
|
|
)
|
|
}
|
|
|
|
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
|
|
let req: GenerateStandupReportTool =
|
|
serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
let cutoff = SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_secs()
|
|
.saturating_sub(req.hours_lookback * 3600);
|
|
let tasks = state
|
|
.tasks
|
|
.read()
|
|
.into_iter()
|
|
.filter(|t| t.updated_at >= cutoff)
|
|
.collect::<Vec<_>>();
|
|
let changes = state
|
|
.ledger
|
|
.read()
|
|
.into_iter()
|
|
.filter(|c| c.timestamp >= cutoff)
|
|
.collect::<Vec<_>>();
|
|
let summaries = state
|
|
.session_summaries
|
|
.read()
|
|
.into_iter()
|
|
.filter(|s| s.namespace == req.namespace && s.timestamp >= cutoff)
|
|
.collect::<Vec<_>>();
|
|
let report = serde_json::json!({ "tasks_updated": tasks, "code_changes": changes, "session_summaries": summaries });
|
|
Ok(report.to_string())
|
|
}
|
|
}
|
|
|