Skip to main content

lean_ctx/tools/registered/
ctx_callgraph.rs

1use rmcp::ErrorData;
2use rmcp::model::Tool;
3use serde_json::{Map, Value, json};
4
5use crate::server::tool_trait::{McpTool, ToolContext, ToolOutput, get_int, get_str};
6use crate::tool_defs::tool_def;
7
8pub struct CtxCallgraphTool;
9
10impl McpTool for CtxCallgraphTool {
11    fn name(&self) -> &'static str {
12        "ctx_callgraph"
13    }
14
15    fn tool_def(&self) -> Tool {
16        tool_def(
17            "ctx_callgraph",
18            "Callers/callees analysis — who calls a function and what it calls.\n\
19             action=callers symbol='fn' returns every call site with file:line.\n\
20             For END-TO-END flow tracing (how does X reach Y), use ctx_compose FIRST\n\
21             — one call returns the path + source. Use ctx_callgraph only when you need\n\
22             exhaustive enumeration of ALL callers/callees for a single symbol.\n\
23             action=trace from→to finds path between two symbols. depth=N for BFS depth.",
24            json!({
25                "type": "object",
26                "properties": {
27                    "action": {
28                        "type": "string",
29                        "description": "callers|callees|trace|risk",
30                        "enum": ["callers", "callees", "trace", "risk"]
31                    },
32                    "symbol": {
33                        "type": "string",
34                        "description": "Symbol name"
35                    },
36                    "file": {
37                        "type": "string",
38                        "description": "Scope results to file"
39                    },
40                    "depth": {
41                        "type": "integer",
42                        "description": "BFS depth (1-5)",
43                        "minimum": 1,
44                        "maximum": 5
45                    },
46                    "from": {
47                        "type": "string",
48                        "description": "Source symbol"
49                    },
50                    "to": {
51                        "type": "string",
52                        "description": "Target symbol"
53                    }
54                }
55            }),
56        )
57    }
58
59    fn handle(
60        &self,
61        args: &Map<String, Value>,
62        ctx: &ToolContext,
63    ) -> Result<ToolOutput, ErrorData> {
64        let action = get_str(args, "action").unwrap_or_else(|| "callers".to_string());
65
66        let action_normalized = match action.to_lowercase().as_str() {
67            "callers" | "caller" => "callers",
68            "callees" | "callee" => "callees",
69            "trace" => "trace",
70            "risk" => "risk",
71            _ => action.as_str(),
72        }
73        .to_string();
74
75        let symbol = get_str(args, "symbol");
76        let file = get_str(args, "file");
77        let depth = get_int(args, "depth").unwrap_or(1).clamp(1, 5) as usize;
78        let from = get_str(args, "from");
79        let to = get_str(args, "to");
80
81        let result = crate::tools::ctx_callgraph::handle(
82            &action_normalized,
83            symbol.as_deref(),
84            file.as_deref(),
85            &ctx.project_root,
86            depth,
87            from.as_deref(),
88            to.as_deref(),
89        );
90
91        Ok(ToolOutput {
92            text: result,
93            original_tokens: 0,
94            saved_tokens: 0,
95            mode: Some(action_normalized),
96            path: None,
97            changed: false,
98            shell_outcome: None,
99        })
100    }
101}