1use blazingly_json::{Map, Value, json};
2use serde::Serialize;
3
4#[derive(Debug, Clone, Serialize)]
5#[serde(rename_all = "camelCase")]
6pub struct ToolDefinition {
7 pub name: &'static str,
8 pub description: &'static str,
9 pub input_schema: Value,
10}
11
12#[must_use]
13#[allow(clippy::too_many_lines)]
14pub fn catalog() -> Vec<ToolDefinition> {
15 let mut tools = vec![
16 tool(
17 "graph_stats",
18 "Graph size, evidence and build freshness.",
19 &[],
20 ),
21 tool("get_node", "Resolve one exact graph node.", &["label"]),
22 tool(
23 "get_neighbors",
24 "Direct typed incoming and outgoing relationships.",
25 &["label"],
26 ),
27 tool(
28 "query_graph",
29 "Bounded BFS or DFS around exact or textual seeds.",
30 &[],
31 ),
32 tool("god_nodes", "Rank high-connectivity production nodes.", &[]),
33 tool(
34 "shortest_path",
35 "Shortest typed dependency path between two nodes.",
36 &["source", "target"],
37 ),
38 tool(
39 "get_dependents",
40 "Bounded transitive reverse blast radius.",
41 &["label"],
42 ),
43 tool(
44 "change_impact",
45 "Read-only Git change impact with graph evidence.",
46 &[],
47 ),
48 tool(
49 "git_history",
50 "Bounded direct Git history without launching git.",
51 &[],
52 ),
53 tool(
54 "cross_repo_git",
55 "Parallel histories, shared commits, or diffs across local repositories.",
56 &["repositories"],
57 ),
58 tool(
59 "verified_change",
60 "Composite pre-commit evidence and conservative verdict.",
61 &["task"],
62 ),
63 tool(
64 "trace_api_contract",
65 "Cross-repository HTTP, GraphQL, gRPC and event-transport contract evidence.",
66 &["backend", "clients"],
67 ),
68 tool(
69 "get_community",
70 "Return one weak graph component.",
71 &["community_id"],
72 ),
73 tool(
74 "search_code",
75 "Literal or Rust-regex repository search without ripgrep.",
76 &["query"],
77 ),
78 tool(
79 "read_source",
80 "Bounded source context by node or repository path.",
81 &[],
82 ),
83 tool(
84 "inspect_symbol",
85 "Definition, direct relationships and source evidence.",
86 &["label"],
87 ),
88 tool(
89 "context_bundle",
90 "Compact graph and source bundle for one symbol.",
91 &["label"],
92 ),
93 tool(
94 "find_duplicates",
95 "Deterministic Type-1/2/3 clone families.",
96 &[],
97 ),
98 tool(
99 "find_dead_code",
100 "Conservative unreferenced-symbol review queue.",
101 &[],
102 ),
103 tool(
104 "run_audit",
105 "Repository structure and evidence completeness audit.",
106 &[],
107 ),
108 tool(
109 "coverage_map",
110 "Measured coverage discovery or explicit static reachability.",
111 &[],
112 ),
113 tool(
114 "hot_path_review",
115 "Rank high-connectivity and large source symbols.",
116 &[],
117 ),
118 tool(
119 "list_communities",
120 "List deterministic weak graph components.",
121 &[],
122 ),
123 tool("module_map", "Production folder and dependency map.", &[]),
124 tool(
125 "list_endpoints",
126 "Inventory statically extracted HTTP endpoints.",
127 &[],
128 ),
129 tool(
130 "trace_endpoint",
131 "Resolve an endpoint and its bounded call neighborhood.",
132 &["path"],
133 ),
134 tool(
135 "rebuild_graph",
136 "Rebuild the derived in-memory graph without source writes.",
137 &[],
138 ),
139 tool(
140 "graph_diff",
141 "Compare the current snapshot with an immutable Git revision.",
142 &["base_ref"],
143 ),
144 tool(
145 "get_architecture_contract",
146 "Read or preview the local target-architecture contract.",
147 &[],
148 ),
149 tool(
150 "prepare_change",
151 "Select architecture rules for intended changed files.",
152 &["files"],
153 ),
154 tool(
155 "verify_architecture",
156 "Verify graph dependencies against the active contract.",
157 &[],
158 ),
159 tool(
160 "explain_architecture_violation",
161 "Explain one active contract violation.",
162 &["fingerprint"],
163 ),
164 tool(
165 "propose_architecture_exception",
166 "Return a reviewable exception proposal without writing it.",
167 &["fingerprint", "reason"],
168 ),
169 tool(
170 "open_repo",
171 "Retarget to another local repository.",
172 &["path"],
173 ),
174 tool(
175 "list_known_repos",
176 "List repositories opened by this server process.",
177 &[],
178 ),
179 tool(
180 "semantic_link",
181 "Build inferred semantic graph evidence from supplied vectors.",
182 &["vectors"],
183 ),
184 tool(
185 "vector_search",
186 "Exact or bounded approximate nearest-neighbor search.",
187 &["vectors", "query"],
188 ),
189 tool(
190 "seo_link_suggestions",
191 "Directional SEO internal-link evidence from supplied page profiles.",
192 &["vectors", "pages"],
193 ),
194 tool(
195 "memory_context",
196 "Compile bounded temporal memory context from supplied events.",
197 &["events", "request"],
198 ),
199 ];
200 tools.retain(|tool| capability_is_compiled(tool.name));
201 tools
202}
203
204#[must_use]
205pub fn catalog_for_profile(profile: crate::mcp::McpProfile) -> Vec<ToolDefinition> {
206 catalog()
207 .into_iter()
208 .filter(|tool| profile.allows(tool.name))
209 .collect()
210}
211
212#[allow(clippy::needless_bool)]
213fn capability_is_compiled(tool: &str) -> bool {
214 if tool == "find_duplicates" {
215 cfg!(feature = "clone")
216 } else if matches!(
217 tool,
218 "change_impact" | "git_history" | "cross_repo_git" | "verified_change" | "graph_diff"
219 ) {
220 cfg!(feature = "git")
221 } else if tool == "search_code" {
222 cfg!(feature = "search")
223 } else if matches!(tool, "semantic_link" | "seo_link_suggestions") {
224 cfg!(feature = "semantic")
225 } else if tool == "vector_search" {
226 cfg!(feature = "vector")
227 } else if tool == "memory_context" {
228 cfg!(feature = "memory")
229 } else {
230 true
231 }
232}
233
234fn tool(
235 tool_name: &'static str,
236 description: &'static str,
237 required: &[&'static str],
238) -> ToolDefinition {
239 let mut properties = Map::from_iter([(
240 "output_format".to_owned(),
241 json!({
242 "type": "string",
243 "enum": ["text", "json"],
244 "default": "json"
245 }),
246 )]);
247 for name in super::catalog_schema::optional_fields(tool_name) {
248 properties.insert(
249 (*name).to_owned(),
250 super::catalog_schema::field_schema(tool_name, name),
251 );
252 }
253 for name in required {
254 let schema = super::catalog_schema::field_schema(tool_name, name);
255 properties.insert((*name).to_owned(), schema);
256 }
257 let required = required
258 .iter()
259 .map(|value| json!(value))
260 .collect::<Vec<_>>();
261 ToolDefinition {
262 name: tool_name,
263 description,
264 input_schema: json!({
265 "type": "object",
266 "additionalProperties": true,
267 "properties": properties,
268 "required": required
269 }),
270 }
271}