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

+138 -1
View File
@@ -116,7 +116,9 @@ impl MemoryHandler {
"cacheScope": "public",
"supportedVersions": ["2026-07-28", "2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"],
"capabilities": {
"tools": serde_json::json!({})
"tools": serde_json::json!({}),
"resources": serde_json::json!({}),
"prompts": serde_json::json!({})
},
"_meta": {
"io.modelcontextprotocol/serverInfo": {
@@ -131,6 +133,8 @@ impl MemoryHandler {
let init = rmcp::model::InitializeResult::new(
rmcp::model::ServerCapabilities::builder()
.enable_tools()
.enable_resources()
.enable_prompts()
.build(),
)
.with_server_info(rmcp::model::Implementation::new(
@@ -158,6 +162,107 @@ impl MemoryHandler {
serde_json::json!({ "tools": tools }),
))
}
"resources/list" => {
let payload = serde_json::json!({
"resources": [
{
"uri": "memory://graph/entities",
"name": "Graph Entities",
"mimeType": "application/json",
"description": "All nodes and entities currently stored in the knowledge graph"
},
{
"uri": "memory://graph/relations",
"name": "Graph Relations",
"mimeType": "application/json",
"description": "All edge relationships between entities in the knowledge graph"
},
{
"uri": "memory://tasks/active",
"name": "Active Tasks",
"mimeType": "application/json",
"description": "All currently active or uncompleted tracking tasks"
}
]
});
Some(crate::mcp::success(id, payload))
}
"resources/templates/list" => {
let payload = serde_json::json!({
"resourceTemplates": []
});
Some(crate::mcp::success(id, payload))
}
"resources/read" => {
let params = req.get("params").unwrap_or(&serde_json::Value::Null);
let uri = params.get("uri").and_then(|u| u.as_str()).unwrap_or("");
let text = match uri {
"memory://graph/entities" => {
let graph = self.state.graph.cache.read().unwrap();
let data: Vec<_> = graph.entities.values().cloned().collect();
serde_json::to_string_pretty(&data).unwrap_or_default()
}
"memory://graph/relations" => {
let graph = self.state.graph.cache.read().unwrap();
let data = graph.relations.clone();
serde_json::to_string_pretty(&data).unwrap_or_default()
}
"memory://tasks/active" => {
let tasks = self.state.tasks.cache.read().unwrap();
let data: Vec<_> = tasks.iter()
.filter(|t| t.status != "completed" && t.status != "done")
.cloned()
.collect();
serde_json::to_string_pretty(&data).unwrap_or_default()
}
_ => {
return Some(crate::mcp::error(id, -32602, "Resource not found"));
}
};
let payload = serde_json::json!({
"contents": [{
"uri": uri,
"mimeType": "application/json",
"text": text
}]
});
Some(crate::mcp::success(id, payload))
}
"prompts/list" => {
let payload = serde_json::json!({
"prompts": [
{
"name": "analyze_tech_debt",
"description": "Analyze the project's current technical debt",
"arguments": []
}
]
});
Some(crate::mcp::success(id, payload))
}
"prompts/get" => {
let params = req.get("params").unwrap_or(&serde_json::Value::Null);
let name = params.get("name").and_then(|n| n.as_str()).unwrap_or("");
if name == "analyze_tech_debt" {
let payload = serde_json::json!({
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Please read the current technical debt from the knowledge graph (or use the list_tech_debt tool) and provide a prioritization plan for addressing it."
}
}
]
});
Some(crate::mcp::success(id, payload))
} else {
Some(crate::mcp::error(id, -32602, "Prompt not found"))
}
}
"tools/call" => {
let params = req.get("params").unwrap_or(&serde_json::Value::Null);
let name = params.get("name").and_then(|n| n.as_str()).unwrap_or("");
@@ -209,3 +314,35 @@ impl MemoryHandler {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
use serde_json::json;
#[tokio::test]
async fn test_memory_handler_tools_registration() {
let dir = tempdir().unwrap();
let state = Arc::new(MemoryState::new(dir.path().to_str().unwrap()));
let handler = MemoryHandler::new(state);
// Assert some known tools are registered
assert!(handler.tools.contains_key("create_entities"));
assert!(handler.tools.contains_key("add_task"));
// Ensure we can fetch list of tools
let list_tools_req = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
});
let res_list = handler.handle_request(list_tools_req).await.unwrap();
assert_eq!(res_list["jsonrpc"], "2.0");
assert_eq!(res_list["id"], 1);
assert!(res_list["result"]["tools"].as_array().unwrap().len() > 10);
}
}