mcp-grocy 2.2.0 → 2.5.0
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.
- package/CHANGELOG.md +31 -0
- package/README.md +112 -32
- package/build/api/client.js +11 -13
- package/build/config/index.js +74 -20
- package/build/resources/CHANGELOG.md +31 -0
- package/build/resources/DOCS.md +8 -3
- package/build/resources/README.md +112 -32
- package/build/resources/api-reference.md +88 -88
- package/build/resources/config.md +30 -17
- package/build/resources/examples.md +120 -221
- package/build/resources/installation.md +9 -14
- package/build/resources/response-format.md +18 -13
- package/build/server/http-server.js +154 -42
- package/build/server/mcp-server.js +143 -96
- package/build/server/resources.js +37 -24
- package/build/server/tool-input-zod.js +86 -0
- package/build/tools/base.js +19 -15
- package/build/tools/household/definitions.js +47 -43
- package/build/tools/household/handlers.js +2 -2
- package/build/tools/household/index.js +2 -2
- package/build/tools/inventory/definitions.js +150 -113
- package/build/tools/inventory/handlers.js +55 -42
- package/build/tools/inventory/index.js +2 -2
- package/build/tools/module-loader.js +15 -12
- package/build/tools/recipes/definitions.js +103 -86
- package/build/tools/recipes/handlers.js +44 -38
- package/build/tools/recipes/index.js +3 -3
- package/build/tools/recipes/validations.js +8 -2
- package/build/tools/shopping/definitions.js +22 -20
- package/build/tools/shopping/handlers.js +1 -1
- package/build/tools/shopping/index.js +2 -2
- package/build/tools/system/definitions.js +25 -22
- package/build/tools/system/handlers.js +74 -12
- package/build/tools/system/index.js +2 -2
- package/build/tools/validation-helpers.js +10 -7
- package/build/types/index.js +12 -10
- package/build/utils/errors.js +10 -5
- package/build/utils/logger.js +16 -15
- package/build/version.js +2 -2
- package/package.json +38 -23
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseToolHandler } from '../base.js';
|
|
2
|
+
import { ValidationError } from '../../utils/errors.js';
|
|
2
3
|
import Fuse from 'fuse.js';
|
|
3
4
|
export class InventoryToolHandlers extends BaseToolHandler {
|
|
4
5
|
// ==================== PRODUCT MANAGEMENT ====================
|
|
@@ -46,8 +47,13 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
46
47
|
const stockEntries = await this.apiCall(`/stock/products/${productId}/entries`);
|
|
47
48
|
// Define essential fields for stock entries
|
|
48
49
|
const entryFields = [
|
|
49
|
-
'id',
|
|
50
|
-
'
|
|
50
|
+
'id',
|
|
51
|
+
'amount',
|
|
52
|
+
'best_before_date',
|
|
53
|
+
'purchased_date',
|
|
54
|
+
'stock_id',
|
|
55
|
+
'note',
|
|
56
|
+
'location_id',
|
|
51
57
|
];
|
|
52
58
|
// Filter entries to only include essential fields
|
|
53
59
|
const filteredEntries = Array.isArray(stockEntries)
|
|
@@ -68,7 +74,7 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
68
74
|
const { locationId } = args || {};
|
|
69
75
|
this.validateRequired({ locationId }, ['locationId']);
|
|
70
76
|
const data = await this.apiCall(`stock`, 'GET', undefined, {
|
|
71
|
-
queryParams: { 'query[]': `location_id=${locationId}` }
|
|
77
|
+
queryParams: { 'query[]': `location_id=${locationId}` },
|
|
72
78
|
});
|
|
73
79
|
return this.createSuccess(data);
|
|
74
80
|
});
|
|
@@ -97,7 +103,7 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
97
103
|
this.validateRequired({ productId, amount }, ['productId', 'amount']);
|
|
98
104
|
const body = {
|
|
99
105
|
amount,
|
|
100
|
-
transaction_type: spoiled ? 'inventory-correction' : 'consume'
|
|
106
|
+
transaction_type: spoiled ? 'inventory-correction' : 'consume',
|
|
101
107
|
};
|
|
102
108
|
if (locationId)
|
|
103
109
|
body.location_id = locationId;
|
|
@@ -112,11 +118,16 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
112
118
|
transferProduct = async (args) => {
|
|
113
119
|
return this.executeToolHandler(async () => {
|
|
114
120
|
const { productId, amount, fromLocationId, toLocationId, note } = args || {};
|
|
115
|
-
this.validateRequired({ productId, amount, fromLocationId, toLocationId }, [
|
|
121
|
+
this.validateRequired({ productId, amount, fromLocationId, toLocationId }, [
|
|
122
|
+
'productId',
|
|
123
|
+
'amount',
|
|
124
|
+
'fromLocationId',
|
|
125
|
+
'toLocationId',
|
|
126
|
+
]);
|
|
116
127
|
const body = {
|
|
117
128
|
amount,
|
|
118
129
|
location_id_from: fromLocationId,
|
|
119
|
-
location_id_to: toLocationId
|
|
130
|
+
location_id_to: toLocationId,
|
|
120
131
|
};
|
|
121
132
|
if (note)
|
|
122
133
|
body.note = note;
|
|
@@ -157,7 +168,7 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
157
168
|
const [productsResponse, locationsResponse, quantityUnitsResponse] = await Promise.all([
|
|
158
169
|
this.apiCall('/objects/products'),
|
|
159
170
|
this.apiCall('/objects/locations'),
|
|
160
|
-
this.apiCall('/objects/quantity_units')
|
|
171
|
+
this.apiCall('/objects/quantity_units'),
|
|
161
172
|
]);
|
|
162
173
|
const products = Array.isArray(productsResponse) ? productsResponse : [];
|
|
163
174
|
const locations = Array.isArray(locationsResponse) ? locationsResponse : [];
|
|
@@ -165,7 +176,7 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
165
176
|
const fuseOptions = {
|
|
166
177
|
keys: [
|
|
167
178
|
{ name: 'name', weight: 1.0 },
|
|
168
|
-
{ name: 'description', weight: 0.3 }
|
|
179
|
+
{ name: 'description', weight: 0.3 },
|
|
169
180
|
],
|
|
170
181
|
threshold: 0.6,
|
|
171
182
|
distance: 100,
|
|
@@ -176,7 +187,7 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
176
187
|
useExtendedSearch: false,
|
|
177
188
|
isCaseSensitive: false,
|
|
178
189
|
shouldSort: true,
|
|
179
|
-
findAllMatches: false
|
|
190
|
+
findAllMatches: false,
|
|
180
191
|
};
|
|
181
192
|
const fuse = new Fuse(products, fuseOptions);
|
|
182
193
|
const fuseResults = fuse.search(productName);
|
|
@@ -184,13 +195,13 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
184
195
|
...result.item,
|
|
185
196
|
matchScore: Math.round((1 - result.score) * 100),
|
|
186
197
|
fuseScore: result.score,
|
|
187
|
-
matches: result.matches
|
|
198
|
+
matches: result.matches,
|
|
188
199
|
}));
|
|
189
200
|
if (productMatches.length === 0) {
|
|
190
201
|
const permissiveFuse = new Fuse(products, {
|
|
191
202
|
...fuseOptions,
|
|
192
203
|
threshold: 0.8,
|
|
193
|
-
distance: 200
|
|
204
|
+
distance: 200,
|
|
194
205
|
});
|
|
195
206
|
const permissiveResults = permissiveFuse.search(productName);
|
|
196
207
|
productMatches = permissiveResults.map((result) => ({
|
|
@@ -198,14 +209,14 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
198
209
|
matchScore: Math.round((1 - result.score) * 100),
|
|
199
210
|
fuseScore: result.score,
|
|
200
211
|
matches: result.matches,
|
|
201
|
-
isPermissiveMatch: true
|
|
212
|
+
isPermissiveMatch: true,
|
|
202
213
|
}));
|
|
203
214
|
}
|
|
204
215
|
productMatches = productMatches.slice(0, 5);
|
|
205
216
|
if (productMatches.length === 0) {
|
|
206
217
|
return this.createError(`No products found matching "${productName}"`, {
|
|
207
218
|
suggestion: 'Try a different product name or check the spelling',
|
|
208
|
-
availableProducts: products.slice(0, 10).map((p) => p.name)
|
|
219
|
+
availableProducts: products.slice(0, 10).map((p) => p.name),
|
|
209
220
|
});
|
|
210
221
|
}
|
|
211
222
|
const enrichedMatches = await Promise.all(productMatches.map(async (product) => {
|
|
@@ -222,7 +233,7 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
222
233
|
amount: stockItem.amount,
|
|
223
234
|
bestBeforeDate: stockItem.best_before_date,
|
|
224
235
|
stockId: stockItem.id,
|
|
225
|
-
locationId: parseInt(stockItem.location_id)
|
|
236
|
+
locationId: parseInt(stockItem.location_id),
|
|
226
237
|
};
|
|
227
238
|
});
|
|
228
239
|
stockEntries.sort((a, b) => {
|
|
@@ -235,9 +246,7 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
235
246
|
return new Date(a.bestBeforeDate).getTime() - new Date(b.bestBeforeDate).getTime();
|
|
236
247
|
});
|
|
237
248
|
const unit = quantityUnits.find((u) => u.id == product.qu_id_stock);
|
|
238
|
-
const unitInfo = unit
|
|
239
|
-
? { id: unit.id, name: unit.name }
|
|
240
|
-
: { id: null, name: 'pieces' };
|
|
249
|
+
const unitInfo = unit ? { id: unit.id, name: unit.name } : { id: null, name: 'pieces' };
|
|
241
250
|
const hasMultipleLocations = stockEntries.length > 1 &&
|
|
242
251
|
new Set(stockEntries.map((entry) => entry.locationId)).size > 1;
|
|
243
252
|
const locationInstructions = hasMultipleLocations
|
|
@@ -249,14 +258,14 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
249
258
|
stockEntries: stockEntries,
|
|
250
259
|
totalStockAmount: productEntries.reduce((sum, s) => sum + parseFloat(s.amount || 0), 0),
|
|
251
260
|
unit: unitInfo,
|
|
252
|
-
locationInstructions
|
|
261
|
+
locationInstructions,
|
|
253
262
|
};
|
|
254
263
|
}));
|
|
255
264
|
return this.createSuccess({
|
|
256
265
|
message: `Found ${enrichedMatches.length} product matches for "${productName}" (ordered from most likely to least likely match)`,
|
|
257
266
|
productMatches: enrichedMatches,
|
|
258
267
|
allAvailableLocations: locations.map((l) => ({ id: l.id, name: l.name })),
|
|
259
|
-
instructions: 'Review the matches above. Use the exact productId and locationId from this data for any product operations (inventory_stock_entry_consume, inventory_stock_entry_transfer, inventory_transactions_purchase, inventory_transactions_adjust, etc.).'
|
|
268
|
+
instructions: 'Review the matches above. Use the exact productId and locationId from this data for any product operations (inventory_stock_entry_consume, inventory_stock_entry_transfer, inventory_transactions_purchase, inventory_transactions_adjust, etc.).',
|
|
260
269
|
});
|
|
261
270
|
});
|
|
262
271
|
};
|
|
@@ -274,10 +283,10 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
274
283
|
this.validateRequired({ stockId, productId }, ['stockId', 'productId']);
|
|
275
284
|
const stockEntryResponse = await this.apiCall(`/stock/entry/${stockId}`);
|
|
276
285
|
if (!stockEntryResponse || !stockEntryResponse.product_id) {
|
|
277
|
-
throw new
|
|
286
|
+
throw new ValidationError(`Could not resolve product ID from stock entry ${stockId}`, 'printStockEntryLabel');
|
|
278
287
|
}
|
|
279
288
|
if (stockEntryResponse.product_id !== productId) {
|
|
280
|
-
throw new
|
|
289
|
+
throw new ValidationError(`Product ID mismatch: stock entry ${stockId} belongs to product ${stockEntryResponse.product_id}, but ${productId} was provided`, 'printStockEntryLabel');
|
|
281
290
|
}
|
|
282
291
|
const result = await this.apiCall(`/stock/entry/${stockId}/printlabel`);
|
|
283
292
|
return this.createSuccess(result, 'Stock entry label printed successfully');
|
|
@@ -289,7 +298,7 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
289
298
|
if (stockAmounts.length === 1) {
|
|
290
299
|
const amount = stockAmounts[0];
|
|
291
300
|
if (typeof amount !== 'number' || amount <= 0) {
|
|
292
|
-
throw new
|
|
301
|
+
throw new ValidationError(`Invalid amount: ${amount}`, 'splitStockEntry');
|
|
293
302
|
}
|
|
294
303
|
const note = `${originalEntry.note || ''} - ${originalEntry.id} - 1`;
|
|
295
304
|
await this.apiCall(`/stock/entry/${originalEntry.id}`, 'PUT', {
|
|
@@ -298,20 +307,20 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
298
307
|
note: note,
|
|
299
308
|
best_before_date: originalEntry.best_before_date,
|
|
300
309
|
purchased_date: originalEntry.purchased_date,
|
|
301
|
-
location_id: originalEntry.location_id
|
|
310
|
+
location_id: originalEntry.location_id,
|
|
302
311
|
});
|
|
303
312
|
splitEntries.push({
|
|
304
313
|
stockId: originalEntry.id,
|
|
305
314
|
amount: amount,
|
|
306
315
|
type: 'updated',
|
|
307
|
-
unit: getUnitForm(amount)
|
|
316
|
+
unit: getUnitForm(amount),
|
|
308
317
|
});
|
|
309
318
|
}
|
|
310
319
|
else {
|
|
311
320
|
for (let i = 0; i < stockAmounts.length; i++) {
|
|
312
321
|
const amount = stockAmounts[i];
|
|
313
322
|
if (typeof amount !== 'number' || amount <= 0) {
|
|
314
|
-
throw new
|
|
323
|
+
throw new ValidationError(`Invalid amount at index ${i}: ${amount}`, 'splitStockEntry');
|
|
315
324
|
}
|
|
316
325
|
const note = `${originalEntry.note || ''} - ${originalEntry.id} - ${i + 1}`;
|
|
317
326
|
if (i === 0) {
|
|
@@ -321,13 +330,13 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
321
330
|
note: note,
|
|
322
331
|
best_before_date: originalEntry.best_before_date,
|
|
323
332
|
purchased_date: originalEntry.purchased_date,
|
|
324
|
-
location_id: originalEntry.location_id
|
|
333
|
+
location_id: originalEntry.location_id,
|
|
325
334
|
});
|
|
326
335
|
splitEntries.push({
|
|
327
336
|
stockId: originalEntry.id,
|
|
328
337
|
amount,
|
|
329
338
|
type: 'updated',
|
|
330
|
-
unit: getUnitForm(amount)
|
|
339
|
+
unit: getUnitForm(amount),
|
|
331
340
|
});
|
|
332
341
|
}
|
|
333
342
|
else {
|
|
@@ -337,21 +346,20 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
337
346
|
purchased_date: originalEntry.purchased_date,
|
|
338
347
|
transaction_type: 'purchase',
|
|
339
348
|
location_id: originalEntry.location_id,
|
|
340
|
-
note: note
|
|
349
|
+
note: note,
|
|
341
350
|
});
|
|
342
351
|
const stockId = createResponse[0].stock_id || createResponse[0].id;
|
|
343
352
|
const stockResponse = await this.apiCall('/objects/stock');
|
|
344
353
|
const stockEntries = Array.isArray(stockResponse) ? stockResponse : [];
|
|
345
|
-
const actualStockEntry = stockEntries.find((entry) => entry.product_id === originalEntry.product_id &&
|
|
346
|
-
entry.stock_id === stockId);
|
|
354
|
+
const actualStockEntry = stockEntries.find((entry) => entry.product_id === originalEntry.product_id && entry.stock_id === stockId);
|
|
347
355
|
if (!actualStockEntry) {
|
|
348
|
-
throw new
|
|
356
|
+
throw new ValidationError(`Could not find created stock entry with product_id ${originalEntry.product_id} and stock_id ${stockId}`, 'splitStockEntry');
|
|
349
357
|
}
|
|
350
358
|
splitEntries.push({
|
|
351
359
|
stockId: actualStockEntry.id,
|
|
352
360
|
amount,
|
|
353
361
|
type: 'created',
|
|
354
|
-
unit: getUnitForm(amount)
|
|
362
|
+
unit: getUnitForm(amount),
|
|
355
363
|
});
|
|
356
364
|
}
|
|
357
365
|
}
|
|
@@ -364,16 +372,16 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
364
372
|
this.validateRequired({ stockId, productId, amount }, ['stockId', 'productId', 'amount']);
|
|
365
373
|
const stockEntryResponse = await this.apiCall(`/stock/entry/${stockId}`);
|
|
366
374
|
if (!stockEntryResponse || !stockEntryResponse.product_id) {
|
|
367
|
-
throw new
|
|
375
|
+
throw new ValidationError(`Could not resolve product ID from stock entry ${stockId}`, 'consumeStockEntry');
|
|
368
376
|
}
|
|
369
377
|
if (stockEntryResponse.product_id !== productId) {
|
|
370
|
-
throw new
|
|
378
|
+
throw new ValidationError(`Product ID mismatch: stock entry ${stockId} belongs to product ${stockEntryResponse.product_id}, but ${productId} was provided`, 'consumeStockEntry');
|
|
371
379
|
}
|
|
372
380
|
const body = {
|
|
373
381
|
amount,
|
|
374
382
|
spoiled,
|
|
375
383
|
stock_entry_id: stockEntryResponse.stock_id,
|
|
376
|
-
location_id: stockEntryResponse.location_id
|
|
384
|
+
location_id: stockEntryResponse.location_id,
|
|
377
385
|
};
|
|
378
386
|
if (note)
|
|
379
387
|
body.note = note;
|
|
@@ -384,20 +392,25 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
384
392
|
transferStockEntry = async (args) => {
|
|
385
393
|
return this.executeToolHandler(async () => {
|
|
386
394
|
const { stockId, productId, amount, locationIdTo, note } = args || {};
|
|
387
|
-
this.validateRequired({ stockId, productId, amount, locationIdTo }, [
|
|
395
|
+
this.validateRequired({ stockId, productId, amount, locationIdTo }, [
|
|
396
|
+
'stockId',
|
|
397
|
+
'productId',
|
|
398
|
+
'amount',
|
|
399
|
+
'locationIdTo',
|
|
400
|
+
]);
|
|
388
401
|
const stockEntryResponse = await this.apiCall(`/stock/entry/${stockId}`);
|
|
389
402
|
if (!stockEntryResponse || !stockEntryResponse.product_id) {
|
|
390
|
-
throw new
|
|
403
|
+
throw new ValidationError(`Could not resolve product ID from stock entry ${stockId}`, 'transferStockEntry');
|
|
391
404
|
}
|
|
392
405
|
if (stockEntryResponse.product_id !== productId) {
|
|
393
|
-
throw new
|
|
406
|
+
throw new ValidationError(`Product ID mismatch: stock entry ${stockId} belongs to product ${stockEntryResponse.product_id}, but ${productId} was provided`, 'transferStockEntry');
|
|
394
407
|
}
|
|
395
408
|
const body = {
|
|
396
409
|
amount,
|
|
397
410
|
location_id_from: stockEntryResponse.location_id,
|
|
398
411
|
location_id_to: locationIdTo,
|
|
399
412
|
transaction_type: 'transfer',
|
|
400
|
-
stock_entry_id: stockEntryResponse.stock_id
|
|
413
|
+
stock_entry_id: stockEntryResponse.stock_id,
|
|
401
414
|
};
|
|
402
415
|
if (note)
|
|
403
416
|
body.note = note;
|
|
@@ -411,15 +424,15 @@ export class InventoryToolHandlers extends BaseToolHandler {
|
|
|
411
424
|
this.validateRequired({ stockId, productId, amount }, ['stockId', 'productId', 'amount']);
|
|
412
425
|
const stockEntryResponse = await this.apiCall(`/stock/entry/${stockId}`);
|
|
413
426
|
if (!stockEntryResponse || !stockEntryResponse.product_id) {
|
|
414
|
-
throw new
|
|
427
|
+
throw new ValidationError(`Could not resolve product ID from stock entry ${stockId}`, 'openStockEntry');
|
|
415
428
|
}
|
|
416
429
|
if (stockEntryResponse.product_id !== productId) {
|
|
417
|
-
throw new
|
|
430
|
+
throw new ValidationError(`Product ID mismatch: stock entry ${stockId} belongs to product ${stockEntryResponse.product_id}, but ${productId} was provided`, 'openStockEntry');
|
|
418
431
|
}
|
|
419
432
|
const body = {
|
|
420
433
|
amount,
|
|
421
434
|
stock_entry_id: stockEntryResponse.stock_id,
|
|
422
|
-
location_id: stockEntryResponse.location_id
|
|
435
|
+
location_id: stockEntryResponse.location_id,
|
|
423
436
|
};
|
|
424
437
|
if (note)
|
|
425
438
|
body.note = note;
|
|
@@ -26,7 +26,7 @@ export const inventoryModule = {
|
|
|
26
26
|
// Granular Stock Entry Operations
|
|
27
27
|
inventory_stock_entry_consume: handlers.consumeStockEntry,
|
|
28
28
|
inventory_stock_entry_transfer: handlers.transferStockEntry,
|
|
29
|
-
inventory_stock_entry_open: handlers.openStockEntry
|
|
30
|
-
}
|
|
29
|
+
inventory_stock_entry_open: handlers.openStockEntry,
|
|
30
|
+
},
|
|
31
31
|
};
|
|
32
32
|
export * from './definitions.js';
|
|
@@ -23,9 +23,9 @@ export class ModuleLoader {
|
|
|
23
23
|
try {
|
|
24
24
|
const toolsDir = __dirname;
|
|
25
25
|
this.discoveredFolders = readdirSync(toolsDir, { withFileTypes: true })
|
|
26
|
-
.filter(dirent => dirent.isDirectory())
|
|
27
|
-
.filter(dirent => !dirent.name.startsWith('.') && dirent.name !== 'node_modules')
|
|
28
|
-
.map(dirent => dirent.name);
|
|
26
|
+
.filter((dirent) => dirent.isDirectory())
|
|
27
|
+
.filter((dirent) => !dirent.name.startsWith('.') && dirent.name !== 'node_modules')
|
|
28
|
+
.map((dirent) => dirent.name);
|
|
29
29
|
logger.module(`Discovered ${this.discoveredFolders.length} tool folders`);
|
|
30
30
|
}
|
|
31
31
|
catch (error) {
|
|
@@ -41,7 +41,7 @@ export class ModuleLoader {
|
|
|
41
41
|
const folders = this.discoverFolders();
|
|
42
42
|
const toolModules = [];
|
|
43
43
|
// Load modules in parallel for better performance
|
|
44
|
-
const loadPromises = folders.map(folder => this.loadModule(folder));
|
|
44
|
+
const loadPromises = folders.map((folder) => this.loadModule(folder));
|
|
45
45
|
const results = await Promise.allSettled(loadPromises);
|
|
46
46
|
results.forEach((result, index) => {
|
|
47
47
|
const folder = folders[index];
|
|
@@ -51,7 +51,7 @@ export class ModuleLoader {
|
|
|
51
51
|
}
|
|
52
52
|
else if (result.status === 'rejected') {
|
|
53
53
|
logger.debug(`Failed to load module ${folder}`, 'MODULE', {
|
|
54
|
-
error: result.reason
|
|
54
|
+
error: result.reason,
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
});
|
|
@@ -97,11 +97,14 @@ export class ModuleLoader {
|
|
|
97
97
|
* Check if an export looks like a ToolModule
|
|
98
98
|
*/
|
|
99
99
|
static isToolModule(obj) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
if (obj === null || typeof obj !== 'object') {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const m = obj;
|
|
104
|
+
return (Array.isArray(m.definitions) &&
|
|
105
|
+
m.definitions.length > 0 &&
|
|
106
|
+
typeof m.handlers === 'object' &&
|
|
107
|
+
m.handlers !== null);
|
|
105
108
|
}
|
|
106
109
|
/**
|
|
107
110
|
* Get module from cache
|
|
@@ -125,7 +128,7 @@ export class ModuleLoader {
|
|
|
125
128
|
return {
|
|
126
129
|
total: this.moduleCache.size,
|
|
127
130
|
loaded,
|
|
128
|
-
errors
|
|
131
|
+
errors,
|
|
129
132
|
};
|
|
130
133
|
}
|
|
131
134
|
}
|
|
@@ -146,6 +149,6 @@ export async function createToolRegistry() {
|
|
|
146
149
|
getDefinitions: () => definitions,
|
|
147
150
|
getHandler: (name) => handlers[name],
|
|
148
151
|
getValidator: (name) => validators[name],
|
|
149
|
-
getToolNames: () => definitions.map(def => def.name)
|
|
152
|
+
getToolNames: () => definitions.map((def) => def.name),
|
|
150
153
|
};
|
|
151
154
|
}
|