@frontmcp/plugins 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/package.json +8 -3
  2. package/src/cache/cache.plugin.js +27 -25
  3. package/src/cache/cache.plugin.js.map +1 -1
  4. package/src/cache/providers/cache-memory.provider.js +2 -1
  5. package/src/cache/providers/cache-memory.provider.js.map +1 -1
  6. package/src/cache/providers/cache-redis.provider.js +1 -0
  7. package/src/cache/providers/cache-redis.provider.js.map +1 -1
  8. package/src/codecall/README.md +999 -0
  9. package/src/codecall/codecall.plugin.d.ts +41 -0
  10. package/src/codecall/codecall.plugin.js +152 -0
  11. package/src/codecall/codecall.plugin.js.map +1 -0
  12. package/src/codecall/codecall.symbol.d.ts +106 -0
  13. package/src/codecall/codecall.symbol.js +4 -0
  14. package/src/codecall/codecall.symbol.js.map +1 -0
  15. package/src/codecall/codecall.types.d.ts +289 -0
  16. package/src/codecall/codecall.types.js +258 -0
  17. package/src/codecall/codecall.types.js.map +1 -0
  18. package/src/codecall/errors/index.d.ts +1 -0
  19. package/src/codecall/errors/index.js +6 -0
  20. package/src/codecall/errors/index.js.map +1 -0
  21. package/src/codecall/errors/tool-call.errors.d.ts +79 -0
  22. package/src/codecall/errors/tool-call.errors.js +119 -0
  23. package/src/codecall/errors/tool-call.errors.js.map +1 -0
  24. package/src/codecall/index.d.ts +2 -0
  25. package/src/codecall/index.js +8 -0
  26. package/src/codecall/index.js.map +1 -0
  27. package/src/codecall/providers/code-call.config.d.ts +29 -0
  28. package/src/codecall/providers/code-call.config.js +120 -0
  29. package/src/codecall/providers/code-call.config.js.map +1 -0
  30. package/src/codecall/security/index.d.ts +2 -0
  31. package/src/codecall/security/index.js +7 -0
  32. package/src/codecall/security/index.js.map +1 -0
  33. package/src/codecall/security/self-reference-guard.d.ts +32 -0
  34. package/src/codecall/security/self-reference-guard.js +70 -0
  35. package/src/codecall/security/self-reference-guard.js.map +1 -0
  36. package/src/codecall/security/tool-access-control.service.d.ts +104 -0
  37. package/src/codecall/security/tool-access-control.service.js +170 -0
  38. package/src/codecall/security/tool-access-control.service.js.map +1 -0
  39. package/src/codecall/services/audit-logger.service.d.ts +186 -0
  40. package/src/codecall/services/audit-logger.service.js +322 -0
  41. package/src/codecall/services/audit-logger.service.js.map +1 -0
  42. package/src/codecall/services/enclave.service.d.ts +62 -0
  43. package/src/codecall/services/enclave.service.js +214 -0
  44. package/src/codecall/services/enclave.service.js.map +1 -0
  45. package/src/codecall/services/error-enrichment.service.d.ts +94 -0
  46. package/src/codecall/services/error-enrichment.service.js +387 -0
  47. package/src/codecall/services/error-enrichment.service.js.map +1 -0
  48. package/src/codecall/services/index.d.ts +6 -0
  49. package/src/codecall/services/index.js +13 -0
  50. package/src/codecall/services/index.js.map +1 -0
  51. package/src/codecall/services/output-sanitizer.d.ts +86 -0
  52. package/src/codecall/services/output-sanitizer.js +260 -0
  53. package/src/codecall/services/output-sanitizer.js.map +1 -0
  54. package/src/codecall/services/synonym-expansion.service.d.ts +66 -0
  55. package/src/codecall/services/synonym-expansion.service.js +374 -0
  56. package/src/codecall/services/synonym-expansion.service.js.map +1 -0
  57. package/src/codecall/services/tool-search.service.d.ts +175 -0
  58. package/src/codecall/services/tool-search.service.js +587 -0
  59. package/src/codecall/services/tool-search.service.js.map +1 -0
  60. package/src/codecall/tools/describe.schema.d.ts +28 -0
  61. package/src/codecall/tools/describe.schema.js +67 -0
  62. package/src/codecall/tools/describe.schema.js.map +1 -0
  63. package/src/codecall/tools/describe.tool.d.ts +35 -0
  64. package/src/codecall/tools/describe.tool.js +207 -0
  65. package/src/codecall/tools/describe.tool.js.map +1 -0
  66. package/src/codecall/tools/execute.schema.d.ts +115 -0
  67. package/src/codecall/tools/execute.schema.js +116 -0
  68. package/src/codecall/tools/execute.schema.js.map +1 -0
  69. package/src/codecall/tools/execute.tool.d.ts +5 -0
  70. package/src/codecall/tools/execute.tool.js +238 -0
  71. package/src/codecall/tools/execute.tool.js.map +1 -0
  72. package/src/codecall/tools/index.d.ts +4 -0
  73. package/src/codecall/tools/index.js +13 -0
  74. package/src/codecall/tools/index.js.map +1 -0
  75. package/src/codecall/tools/invoke.schema.d.ts +99 -0
  76. package/src/codecall/tools/invoke.schema.js +27 -0
  77. package/src/codecall/tools/invoke.schema.js.map +1 -0
  78. package/src/codecall/tools/invoke.tool.d.ts +13 -0
  79. package/src/codecall/tools/invoke.tool.js +70 -0
  80. package/src/codecall/tools/invoke.tool.js.map +1 -0
  81. package/src/codecall/tools/search.schema.d.ts +30 -0
  82. package/src/codecall/tools/search.schema.js +60 -0
  83. package/src/codecall/tools/search.schema.js.map +1 -0
  84. package/src/codecall/tools/search.tool.d.ts +5 -0
  85. package/src/codecall/tools/search.tool.js +108 -0
  86. package/src/codecall/tools/search.tool.js.map +1 -0
  87. package/src/codecall/utils/describe.utils.d.ts +86 -0
  88. package/src/codecall/utils/describe.utils.js +531 -0
  89. package/src/codecall/utils/describe.utils.js.map +1 -0
  90. package/src/codecall/utils/index.d.ts +2 -0
  91. package/src/codecall/utils/index.js +7 -0
  92. package/src/codecall/utils/index.js.map +1 -0
  93. package/src/codecall/utils/mcp-result.d.ts +6 -0
  94. package/src/codecall/utils/mcp-result.js +36 -0
  95. package/src/codecall/utils/mcp-result.js.map +1 -0
  96. package/src/index.d.ts +2 -0
  97. package/src/index.js +3 -1
  98. package/src/index.js.map +1 -1
