lean_ctx/tools/registered/
ctx_symbol.rs1use 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 CtxSymbolTool;
9
10impl McpTool for CtxSymbolTool {
11 fn name(&self) -> &'static str {
12 "ctx_symbol"
13 }
14
15 fn tool_def(&self) -> Tool {
16 tool_def(
17 "ctx_symbol",
18 "Read a specific symbol (function, struct, class) by name. Returns only the symbol \
19code block instead of the entire file. 90-97% fewer tokens than full file read.",
20 json!({
21 "type": "object",
22 "properties": {
23 "name": { "type": "string", "description": "Symbol name (function, struct, class, method)" },
24 "file": { "type": "string", "description": "Optional: file path to narrow search" },
25 "kind": { "type": "string", "description": "Optional: fn|struct|class|method|trait|enum" }
26 },
27 "required": ["name"]
28 }),
29 )
30 }
31
32 fn handle(
33 &self,
34 args: &Map<String, Value>,
35 ctx: &ToolContext,
36 ) -> Result<ToolOutput, ErrorData> {
37 let sym_name = get_str(args, "name")
38 .ok_or_else(|| ErrorData::invalid_params("name is required", None))?;
39 let file = get_str(args, "file");
40 let kind = get_str(args, "kind");
41
42 let (result, original) = crate::tools::ctx_symbol::handle(
43 &sym_name,
44 file.as_deref(),
45 kind.as_deref(),
46 &ctx.project_root,
47 );
48 let sent = crate::core::tokens::count_tokens(&result);
49 let saved = original.saturating_sub(sent);
50
51 Ok(ToolOutput {
52 text: result,
53 original_tokens: original,
54 saved_tokens: saved,
55 mode: kind,
56 path: file,
57 })
58 }
59}