Skip to main content

lean_ctx/tools/registered/
ctx_overview.rs

1use rmcp::model::Tool;
2use rmcp::ErrorData;
3use serde_json::{json, Map, Value};
4
5use crate::server::tool_trait::{get_str, McpTool, ToolContext, ToolOutput};
6use crate::tool_defs::tool_def;
7
8pub struct CtxOverviewTool;
9
10impl McpTool for CtxOverviewTool {
11    fn name(&self) -> &'static str {
12        "ctx_overview"
13    }
14
15    fn tool_def(&self) -> Tool {
16        tool_def(
17            "ctx_overview",
18            "Task-relevant project map — use at session start.",
19            json!({
20                "type": "object",
21                "properties": {
22                    "task": {
23                        "type": "string",
24                        "description": "Task description for relevance scoring"
25                    },
26                    "path": {
27                        "type": "string",
28                        "description": "Project root directory (default: .)"
29                    }
30                }
31            }),
32        )
33    }
34
35    fn handle(
36        &self,
37        args: &Map<String, Value>,
38        ctx: &ToolContext,
39    ) -> Result<ToolOutput, ErrorData> {
40        let task = get_str(args, "task");
41
42        let resolved_path = if get_str(args, "path").is_some() {
43            if let Some(p) = ctx.resolved_path("path") {
44                Some(p.to_string())
45            } else if let Some(err) = ctx.path_error("path") {
46                return Err(ErrorData::invalid_params(format!("path: {err}"), None));
47            } else {
48                None
49            }
50        } else if let Some(ref session) = ctx.session {
51            let guard = crate::server::bounded_lock::read(session, "ctx_overview:session");
52            guard.as_ref().and_then(|g| g.project_root.clone())
53        } else {
54            None
55        };
56
57        let cache = ctx
58            .cache
59            .as_ref()
60            .ok_or_else(|| ErrorData::internal_error("cache not available", None))?;
61        let Some(guard) = crate::server::bounded_lock::read(cache, "ctx_overview:cache") else {
62            return Ok(ToolOutput::simple(
63                "[overview temporarily unavailable — cache busy]".to_string(),
64            ));
65        };
66        let result = crate::tools::ctx_overview::handle(
67            &guard,
68            task.as_deref(),
69            resolved_path.as_deref(),
70            ctx.crp_mode,
71        );
72
73        Ok(ToolOutput {
74            text: result,
75            original_tokens: 0,
76            saved_tokens: 0,
77            mode: Some("overview".to_string()),
78            path: None,
79            changed: false,
80        })
81    }
82}