@@ -0,0 +1,587 @@
1
+ "use strict";
2
+ // file: libs/plugins/src/codecall/services/tool-search.service.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ToolSearchService = void 0;
5
+ const vectoriadb_1 = require("vectoriadb");
6
+ const synonym_expansion_service_1 = require("./synonym-expansion.service");
7
+ /**
8
+ * Universal Intent Mapping & Query Normalization
9
+ * - This module defines the semantic knowledge base for the MCP tool search engine.
10
+ * It is designed to bridge the gap between natural language user intents
11
+ * (e.g., "fix," "chat," "buy") and technical tool definitions (e.g., "update," "post," "create").
12
+ * - Key Parts:
13
+ * 1. DEFAULT_SYNONYM_GROUPS: A domain-agnostic mapping of bidirectional synonyms covering
14
+ * CRUD, DevOps, Financial, Social, and Lifecycle operations. This ensures that a
15
+ * query for "Show me the bill" matches a tool named "get_invoice".
16
+ * 2. STOP_WORDS: A curated exclusion list strictly optimized for Command-Line/Chat
17
+ * interfaces. Unlike standard NLP stop lists, this PRESERVES action verbs
18
+ * ("find", "start", "make") as they are critical signals of user intent in a
19
+ * tool-execution context.
20
+ */
21
+ const STOP_WORDS = new Set([
22
+ // Articles & Determiners
23
+ 'the',
24
+ 'a',
25
+ 'an',
26
+ 'this',
27
+ 'that',
28
+ 'these',
29
+ 'those',
30
+ // Prepositions
31
+ 'in',
32
+ 'on',
33
+ 'at',
34
+ 'to',
35
+ 'for',
36
+ 'of',
37
+ 'with',
38
+ 'by',
39
+ 'from',
40
+ 'into',
41
+ 'over',
42
+ 'after',
43
+ 'before',
44
+ 'between',
45
+ 'under',
46
+ 'about',
47
+ 'against',
48
+ 'during',
49
+ 'through',
50
+ // Conjunctions
51
+ 'and',
52
+ 'or',
53
+ 'but',
54
+ 'nor',
55
+ 'so',
56
+ 'yet',
57
+ 'as',
58
+ 'than',
59
+ 'if',
60
+ 'because',
61
+ 'while',
62
+ 'when',
63
+ 'where',
64
+ 'unless',
65
+ // Pronouns (Subject/Object/Possessive)
66
+ 'i',
67
+ 'me',
68
+ 'my',
69
+ 'mine',
70
+ 'myself',
71
+ 'you',
72
+ 'your',
73
+ 'yours',
74
+ 'yourself',
75
+ 'he',
76
+ 'him',
77
+ 'his',
78
+ 'himself',
79
+ 'she',
80
+ 'her',
81
+ 'hers',
82
+ 'herself',
83
+ 'it',
84
+ 'its',
85
+ 'itself',
86
+ 'we',
87
+ 'us',
88
+ 'our',
89
+ 'ours',
90
+ 'ourselves',
91
+ 'they',
92
+ 'them',
93
+ 'their',
94
+ 'theirs',
95
+ 'themselves',
96
+ 'who',
97
+ 'whom',
98
+ 'whose',
99
+ 'which',
100
+ 'what',
101
+ // Auxiliary/Linking Verbs (State of being is usually noise, Action is signal)
102
+ 'is',
103
+ 'was',
104
+ 'are',
105
+ 'were',
106
+ 'been',
107
+ 'be',
108
+ 'being',
109
+ 'have',
110
+ 'has',
111
+ 'had',
112
+ 'having',
113
+ 'do',
114
+ 'does',
115
+ 'did',
116
+ 'doing', // "do" is usually auxiliary ("do you have..."). "run" or "execute" is better.
117
+ 'will',
118
+ 'would',
119
+ 'shall',
120
+ 'should',
121
+ 'can',
122
+ 'could',
123
+ 'may',
124
+ 'might',
125
+ 'must',
126
+ // Quantifiers / Adverbs of degree
127
+ 'all',
128
+ 'any',
129
+ 'both',
130
+ 'each',
131
+ 'few',
132
+ 'more',
133
+ 'most',
134
+ 'other',
135
+ 'some',
136
+ 'such',
137
+ 'no',
138
+ 'nor',
139
+ 'not',
140
+ 'only',
141
+ 'own',
142
+ 'same',
143
+ 'too',
144
+ 'very',
145
+ 'just',
146
+ 'even',
147
+ // Conversational / Chat Fillers (Common in LLM prompts)
148
+ 'please',
149
+ 'pls',
150
+ 'plz',
151
+ 'thanks',
152
+ 'thank',
153
+ 'thx',
154
+ 'hello',
155
+ 'hi',
156
+ 'hey',
157
+ 'ok',
158
+ 'okay',
159
+ 'yes',
160
+ 'no',
161
+ 'actually',
162
+ 'basically',
163
+ 'literally',
164
+ 'maybe',
165
+ 'perhaps',
166
+ 'now',
167
+ 'then',
168
+ 'here',
169
+ 'there',
170
+ 'again',
171
+ 'once',
172
+ 'back', // "back" can be tricky, but usually implies direction not action
173
+ // Meta/Structural words
174
+ 'example',
175
+ 'context',
176
+ 'optionally',
177
+ 'optional', // Users rarely search for "optional", they search for the thing itself.
178
+ 'etc',
179
+ 'ie',
180
+ 'eg',
181
+ ]);
182
+ /**
183
+ * Service that maintains a searchable index of tools from the ToolRegistry
184
+ * Supports both TF-IDF (lightweight, synchronous) and ML-based (semantic) embeddings
185
+ * Implements the ToolSearch interface for dependency injection
186
+ */
187
+ class ToolSearchService {
188
+ static MAX_SUBSCRIPTION_RETRIES = 100;
189
+ vectorDB;
190
+ strategy;
191
+ initialized = false;
192
+ mlInitialized = false;
193
+ subscriptionRetries = 0;
194
+ config;
195
+ scope;
196
+ unsubscribe;
197
+ synonymService = null;
198
+ constructor(config = {}, scope) {
199
+ this.scope = scope;
200
+ const embeddingOptions = config.embeddingOptions || {
201
+ strategy: 'tfidf',
202
+ modelName: 'Xenova/all-MiniLM-L6-v2',
203
+ cacheDir: './.cache/transformers',
204
+ useHNSW: false,
205
+ synonymExpansion: { enabled: true, replaceDefaults: false, maxExpansionsPerTerm: 5 },
206
+ };
207
+ this.strategy = config.strategy || embeddingOptions.strategy || 'tfidf';
208
+ this.config = {
209
+ strategy: this.strategy,
210
+ embeddingOptions,
211
+ defaultTopK: config.defaultTopK ?? 8,
212
+ defaultSimilarityThreshold: config.defaultSimilarityThreshold ?? 0.0,
213
+ mode: config.mode ?? 'codecall_only',
214
+ includeTools: config.includeTools,
215
+ };
216
+ // Validate mode parameter at runtime
217
+ const validModes = ['codecall_only', 'codecall_opt_in', 'metadata_driven'];
218
+ if (!validModes.includes(this.config.mode)) {
219
+ throw new Error(`Invalid CodeCall mode: ${this.config.mode}. Valid modes: ${validModes.join(', ')}`);
220
+ }
221
+ // Initialize the appropriate vector database
222
+ if (this.strategy === 'ml') {
223
+ this.vectorDB = new vectoriadb_1.VectoriaDB({
224
+ modelName: embeddingOptions.modelName || 'Xenova/all-MiniLM-L6-v2',
225
+ cacheDir: embeddingOptions.cacheDir || './.cache/transformers',
226
+ defaultTopK: this.config.defaultTopK,
227
+ defaultSimilarityThreshold: this.config.defaultSimilarityThreshold,
228
+ useHNSW: embeddingOptions.useHNSW || false,
229
+ });
230
+ }
231
+ else {
232
+ this.vectorDB = new vectoriadb_1.TFIDFVectoria({
233
+ defaultTopK: this.config.defaultTopK,
234
+ defaultSimilarityThreshold: this.config.defaultSimilarityThreshold,
235
+ });
236
+ }
237
+ // Initialize synonym expansion for TF-IDF strategy (ML already handles semantic similarity)
238
+ if (config.synonymExpansion === false) {
239
+ this.synonymService = null;
240
+ }
241
+ else if (this.strategy === 'tfidf') {
242
+ const synonymConfig = typeof config.synonymExpansion === 'object' ? config.synonymExpansion : {};
243
+ // Only enable if not explicitly disabled
244
+ if (synonymConfig.enabled !== false) {
245
+ this.synonymService = new synonym_expansion_service_1.SynonymExpansionService(synonymConfig);
246
+ }
247
+ }
248
+ // Defer subscription until scope.tools is available
249
+ // During plugin initialization, scope.tools may not exist yet
250
+ this.setupSubscription();
251
+ }
252
+ /**
253
+ * Sets up subscription to tool changes. Handles the case where scope.tools
254
+ * may not be available yet during plugin initialization.
255
+ */
256
+ setupSubscription() {
257
+ // If tools registry is not yet available, retry after a microtask
258
+ if (!this.scope.tools) {
259
+ if (this.subscriptionRetries++ >= ToolSearchService.MAX_SUBSCRIPTION_RETRIES) {
260
+ console.warn('ToolSearchService: scope.tools not available after max retries');
261
+ return;
262
+ }
263
+ // Use queueMicrotask to defer until after current initialization
264
+ queueMicrotask(() => this.setupSubscription());
265
+ return;
266
+ }
267
+ // Reset retry counter on success
268
+ this.subscriptionRetries = 0;
269
+ // Subscribe to tool changes with immediate=true to get current snapshot
270
+ // This ensures tools are indexed as they become available, regardless of loading order
271
+ this.unsubscribe = this.scope.tools.subscribe({ immediate: true }, (event) => {
272
+ // Handle tool change event - reindex all tools from the snapshot
273
+ this.handleToolChange(event.snapshot);
274
+ });
275
+ }
276
+ /**
277
+ * Handles tool change events by reindexing all tools from the snapshot
278
+ */
279
+ async handleToolChange(tools) {
280
+ // Clear and rebuild index
281
+ this.vectorDB.clear();
282
+ if (tools.length === 0) {
283
+ this.initialized = true;
284
+ return;
285
+ }
286
+ // Initialize ML model if needed (first time only, and only when we have tools)
287
+ // Deferred initialization avoids async operations when there's nothing to index
288
+ if (!this.mlInitialized && this.strategy === 'ml' && this.vectorDB instanceof vectoriadb_1.VectoriaDB) {
289
+ await this.vectorDB.initialize();
290
+ this.mlInitialized = true;
291
+ }
292
+ // Filter tools based on CodeCall config and per-tool metadata
293
+ const filteredTools = tools.filter((tool) => this.shouldIndexTool(tool));
294
+ if (filteredTools.length === 0) {
295
+ this.initialized = true;
296
+ return;
297
+ }
298
+ const documents = filteredTools.map((tool) => {
299
+ const searchableText = this.extractSearchableText(tool);
300
+ const appId = this.extractAppId(tool);
301
+ const toolName = tool.name;
302
+ const qualifiedName = tool.fullName || toolName;
303
+ return {
304
+ id: toolName,
305
+ text: searchableText,
306
+ metadata: {
307
+ id: toolName,
308
+ toolName,
309
+ qualifiedName,
310
+ appId,
311
+ toolInstance: tool,
312
+ },
313
+ };
314
+ });
315
+ if (this.strategy === 'ml' && this.vectorDB instanceof vectoriadb_1.VectoriaDB) {
316
+ await this.vectorDB.addMany(documents);
317
+ }
318
+ else if (this.vectorDB instanceof vectoriadb_1.TFIDFVectoria) {
319
+ this.vectorDB.addDocuments(documents);
320
+ this.vectorDB.reindex();
321
+ }
322
+ this.initialized = true;
323
+ }
324
+ /**
325
+ * Determines if a tool should be indexed in the search database.
326
+ * Filters based on:
327
+ * - Excludes codecall:* meta-tools (they should not be searchable)
328
+ * - Mode-based filtering (codecall_only, codecall_opt_in, metadata_driven)
329
+ * - Per-tool metadata.codecall.enabledInCodeCall
330
+ * - Custom includeTools filter function
331
+ */
332
+ shouldIndexTool(tool) {
333
+ const toolName = tool.name || tool.fullName;
334
+ // Never index codecall:* meta-tools - they are for orchestration, not for search
335
+ if (toolName.startsWith('codecall:')) {
336
+ return false;
337
+ }
338
+ // Get CodeCall-specific metadata from the tool
339
+ const codecallMeta = this.getCodeCallMetadata(tool);
340
+ // Apply mode-based filtering
341
+ switch (this.config.mode) {
342
+ case 'codecall_only':
343
+ // In codecall_only mode, all non-codecall tools are searchable
344
+ // unless explicitly disabled via metadata
345
+ if (codecallMeta?.enabledInCodeCall === false) {
346
+ return false;
347
+ }
348
+ break;
349
+ case 'codecall_opt_in':
350
+ // In opt_in mode, tools must explicitly opt-in via metadata
351
+ if (codecallMeta?.enabledInCodeCall !== true) {
352
+ return false;
353
+ }
354
+ break;
355
+ case 'metadata_driven':
356
+ // In metadata_driven mode, default to enabled unless explicitly disabled
357
+ if (codecallMeta?.enabledInCodeCall === false) {
358
+ return false;
359
+ }
360
+ break;
361
+ default:
362
+ // This should never happen due to constructor validation
363
+ // but provides defense-in-depth and satisfies exhaustive checking
364
+ throw new Error(`Unknown CodeCall mode: ${this.config.mode}`);
365
+ }
366
+ // Apply custom includeTools filter if provided
367
+ if (this.config.includeTools) {
368
+ const appId = this.extractAppId(tool);
369
+ const filterInfo = {
370
+ name: toolName,
371
+ appId,
372
+ source: codecallMeta?.source,
373
+ description: tool.metadata.description,
374
+ tags: codecallMeta?.tags || tool.metadata.tags,
375
+ };
376
+ if (!this.config.includeTools(filterInfo)) {
377
+ return false;
378
+ }
379
+ }
380
+ return true;
381
+ }
382
+ /**
383
+ * Extract CodeCall-specific metadata from a tool.
384
+ */
385
+ getCodeCallMetadata(tool) {
386
+ // NOTE: `any` cast is intentional - ToolMetadata has constrained generics
387
+ return tool.metadata?.codecall;
388
+ }
389
+ /**
390
+ * Initializes the search service by indexing all tools from the registry.
391
+ * NOTE: This method is now a no-op. Initialization is handled reactively
392
+ * via subscription to tool change events in the constructor.
393
+ * This method exists for interface compatibility.
394
+ */
395
+ async initialize() {
396
+ // Initialization is now handled reactively via subscription
397
+ // The subscription with immediate=true in the constructor ensures tools are indexed
398
+ // This method exists for interface compatibility
399
+ }
400
+ /**
401
+ * Cleanup subscription when service is destroyed
402
+ */
403
+ dispose() {
404
+ if (this.unsubscribe) {
405
+ this.unsubscribe();
406
+ this.unsubscribe = undefined;
407
+ }
408
+ }
409
+ /**
410
+ * Extracts searchable text from a tool instance.
411
+ * Uses term weighting to improve relevance:
412
+ * - Description terms are heavily weighted (most important for semantic matching)
413
+ * - Tool name parts are tokenized and weighted
414
+ * - Tags provide additional context
415
+ */
416
+ extractSearchableText(tool) {
417
+ const parts = [];
418
+ // Extract and weight tool name parts
419
+ // Split on common delimiters (: - _ .) to get meaningful tokens
420
+ if (tool.name) {
421
+ const nameParts = tool.name.split(/[:\-_.]/).filter(Boolean);
422
+ // Add each part twice for moderate weighting
423
+ for (const part of nameParts) {
424
+ parts.push(part, part);
425
+ }
426
+ }
427
+ // Description is the most important for semantic matching
428
+ // Weight it heavily by repeating 3x
429
+ if (tool.metadata.description) {
430
+ const description = tool.metadata.description;
431
+ parts.push(description, description, description);
432
+ // Also extract key terms from description (words 4+ chars) for extra weight
433
+ const keyTerms = description
434
+ .toLowerCase()
435
+ .split(/\s+/)
436
+ .filter((word) => word.length >= 4 && !this.isStopWord(word));
437
+ parts.push(...keyTerms);
438
+ }
439
+ // Add tags with moderate weight (2x)
440
+ if (tool.metadata.tags && tool.metadata.tags.length > 0) {
441
+ for (const tag of tool.metadata.tags) {
442
+ parts.push(tag, tag);
443
+ }
444
+ }
445
+ // Add input schema property names (useful for parameter-based searches)
446
+ if (tool.rawInputSchema && typeof tool.rawInputSchema === 'object') {
447
+ const schema = tool.rawInputSchema;
448
+ if (schema.properties) {
449
+ parts.push(...Object.keys(schema.properties));
450
+ }
451
+ }
452
+ // Add example descriptions and input values to searchable text
453
+ // Examples help users find tools by use-case descriptions
454
+ const examples = tool.metadata?.examples;
455
+ if (examples && Array.isArray(examples)) {
456
+ for (const ex of examples) {
457
+ // Add example description (2x weight for relevance)
458
+ if (ex.description) {
459
+ parts.push(ex.description, ex.description);
460
+ }
461
+ // Add example input keys and string values
462
+ if (ex.input && typeof ex.input === 'object') {
463
+ for (const [key, value] of Object.entries(ex.input)) {
464
+ parts.push(key);
465
+ if (typeof value === 'string') {
466
+ parts.push(value);
467
+ }
468
+ }
469
+ }
470
+ }
471
+ }
472
+ return parts.join(' ');
473
+ }
474
+ /**
475
+ * Checks if a word is a common stop word that should not receive extra weighting.
476
+ * Uses module-level STOP_WORDS constant to avoid recreating the Set on each call.
477
+ */
478
+ isStopWord(word) {
479
+ return STOP_WORDS.has(word);
480
+ }
481
+ /**
482
+ * Extracts app ID from tool's owner lineage
483
+ */
484
+ extractAppId(tool) {
485
+ if (!tool.owner)
486
+ return undefined;
487
+ // The owner structure has kind and id
488
+ if (tool.owner.kind === 'app') {
489
+ return tool.owner.id;
490
+ }
491
+ // If the tool is owned by a plugin, we need to look at its parent scope
492
+ // For now, we'll return undefined and rely on the lineage if needed
493
+ return undefined;
494
+ }
495
+ /**
496
+ * Searches for tools matching the query
497
+ * Implements the ToolSearch interface
498
+ */
499
+ async search(query, options = {}) {
500
+ const { topK = this.config.defaultTopK, appIds, excludeToolNames = [] } = options;
501
+ const minScore = this.config.defaultSimilarityThreshold;
502
+ // Build filter function
503
+ const filter = (metadata) => {
504
+ // Exclude tools
505
+ if (excludeToolNames.includes(metadata.toolName)) {
506
+ return false;
507
+ }
508
+ // Filter by appId if specified
509
+ if (appIds && appIds.length > 0) {
510
+ if (!metadata.appId || !appIds.includes(metadata.appId)) {
511
+ return false;
512
+ }
513
+ }
514
+ return true;
515
+ };
516
+ // Expand query with synonyms for TF-IDF strategy to improve relevance
517
+ // For example: "add user" -> "add create new insert make user account member profile"
518
+ const effectiveQuery = this.synonymService ? this.synonymService.expandQuery(query) : query;
519
+ // Search using vectoriadb
520
+ const results = await this.vectorDB.search(effectiveQuery, {
521
+ topK,
522
+ threshold: minScore,
523
+ filter,
524
+ });
525
+ // Transform results to match the ToolSearch interface
526
+ return results.map((result) => ({
527
+ toolName: result.metadata.toolName,
528
+ appId: result.metadata.appId,
529
+ description: result.metadata.toolInstance.metadata.description || '',
530
+ relevanceScore: result.score,
531
+ }));
532
+ }
533
+ /**
534
+ * Gets all indexed tool names
535
+ */
536
+ getAllToolNames() {
537
+ if (this.vectorDB instanceof vectoriadb_1.VectoriaDB) {
538
+ return this.vectorDB.getAll().map((doc) => doc.id);
539
+ }
540
+ else {
541
+ return this.vectorDB.getAllDocumentIds();
542
+ }
543
+ }
544
+ /**
545
+ * Gets the total number of indexed tools
546
+ */
547
+ getTotalCount() {
548
+ if (this.vectorDB instanceof vectoriadb_1.VectoriaDB) {
549
+ return this.vectorDB.size();
550
+ }
551
+ else {
552
+ return this.vectorDB.getDocumentCount();
553
+ }
554
+ }
555
+ /**
556
+ * Checks if a tool exists in the index
557
+ */
558
+ hasTool(toolName) {
559
+ if (this.vectorDB instanceof vectoriadb_1.VectoriaDB) {
560
+ return this.vectorDB.has(toolName);
561
+ }
562
+ else {
563
+ return this.vectorDB.hasDocument(toolName);
564
+ }
565
+ }
566
+ /**
567
+ * Clears the entire index
568
+ */
569
+ clear() {
570
+ this.vectorDB.clear();
571
+ this.initialized = false;
572
+ }
573
+ /**
574
+ * Get the current embedding strategy
575
+ */
576
+ getStrategy() {
577
+ return this.strategy;
578
+ }
579
+ /**
580
+ * Check if the service is initialized
581
+ */
582
+ isInitialized() {
583
+ return this.initialized;
584
+ }
585
+ }
586
+ exports.ToolSearchService = ToolSearchService;
587
+ //# sourceMappingURL=tool-search.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-search.service.js","sourceRoot":"","sources":["../../../../src/codecall/services/tool-search.service.ts"],"names":[],"mappings":"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","sourcesContent":["// file: libs/plugins/src/codecall/services/tool-search.service.ts\n\nimport { ToolEntry, ScopeEntry } from '@frontmcp/sdk';\nimport { TFIDFVectoria, VectoriaDB, DocumentMetadata } from 'vectoriadb';\nimport type {\n EmbeddingStrategy,\n CodeCallEmbeddingOptions,\n CodeCallMode,\n CodeCallToolMetadata,\n} from '../codecall.types';\nimport type {\n ToolSearch,\n ToolSearchResult as SymbolToolSearchResult,\n ToolSearchOptions as SymbolToolSearchOptions,\n} from '../codecall.symbol';\nimport { SynonymExpansionService, SynonymExpansionConfig } from './synonym-expansion.service';\n\n/**\n * Universal Intent Mapping & Query Normalization\n * - This module defines the semantic knowledge base for the MCP tool search engine.\n * It is designed to bridge the gap between natural language user intents\n * (e.g., \"fix,\" \"chat,\" \"buy\") and technical tool definitions (e.g., \"update,\" \"post,\" \"create\").\n * - Key Parts:\n * 1. DEFAULT_SYNONYM_GROUPS: A domain-agnostic mapping of bidirectional synonyms covering\n * CRUD, DevOps, Financial, Social, and Lifecycle operations. This ensures that a\n * query for \"Show me the bill\" matches a tool named \"get_invoice\".\n * 2. STOP_WORDS: A curated exclusion list strictly optimized for Command-Line/Chat\n * interfaces. Unlike standard NLP stop lists, this PRESERVES action verbs\n * (\"find\", \"start\", \"make\") as they are critical signals of user intent in a\n * tool-execution context.\n */\nconst STOP_WORDS: ReadonlySet<string> = new Set([\n // Articles & Determiners\n 'the',\n 'a',\n 'an',\n 'this',\n 'that',\n 'these',\n 'those',\n\n // Prepositions\n 'in',\n 'on',\n 'at',\n 'to',\n 'for',\n 'of',\n 'with',\n 'by',\n 'from',\n 'into',\n 'over',\n 'after',\n 'before',\n 'between',\n 'under',\n 'about',\n 'against',\n 'during',\n 'through',\n\n // Conjunctions\n 'and',\n 'or',\n 'but',\n 'nor',\n 'so',\n 'yet',\n 'as',\n 'than',\n 'if',\n 'because',\n 'while',\n 'when',\n 'where',\n 'unless',\n\n // Pronouns (Subject/Object/Possessive)\n 'i',\n 'me',\n 'my',\n 'mine',\n 'myself',\n 'you',\n 'your',\n 'yours',\n 'yourself',\n 'he',\n 'him',\n 'his',\n 'himself',\n 'she',\n 'her',\n 'hers',\n 'herself',\n 'it',\n 'its',\n 'itself',\n 'we',\n 'us',\n 'our',\n 'ours',\n 'ourselves',\n 'they',\n 'them',\n 'their',\n 'theirs',\n 'themselves',\n 'who',\n 'whom',\n 'whose',\n 'which',\n 'what',\n\n // Auxiliary/Linking Verbs (State of being is usually noise, Action is signal)\n 'is',\n 'was',\n 'are',\n 'were',\n 'been',\n 'be',\n 'being',\n 'have',\n 'has',\n 'had',\n 'having',\n 'do',\n 'does',\n 'did',\n 'doing', // \"do\" is usually auxiliary (\"do you have...\"). \"run\" or \"execute\" is better.\n 'will',\n 'would',\n 'shall',\n 'should',\n 'can',\n 'could',\n 'may',\n 'might',\n 'must',\n\n // Quantifiers / Adverbs of degree\n 'all',\n 'any',\n 'both',\n 'each',\n 'few',\n 'more',\n 'most',\n 'other',\n 'some',\n 'such',\n 'no',\n 'nor',\n 'not',\n 'only',\n 'own',\n 'same',\n 'too',\n 'very',\n 'just',\n 'even',\n\n // Conversational / Chat Fillers (Common in LLM prompts)\n 'please',\n 'pls',\n 'plz',\n 'thanks',\n 'thank',\n 'thx',\n 'hello',\n 'hi',\n 'hey',\n 'ok',\n 'okay',\n 'yes',\n 'no',\n 'actually',\n 'basically',\n 'literally',\n 'maybe',\n 'perhaps',\n 'now',\n 'then',\n 'here',\n 'there',\n 'again',\n 'once',\n 'back', // \"back\" can be tricky, but usually implies direction not action\n\n // Meta/Structural words\n 'example',\n 'context',\n 'optionally',\n 'optional', // Users rarely search for \"optional\", they search for the thing itself.\n 'etc',\n 'ie',\n 'eg',\n]);\n/**\n * Metadata structure for tool documents in the vector database\n */\n// NOTE: `any` is intentional - ToolEntry has constrained generics that don't work with `unknown`\ninterface ToolMetadata extends DocumentMetadata {\n id: string;\n toolName: string;\n qualifiedName: string;\n appId?: string;\n toolInstance: ToolEntry<any, any>;\n}\n\n/**\n * Search result for tool search\n */\n// NOTE: `any` is intentional - ToolEntry has constrained generics that don't work with `unknown`\nexport interface SearchResult {\n tool: ToolEntry<any, any>;\n score: number;\n toolName: string;\n qualifiedName: string;\n appId?: string;\n}\n\n/**\n * Search options for tool search\n */\nexport interface SearchOptions {\n topK?: number;\n appIds?: string[];\n excludeToolNames?: string[];\n minScore?: number;\n}\n\n/**\n * Filter function type for including tools\n */\nexport type IncludeToolsFilter = (info: {\n name: string;\n appId?: string;\n source?: string;\n description?: string;\n tags?: string[];\n}) => boolean;\n\n/**\n * Configuration for tool search service\n */\nexport interface ToolSearchServiceConfig {\n /**\n * Embedding strategy to use\n * @default 'tfidf'\n */\n strategy?: EmbeddingStrategy;\n\n /**\n * Full embedding options (alternative to just strategy)\n */\n embeddingOptions?: CodeCallEmbeddingOptions;\n\n /**\n * Default number of results to return\n * @default 8\n */\n defaultTopK?: number;\n\n /**\n * Default similarity threshold\n * @default 0.0\n */\n defaultSimilarityThreshold?: number;\n\n /**\n * CodeCall mode for filtering tools\n * @default 'codecall_only'\n */\n mode?: CodeCallMode;\n\n /**\n * Optional filter function for including tools in the search index\n */\n includeTools?: IncludeToolsFilter;\n\n /**\n * Synonym expansion configuration.\n * When enabled, queries are expanded with synonyms to improve search relevance.\n * For example, \"add user\" will also match tools containing \"create user\".\n * Only applies when strategy is 'tfidf' (ML already handles semantic similarity).\n *\n * Set to false to disable, or provide a config object to customize.\n * @default { enabled: true } when strategy is 'tfidf'\n */\n synonymExpansion?: false | (SynonymExpansionConfig & { enabled?: boolean });\n}\n\n/**\n * Service that maintains a searchable index of tools from the ToolRegistry\n * Supports both TF-IDF (lightweight, synchronous) and ML-based (semantic) embeddings\n * Implements the ToolSearch interface for dependency injection\n */\nexport class ToolSearchService implements ToolSearch {\n private static readonly MAX_SUBSCRIPTION_RETRIES = 100;\n private vectorDB: TFIDFVectoria<ToolMetadata> | VectoriaDB<ToolMetadata>;\n private strategy: EmbeddingStrategy;\n private initialized = false;\n private mlInitialized = false;\n private subscriptionRetries = 0;\n private config: Required<Omit<ToolSearchServiceConfig, 'includeTools' | 'mode' | 'synonymExpansion'>> & {\n mode: CodeCallMode;\n includeTools?: IncludeToolsFilter;\n };\n private scope: ScopeEntry;\n private unsubscribe?: () => void;\n private synonymService: SynonymExpansionService | null = null;\n\n constructor(config: ToolSearchServiceConfig = {}, scope: ScopeEntry) {\n this.scope = scope;\n const embeddingOptions: CodeCallEmbeddingOptions = config.embeddingOptions || {\n strategy: 'tfidf',\n modelName: 'Xenova/all-MiniLM-L6-v2',\n cacheDir: './.cache/transformers',\n useHNSW: false,\n synonymExpansion: { enabled: true, replaceDefaults: false, maxExpansionsPerTerm: 5 },\n };\n this.strategy = config.strategy || embeddingOptions.strategy || 'tfidf';\n\n this.config = {\n strategy: this.strategy,\n embeddingOptions,\n defaultTopK: config.defaultTopK ?? 8,\n defaultSimilarityThreshold: config.defaultSimilarityThreshold ?? 0.0,\n mode: config.mode ?? 'codecall_only',\n includeTools: config.includeTools,\n };\n\n // Validate mode parameter at runtime\n const validModes = ['codecall_only', 'codecall_opt_in', 'metadata_driven'] as const;\n if (!validModes.includes(this.config.mode as (typeof validModes)[number])) {\n throw new Error(`Invalid CodeCall mode: ${this.config.mode}. Valid modes: ${validModes.join(', ')}`);\n }\n\n // Initialize the appropriate vector database\n if (this.strategy === 'ml') {\n this.vectorDB = new VectoriaDB<ToolMetadata>({\n modelName: embeddingOptions.modelName || 'Xenova/all-MiniLM-L6-v2',\n cacheDir: embeddingOptions.cacheDir || './.cache/transformers',\n defaultTopK: this.config.defaultTopK,\n defaultSimilarityThreshold: this.config.defaultSimilarityThreshold,\n useHNSW: embeddingOptions.useHNSW || false,\n });\n } else {\n this.vectorDB = new TFIDFVectoria<ToolMetadata>({\n defaultTopK: this.config.defaultTopK,\n defaultSimilarityThreshold: this.config.defaultSimilarityThreshold,\n });\n }\n\n // Initialize synonym expansion for TF-IDF strategy (ML already handles semantic similarity)\n if (config.synonymExpansion === false) {\n this.synonymService = null;\n } else if (this.strategy === 'tfidf') {\n const synonymConfig = typeof config.synonymExpansion === 'object' ? config.synonymExpansion : {};\n // Only enable if not explicitly disabled\n if (synonymConfig.enabled !== false) {\n this.synonymService = new SynonymExpansionService(synonymConfig);\n }\n }\n\n // Defer subscription until scope.tools is available\n // During plugin initialization, scope.tools may not exist yet\n this.setupSubscription();\n }\n\n /**\n * Sets up subscription to tool changes. Handles the case where scope.tools\n * may not be available yet during plugin initialization.\n */\n private setupSubscription(): void {\n // If tools registry is not yet available, retry after a microtask\n if (!this.scope.tools) {\n if (this.subscriptionRetries++ >= ToolSearchService.MAX_SUBSCRIPTION_RETRIES) {\n console.warn('ToolSearchService: scope.tools not available after max retries');\n return;\n }\n // Use queueMicrotask to defer until after current initialization\n queueMicrotask(() => this.setupSubscription());\n return;\n }\n // Reset retry counter on success\n this.subscriptionRetries = 0;\n\n // Subscribe to tool changes with immediate=true to get current snapshot\n // This ensures tools are indexed as they become available, regardless of loading order\n this.unsubscribe = this.scope.tools.subscribe({ immediate: true }, (event) => {\n // Handle tool change event - reindex all tools from the snapshot\n this.handleToolChange(event.snapshot as unknown as ToolEntry<any, any>[]);\n });\n }\n\n /**\n * Handles tool change events by reindexing all tools from the snapshot\n */\n private async handleToolChange(tools: ToolEntry<any, any>[]): Promise<void> {\n // Clear and rebuild index\n this.vectorDB.clear();\n\n if (tools.length === 0) {\n this.initialized = true;\n return;\n }\n\n // Initialize ML model if needed (first time only, and only when we have tools)\n // Deferred initialization avoids async operations when there's nothing to index\n if (!this.mlInitialized && this.strategy === 'ml' && this.vectorDB instanceof VectoriaDB) {\n await this.vectorDB.initialize();\n this.mlInitialized = true;\n }\n\n // Filter tools based on CodeCall config and per-tool metadata\n const filteredTools = tools.filter((tool) => this.shouldIndexTool(tool));\n\n if (filteredTools.length === 0) {\n this.initialized = true;\n return;\n }\n\n const documents = filteredTools.map((tool) => {\n const searchableText = this.extractSearchableText(tool);\n const appId = this.extractAppId(tool);\n const toolName = tool.name;\n const qualifiedName = tool.fullName || toolName;\n\n return {\n id: toolName,\n text: searchableText,\n metadata: {\n id: toolName,\n toolName,\n qualifiedName,\n appId,\n toolInstance: tool,\n },\n };\n });\n\n if (this.strategy === 'ml' && this.vectorDB instanceof VectoriaDB) {\n await this.vectorDB.addMany(documents);\n } else if (this.vectorDB instanceof TFIDFVectoria) {\n this.vectorDB.addDocuments(documents);\n this.vectorDB.reindex();\n }\n\n this.initialized = true;\n }\n\n /**\n * Determines if a tool should be indexed in the search database.\n * Filters based on:\n * - Excludes codecall:* meta-tools (they should not be searchable)\n * - Mode-based filtering (codecall_only, codecall_opt_in, metadata_driven)\n * - Per-tool metadata.codecall.enabledInCodeCall\n * - Custom includeTools filter function\n */\n private shouldIndexTool(tool: ToolEntry<any, any>): boolean {\n const toolName = tool.name || tool.fullName;\n\n // Never index codecall:* meta-tools - they are for orchestration, not for search\n if (toolName.startsWith('codecall:')) {\n return false;\n }\n\n // Get CodeCall-specific metadata from the tool\n const codecallMeta = this.getCodeCallMetadata(tool);\n\n // Apply mode-based filtering\n switch (this.config.mode) {\n case 'codecall_only':\n // In codecall_only mode, all non-codecall tools are searchable\n // unless explicitly disabled via metadata\n if (codecallMeta?.enabledInCodeCall === false) {\n return false;\n }\n break;\n\n case 'codecall_opt_in':\n // In opt_in mode, tools must explicitly opt-in via metadata\n if (codecallMeta?.enabledInCodeCall !== true) {\n return false;\n }\n break;\n\n case 'metadata_driven':\n // In metadata_driven mode, default to enabled unless explicitly disabled\n if (codecallMeta?.enabledInCodeCall === false) {\n return false;\n }\n break;\n\n default:\n // This should never happen due to constructor validation\n // but provides defense-in-depth and satisfies exhaustive checking\n throw new Error(`Unknown CodeCall mode: ${this.config.mode}`);\n }\n\n // Apply custom includeTools filter if provided\n if (this.config.includeTools) {\n const appId = this.extractAppId(tool);\n const filterInfo = {\n name: toolName,\n appId,\n source: codecallMeta?.source,\n description: tool.metadata.description,\n tags: codecallMeta?.tags || tool.metadata.tags,\n };\n\n if (!this.config.includeTools(filterInfo)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Extract CodeCall-specific metadata from a tool.\n */\n private getCodeCallMetadata(tool: ToolEntry<any, any>): CodeCallToolMetadata | undefined {\n // NOTE: `any` cast is intentional - ToolMetadata has constrained generics\n return (tool.metadata as any)?.codecall as CodeCallToolMetadata | undefined;\n }\n\n /**\n * Initializes the search service by indexing all tools from the registry.\n * NOTE: This method is now a no-op. Initialization is handled reactively\n * via subscription to tool change events in the constructor.\n * This method exists for interface compatibility.\n */\n async initialize(): Promise<void> {\n // Initialization is now handled reactively via subscription\n // The subscription with immediate=true in the constructor ensures tools are indexed\n // This method exists for interface compatibility\n }\n\n /**\n * Cleanup subscription when service is destroyed\n */\n dispose(): void {\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n }\n\n /**\n * Extracts searchable text from a tool instance.\n * Uses term weighting to improve relevance:\n * - Description terms are heavily weighted (most important for semantic matching)\n * - Tool name parts are tokenized and weighted\n * - Tags provide additional context\n */\n private extractSearchableText(tool: ToolEntry<any, any>): string {\n const parts: string[] = [];\n\n // Extract and weight tool name parts\n // Split on common delimiters (: - _ .) to get meaningful tokens\n if (tool.name) {\n const nameParts = tool.name.split(/[:\\-_.]/).filter(Boolean);\n // Add each part twice for moderate weighting\n for (const part of nameParts) {\n parts.push(part, part);\n }\n }\n\n // Description is the most important for semantic matching\n // Weight it heavily by repeating 3x\n if (tool.metadata.description) {\n const description = tool.metadata.description;\n parts.push(description, description, description);\n\n // Also extract key terms from description (words 4+ chars) for extra weight\n const keyTerms = description\n .toLowerCase()\n .split(/\\s+/)\n .filter((word) => word.length >= 4 && !this.isStopWord(word));\n parts.push(...keyTerms);\n }\n\n // Add tags with moderate weight (2x)\n if (tool.metadata.tags && tool.metadata.tags.length > 0) {\n for (const tag of tool.metadata.tags) {\n parts.push(tag, tag);\n }\n }\n\n // Add input schema property names (useful for parameter-based searches)\n if (tool.rawInputSchema && typeof tool.rawInputSchema === 'object') {\n const schema = tool.rawInputSchema as any;\n if (schema.properties) {\n parts.push(...Object.keys(schema.properties));\n }\n }\n\n // Add example descriptions and input values to searchable text\n // Examples help users find tools by use-case descriptions\n const examples = tool.metadata?.examples;\n if (examples && Array.isArray(examples)) {\n for (const ex of examples) {\n // Add example description (2x weight for relevance)\n if (ex.description) {\n parts.push(ex.description, ex.description);\n }\n // Add example input keys and string values\n if (ex.input && typeof ex.input === 'object') {\n for (const [key, value] of Object.entries(ex.input as Record<string, unknown>)) {\n parts.push(key);\n if (typeof value === 'string') {\n parts.push(value);\n }\n }\n }\n }\n }\n\n return parts.join(' ');\n }\n\n /**\n * Checks if a word is a common stop word that should not receive extra weighting.\n * Uses module-level STOP_WORDS constant to avoid recreating the Set on each call.\n */\n private isStopWord(word: string): boolean {\n return STOP_WORDS.has(word);\n }\n\n /**\n * Extracts app ID from tool's owner lineage\n */\n private extractAppId(tool: ToolEntry<any, any>): string | undefined {\n if (!tool.owner) return undefined;\n\n // The owner structure has kind and id\n if (tool.owner.kind === 'app') {\n return tool.owner.id;\n }\n\n // If the tool is owned by a plugin, we need to look at its parent scope\n // For now, we'll return undefined and rely on the lineage if needed\n return undefined;\n }\n\n /**\n * Searches for tools matching the query\n * Implements the ToolSearch interface\n */\n async search(query: string, options: SymbolToolSearchOptions = {}): Promise<SymbolToolSearchResult[]> {\n const { topK = this.config.defaultTopK, appIds, excludeToolNames = [] } = options;\n const minScore = this.config.defaultSimilarityThreshold;\n\n // Build filter function\n const filter = (metadata: ToolMetadata): boolean => {\n // Exclude tools\n if (excludeToolNames.includes(metadata.toolName)) {\n return false;\n }\n\n // Filter by appId if specified\n if (appIds && appIds.length > 0) {\n if (!metadata.appId || !appIds.includes(metadata.appId)) {\n return false;\n }\n }\n\n return true;\n };\n\n // Expand query with synonyms for TF-IDF strategy to improve relevance\n // For example: \"add user\" -> \"add create new insert make user account member profile\"\n const effectiveQuery = this.synonymService ? this.synonymService.expandQuery(query) : query;\n\n // Search using vectoriadb\n const results = await this.vectorDB.search(effectiveQuery, {\n topK,\n threshold: minScore,\n filter,\n });\n\n // Transform results to match the ToolSearch interface\n return results.map((result) => ({\n toolName: result.metadata.toolName,\n appId: result.metadata.appId,\n description: result.metadata.toolInstance.metadata.description || '',\n relevanceScore: result.score,\n }));\n }\n\n /**\n * Gets all indexed tool names\n */\n getAllToolNames(): string[] {\n if (this.vectorDB instanceof VectoriaDB) {\n return this.vectorDB.getAll().map((doc) => doc.id);\n } else {\n return this.vectorDB.getAllDocumentIds();\n }\n }\n\n /**\n * Gets the total number of indexed tools\n */\n getTotalCount(): number {\n if (this.vectorDB instanceof VectoriaDB) {\n return this.vectorDB.size();\n } else {\n return this.vectorDB.getDocumentCount();\n }\n }\n\n /**\n * Checks if a tool exists in the index\n */\n hasTool(toolName: string): boolean {\n if (this.vectorDB instanceof VectoriaDB) {\n return this.vectorDB.has(toolName);\n } else {\n return this.vectorDB.hasDocument(toolName);\n }\n }\n\n /**\n * Clears the entire index\n */\n clear(): void {\n this.vectorDB.clear();\n this.initialized = false;\n }\n\n /**\n * Get the current embedding strategy\n */\n getStrategy(): EmbeddingStrategy {\n return this.strategy;\n }\n\n /**\n * Check if the service is initialized\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n}\n"]}