lean_ctx/tools/registered/
ctx_session.rs1use rmcp::ErrorData;
2use rmcp::model::Tool;
3use serde_json::{Map, Value, json};
4
5use crate::server::tool_trait::{McpTool, ToolContext, ToolOutput, get_bool, get_str};
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 "Session memory. save at session end, load at start, status = snapshot;\n\
19 task|finding|decision record progress (value=text).\n\
20 ANTIPATTERN: permanent project knowledge → ctx_knowledge.",
21 json!({
22 "type": "object",
23 "properties": {
24 "action": {
25 "type": "string",
26 "description": "status|load|save|task|finding|decision|list|… (invalid action lists all)"
27 },
28 "value": { "type": "string" },
29 "session_id": { "type": "string", "description": "Omit for latest" }
30 },
31 "required": ["action"]
32 }),
33 )
34 }
35
36 fn handle(
37 &self,
38 args: &Map<String, Value>,
39 ctx: &ToolContext,
40 ) -> Result<ToolOutput, ErrorData> {
41 let action = get_str(args, "action")
42 .ok_or_else(|| ErrorData::invalid_params("action is required", None))?;
43 let value = get_str(args, "value");
44 let sid = get_str(args, "session_id");
45 let format = get_str(args, "format");
46 let path = get_str(args, "path");
47 let write = get_bool(args, "write").unwrap_or(false);
48 let privacy = get_str(args, "privacy");
49 let terse = get_bool(args, "terse");
50
51 let tool_calls_handle = ctx
52 .tool_calls
53 .as_ref()
54 .ok_or_else(|| ErrorData::internal_error("tool_calls not available", None))?;
55 let call_durations: Vec<(String, u64)> = {
56 let tc = tool_calls_handle.blocking_read();
57 tc.iter().map(|c| (c.tool.clone(), c.duration_ms)).collect()
58 };
59 let agent_id = ctx
60 .agent_id
61 .as_ref()
62 .and_then(|agent_id| agent_id.blocking_read().clone());
63
64 let session_handle = ctx
65 .session
66 .as_ref()
67 .ok_or_else(|| ErrorData::internal_error("session not available", None))?;
68 let mut session = session_handle.blocking_write();
69 let result = crate::tools::ctx_session::handle(
70 &mut session,
71 &call_durations,
72 &action,
73 value.as_deref(),
74 sid.as_deref(),
75 crate::tools::ctx_session::SessionToolOptions {
76 format: format.as_deref(),
77 path: path.as_deref(),
78 write,
79 privacy: privacy.as_deref(),
80 terse,
81 agent_id: agent_id.as_deref(),
82 },
83 );
84
85 Ok(ToolOutput {
86 text: result,
87 original_tokens: 0,
88 saved_tokens: 0,
89 mode: Some(action),
90 path: None,
91 changed: false,
92 shell_outcome: None,
93 content_blocks: None,
94 })
95 }
96}