feat(server): refactor handlers, router, state management, and memory tools

This commit is contained in:
Riz Ashraf committed 2026-10-02 07:27:37 +01:00
1 parent 87ddb01063
commit a083719cf1
36 files changed
+1899 -597

No files matched your search

+106 -9
View File
@@ -37,7 +37,11 @@ pub trait McpPrompt: Send + Sync {
fn arguments(&self) -> serde_json::Value {
serde_json::json!([])
}
async fn get(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<serde_json::Value>;
async fn get(
&self,
args: Value,
state: Arc<MemoryState>,
) -> crate::error::Result<serde_json::Value>;
}
struct GraphEntitiesResource;
@@ -59,7 +63,8 @@ impl McpResource for GraphEntitiesResource {
let data: Vec<_> = graph.entities.values().collect();
Ok(serde_json::to_string_pretty(&data)?)
})
.await.unwrap()
.await
.unwrap()
}
}
@@ -82,7 +87,8 @@ impl McpResource for GraphRelationsResource {
let data = &graph.relations;
Ok(serde_json::to_string_pretty(&data)?)
})
.await.unwrap()
.await
.unwrap()
}
}
@@ -108,7 +114,56 @@ impl McpResource for TasksActiveResource {
.collect();
Ok(serde_json::to_string_pretty(&data)?)
})
.await.unwrap()
.await
.unwrap()
}
}
struct DecisionsActiveResource;
#[async_trait]
impl McpResource for DecisionsActiveResource {
fn uri(&self) -> &'static str {
"memory://decisions/active"
}
fn name(&self) -> &'static str {
"Active ADR Decisions"
}
fn description(&self) -> Option<&'static str> {
Some("All accepted Architectural Decision Records (ADRs)")
}
async fn read(&self, state: Arc<MemoryState>) -> crate::error::Result<String> {
let state_clone = Arc::clone(&state);
tokio::task::spawn_blocking(move || -> crate::error::Result<String> {
let adrs = state_clone.code.adrs.cache.read().unwrap();
let data: Vec<_> = adrs.iter().filter(|a| a.status == "accepted").collect();
Ok(serde_json::to_string_pretty(&data)?)
})
.await
.unwrap()
}
}
struct TechDebtActiveResource;
#[async_trait]
impl McpResource for TechDebtActiveResource {
fn uri(&self) -> &'static str {
"memory://tech_debt/unresolved"
}
fn name(&self) -> &'static str {
"Unresolved Tech Debt"
}
fn description(&self) -> Option<&'static str> {
Some("All currently unresolved technical debt items")
}
async fn read(&self, state: Arc<MemoryState>) -> crate::error::Result<String> {
let state_clone = Arc::clone(&state);
tokio::task::spawn_blocking(move || -> crate::error::Result<String> {
let debts = state_clone.code.tech_debts.cache.read().unwrap();
let data: Vec<_> = debts.iter().filter(|d| !d.is_resolved).collect();
Ok(serde_json::to_string_pretty(&data)?)
})
.await
.unwrap()
}
}
@@ -140,6 +195,34 @@ impl McpPrompt for AnalyzeTechDebtPrompt {
}
}
struct SummarizeArchitecturePrompt;
#[async_trait]
impl McpPrompt for SummarizeArchitecturePrompt {
fn name(&self) -> &'static str {
"summarize_architecture"
}
fn description(&self) -> Option<&'static str> {
Some("Summarize project architecture using active ADRs and knowledge graph entities")
}
async fn get(
&self,
_args: Value,
_state: Arc<MemoryState>,
) -> crate::error::Result<serde_json::Value> {
Ok(serde_json::json!({
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Please read active decisions from memory://decisions/active and graph entities from memory://graph/entities, and synthesize a clean architectural overview report."
}
}
]
}))
}
}
pub struct MemoryHandler {
pub state: Arc<MemoryState>,
pub tools: std::collections::HashMap<String, Box<dyn McpTool>>,
@@ -173,8 +256,11 @@ impl MemoryHandler {
register_resource!(GraphEntitiesResource);
register_resource!(GraphRelationsResource);
register_resource!(TasksActiveResource);
register_resource!(DecisionsActiveResource);
register_resource!(TechDebtActiveResource);
register_prompt!(AnalyzeTechDebtPrompt);
register_prompt!(SummarizeArchitecturePrompt);
struct TerminalHistoryResource;
#[async_trait]
impl McpResource for TerminalHistoryResource {
@@ -193,7 +279,8 @@ impl MemoryHandler {
let items = state_clone.telemetry.terminal_history.cache.read().unwrap();
Ok(serde_json::to_string_pretty(&*items)?)
})
.await.unwrap()
.await
.unwrap()
}
}
struct PinnedFilesResource;
@@ -214,7 +301,8 @@ impl MemoryHandler {
let items = state_clone.project.pinned_files.cache.read().unwrap();
Ok(serde_json::to_string_pretty(&*items)?)
})
.await.unwrap()
.await
.unwrap()
}
}
@@ -236,7 +324,8 @@ impl MemoryHandler {
let items = state_clone.project.milestones.cache.read().unwrap();
Ok(serde_json::to_string_pretty(&*items)?)
})
.await.unwrap()
.await
.unwrap()
}
}
@@ -387,6 +476,10 @@ impl MemoryHandler {
register!(ast::ReplaceAstNodeHandler);
register!(workspaces::ReadDirectoryArchitectureHandler);
register!(workspaces::SemanticCodeSearchHandler);
register!(workspaces::CreateSnapshotHandler);
register!(workspaces::RestoreSnapshotHandler);
register!(workspaces::CreateSubagentNamespaceHandler);
register!(workspaces::PurgeSubagentNamespaceHandler);
Self {
state,
@@ -556,10 +649,14 @@ impl MemoryHandler {
self.state
.broadcast_activity(&format!("Agent executed tool: {}", name));
let result: crate::error::Result<String> = if let Some(tool) = self.tools.get(name) {
let result: crate::error::Result<String> = if let Some(tool) = self.tools.get(name)
{
tool.execute(args, self.state.clone()).await
} else {
Err(crate::error::AppError::Internal(format!("Unknown tool: {}", name)))
Err(crate::error::AppError::Internal(format!(
"Unknown tool: {}",
name
)))
};
match result {