feat(mcp): implement Prompts capability, Resources, and clean up daemon architecture
This commit is contained in:
1 parent
9b9b403ef2
commit
251757f8fc
29 files changed
+1276
-182
No files matched your search
+138
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user