Skip to main content

lean_ctx/tools/registered/
ctx_package.rs

1use 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 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            "WORKFLOW: save -> resume in new session for agent handoff.\n\
19            ANTIPATTERN: NOT for internal session persistence (use ctx_session).\n\
20            Self-contained JSON bundles: session state, summaries,\n\
21            knowledge. Actions: save, resume, list, info.\n\
22            Saves tokens: portable across sessions/agents.",
23            json!({
24                "type": "object",
25                "properties": {
26                    "action": {
27                        "type": "string",
28                        "enum": ["save", "resume", "list", "info"],
29                        "description": "save|resume|list|info"
30                    },
31                    "path": {
32                        "type": "string",
33                        "description": "File path for save/resume JSON bundle"
34                    },
35                    "description": {
36                        "type": "string",
37                        "description": "Package description (for save action)"
38                    }
39                },
40                "required": []
41            }),
42        )
43    }
44
45    fn handle(
46        &self,
47        args: &Map<String, Value>,
48        ctx: &ToolContext,
49    ) -> Result<ToolOutput, ErrorData> {
50        let action = get_str(args, "action").unwrap_or_else(|| "save".to_string());
51        let path = get_str(args, "path");
52        let description = get_str(args, "description");
53
54        let guard = ctx
55            .session
56            .as_ref()
57            .and_then(|s| crate::server::bounded_lock::read(s, "ctx_package:session"));
58        let session_ref = guard.as_deref();
59        let root = session_ref
60            .and_then(|s| s.project_root.clone())
61            .unwrap_or_else(|| ctx.project_root.clone());
62
63        let agent_id_guard = ctx.agent_id.as_ref().map(|a| a.blocking_read());
64        let agent_id = agent_id_guard.as_ref().and_then(|g| g.as_deref());
65        let result = crate::tools::ctx_package::handle(
66            &root,
67            session_ref,
68            &action,
69            path.as_deref(),
70            agent_id,
71            description.as_deref(),
72        );
73        Ok(ToolOutput::simple(result))
74    }
75}