Skip to main content

lean_ctx/tools/registered/
ctx_summary.rs

1use rmcp::ErrorData;
2use rmcp::model::Tool;
3use serde_json::{Map, Value, json};
4
5use crate::server::tool_trait::{McpTool, ToolContext, ToolOutput, get_str};
6use crate::tool_defs::tool_def;
7
8pub struct CtxSummaryTool;
9
10impl McpTool for CtxSummaryTool {
11    fn name(&self) -> &'static str {
12        "ctx_summary"
13    }
14
15    fn tool_def(&self) -> Tool {
16        tool_def(
17            "ctx_summary",
18            "Record and recall AI session summaries — compact, semantically-recallable digests of what was done (task, files, decisions, next steps). Actions: recall (find past summaries by query; semantic when embeddings are warm, else lexical), record (snapshot the current session now), list (recent summaries). Summaries are also captured automatically on the checkpoint cadence.",
19            json!({
20                "type": "object",
21                "properties": {
22                    "action": {
23                        "type": "string",
24                        "enum": ["recall", "record", "list"],
25                        "description": "Summary action (default: recall)"
26                    },
27                    "query": {
28                        "type": "string",
29                        "description": "Recall query, e.g. \"what did I change in the graph index?\""
30                    },
31                    "top_k": {
32                        "type": "integer",
33                        "description": "Max summaries to return for recall (default 5, max 20)"
34                    }
35                }
36            }),
37        )
38    }
39
40    fn handle(
41        &self,
42        args: &Map<String, Value>,
43        ctx: &ToolContext,
44    ) -> Result<ToolOutput, ErrorData> {
45        let action = get_str(args, "action").unwrap_or_else(|| "recall".to_string());
46        let query = get_str(args, "query");
47        let top_k = args
48            .get("top_k")
49            .and_then(Value::as_u64)
50            .map_or(5, |n| n as usize);
51
52        let guard = ctx
53            .session
54            .as_ref()
55            .and_then(|s| crate::server::bounded_lock::read(s, "ctx_summary:session"));
56        let session_ref = guard.as_deref();
57        let root = session_ref
58            .and_then(|s| s.project_root.clone())
59            .unwrap_or_else(|| ctx.project_root.clone());
60
61        let result =
62            crate::tools::ctx_summary::handle(&root, session_ref, &action, query.as_deref(), top_k);
63        Ok(ToolOutput::simple(result))
64    }
65}