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 (CCP). Actions: load (restore ~400 tok), save, status, task, \
19finding, decision, reset, list, cleanup, snapshot, restore, resume, profile (context profiles), \
20role (governance), budget (limits), slo (observability), diff (compare sessions), verify (output verification stats), \
21episodes (episodic memory), procedures (procedural memory).",
22 json!({
23 "type": "object",
24 "properties": {
25 "action": {
26 "type": "string",
27 "enum": ["status", "load", "save", "task", "finding", "decision", "reset", "list", "cleanup", "snapshot", "restore", "resume", "profile", "role", "budget", "slo", "diff", "verify", "episodes", "procedures"],
28 "description": "Session operation to perform"
29 },
30 "value": {
31 "type": "string",
32 "description": "Value for task/finding/decision/profile actions"
33 },
34 "session_id": {
35 "type": "string",
36 "description": "Session ID for load action (default: latest)"
37 }
38 },
39 "required": ["action"]
40 }),
41 )
42 }
43
44 fn handle(
45 &self,
46 args: &Map<String, Value>,
47 ctx: &ToolContext,
48 ) -> Result<ToolOutput, ErrorData> {
49 let action = get_str(args, "action")
50 .ok_or_else(|| ErrorData::invalid_params("action is required", None))?;
51 let value = get_str(args, "value");
52 let sid = get_str(args, "session_id");
53 let format = get_str(args, "format");
54 let path = get_str(args, "path");
55 let write = get_bool(args, "write").unwrap_or(false);
56 let privacy = get_str(args, "privacy");
57 let terse = get_bool(args, "terse");
58
59 let tool_calls_handle = ctx.tool_calls.as_ref().unwrap();
60 let call_durations: Vec<(String, u64)> = {
61 let tc = tool_calls_handle.blocking_read();
62 tc.iter().map(|c| (c.tool.clone(), c.duration_ms)).collect()
63 };
64
65 let session_handle = ctx.session.as_ref().unwrap();
66 let mut session = session_handle.blocking_write();
67 let result = crate::tools::ctx_session::handle(
68 &mut session,
69 &call_durations,
70 &action,
71 value.as_deref(),
72 sid.as_deref(),
73 crate::tools::ctx_session::SessionToolOptions {
74 format: format.as_deref(),
75 path: path.as_deref(),
76 write,
77 privacy: privacy.as_deref(),
78 terse,
79 },
80 );
81 drop(session);
82
83 Ok(ToolOutput {
84 text: result,
85 original_tokens: 0,
86 saved_tokens: 0,
87 mode: Some(action),
88 path: None,
89 })
90 }
91}