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