Skip to main content

lean_ctx/tools/registered/
ctx_pack.rs

1use rmcp::model::Tool;
2use rmcp::ErrorData;
3use serde_json::{json, Map, Value};
4
5use crate::server::tool_trait::{
6    get_bool, get_int, get_str, get_str_array, McpTool, ToolContext, ToolOutput,
7};
8use crate::tool_defs::tool_def;
9
10pub struct CtxPackTool;
11
12impl McpTool for CtxPackTool {
13    fn name(&self) -> &'static str {
14        "ctx_pack"
15    }
16
17    fn tool_def(&self) -> Tool {
18        tool_def(
19            "ctx_pack",
20            "Context Package Manager. Actions: pr (PR context), create (build package from project), list, info, remove, install, export, import, auto_load, summary.",
21            json!({
22                "type": "object",
23                "properties": {
24                    "action": {
25                        "type": "string",
26                        "enum": ["pr", "create", "list", "info", "remove", "install", "export", "import", "auto_load", "summary"],
27                        "description": "Pack action to perform"
28                    },
29                    "project_root": {
30                        "type": "string",
31                        "description": "Project root (default: session project root)"
32                    },
33                    "name": {
34                        "type": "string",
35                        "description": "Package name (required for create, info, remove, install, export, auto_load)"
36                    },
37                    "version": {
38                        "type": "string",
39                        "description": "Package version (default: latest or '1.0.0' for create)"
40                    },
41                    "description": {
42                        "type": "string",
43                        "description": "Package description (for create)"
44                    },
45                    "author": {
46                        "type": "string",
47                        "description": "Package author (for create)"
48                    },
49                    "tags": {
50                        "type": "array",
51                        "items": { "type": "string" },
52                        "description": "Tags (for create)"
53                    },
54                    "layers": {
55                        "type": "array",
56                        "items": { "type": "string" },
57                        "description": "Layers to include: knowledge, graph, session, patterns, gotchas (for create)"
58                    },
59                    "level": {
60                        "type": "integer",
61                        "description": "Conformance level 1-3 (for create, default: 1)"
62                    },
63                    "scope": {
64                        "type": "string",
65                        "description": "Package scope like @org (for create)"
66                    },
67                    "base": {
68                        "type": "string",
69                        "description": "Git base ref (for pr action, default: auto-detect)"
70                    },
71                    "format": {
72                        "type": "string",
73                        "enum": ["markdown", "json"],
74                        "description": "Output format (for pr action, default: markdown)"
75                    },
76                    "depth": {
77                        "type": "integer",
78                        "description": "Impact depth (for pr action, default: 3)"
79                    },
80                    "diff": {
81                        "type": "string",
82                        "description": "Git diff --name-status text (for pr action; if omitted, computed via git)"
83                    },
84                    "file": {
85                        "type": "string",
86                        "description": "File path (for import/export)"
87                    },
88                    "apply": {
89                        "type": "boolean",
90                        "description": "Apply package after import (for import action, default: false)"
91                    },
92                    "enable": {
93                        "type": "boolean",
94                        "description": "Enable or disable auto-load (for auto_load action, default: true)"
95                    }
96                },
97                "required": ["action"]
98            }),
99        )
100    }
101
102    fn handle(
103        &self,
104        args: &Map<String, Value>,
105        ctx: &ToolContext,
106    ) -> Result<ToolOutput, ErrorData> {
107        let action = get_str(args, "action")
108            .ok_or_else(|| ErrorData::invalid_params("action is required", None))?;
109
110        let project_root = if let Some(p) = ctx
111            .resolved_path("project_root")
112            .or(ctx.resolved_path("root"))
113        {
114            p.to_string()
115        } else if let Some(err) = ctx.path_error("project_root").or(ctx.path_error("root")) {
116            return Err(ErrorData::invalid_params(
117                format!("project_root: {err}"),
118                None,
119            ));
120        } else {
121            ctx.project_root.clone()
122        };
123
124        let result = match action.as_str() {
125            "pr" => {
126                let base = get_str(args, "base");
127                let format = get_str(args, "format");
128                let depth = get_int(args, "depth").map(|d| d as usize);
129                let diff = get_str(args, "diff");
130                crate::tools::ctx_pack::handle(
131                    "pr",
132                    &project_root,
133                    base.as_deref(),
134                    format.as_deref(),
135                    depth,
136                    diff.as_deref(),
137                )
138            }
139            "create" => {
140                let name = get_str(args, "name")
141                    .ok_or_else(|| ErrorData::invalid_params("name is required for create", None))?;
142                let version = get_str(args, "version");
143                let description = get_str(args, "description");
144                let author = get_str(args, "author");
145                let tags = get_str_array(args, "tags");
146                let layers = get_str_array(args, "layers");
147                let level = get_int(args, "level").map(|l| l as u32);
148                let scope = get_str(args, "scope");
149                crate::tools::ctx_pack::handle_create(
150                    &project_root,
151                    &name,
152                    version.as_deref(),
153                    description.as_deref(),
154                    author.as_deref(),
155                    tags.as_deref(),
156                    layers.as_deref(),
157                    level,
158                    scope.as_deref(),
159                )
160            }
161            "list" => crate::tools::ctx_pack::handle_list(),
162            "info" => {
163                let name = get_str(args, "name")
164                    .ok_or_else(|| ErrorData::invalid_params("name is required for info", None))?;
165                let version = get_str(args, "version");
166                crate::tools::ctx_pack::handle_info(&name, version.as_deref())
167            }
168            "remove" => {
169                let name = get_str(args, "name")
170                    .ok_or_else(|| ErrorData::invalid_params("name is required for remove", None))?;
171                let version = get_str(args, "version");
172                crate::tools::ctx_pack::handle_remove(&name, version.as_deref())
173            }
174            "install" => {
175                let name = get_str(args, "name").ok_or_else(|| {
176                    ErrorData::invalid_params("name is required for install", None)
177                })?;
178                let version = get_str(args, "version");
179                crate::tools::ctx_pack::handle_install(&name, version.as_deref(), &project_root)
180            }
181            "export" => {
182                let name = get_str(args, "name").ok_or_else(|| {
183                    ErrorData::invalid_params("name is required for export", None)
184                })?;
185                let version = get_str(args, "version");
186                let file = get_str(args, "file");
187                crate::tools::ctx_pack::handle_export(&name, version.as_deref(), file.as_deref())
188            }
189            "import" => {
190                let file = get_str(args, "file")
191                    .ok_or_else(|| ErrorData::invalid_params("file is required for import", None))?;
192                let apply = get_bool(args, "apply").unwrap_or(false);
193                crate::tools::ctx_pack::handle_import(&file, apply, &project_root)
194            }
195            "auto_load" => {
196                let name = get_str(args, "name");
197                let version = get_str(args, "version");
198                let enable = get_bool(args, "enable").unwrap_or(true);
199                crate::tools::ctx_pack::handle_auto_load(
200                    name.as_deref(),
201                    version.as_deref(),
202                    enable,
203                )
204            }
205            "summary" => crate::tools::ctx_pack::handle_summary(&project_root),
206            _ => "Unknown action. Use: pr, create, list, info, remove, install, export, import, auto_load, summary".to_string(),
207        };
208
209        Ok(ToolOutput::simple(result))
210    }
211}