feat(mcp): implement Prompts capability, Resources, and clean up daemon architecture

This commit is contained in:
Riz Ashraf committed 2026-09-22 21:41:17 +01:00
1 parent 9b9b403ef2
commit 251757f8fc
29 files changed
+1276 -182

No files matched your search

+130
View File
@@ -577,3 +577,133 @@ impl McpTool for FindOrphansHandler {
}
use crate::handlers::utils::*;
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
use serde_json::json;
#[tokio::test]
async fn test_create_and_read_entities() {
let dir = tempdir().unwrap();
let state = Arc::new(MemoryState::new(dir.path().to_str().unwrap()));
let create_handler = CreateEntitiesHandler;
let args = json!({
"entities": [
{"name": "Alice", "entityType": "Person", "observations": ["Likes Bob"]}
]
});
let res = create_handler.execute(args, state.clone()).await.unwrap();
assert_eq!(res, "Entities created");
// Ensure graph contains the entity
state.graph.read_with(|g| {
assert!(g.entities.contains_key("Alice"));
assert_eq!(g.entities["Alice"].entity_type, "Person");
});
}
#[tokio::test]
async fn test_create_relations() {
let dir = tempdir().unwrap();
let state = Arc::new(MemoryState::new(dir.path().to_str().unwrap()));
// Needs entities first
state.graph.modify(|g| {
g.entities.insert("A".to_string(), crate::models::Entity { name: "A".to_string(), entity_type: "Node".to_string(), observations: vec![], namespace: "global".to_string(), git_branch: None });
g.entities.insert("B".to_string(), crate::models::Entity { name: "B".to_string(), entity_type: "Node".to_string(), observations: vec![], namespace: "global".to_string(), git_branch: None });
});
let handler = CreateRelationsHandler;
let args = json!({
"relations": [
{"from": "A", "to": "B", "relationType": "knows"}
]
});
let res = handler.execute(args, state.clone()).await.unwrap();
assert_eq!(res, "Relations created");
}
#[tokio::test]
async fn test_observations_and_reads() {
let dir = tempdir().unwrap();
let state = Arc::new(MemoryState::new(dir.path().to_str().unwrap()));
// Need entity first
state.graph.modify(|g| {
g.entities.insert("A".to_string(), crate::models::Entity { name: "A".to_string(), entity_type: "Node".to_string(), observations: vec![], namespace: "global".to_string(), git_branch: None });
});
let add_obs = AddObservationsHandler;
let args_obs = json!({
"observations": [
{"entityName": "A", "contents": ["Obs 1", "Obs 2"]}
]
});
let res1 = add_obs.execute(args_obs, state.clone()).await.unwrap();
assert_eq!(res1, "Observations added");
let read_graph = ReadGraphHandler;
let res2 = read_graph.execute(json!({"namespace": "global"}), state.clone()).await.unwrap();
assert!(res2.contains("Obs 1"));
assert!(res2.contains("Obs 2"));
let del_entity = DeleteEntitiesHandler;
let res4 = del_entity.execute(json!({"entityNames": ["A"]}), state.clone()).await.unwrap();
assert_eq!(res4, "Entities deleted");
let res5 = read_graph.execute(json!({"namespace": "global"}), state.clone()).await.unwrap();
assert!(!res5.contains("A"));
}
#[tokio::test]
async fn test_advanced_graph_operations() {
let dir = tempfile::tempdir().unwrap();
let state = Arc::new(MemoryState::new(dir.path().to_str().unwrap()));
let create_handler = CreateEntitiesHandler;
let args_ent = json!({
"entities": [
{"name": "X", "entityType": "File", "observations": ["Obs X"], "namespace": "global"},
{"name": "Y", "entityType": "File", "observations": ["Obs Y"], "namespace": "global"}
]
});
create_handler.execute(args_ent, state.clone()).await.unwrap();
let rel_handler = CreateRelationsHandler;
let args_rel = json!({
"relations": [
{"from": "X", "to": "Y", "relationType": "depends_on", "namespace": "global"}
]
});
rel_handler.execute(args_rel, state.clone()).await.unwrap();
let read_handler = ReadGraphHandler;
let res_read = read_handler.execute(json!({"namespace": "global"}), state.clone()).await.unwrap();
assert!(res_read.contains("X"));
assert!(res_read.contains("depends_on"));
let open_handler = OpenNodesHandler;
let res_open = open_handler.execute(json!({"names": ["X"]}), state.clone()).await.unwrap();
assert!(res_open.contains("Y"));
let viz_handler = VisualizeGraphHandler;
let res_viz = viz_handler.execute(json!({"query": "X"}), state.clone()).await.unwrap();
assert!(res_viz.len() > 0);
let condense = CondenseEntityHandler;
let res_cond = condense.execute(json!({"entityName": "X", "summarized_observations": ["X condensed"]}), state.clone()).await.unwrap();
assert_eq!(res_cond, "Entity condensed");
let merge = MergeEntitiesHandler;
let res_merge = merge.execute(json!({"sourceEntity": "X", "targetEntity": "Y"}), state.clone()).await.unwrap();
assert_eq!(res_merge, "Entities merged");
let orphans = FindOrphansHandler;
let res_orphans = orphans.execute(json!({}), state.clone()).await.unwrap();
assert!(!res_orphans.contains("Y"));
}
}