vtcode_core/tools/registry/
registration.rs1use super::ToolRegistry;
2use crate::config::types::CapabilityLevel;
3use crate::tool_policy::ToolPolicy;
4use crate::tools::tool_intent::ToolBehavior;
5use crate::tools::traits::Tool;
6use futures::future::BoxFuture;
7use serde_json::Value;
8use std::path::PathBuf;
9use std::sync::Arc;
10
11pub type ToolExecutorFn =
12 for<'a> fn(&'a ToolRegistry, Value) -> BoxFuture<'a, anyhow::Result<Value>>;
13pub type NativeCgpToolFactory = Arc<
14 dyn for<'a> Fn(
15 &'a ToolRegistration,
16 PathBuf,
17 super::cgp_facade::CgpRuntimeMode,
18 ) -> Arc<dyn Tool>
19 + Send
20 + Sync,
21>;
22
23use std::fmt;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
26pub enum ToolCatalogSource {
27 Builtin,
28 Mcp,
29 #[default]
30 Dynamic,
31}
32
33#[derive(Debug, Clone, Default)]
34pub struct ToolMetadata {
35 description: Option<String>,
36 parameter_schema: Option<Value>,
37 config_schema: Option<Value>,
38 state_schema: Option<Value>,
39 prompt_path: Option<String>,
40 default_permission: Option<ToolPolicy>,
41 allowlist: Vec<String>,
42 denylist: Vec<String>,
43 aliases: Vec<String>,
44 server_hint: Option<String>,
45 behavior: Option<ToolBehavior>,
46}
47
48impl ToolMetadata {
49 pub fn with_description(mut self, description: impl Into<String>) -> Self {
50 self.description = Some(description.into());
51 self
52 }
53
54 pub fn with_parameter_schema(mut self, schema: Value) -> Self {
55 self.parameter_schema = Some(schema);
56 self
57 }
58
59 pub fn with_config_schema(mut self, schema: Value) -> Self {
60 self.config_schema = Some(schema);
61 self
62 }
63
64 pub fn with_state_schema(mut self, schema: Value) -> Self {
65 self.state_schema = Some(schema);
66 self
67 }
68
69 pub fn with_prompt_path(mut self, path: impl Into<String>) -> Self {
70 self.prompt_path = Some(path.into());
71 self
72 }
73
74 pub fn with_permission(mut self, permission: ToolPolicy) -> Self {
75 self.default_permission = Some(permission);
76 self
77 }
78
79 pub fn with_allowlist(mut self, patterns: impl IntoIterator<Item = impl Into<String>>) -> Self {
80 self.allowlist.extend(patterns.into_iter().map(Into::into));
81 self
82 }
83
84 pub fn with_denylist(mut self, patterns: impl IntoIterator<Item = impl Into<String>>) -> Self {
85 self.denylist.extend(patterns.into_iter().map(Into::into));
86 self
87 }
88
89 pub fn with_aliases(mut self, aliases: impl IntoIterator<Item = impl Into<String>>) -> Self {
90 self.aliases.extend(aliases.into_iter().map(Into::into));
91 self
92 }
93
94 pub fn with_server_hint(mut self, hint: impl Into<String>) -> Self {
95 self.server_hint = Some(hint.into());
96 self
97 }
98
99 pub fn with_behavior(mut self, behavior: ToolBehavior) -> Self {
100 self.behavior = Some(behavior);
101 self
102 }
103
104 pub fn description(&self) -> Option<&str> {
105 self.description.as_deref()
106 }
107
108 pub fn parameter_schema(&self) -> Option<&Value> {
109 self.parameter_schema.as_ref()
110 }
111
112 pub fn config_schema(&self) -> Option<&Value> {
113 self.config_schema.as_ref()
114 }
115
116 pub fn state_schema(&self) -> Option<&Value> {
117 self.state_schema.as_ref()
118 }
119
120 pub fn prompt_path(&self) -> Option<&str> {
121 self.prompt_path.as_deref()
122 }
123
124 pub fn default_permission(&self) -> Option<ToolPolicy> {
125 self.default_permission.clone()
126 }
127
128 pub fn allowlist(&self) -> &[String] {
129 &self.allowlist
130 }
131
132 pub fn denylist(&self) -> &[String] {
133 &self.denylist
134 }
135
136 pub fn aliases(&self) -> &[String] {
137 &self.aliases
138 }
139
140 pub fn server_hint(&self) -> Option<&str> {
141 self.server_hint.as_deref()
142 }
143
144 pub fn behavior(&self) -> Option<ToolBehavior> {
145 self.behavior
146 }
147}
148
149#[derive(Clone)]
150pub enum ToolHandler {
151 RegistryFn(ToolExecutorFn),
152 TraitObject(Arc<dyn Tool>),
153}
154
155impl fmt::Debug for ToolHandler {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 match self {
158 ToolHandler::RegistryFn(_) => write!(f, "ToolHandler::RegistryFn"),
159 ToolHandler::TraitObject(_) => write!(f, "ToolHandler::TraitObject"),
160 }
161 }
162}
163
164#[derive(Clone)]
165pub struct ToolRegistration {
166 name: Arc<str>,
167 capability: CapabilityLevel,
168 catalog_source: ToolCatalogSource,
169 uses_pty: bool,
170 expose_in_llm: bool,
171 deprecated: bool,
172 deprecation_message: Option<String>,
173 cgp_wrapped: bool,
174 handler: ToolHandler,
175 metadata: ToolMetadata,
176 native_cgp_factory: Option<NativeCgpToolFactory>,
177}
178
179impl fmt::Debug for ToolRegistration {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 f.debug_struct("ToolRegistration")
182 .field("name", &self.name)
183 .field("capability", &self.capability)
184 .field("catalog_source", &self.catalog_source)
185 .field("uses_pty", &self.uses_pty)
186 .field("expose_in_llm", &self.expose_in_llm)
187 .field("deprecated", &self.deprecated)
188 .field("deprecation_message", &self.deprecation_message)
189 .field("cgp_wrapped", &self.cgp_wrapped)
190 .field("handler", &self.handler)
191 .field("metadata", &self.metadata)
192 .field("has_native_cgp_factory", &self.native_cgp_factory.is_some())
193 .finish()
194 }
195}
196
197impl ToolRegistration {
198 pub fn new(
199 name: impl Into<Arc<str>>,
200 capability: CapabilityLevel,
201 uses_pty: bool,
202 executor: ToolExecutorFn,
203 ) -> Self {
204 Self {
205 name: name.into(),
206 capability,
207 catalog_source: ToolCatalogSource::Dynamic,
208 uses_pty,
209 expose_in_llm: true,
210 deprecated: false,
211 deprecation_message: None,
212 cgp_wrapped: false,
213 handler: ToolHandler::RegistryFn(executor),
214 metadata: ToolMetadata::default(),
215 native_cgp_factory: None,
216 }
217 }
218
219 pub fn from_tool(
220 name: impl Into<Arc<str>>,
221 capability: CapabilityLevel,
222 tool: Arc<dyn Tool>,
223 ) -> Self {
224 let mut metadata = ToolMetadata::default().with_description(tool.description());
225 if let Some(schema) = tool.parameter_schema() {
226 metadata = metadata.with_parameter_schema(schema);
227 }
228 if let Some(schema) = tool.config_schema() {
229 metadata = metadata.with_config_schema(schema);
230 }
231 if let Some(schema) = tool.state_schema() {
232 metadata = metadata.with_state_schema(schema);
233 }
234 if let Some(path) = tool.prompt_path() {
235 metadata = metadata.with_prompt_path(path.into_owned());
236 }
237 metadata = metadata.with_permission(tool.default_permission());
238 if let Some(patterns) = tool.allow_patterns() {
239 metadata = metadata.with_allowlist(patterns.iter().copied());
240 }
241 if let Some(patterns) = tool.deny_patterns() {
242 metadata = metadata.with_denylist(patterns.iter().copied());
243 }
244
245 Self::from_tool_with_metadata(name, capability, tool, metadata)
246 }
247
248 pub fn from_tool_with_metadata(
249 name: impl Into<Arc<str>>,
250 capability: CapabilityLevel,
251 tool: Arc<dyn Tool>,
252 metadata: ToolMetadata,
253 ) -> Self {
254 Self {
255 name: name.into(),
256 capability,
257 catalog_source: ToolCatalogSource::Dynamic,
258 uses_pty: false,
259 expose_in_llm: true,
260 deprecated: false,
261 deprecation_message: None,
262 cgp_wrapped: false,
263 handler: ToolHandler::TraitObject(tool),
264 metadata,
265 native_cgp_factory: None,
266 }
267 }
268
269 pub fn from_tool_instance<T>(
270 name: impl Into<Arc<str>>,
271 capability: CapabilityLevel,
272 tool: T,
273 ) -> Self
274 where
275 T: Tool + 'static,
276 {
277 Self::from_tool(name, capability, Arc::new(tool))
278 }
279
280 pub fn from_cgp_tool<Ctx>(
302 name: impl Into<Arc<str>>,
303 capability: CapabilityLevel,
304 facade: crate::components::ToolFacade<Ctx>,
305 ) -> Self
306 where
307 crate::components::ToolFacade<Ctx>: Tool + 'static,
308 {
309 Self::from_tool_instance(name, capability, facade).with_cgp_wrapped(true)
310 }
311
312 pub fn with_llm_visibility(mut self, expose: bool) -> Self {
313 self.expose_in_llm = expose;
314 self
315 }
316
317 pub fn with_catalog_source(mut self, catalog_source: ToolCatalogSource) -> Self {
318 self.catalog_source = catalog_source;
319 self
320 }
321
322 pub fn with_pty(mut self, uses_pty: bool) -> Self {
323 self.uses_pty = uses_pty;
324 self
325 }
326
327 pub fn with_deprecated(mut self, deprecated: bool) -> Self {
328 self.deprecated = deprecated;
329 self
330 }
331
332 pub fn with_deprecation_message(mut self, message: impl Into<String>) -> Self {
333 self.deprecation_message = Some(message.into());
334 self
335 }
336
337 pub fn with_cgp_wrapped(mut self, wrapped: bool) -> Self {
338 self.cgp_wrapped = wrapped;
339 self
340 }
341
342 pub fn with_handler(mut self, handler: ToolHandler) -> Self {
343 self.handler = handler;
344 self
345 }
346
347 pub fn with_native_cgp_factory(mut self, factory: NativeCgpToolFactory) -> Self {
348 self.native_cgp_factory = Some(factory);
349 self
350 }
351
352 pub fn name(&self) -> &str {
353 self.name.as_ref()
354 }
355
356 pub fn capability(&self) -> CapabilityLevel {
357 self.capability
358 }
359
360 pub fn catalog_source(&self) -> ToolCatalogSource {
361 self.catalog_source
362 }
363
364 pub fn uses_pty(&self) -> bool {
365 self.uses_pty
366 }
367
368 pub fn expose_in_llm(&self) -> bool {
369 self.expose_in_llm
370 }
371
372 pub fn is_deprecated(&self) -> bool {
373 self.deprecated
374 }
375
376 pub fn deprecation_message(&self) -> Option<&str> {
377 self.deprecation_message.as_deref()
378 }
379
380 pub fn is_cgp_wrapped(&self) -> bool {
381 self.cgp_wrapped
382 }
383
384 pub fn handler(&self) -> ToolHandler {
385 self.handler.clone()
386 }
387
388 pub fn native_cgp_factory(&self) -> Option<NativeCgpToolFactory> {
389 self.native_cgp_factory.clone()
390 }
391
392 pub fn metadata(&self) -> &ToolMetadata {
393 &self.metadata
394 }
395
396 pub fn parameter_schema(&self) -> Option<&Value> {
397 self.metadata.parameter_schema()
398 }
399
400 pub fn config_schema(&self) -> Option<&Value> {
401 self.metadata.config_schema()
402 }
403
404 pub fn state_schema(&self) -> Option<&Value> {
405 self.metadata.state_schema()
406 }
407
408 pub fn prompt_path(&self) -> Option<&str> {
409 self.metadata.prompt_path()
410 }
411
412 pub fn default_permission(&self) -> Option<ToolPolicy> {
413 self.metadata.default_permission()
414 }
415
416 pub fn with_metadata(mut self, metadata: ToolMetadata) -> Self {
417 self.metadata = metadata;
418 self
419 }
420
421 pub fn with_description(mut self, description: impl Into<String>) -> Self {
422 self.metadata = self.metadata.with_description(description);
423 self
424 }
425
426 pub fn with_prompt_path(mut self, path: impl Into<String>) -> Self {
427 self.metadata = self.metadata.with_prompt_path(path);
428 self
429 }
430
431 pub fn with_parameter_schema(mut self, schema: Value) -> Self {
432 self.metadata = self.metadata.with_parameter_schema(schema);
433 self
434 }
435
436 pub fn with_config_schema(mut self, schema: Value) -> Self {
437 self.metadata = self.metadata.with_config_schema(schema);
438 self
439 }
440
441 pub fn with_state_schema(mut self, schema: Value) -> Self {
442 self.metadata = self.metadata.with_state_schema(schema);
443 self
444 }
445
446 pub fn with_permission(mut self, permission: ToolPolicy) -> Self {
447 self.metadata = self.metadata.with_permission(permission);
448 self
449 }
450
451 pub fn with_allowlist(mut self, patterns: impl IntoIterator<Item = impl Into<String>>) -> Self {
452 self.metadata = self.metadata.with_allowlist(patterns);
453 self
454 }
455
456 pub fn with_denylist(mut self, patterns: impl IntoIterator<Item = impl Into<String>>) -> Self {
457 self.metadata = self.metadata.with_denylist(patterns);
458 self
459 }
460
461 pub fn with_aliases(mut self, aliases: impl IntoIterator<Item = impl Into<String>>) -> Self {
462 self.metadata = self.metadata.with_aliases(aliases);
463 self
464 }
465
466 pub fn with_server_hint(mut self, hint: impl Into<String>) -> Self {
467 self.metadata = self.metadata.with_server_hint(hint);
468 self
469 }
470
471 pub fn with_behavior(mut self, behavior: ToolBehavior) -> Self {
472 self.metadata = self.metadata.with_behavior(behavior);
473 self
474 }
475}