lean_ctx/tools/registered/
ctx_summary.rs1use 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 "WORKFLOW: record after tasks → recall with query.\n\
19 Compact session digests (task, files, decisions, next steps).\n\
20 Actions: recall|record|list. Auto-captured on checkpoints.\n\
21 ANTIPATTERN: structured facts → ctx_knowledge.",
22 json!({
23 "type": "object",
24 "properties": {
25 "action": {
26 "type": "string",
27 "enum": ["recall", "record", "list"],
28 "description": "recall|record|list"
29 },
30 "query": {
31 "type": "string",
32 "description": "Recall query, e.g. \"what did I change?\""
33 },
34 "top_k": {
35 "type": "integer",
36 "description": "Max summaries to return"
37 }
38 }
39 }),
40 )
41 }
42
43 fn handle(
44 &self,
45 args: &Map<String, Value>,
46 ctx: &ToolContext,
47 ) -> Result<ToolOutput, ErrorData> {
48 let action = get_str(args, "action").unwrap_or_else(|| "recall".to_string());
49 let query = get_str(args, "query");
50 let top_k = args
51 .get("top_k")
52 .and_then(Value::as_u64)
53 .map_or(5, |n| n as usize);
54
55 let guard = ctx
56 .session
57 .as_ref()
58 .and_then(|s| crate::server::bounded_lock::read(s, "ctx_summary:session"));
59 let session_ref = guard.as_deref();
60 let root = session_ref
61 .and_then(|s| s.project_root.clone())
62 .unwrap_or_else(|| ctx.project_root.clone());
63
64 let result =
65 crate::tools::ctx_summary::handle(&root, session_ref, &action, query.as_deref(), top_k);
66 Ok(ToolOutput::simple(result))
67 }
68}