lean_ctx/core/tool_catalog.rs
1// SPDX-License-Identifier: Apache-2.0
2//! The MCP tool catalog: the definitions of every registered tool.
3//!
4//! The tools themselves live in the MCP server layer, which registers its
5//! catalog here (`server::registry::register_tool_catalog`, called by the CLI
6//! at start-up and by every MCP server construction). Engine code that only
7//! needs the definitions — manifests, overhead measurement, discovery cards,
8//! docs — reads them through this module instead of building the registry.
9//!
10//! Without a registration (library use that never starts a server) the
11//! catalog is empty. It describes and counts tools; it never decides whether a
12//! call is allowed — tool admission, policy and role gates do not read it.
13//! Engine unit tests see it empty as well; the tests that measure the real
14//! catalog register it and live with the server (`server/catalog_tests.rs`).
15
16use std::sync::OnceLock;
17
18use rmcp::model::Tool;
19
20/// The registered catalog entry points.
21#[derive(Clone, Copy)]
22pub struct ToolCatalog {
23 /// The official public surface: sorted, annotated definitions of every
24 /// publicly advertised tool (what `tools/list` may offer).
25 pub tool_defs: fn() -> Vec<Tool>,
26 /// Every registered tool, including local compatibility substrate and
27 /// internal host hooks that stay callable but are never advertised.
28 pub registered_tool_defs: fn() -> Vec<Tool>,
29}
30
31static CATALOG: OnceLock<ToolCatalog> = OnceLock::new();
32
33/// Register the tool catalog. The first registration wins.
34pub fn register_tool_catalog(catalog: ToolCatalog) {
35 let _ = CATALOG.set(catalog);
36}
37
38/// Whether a catalog has been registered.
39#[must_use]
40pub fn is_registered() -> bool {
41 catalog().is_some()
42}
43
44fn catalog() -> Option<&'static ToolCatalog> {
45 CATALOG.get()
46}
47
48/// Definitions of the publicly advertised tools; empty when unregistered.
49#[must_use]
50pub fn tool_defs() -> Vec<Tool> {
51 catalog().map_or_else(Vec::new, |c| (c.tool_defs)())
52}
53
54/// Definitions of every registered tool; empty when unregistered.
55#[must_use]
56pub fn registered_tool_defs() -> Vec<Tool> {
57 catalog().map_or_else(Vec::new, |c| (c.registered_tool_defs)())
58}
59
60/// Number of official MCP tools (the public surface).
61#[must_use]
62pub fn tool_count() -> usize {
63 tool_defs().len()
64}
65
66/// Sorted names of every registered tool.
67#[must_use]
68pub fn registered_tool_names() -> Vec<String> {
69 let mut names: Vec<String> = registered_tool_defs()
70 .into_iter()
71 .map(|t| t.name.to_string())
72 .collect();
73 names.sort_unstable();
74 names
75}