lean_ctx_server/server/
dynamic_tools.rs1pub use lean_ctx::core::dynamic_tools::*;
6
7#[must_use]
12pub fn targets_internal_host_tool(
13 name: &str,
14 args: Option<&serde_json::Map<String, serde_json::Value>>,
15) -> bool {
16 crate::tools::registered::ctx_call::resolve(name, args)
17 .is_ok_and(|(inner, _)| INTERNAL_HOST_TOOLS.contains(&inner.as_str()))
18}
19
20#[must_use]
23pub fn withheld_hive_tool_refusal(
24 name: &str,
25 args: Option<&serde_json::Map<String, serde_json::Value>>,
26) -> Option<&'static str> {
27 crate::tools::registered::ctx_call::resolve(name, args)
28 .is_ok_and(|(inner, _)| is_withheld_hive_tool(&inner))
29 .then_some(lean_ctx_hive::TOOL_NOT_AVAILABLE)
30}
31
32#[cfg(test)]
33mod tests {
34 use super::*;
35
36 #[test]
37 fn removed_tools_point_at_a_live_public_tool() {
38 assert_eq!(
39 removed_tool("ctx_multi_read").unwrap().replacement,
40 Some("ctx_read")
41 );
42 assert!(removed_tool("ctx_read").is_none());
43 let registry = crate::server::registry::build_registry();
45 let live = registry.names();
46 for name in [
47 "ctx_smart_read",
48 "ctx_multi_read",
49 "ctx_semantic_search",
50 "ctx_symbol",
51 "ctx_overview",
52 "ctx_summary",
53 "ctx_explore",
54 "ctx_plan",
55 "ctx_compile",
56 "ctx_fill",
57 "ctx_impact",
58 "ctx_architecture",
59 "ctx_repomap",
60 "ctx_routes",
61 "ctx_retrieve",
62 "ctx_cache",
63 ] {
64 let replacement = removed_tool(name).and_then(|r| r.replacement).unwrap();
65 assert!(!live.contains(&name), "{name} is still registered");
66 assert!(
67 live.contains(&replacement) && is_publicly_advertised_tool(replacement),
68 "{name} points at {replacement}, which is not a live public tool"
69 );
70 }
71 }
72
73 #[test]
76 fn host_hook_calls_are_recognised_in_every_spelling() {
77 let args = |v: serde_json::Value| v.as_object().cloned();
78 let shape = args(serde_json::json!({"tool": "Bash", "output": "x"}));
79 assert!(targets_internal_host_tool("ctx_shape", shape.as_ref()));
80 let via_call =
81 args(serde_json::json!({"name": "ctx_shape", "arguments": {"tool": "Bash"}}));
82 assert!(targets_internal_host_tool("ctx_call", via_call.as_ref()));
83 let via_ctx = args(serde_json::json!({"tool": "shape", "output": "x"}));
84 assert!(targets_internal_host_tool("ctx", via_ctx.as_ref()));
85
86 let read = args(serde_json::json!({"name": "ctx_read", "arguments": {"path": "a"}}));
87 assert!(!targets_internal_host_tool("ctx_call", read.as_ref()));
88 assert!(!targets_internal_host_tool("ctx_read", None));
89 assert!(!targets_internal_host_tool("ctx_call", None));
90 }
91}