lean_ctx/tools/registered/
ctx_pack.rs1use rmcp::model::Tool;
2use rmcp::ErrorData;
3use serde_json::{json, Map, Value};
4
5use crate::server::tool_trait::{get_int, get_str, McpTool, ToolContext, ToolOutput};
6use crate::tool_defs::tool_def;
7
8pub struct CtxPackTool;
9
10impl McpTool for CtxPackTool {
11 fn name(&self) -> &'static str {
12 "ctx_pack"
13 }
14
15 fn tool_def(&self) -> Tool {
16 tool_def(
17 "ctx_pack",
18 "PR Context Pack. action=pr yields changed files, related tests, impact summary, and relevant context artifacts.",
19 json!({
20 "type": "object",
21 "properties": {
22 "action": {
23 "type": "string",
24 "enum": ["pr"],
25 "description": "Pack action"
26 },
27 "project_root": {
28 "type": "string",
29 "description": "Project root (default: session project root)"
30 },
31 "base": {
32 "type": "string",
33 "description": "Git base ref (default: auto-detect or HEAD~1)"
34 },
35 "format": {
36 "type": "string",
37 "enum": ["markdown", "json"],
38 "description": "Output format (default: markdown)"
39 },
40 "depth": {
41 "type": "integer",
42 "description": "Impact depth (default: 3)"
43 },
44 "diff": {
45 "type": "string",
46 "description": "Optional git diff --name-status text. If omitted, computed via git."
47 }
48 },
49 "required": ["action"]
50 }),
51 )
52 }
53
54 fn handle(
55 &self,
56 args: &Map<String, Value>,
57 ctx: &ToolContext,
58 ) -> Result<ToolOutput, ErrorData> {
59 let action = get_str(args, "action")
60 .ok_or_else(|| ErrorData::invalid_params("action is required", None))?;
61 let base = get_str(args, "base");
62 let format = get_str(args, "format");
63 let depth = get_int(args, "depth").map(|d| d as usize);
64 let diff = get_str(args, "diff");
65 let project_root = ctx
66 .resolved_path("project_root")
67 .or(ctx.resolved_path("root"))
68 .unwrap_or(&ctx.project_root);
69
70 let result = crate::tools::ctx_pack::handle(
71 &action,
72 project_root,
73 base.as_deref(),
74 format.as_deref(),
75 depth,
76 diff.as_deref(),
77 );
78
79 Ok(ToolOutput::simple(result))
80 }
81}