lean_ctx/tool_defs/
mod.rs1use std::sync::Arc;
2
3use rmcp::model::Tool;
4use serde_json::{json, Map, Value};
5
6mod granular;
7pub use granular::{granular_tool_defs, list_all_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
17const CORE_TOOL_NAMES: &[&str] = &[
18 "ctx_read",
19 "ctx_multi_read",
20 "ctx_call",
21 "ctx_shell",
22 "ctx_search",
23 "ctx_tree",
24 "ctx_edit",
25 "ctx_session",
26 "ctx_knowledge",
27];
28
29pub fn lazy_tool_defs() -> Vec<Tool> {
30 let all = granular_tool_defs();
31 let mut core: Vec<Tool> = all
32 .into_iter()
33 .filter(|t| CORE_TOOL_NAMES.contains(&t.name.as_ref()))
34 .collect();
35
36 core.push(tool_def(
37 "ctx_discover_tools",
38 "Search available lean-ctx tools by keyword. Returns matching tool names + descriptions for on-demand loading.",
39 json!({
40 "type": "object",
41 "properties": {
42 "query": {
43 "type": "string",
44 "description": "Search keyword (e.g. 'graph', 'cost', 'workflow', 'dedup')"
45 }
46 },
47 "required": ["query"]
48 }),
49 ));
50
51 core
52}
53
54pub fn discover_tools(query: &str) -> String {
55 let all = list_all_tool_defs();
56 let query_lower = query.to_lowercase();
57 let matches: Vec<(&str, &str)> = all
58 .iter()
59 .filter(|(name, desc, _)| {
60 name.to_lowercase().contains(&query_lower) || desc.to_lowercase().contains(&query_lower)
61 })
62 .map(|(name, desc, _)| (*name, *desc))
63 .collect();
64
65 if matches.is_empty() {
66 return format!("No tools found matching '{query}'. Try broader terms like: graph, cost, session, search, compress, agent, workflow, gain.");
67 }
68
69 let mut out = format!("{} tools matching '{query}':\n", matches.len());
70 for (name, desc) in &matches {
71 let short = if desc.len() > 80 { &desc[..80] } else { desc };
72 out.push_str(&format!(" {name} — {short}\n"));
73 }
74 out.push_str(
75 "\nIf your MCP client registers tools only once at startup (static tools/list), \
76use ctx_call (available in lazy mode) to invoke discovered tools:\n\
77 ctx_call {\"name\":\"ctx_graph\",\"arguments\":{\"action\":\"status\"}}\n",
78 );
79 out
80}
81
82pub fn is_full_mode() -> bool {
83 std::env::var("LEAN_CTX_FULL_TOOLS").is_ok()
84 || std::env::var("LEAN_CTX_LAZY_TOOLS")
85 .is_ok_and(|v| v == "0" || v.eq_ignore_ascii_case("false"))
86}