lean_ctx/tools/registered/
ctx_session.rs1use rmcp::model::Tool;
2use rmcp::ErrorData;
3use serde_json::{json, Map, Value};
4
5use crate::server::tool_trait::{get_bool, get_str, McpTool, ToolContext, ToolOutput};
6use crate::tool_defs::tool_def;
7
8pub struct CtxSessionTool;
9
10impl McpTool for CtxSessionTool {
11 fn name(&self) -> &'static str {
12 "ctx_session"
13 }
14
15 fn tool_def(&self) -> Tool {
16 tool_def(
17 "ctx_session",
18 "Cross-session memory: record task/finding/decision, restore previous session state.",
19 json!({
20 "type": "object",
21 "properties": {
22 "action": {
23 "type": "string",
24 "description": "status|load|save|task|finding|decision|reset|list|cleanup|snapshot|restore|resume|profile|role|budget|slo|diff|verify|episodes|procedures"
25 },
26 "value": { "type": "string", "description": "Text for task/finding/decision" },
27 "session_id": { "type": "string", "description": "For load (default: latest)" }
28 },
29 "required": ["action"]
30 }),
31 )
32 }
33
34 fn handle(
35 &self,
36 args: &Map<String, Value>,
37 ctx: &ToolContext,
38 ) -> Result<ToolOutput, ErrorData> {
39 let action = get_str(args, "action")
40 .ok_or_else(|| ErrorData::invalid_params("action is required", None))?;
41 let value = get_str(args, "value");
42 let sid = get_str(args, "session_id");
43 let format = get_str(args, "format");
44 let path = get_str(args, "path");
45 let write = get_bool(args, "write").unwrap_or(false);
46 let privacy = get_str(args, "privacy");
47 let terse = get_bool(args, "terse");
48
49 let tool_calls_handle = ctx
50 .tool_calls
51 .as_ref()
52 .ok_or_else(|| ErrorData::internal_error("tool_calls not available", None))?;
53 let call_durations: Vec<(String, u64)> = {
54 let tc = tool_calls_handle.blocking_read();
55 tc.iter().map(|c| (c.tool.clone(), c.duration_ms)).collect()
56 };
57
58 let session_handle = ctx
59 .session
60 .as_ref()
61 .ok_or_else(|| ErrorData::internal_error("session not available", None))?;
62 let mut session = session_handle.blocking_write();
63 let result = crate::tools::ctx_session::handle(
64 &mut session,
65 &call_durations,
66 &action,
67 value.as_deref(),
68 sid.as_deref(),
69 crate::tools::ctx_session::SessionToolOptions {
70 format: format.as_deref(),
71 path: path.as_deref(),
72 write,
73 privacy: privacy.as_deref(),
74 terse,
75 },
76 );
77 drop(session);
78
79 Ok(ToolOutput {
80 text: result,
81 original_tokens: 0,
82 saved_tokens: 0,
83 mode: Some(action),
84 path: None,
85 changed: false,
86 shell_outcome: None,
87 })
88 }
89}