lean_ctx/tools/registered/
ctx_architecture.rs1use rmcp::ErrorData;
2use rmcp::model::Tool;
3use serde_json::{Map, Value, json};
4
5use crate::server::tool_trait::{McpTool, ToolContext, ToolOutput, get_str};
6use crate::tool_defs::tool_def;
7
8pub struct CtxArchitectureTool;
9
10impl McpTool for CtxArchitectureTool {
11 fn name(&self) -> &'static str {
12 "ctx_architecture"
13 }
14
15 fn tool_def(&self) -> Tool {
16 tool_def(
17 "ctx_architecture",
18 "Architecture analysis — understand module structure without reading every file.\n\
19 WORKFLOW: use ctx_compose FIRST for code understanding; ctx_architecture for high-level structure.\n\
20 action=overview→high-level; clusters|communities→groupings;\n\
21 layers|cycles→dependency violations; entrypoints|hotspots→risk areas;\n\
22 health→quality; module path='src/' to zoom into a specific module.\n\
23 ANTIPATTERN: does NOT show source code — only structural relationships.",
24 json!({
25 "type": "object",
26 "properties": {
27 "action": {
28 "type": "string",
29 "enum": ["overview", "clusters", "communities", "layers", "cycles", "entrypoints", "hotspots", "health", "module"],
30 "description": "overview|clusters|communities|layers|cycles|entrypoints|hotspots|health|module"
31 },
32 "path": {
33 "type": "string",
34 "description": "Module/file path"
35 },
36 "root": {
37 "type": "string",
38 "description": "Project root"
39 },
40 "format": {
41 "type": "string",
42 "description": "Output format: text|json (default text)"
43 }
44 },
45 "allOf": [
46 {
47 "if": { "properties": { "action": { "const": "module" } }, "required": ["action"] },
48 "then": { "required": ["action", "path"] }
49 }
50 ]
51 }),
52 )
53 }
54
55 fn handle(
56 &self,
57 args: &Map<String, Value>,
58 ctx: &ToolContext,
59 ) -> Result<ToolOutput, ErrorData> {
60 let action = get_str(args, "action").unwrap_or_else(|| "overview".to_string());
61 let path = get_str(args, "path");
62 let format = get_str(args, "format");
63 let root = if let Some(p) = ctx
64 .resolved_path("root")
65 .or(ctx.resolved_path("project_root"))
66 {
67 p
68 } else if let Some(err) = ctx.path_error("root").or(ctx.path_error("project_root")) {
69 return Err(ErrorData::invalid_params(format!("root: {err}"), None));
70 } else {
71 &ctx.project_root
72 };
73
74 let result = crate::tools::ctx_architecture::handle(
75 &action,
76 path.as_deref(),
77 root,
78 format.as_deref(),
79 );
80
81 Ok(ToolOutput::simple(result))
82 }
83}