lean_ctx/tool_defs/
mod.rs1use std::sync::Arc;
2
3use rmcp::model::Tool;
4use serde_json::{Map, Value};
5
6mod granular;
7pub use granular::{granular_tool_defs, unified_tool_defs};
8
9pub fn tool_def(name: &'static str, description: &'static str, schema_value: Value) -> Tool {
10 let schema: Map<String, Value> = match schema_value {
11 Value::Object(map) => map,
12 _ => Map::new(),
13 };
14 Tool::new(name, description, Arc::new(schema))
15}
16
17pub const CORE_TOOL_NAMES: &[&str] = &[
18 "ctx_read",
19 "ctx_search",
20 "ctx_shell",
21 "shell",
22 "ctx_tree",
23 "ctx_edit",
24 "ctx_session",
25 "ctx_knowledge",
26 "ctx_overview",
27 "ctx_graph",
28 "ctx_call",
29 "ctx_provider",
30 "ctx_expand",
31];
32
33pub fn core_tool_names() -> &'static [&'static str] {
34 CORE_TOOL_NAMES
35}
36
37pub fn lazy_tool_defs() -> Vec<Tool> {
38 let all = granular_tool_defs();
39 all.into_iter()
40 .filter(|t| CORE_TOOL_NAMES.contains(&t.name.as_ref()))
41 .collect()
42}
43
44pub fn discover_tools(query: &str) -> String {
45 let all = crate::server::registry::build_registry().tool_defs();
48 let query_lower = query.to_lowercase();
49 let matches: Vec<(String, String)> = all
50 .iter()
51 .filter_map(|t| {
52 let name = t.name.as_ref();
53 let desc = t.description.as_deref().unwrap_or("");
54 if name.to_lowercase().contains(&query_lower)
55 || desc.to_lowercase().contains(&query_lower)
56 {
57 Some((name.to_string(), desc.to_string()))
58 } else {
59 None
60 }
61 })
62 .collect();
63
64 if matches.is_empty() {
65 return format!("No tools found matching '{query}'. Try broader terms like: graph, cost, session, search, compress, agent, workflow, gain.");
66 }
67
68 let mut out = format!("{} tools matching '{query}':\n", matches.len());
69 for (name, desc) in &matches {
70 let first = desc.lines().next().unwrap_or(desc);
72 let short = if first.len() > 80 {
73 &first[..first.floor_char_boundary(80)]
74 } else {
75 first
76 };
77 out.push_str(&format!(" {name} — {short}\n"));
78 }
79 out.push_str(
80 "\nIf your MCP client registers tools only once at startup (static tools/list), \
81use ctx_call (available in lazy mode) to invoke discovered tools:\n\
82 ctx_call {\"name\":\"ctx_graph\",\"arguments\":{\"action\":\"status\"}}\n",
83 );
84 out
85}
86
87pub fn is_full_mode() -> bool {
88 std::env::var("LEAN_CTX_FULL_TOOLS").is_ok_and(|v| v != "0" && !v.eq_ignore_ascii_case("false"))
89 || std::env::var("LEAN_CTX_LAZY_TOOLS")
90 .is_ok_and(|v| v == "0" || v.eq_ignore_ascii_case("false"))
91}