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            }),
47        )
48    }
49
50    fn handle(
51        &self,
52        args: &Map<String, Value>,
53        ctx: &ToolContext,
54    ) -> Result<ToolOutput, ErrorData> {
55        let action = get_str(args, "action").unwrap_or_else(|| "analyze".to_string());
56        let path = get_str(args, "path");
57        let depth = get_usize(args, "depth").map(|d| d.min(64));
58        let format = get_str(args, "format");
59        let root = if let Some(p) = ctx
60            .resolved_path("root")
61            .or(ctx.resolved_path("project_root"))
62        {
63            p
64        } else if let Some(err) = ctx.path_error("root").or(ctx.path_error("project_root")) {
65            return Err(ErrorData::invalid_params(format!("root: {err}"), None));
66        } else {
67            &ctx.project_root
68        };
69
70        let result = crate::tools::ctx_impact::handle(
71            &action,
72            path.as_deref(),
73            root,
74            depth,
75            format.as_deref(),
76        );
77
78        Ok(ToolOutput::simple(result))
79    }
80}