Skip to main content

vtcode_core/tools/registry/
registration.rs

1use 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    /// Register a tool wrapped with a CGP runtime context.
281    ///
282    /// Wraps an existing `Arc<dyn Tool>` in a CGP `ToolFacade` with the
283    /// specified runtime context's approval, metadata, sandbox, logging,
284    /// cache, and retry providers.
285    ///
286    /// Prefer `wrap_native_tool_interactive()` or `wrap_native_tool_ci()` when
287    /// the caller still owns the concrete tool instance. Use this bridge when the
288    /// tool already has genuine shared ownership.
289    ///
290    /// # Example
291    /// ```rust,ignore
292    /// use vtcode_core::components::{InteractiveCtx, ToolBridgeCtx, wrap_tool_interactive};
293    ///
294    /// let tool: Arc<dyn Tool> = Arc::new(MyTool);
295    /// let reg = ToolRegistration::from_cgp_tool(
296    ///     "my_tool",
297    ///     CapabilityLevel::Basic,
298    ///     wrap_tool_interactive(tool, workspace_root),
299    /// );
300    /// ```
301    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}