Skip to main content

lean_ctx/tools/registered/
ctx_impact.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, get_usize};
6use crate::tool_defs::tool_def;
7
8pub struct CtxImpactTool;
9
10impl McpTool for CtxImpactTool {
11    fn name(&self) -> &'static str {
12        "ctx_impact"
13    }
14
15    fn tool_def(&self) -> Tool {
16        tool_def(
17            "ctx_impact",
18            "Change impact analysis — assess blast radius before refactoring.\n\
19             action=analyze path=\"file.rs\" maps downstream dependents; action=diff compares git refs;\n\
20             action=chain traces from→to dependency paths. depth controls traversal (default 5).",
21            json!({
22                "type": "object",
23                "properties": {
24                    "action": {
25                        "type": "string",
26                        "enum": ["analyze", "diff", "chain", "build", "update", "status"],
27                        "description": "analyze|diff|chain|build|update|status"
28                    },
29                    "path": {
30                        "type": "string",
31                        "description": "File path or type name"
32                    },
33                    "root": {
34                        "type": "string",
35                        "description": "Project root"
36                    },
37                    "depth": {
38                        "type": "integer",
39                        "description": "Max depth (default: 5)"
40                    },
41                    "format": {
42                        "type": "string",
43                        "description": "Output format"
44                    }
45                },
46                "allOf": [
47                    {
48                        "if": {
49                            "properties": { "action": { "const": "analyze" } },
50                            "required": ["action"]
51                        },
52                        "then": {
53                            "required": ["action", "path"]
54                        }
55                    },
56                    {
57                        "if": {
58                            "properties": { "action": { "const": "chain" } },
59                            "required": ["action"]
60                        },
61                        "then": {
62                            "required": ["action", "path"]
63                        }
64                    }
65                ]
66            }),
67        )
68    }
69
70    fn handle(
71        &self,
72        args: &Map<String, Value>,
73        ctx: &ToolContext,
74    ) -> Result<ToolOutput, ErrorData> {
75        let action = get_str(args, "action").unwrap_or_else(|| "analyze".to_string());
76        let path = get_str(args, "path");
77        let depth = get_usize(args, "depth").map(|d| d.min(64));
78        let format = get_str(args, "format");
79        let root = if let Some(p) = ctx
80            .resolved_path("root")
81            .or(ctx.resolved_path("project_root"))
82        {
83            p
84        } else if let Some(err) = ctx.path_error("root").or(ctx.path_error("project_root")) {
85            return Err(ErrorData::invalid_params(format!("root: {err}"), None));
86        } else {
87            &ctx.project_root
88        };
89
90        let result = crate::tools::ctx_impact::handle(
91            &action,
92            path.as_deref(),
93            root,
94            depth,
95            format.as_deref(),
96        );
97
98        Ok(ToolOutput::simple(result))
99    }
100}