feat(server): refactor handlers, router, state management, and memory tools
This commit is contained in:
1 parent
87ddb01063
commit
a083719cf1
36 files changed
+1899
-597
No files matched your search
+106
-9
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user