Skip to main content

lean_ctx/tool_defs/
mod.rs

1use std::sync::Arc;
2
3use rmcp::model::*;
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_shell",
21    "ctx_search",
22    "ctx_tree",
23    "ctx_edit",
24    "ctx_session",
25    "ctx_knowledge",
26];
27
28pub fn lazy_tool_defs() -> Vec<Tool> {
29    let all = granular_tool_defs();
30    let mut core: Vec<Tool> = all
31        .into_iter()
32        .filter(|t| CORE_TOOL_NAMES.contains(&t.name.as_ref()))
33        .collect();
34
35    core.push(tool_def(
36        "ctx_discover_tools",
37        "Search available lean-ctx tools by keyword. Returns matching tool names + descriptions for on-demand loading.",
38        json!({
39            "type": "object",
40            "properties": {
41                "query": {
42                    "type": "string",
43                    "description": "Search keyword (e.g. 'graph', 'cost', 'workflow', 'dedup')"
44                }
45            },
46            "required": ["query"]
47        }),
48    ));
49
50    core
51}
52
53pub fn discover_tools(query: &str) -> String {
54    let all = list_all_tool_defs();
55    let query_lower = query.to_lowercase();
56    let matches: Vec<(&str, &str)> = all
57        .iter()
58        .filter(|(name, desc, _)| {
59            name.to_lowercase().contains(&query_lower) || desc.to_lowercase().contains(&query_lower)
60        })
61        .map(|(name, desc, _)| (*name, *desc))
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 short = if desc.len() > 80 { &desc[..80] } else { desc };
71        out.push_str(&format!("  {name} — {short}\n"));
72    }
73    out.push_str("\nCall the tool directly by name to use it.");
74    out
75}
76
77pub fn is_lazy_mode() -> bool {
78    std::env::var("LEAN_CTX_LAZY_TOOLS").is_ok()
79}