Skip to main content

lean_ctx/server/
registry.rs

1use std::collections::HashMap;
2
3use rmcp::model::Tool;
4
5use super::tool_trait::McpTool;
6
7/// Central registry mapping tool names to their trait-based handlers.
8/// Replaces the match-cascade dispatch for migrated tools while
9/// coexisting with the legacy dispatch for tools not yet migrated.
10pub struct ToolRegistry {
11    tools: HashMap<&'static str, Box<dyn McpTool>>,
12}
13
14impl ToolRegistry {
15    pub fn new() -> Self {
16        Self {
17            tools: HashMap::new(),
18        }
19    }
20
21    pub fn register(&mut self, tool: Box<dyn McpTool>) {
22        self.tools.insert(tool.name(), tool);
23    }
24
25    pub fn get(&self, name: &str) -> Option<&dyn McpTool> {
26        self.tools.get(name).map(AsRef::as_ref)
27    }
28
29    pub fn contains(&self, name: &str) -> bool {
30        self.tools.contains_key(name)
31    }
32
33    /// Returns MCP Tool definitions for all registered tools.
34    /// Used by `list_tools` to expose schemas to clients.
35    pub fn tool_defs(&self) -> Vec<Tool> {
36        let mut defs: Vec<Tool> = self.tools.values().map(|t| t.tool_def()).collect();
37        defs.sort_by(|a, b| a.name.as_ref().cmp(b.name.as_ref()));
38        defs
39    }
40
41    /// Returns tool definitions filtered by the dynamic tool state.
42    /// Only includes tools whose category is currently active.
43    pub fn active_tool_defs(&self) -> Vec<Tool> {
44        let Ok(state) = super::dynamic_tools::global().lock() else {
45            tracing::warn!("dynamic_tools mutex poisoned in active_tool_defs; returning all");
46            return self.tool_defs();
47        };
48        let mut defs: Vec<Tool> = self
49            .tools
50            .values()
51            .filter(|t| state.is_tool_active(t.name()))
52            .map(|t| t.tool_def())
53            .collect();
54        defs.sort_by(|a, b| a.name.as_ref().cmp(b.name.as_ref()));
55        defs
56    }
57
58    /// Returns tool definitions filtered by a tool profile.
59    /// Only includes tools whose name is enabled by the given profile.
60    pub fn profile_tool_defs(
61        &self,
62        profile: &crate::core::tool_profiles::ToolProfile,
63    ) -> Vec<Tool> {
64        let mut defs: Vec<Tool> = self
65            .tools
66            .values()
67            .filter(|t| profile.is_tool_enabled(t.name()))
68            .map(|t| t.tool_def())
69            .collect();
70        defs.sort_by(|a, b| a.name.as_ref().cmp(b.name.as_ref()));
71        defs
72    }
73
74    pub fn len(&self) -> usize {
75        self.tools.len()
76    }
77
78    pub fn is_empty(&self) -> bool {
79        self.tools.is_empty()
80    }
81
82    pub fn names(&self) -> Vec<&'static str> {
83        let mut names: Vec<_> = self.tools.keys().copied().collect();
84        names.sort_unstable();
85        names
86    }
87}
88
89impl Default for ToolRegistry {
90    fn default() -> Self {
91        Self::new()
92    }
93}
94
95/// Number of registered MCP tools — the single source of truth for the
96/// "N MCP tools" count shown in `--help`, the README, and the feature catalog.
97/// Deriving it here means the count can never drift from the actual registry.
98pub fn tool_count() -> usize {
99    build_registry().len()
100}
101
102/// Register all trait-based tools. Called once during server startup.
103/// Tools are added here as they are migrated from the legacy dispatch.
104pub fn build_registry() -> ToolRegistry {
105    let mut registry = ToolRegistry::new();
106
107    use crate::tools::registered;
108    registry.register(Box::new(registered::ctx_tree::CtxTreeTool));
109    registry.register(Box::new(registered::ctx_benchmark::CtxBenchmarkTool));
110    registry.register(Box::new(registered::ctx_analyze::CtxAnalyzeTool));
111    registry.register(Box::new(registered::ctx_discover::CtxDiscoverTool));
112    registry.register(Box::new(registered::ctx_response::CtxResponseTool));
113    registry.register(Box::new(registered::ctx_heatmap::CtxHeatmapTool));
114    registry.register(Box::new(registered::ctx_verify::CtxVerifyTool));
115    registry.register(Box::new(registered::ctx_outline::CtxOutlineTool));
116    registry.register(Box::new(registered::ctx_cost::CtxCostTool));
117    registry.register(Box::new(registered::ctx_gain::CtxGainTool));
118    registry.register(Box::new(registered::ctx_expand::CtxExpandTool));
119    registry.register(Box::new(registered::ctx_routes::CtxRoutesTool));
120    registry.register(Box::new(registered::ctx_call::CtxCallTool));
121    registry.register(Box::new(registered::ctx_callgraph::CtxCallgraphTool));
122    registry.register(Box::new(registered::ctx_refactor::CtxRefactorTool));
123    registry.register(Box::new(registered::ctx_repomap::CtxRepomapTool));
124    registry.register(Box::new(registered::ctx_symbol::CtxSymbolTool));
125    registry.register(Box::new(
126        registered::ctx_discover_tools::CtxDiscoverToolsTool,
127    ));
128    registry.register(Box::new(registered::ctx_review::CtxReviewTool));
129    registry.register(Box::new(registered::ctx_provider::CtxProviderTool));
130    registry.register(Box::new(registered::ctx_impact::CtxImpactTool));
131    registry.register(Box::new(registered::ctx_architecture::CtxArchitectureTool));
132    registry.register(Box::new(registered::ctx_smells::CtxSmellsTool));
133    registry.register(Box::new(registered::ctx_pack::CtxPackTool));
134    registry.register(Box::new(registered::ctx_plugins::CtxPluginsTool));
135    registry.register(Box::new(registered::ctx_rules::CtxRulesTool));
136    registry.register(Box::new(registered::ctx_index::CtxIndexTool));
137    registry.register(Box::new(registered::ctx_artifacts::CtxArtifactsTool));
138    registry.register(Box::new(
139        registered::ctx_compress_memory::CtxCompressMemoryTool,
140    ));
141    registry.register(Box::new(registered::ctx_read::CtxReadTool));
142    registry.register(Box::new(registered::ctx_multi_read::CtxMultiReadTool));
143    registry.register(Box::new(registered::ctx_multi_repo::CtxMultiRepoTool));
144    registry.register(Box::new(registered::ctx_smart_read::CtxSmartReadTool));
145    registry.register(Box::new(registered::ctx_delta::CtxDeltaTool));
146    registry.register(Box::new(registered::ctx_edit::CtxEditTool));
147    registry.register(Box::new(registered::ctx_fill::CtxFillTool));
148    registry.register(Box::new(registered::ctx_shell::CtxShellTool));
149    registry.register(Box::new(registered::ctx_search::CtxSearchTool));
150    registry.register(Box::new(registered::ctx_compose::CtxComposeTool));
151    registry.register(Box::new(registered::ctx_execute::CtxExecuteTool));
152
153    // Utility tools (migrated from dispatch/utility_tools.rs)
154    registry.register(Box::new(registered::ctx_compress::CtxCompressTool));
155    registry.register(Box::new(registered::ctx_metrics::CtxMetricsTool));
156    registry.register(Box::new(registered::ctx_radar::CtxRadarTool));
157    registry.register(Box::new(registered::ctx_dedup::CtxDedupTool));
158    registry.register(Box::new(registered::ctx_intent::CtxIntentTool));
159    registry.register(Box::new(registered::ctx_context::CtxContextTool));
160    registry.register(Box::new(registered::ctx_graph::CtxGraphTool));
161    registry.register(Box::new(registered::ctx_proof::CtxProofTool));
162    registry.register(Box::new(registered::ctx_cache::CtxCacheTool));
163    registry.register(Box::new(registered::ctx_ledger::CtxLedgerTool));
164    registry.register(Box::new(registered::ctx_retrieve::CtxRetrieveTool));
165    registry.register(Box::new(registered::ctx_overview::CtxOverviewTool));
166    registry.register(Box::new(registered::ctx_preload::CtxPreloadTool));
167    registry.register(Box::new(registered::ctx_prefetch::CtxPrefetchTool));
168    registry.register(Box::new(
169        registered::ctx_semantic_search::CtxSemanticSearchTool,
170    ));
171    registry.register(Box::new(registered::ctx_feedback::CtxFeedbackTool));
172    registry.register(Box::new(registered::ctx_control::CtxControlTool));
173    registry.register(Box::new(registered::ctx_plan::CtxPlanTool));
174    registry.register(Box::new(registered::ctx_compile::CtxCompileTool));
175
176    // Session tools (migrated from legacy dispatch)
177    registry.register(Box::new(registered::ctx_session::CtxSessionTool));
178    registry.register(Box::new(registered::ctx_knowledge::CtxKnowledgeTool));
179    registry.register(Box::new(registered::ctx_agent::CtxAgentTool));
180    registry.register(Box::new(registered::ctx_share::CtxShareTool));
181    registry.register(Box::new(registered::ctx_task::CtxTaskTool));
182    registry.register(Box::new(registered::ctx_handoff::CtxHandoffTool));
183    registry.register(Box::new(registered::ctx_workflow::CtxWorkflowTool));
184    registry.register(Box::new(registered::ctx_load_tools::CtxLoadToolsTool));
185
186    registry
187}