Skip to main content

lean_ctx/tools/registered/
ctx_package.rs

1use rmcp::model::Tool;
2use rmcp::ErrorData;
3use serde_json::{json, Map, Value};
4
5use crate::server::tool_trait::{get_str, McpTool, ToolContext, ToolOutput};
6use crate::tool_defs::tool_def;
7
8pub struct CtxPackageTool;
9
10impl McpTool for CtxPackageTool {
11    fn name(&self) -> &'static str {
12        "ctx_package"
13    }
14
15    fn tool_def(&self) -> Tool {
16        tool_def(
17            "ctx_package",
18            "Save or resume portable context packages — self-contained JSON bundles with session state, summaries, and knowledge. Use to hand off context between agents, persist session snapshots for later, or onboard a new agent into a previous session's context. Actions: save (export current session), resume (import from a package file), list (show saved packages), info (inspect a package without importing).",
19            json!({
20                "type": "object",
21                "properties": {
22                    "action": {
23                        "type": "string",
24                        "enum": ["save", "resume", "list", "info"],
25                        "description": "Package action (default: save)"
26                    },
27                    "path": {
28                        "type": "string",
29                        "description": "File path for resume/info, or custom output path for save"
30                    },
31                    "description": {
32                        "type": "string",
33                        "description": "Human-readable description for the saved package"
34                    }
35                },
36                "required": []
37            }),
38        )
39    }
40
41    fn handle(
42        &self,
43        args: &Map<String, Value>,
44        ctx: &ToolContext,
45    ) -> Result<ToolOutput, ErrorData> {
46        let action = get_str(args, "action").unwrap_or_else(|| "save".to_string());
47        let path = get_str(args, "path");
48        let description = get_str(args, "description");
49
50        let guard = ctx
51            .session
52            .as_ref()
53            .and_then(|s| crate::server::bounded_lock::read(s, "ctx_package:session"));
54        let session_ref = guard.as_deref();
55        let root = session_ref
56            .and_then(|s| s.project_root.clone())
57            .unwrap_or_else(|| ctx.project_root.clone());
58
59        let agent_id_guard = ctx.agent_id.as_ref().map(|a| a.blocking_read());
60        let agent_id = agent_id_guard.as_ref().and_then(|g| g.as_deref());
61        let result = crate::tools::ctx_package::handle(
62            &root,
63            session_ref,
64            &action,
65            path.as_deref(),
66            agent_id,
67            description.as_deref(),
68        );
69        Ok(ToolOutput::simple(result))
70    }
71}