converse-mcp-server 2.3.1 → 2.4.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.
- package/README.md +771 -738
- package/docs/API.md +10 -1
- package/docs/PROVIDERS.md +8 -4
- package/package.json +12 -12
- package/src/async/asyncJobStore.js +82 -52
- package/src/async/eventBus.js +25 -20
- package/src/async/fileCache.js +121 -40
- package/src/async/jobRunner.js +65 -39
- package/src/async/providerStreamNormalizer.js +203 -117
- package/src/config.js +374 -102
- package/src/continuationStore.js +32 -24
- package/src/index.js +45 -25
- package/src/prompts/helpPrompt.js +328 -305
- package/src/providers/anthropic.js +303 -119
- package/src/providers/codex.js +103 -45
- package/src/providers/deepseek.js +24 -8
- package/src/providers/google.js +337 -93
- package/src/providers/index.js +1 -1
- package/src/providers/interface.js +16 -11
- package/src/providers/mistral.js +179 -69
- package/src/providers/openai-compatible.js +231 -94
- package/src/providers/openai.js +1094 -914
- package/src/providers/openrouter-endpoints-client.js +220 -216
- package/src/providers/openrouter.js +426 -381
- package/src/providers/xai.js +153 -56
- package/src/resources/helpResource.js +70 -67
- package/src/router.js +95 -67
- package/src/services/summarizationService.js +51 -24
- package/src/systemPrompts.js +89 -89
- package/src/tools/cancelJob.js +31 -19
- package/src/tools/chat.js +997 -883
- package/src/tools/checkStatus.js +86 -65
- package/src/tools/consensus.js +400 -234
- package/src/tools/index.js +39 -16
- package/src/transport/httpTransport.js +82 -55
- package/src/utils/contextProcessor.js +54 -37
- package/src/utils/errorHandler.js +95 -45
- package/src/utils/fileValidator.js +107 -98
- package/src/utils/formatStatus.js +122 -64
- package/src/utils/logger.js +459 -449
- package/src/utils/pathUtils.js +2 -2
- package/src/utils/tokenLimiter.js +216 -216
|
@@ -61,14 +61,19 @@ export const ERROR_CODES = {
|
|
|
61
61
|
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
62
62
|
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
63
63
|
TIMEOUT_ERROR: 'TIMEOUT_ERROR',
|
|
64
|
-
NETWORK_ERROR: 'NETWORK_ERROR'
|
|
64
|
+
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
68
|
* Base error class for structured error handling
|
|
69
69
|
*/
|
|
70
70
|
export class ConverseMCPError extends Error {
|
|
71
|
-
constructor(
|
|
71
|
+
constructor(
|
|
72
|
+
message,
|
|
73
|
+
code = ERROR_CODES.UNKNOWN_ERROR,
|
|
74
|
+
details = {},
|
|
75
|
+
statusCode = 500,
|
|
76
|
+
) {
|
|
72
77
|
super(message);
|
|
73
78
|
this.name = 'ConverseMCPError';
|
|
74
79
|
this.code = code;
|
|
@@ -94,7 +99,7 @@ export class ConverseMCPError extends Error {
|
|
|
94
99
|
details: this.details,
|
|
95
100
|
statusCode: this.statusCode,
|
|
96
101
|
timestamp: this.timestamp,
|
|
97
|
-
stack: this.stack
|
|
102
|
+
stack: this.stack,
|
|
98
103
|
};
|
|
99
104
|
}
|
|
100
105
|
|
|
@@ -107,8 +112,8 @@ export class ConverseMCPError extends Error {
|
|
|
107
112
|
content: [
|
|
108
113
|
{
|
|
109
114
|
type: 'text',
|
|
110
|
-
text: this.message
|
|
111
|
-
}
|
|
115
|
+
text: this.message,
|
|
116
|
+
},
|
|
112
117
|
],
|
|
113
118
|
isError: true,
|
|
114
119
|
error: {
|
|
@@ -116,8 +121,8 @@ export class ConverseMCPError extends Error {
|
|
|
116
121
|
code: this.code,
|
|
117
122
|
message: this.message,
|
|
118
123
|
details: this.details,
|
|
119
|
-
timestamp: this.timestamp
|
|
120
|
-
}
|
|
124
|
+
timestamp: this.timestamp,
|
|
125
|
+
},
|
|
121
126
|
};
|
|
122
127
|
}
|
|
123
128
|
}
|
|
@@ -126,7 +131,12 @@ export class ConverseMCPError extends Error {
|
|
|
126
131
|
* Provider-specific error class
|
|
127
132
|
*/
|
|
128
133
|
export class ProviderError extends ConverseMCPError {
|
|
129
|
-
constructor(
|
|
134
|
+
constructor(
|
|
135
|
+
message,
|
|
136
|
+
code = ERROR_CODES.PROVIDER_ERROR,
|
|
137
|
+
details = {},
|
|
138
|
+
provider = 'unknown',
|
|
139
|
+
) {
|
|
130
140
|
super(message, code, { ...details, provider }, 503);
|
|
131
141
|
this.name = 'ProviderError';
|
|
132
142
|
this.provider = provider;
|
|
@@ -137,7 +147,12 @@ export class ProviderError extends ConverseMCPError {
|
|
|
137
147
|
* Tool-specific error class
|
|
138
148
|
*/
|
|
139
149
|
export class ToolError extends ConverseMCPError {
|
|
140
|
-
constructor(
|
|
150
|
+
constructor(
|
|
151
|
+
message,
|
|
152
|
+
code = ERROR_CODES.TOOL_ERROR,
|
|
153
|
+
details = {},
|
|
154
|
+
toolName = 'unknown',
|
|
155
|
+
) {
|
|
141
156
|
super(message, code, { ...details, toolName }, 400);
|
|
142
157
|
this.name = 'ToolError';
|
|
143
158
|
this.toolName = toolName;
|
|
@@ -182,15 +197,20 @@ export class ContextError extends ConverseMCPError {
|
|
|
182
197
|
* @param {object} details - Additional details
|
|
183
198
|
* @returns {ConverseMCPError} Enhanced error
|
|
184
199
|
*/
|
|
185
|
-
export function wrapError(
|
|
200
|
+
export function wrapError(
|
|
201
|
+
originalError,
|
|
202
|
+
message,
|
|
203
|
+
code = ERROR_CODES.UNKNOWN_ERROR,
|
|
204
|
+
details = {},
|
|
205
|
+
) {
|
|
186
206
|
const enhancedDetails = {
|
|
187
207
|
...details,
|
|
188
208
|
originalError: {
|
|
189
209
|
name: originalError.name,
|
|
190
210
|
message: originalError.message,
|
|
191
211
|
code: originalError.code,
|
|
192
|
-
stack: originalError.stack
|
|
193
|
-
}
|
|
212
|
+
stack: originalError.stack,
|
|
213
|
+
},
|
|
194
214
|
};
|
|
195
215
|
|
|
196
216
|
const wrappedError = new ConverseMCPError(message, code, enhancedDetails);
|
|
@@ -218,7 +238,7 @@ export function withErrorHandler(fn, operation = 'unknown', context = {}) {
|
|
|
218
238
|
} catch (error) {
|
|
219
239
|
operationLogger.error('Operation failed', {
|
|
220
240
|
error,
|
|
221
|
-
data: { args: args.length, context }
|
|
241
|
+
data: { args: args.length, context },
|
|
222
242
|
});
|
|
223
243
|
|
|
224
244
|
// Re-throw enhanced error if it's already structured
|
|
@@ -227,7 +247,12 @@ export function withErrorHandler(fn, operation = 'unknown', context = {}) {
|
|
|
227
247
|
}
|
|
228
248
|
|
|
229
249
|
// Wrap unknown errors
|
|
230
|
-
throw wrapError(
|
|
250
|
+
throw wrapError(
|
|
251
|
+
error,
|
|
252
|
+
`${operation} failed: ${error.message}`,
|
|
253
|
+
ERROR_CODES.INTERNAL_ERROR,
|
|
254
|
+
context,
|
|
255
|
+
);
|
|
231
256
|
}
|
|
232
257
|
};
|
|
233
258
|
}
|
|
@@ -252,14 +277,16 @@ export function createMCPErrorResponse(error, toolName = null, context = {}) {
|
|
|
252
277
|
|
|
253
278
|
// Create structured response for unknown errors
|
|
254
279
|
const errorCode = error.code || ERROR_CODES.UNKNOWN_ERROR;
|
|
255
|
-
const message = toolName
|
|
280
|
+
const message = toolName
|
|
281
|
+
? `Error in ${toolName}: ${error.message}`
|
|
282
|
+
: error.message;
|
|
256
283
|
|
|
257
284
|
return {
|
|
258
285
|
content: [
|
|
259
286
|
{
|
|
260
287
|
type: 'text',
|
|
261
|
-
text: message
|
|
262
|
-
}
|
|
288
|
+
text: message,
|
|
289
|
+
},
|
|
263
290
|
],
|
|
264
291
|
isError: true,
|
|
265
292
|
error: {
|
|
@@ -269,8 +296,9 @@ export function createMCPErrorResponse(error, toolName = null, context = {}) {
|
|
|
269
296
|
toolName,
|
|
270
297
|
context,
|
|
271
298
|
timestamp: new Date().toISOString(),
|
|
272
|
-
...(process.env.NODE_ENV === 'development' &&
|
|
273
|
-
|
|
299
|
+
...(process.env.NODE_ENV === 'development' &&
|
|
300
|
+
error.stack && { stack: error.stack }),
|
|
301
|
+
},
|
|
274
302
|
};
|
|
275
303
|
}
|
|
276
304
|
|
|
@@ -290,10 +318,10 @@ export function isRecoverableError(error) {
|
|
|
290
318
|
/timeout/i,
|
|
291
319
|
/rate limit/i,
|
|
292
320
|
/quota/i,
|
|
293
|
-
/temporary/i
|
|
321
|
+
/temporary/i,
|
|
294
322
|
];
|
|
295
323
|
|
|
296
|
-
return recoverablePatterns.some(pattern => pattern.test(error.message));
|
|
324
|
+
return recoverablePatterns.some((pattern) => pattern.test(error.message));
|
|
297
325
|
}
|
|
298
326
|
|
|
299
327
|
/**
|
|
@@ -307,14 +335,20 @@ export function logError(error, operation = 'unknown', metadata = {}) {
|
|
|
307
335
|
|
|
308
336
|
if (error instanceof ConverseMCPError) {
|
|
309
337
|
if (error.statusCode >= 500) {
|
|
310
|
-
operationLogger.error('Internal error occurred', {
|
|
338
|
+
operationLogger.error('Internal error occurred', {
|
|
339
|
+
error,
|
|
340
|
+
data: metadata,
|
|
341
|
+
});
|
|
311
342
|
} else if (error.statusCode >= 400) {
|
|
312
343
|
operationLogger.warn('Client error occurred', { error, data: metadata });
|
|
313
344
|
} else {
|
|
314
345
|
operationLogger.info('Handled error occurred', { error, data: metadata });
|
|
315
346
|
}
|
|
316
347
|
} else {
|
|
317
|
-
operationLogger.error('Unhandled error occurred', {
|
|
348
|
+
operationLogger.error('Unhandled error occurred', {
|
|
349
|
+
error,
|
|
350
|
+
data: metadata,
|
|
351
|
+
});
|
|
318
352
|
}
|
|
319
353
|
}
|
|
320
354
|
|
|
@@ -335,7 +369,11 @@ export class ErrorAggregator {
|
|
|
335
369
|
* @param {string} identifier - Result identifier
|
|
336
370
|
*/
|
|
337
371
|
addSuccess(result, identifier = null) {
|
|
338
|
-
this.successes.push({
|
|
372
|
+
this.successes.push({
|
|
373
|
+
result,
|
|
374
|
+
identifier,
|
|
375
|
+
timestamp: new Date().toISOString(),
|
|
376
|
+
});
|
|
339
377
|
}
|
|
340
378
|
|
|
341
379
|
/**
|
|
@@ -344,10 +382,14 @@ export class ErrorAggregator {
|
|
|
344
382
|
* @param {string} identifier - Error identifier
|
|
345
383
|
*/
|
|
346
384
|
addError(error, identifier = null) {
|
|
347
|
-
this.errors.push({
|
|
385
|
+
this.errors.push({
|
|
386
|
+
error,
|
|
387
|
+
identifier,
|
|
388
|
+
timestamp: new Date().toISOString(),
|
|
389
|
+
});
|
|
348
390
|
this.logger.warn('Batch operation error', {
|
|
349
391
|
error,
|
|
350
|
-
data: { identifier, totalErrors: this.errors.length }
|
|
392
|
+
data: { identifier, totalErrors: this.errors.length },
|
|
351
393
|
});
|
|
352
394
|
}
|
|
353
395
|
|
|
@@ -362,7 +404,8 @@ export class ErrorAggregator {
|
|
|
362
404
|
successes: this.successes.length,
|
|
363
405
|
errors: this.errors.length,
|
|
364
406
|
hasErrors: this.errors.length > 0,
|
|
365
|
-
successRate:
|
|
407
|
+
successRate:
|
|
408
|
+
this.successes.length / (this.errors.length + this.successes.length),
|
|
366
409
|
};
|
|
367
410
|
}
|
|
368
411
|
|
|
@@ -380,12 +423,12 @@ export class ErrorAggregator {
|
|
|
380
423
|
ERROR_CODES.INTERNAL_ERROR,
|
|
381
424
|
{
|
|
382
425
|
summary: this.getSummary(),
|
|
383
|
-
errors: this.errors.map(e => ({
|
|
426
|
+
errors: this.errors.map((e) => ({
|
|
384
427
|
identifier: e.identifier,
|
|
385
428
|
error: e.error.message,
|
|
386
|
-
code: e.error.code
|
|
387
|
-
}))
|
|
388
|
-
}
|
|
429
|
+
code: e.error.code,
|
|
430
|
+
})),
|
|
431
|
+
},
|
|
389
432
|
);
|
|
390
433
|
|
|
391
434
|
throw aggregatedError;
|
|
@@ -399,9 +442,13 @@ export class ErrorAggregator {
|
|
|
399
442
|
const summary = this.getSummary();
|
|
400
443
|
|
|
401
444
|
if (summary.hasErrors) {
|
|
402
|
-
this.logger.warn('Batch operation completed with errors', {
|
|
445
|
+
this.logger.warn('Batch operation completed with errors', {
|
|
446
|
+
data: summary,
|
|
447
|
+
});
|
|
403
448
|
} else {
|
|
404
|
-
this.logger.info('Batch operation completed successfully', {
|
|
449
|
+
this.logger.info('Batch operation completed successfully', {
|
|
450
|
+
data: summary,
|
|
451
|
+
});
|
|
405
452
|
}
|
|
406
453
|
}
|
|
407
454
|
}
|
|
@@ -418,7 +465,7 @@ export async function retryWithBackoff(fn, options = {}) {
|
|
|
418
465
|
delay = 1000,
|
|
419
466
|
backoffFactor = 2,
|
|
420
467
|
maxDelay = 10000,
|
|
421
|
-
operation = 'retry-operation'
|
|
468
|
+
operation = 'retry-operation',
|
|
422
469
|
} = options;
|
|
423
470
|
|
|
424
471
|
const operationLogger = logger.operation(operation);
|
|
@@ -431,30 +478,34 @@ export async function retryWithBackoff(fn, options = {}) {
|
|
|
431
478
|
}
|
|
432
479
|
|
|
433
480
|
return await fn();
|
|
434
|
-
|
|
435
481
|
} catch (error) {
|
|
436
482
|
lastError = error;
|
|
437
483
|
|
|
438
484
|
if (attempt === retries) {
|
|
439
485
|
operationLogger.error('All retry attempts failed', {
|
|
440
486
|
error,
|
|
441
|
-
data: { attempts: attempt + 1, maxRetries: retries }
|
|
487
|
+
data: { attempts: attempt + 1, maxRetries: retries },
|
|
442
488
|
});
|
|
443
489
|
break;
|
|
444
490
|
}
|
|
445
491
|
|
|
446
492
|
if (!isRecoverableError(error)) {
|
|
447
|
-
operationLogger.warn('Non-recoverable error, stopping retries', {
|
|
493
|
+
operationLogger.warn('Non-recoverable error, stopping retries', {
|
|
494
|
+
error,
|
|
495
|
+
});
|
|
448
496
|
break;
|
|
449
497
|
}
|
|
450
498
|
|
|
451
|
-
const currentDelay = Math.min(
|
|
499
|
+
const currentDelay = Math.min(
|
|
500
|
+
delay * Math.pow(backoffFactor, attempt),
|
|
501
|
+
maxDelay,
|
|
502
|
+
);
|
|
452
503
|
operationLogger.debug(`Retrying in ${currentDelay}ms`, {
|
|
453
504
|
error: error.message,
|
|
454
|
-
data: { attempt: attempt + 1, delay: currentDelay }
|
|
505
|
+
data: { attempt: attempt + 1, delay: currentDelay },
|
|
455
506
|
});
|
|
456
507
|
|
|
457
|
-
await new Promise(resolve => setTimeout(resolve, currentDelay));
|
|
508
|
+
await new Promise((resolve) => setTimeout(resolve, currentDelay));
|
|
458
509
|
}
|
|
459
510
|
}
|
|
460
511
|
|
|
@@ -490,7 +541,7 @@ export class CircuitBreaker {
|
|
|
490
541
|
throw new ConverseMCPError(
|
|
491
542
|
`Circuit breaker is OPEN for ${this.operation}`,
|
|
492
543
|
ERROR_CODES.PROVIDER_UNAVAILABLE,
|
|
493
|
-
{ state: this.state, nextAttempt: this.nextAttempt }
|
|
544
|
+
{ state: this.state, nextAttempt: this.nextAttempt },
|
|
494
545
|
);
|
|
495
546
|
}
|
|
496
547
|
|
|
@@ -506,7 +557,6 @@ export class CircuitBreaker {
|
|
|
506
557
|
}
|
|
507
558
|
|
|
508
559
|
return result;
|
|
509
|
-
|
|
510
560
|
} catch (error) {
|
|
511
561
|
this.recordFailure();
|
|
512
562
|
throw error;
|
|
@@ -528,8 +578,8 @@ export class CircuitBreaker {
|
|
|
528
578
|
data: {
|
|
529
579
|
failures: this.failures,
|
|
530
580
|
threshold: this.failureThreshold,
|
|
531
|
-
resetTime: new Date(this.nextAttempt).toISOString()
|
|
532
|
-
}
|
|
581
|
+
resetTime: new Date(this.nextAttempt).toISOString(),
|
|
582
|
+
},
|
|
533
583
|
});
|
|
534
584
|
}
|
|
535
585
|
}
|
|
@@ -556,7 +606,7 @@ export class CircuitBreaker {
|
|
|
556
606
|
state: this.state,
|
|
557
607
|
failures: this.failures,
|
|
558
608
|
lastFailureTime: this.lastFailureTime,
|
|
559
|
-
nextAttempt: this.nextAttempt
|
|
609
|
+
nextAttempt: this.nextAttempt,
|
|
560
610
|
};
|
|
561
611
|
}
|
|
562
612
|
}
|
|
@@ -1,98 +1,107 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File Validator Utility
|
|
3
|
-
*
|
|
4
|
-
* Validates that file paths exist before processing them.
|
|
5
|
-
* Returns early with clear error messages if any files are not found.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { access, constants } from 'fs/promises';
|
|
9
|
-
import { resolve, isAbsolute } from 'path';
|
|
10
|
-
import { createToolError } from '../tools/index.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Validate that all provided file paths exist
|
|
14
|
-
* @param {string[]} filePaths - Array of file paths to validate
|
|
15
|
-
* @param {string} fileType - Type of files being validated (e.g., 'file', 'image')
|
|
16
|
-
* @param {object} options - Validation options including clientCwd
|
|
17
|
-
* @returns {Promise<{valid: boolean, missingPaths: string[], error?: object}>}
|
|
18
|
-
*/
|
|
19
|
-
export async function validateFilePaths(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
errors
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
1
|
+
/**
|
|
2
|
+
* File Validator Utility
|
|
3
|
+
*
|
|
4
|
+
* Validates that file paths exist before processing them.
|
|
5
|
+
* Returns early with clear error messages if any files are not found.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { access, constants } from 'fs/promises';
|
|
9
|
+
import { resolve, isAbsolute } from 'path';
|
|
10
|
+
import { createToolError } from '../tools/index.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validate that all provided file paths exist
|
|
14
|
+
* @param {string[]} filePaths - Array of file paths to validate
|
|
15
|
+
* @param {string} fileType - Type of files being validated (e.g., 'file', 'image')
|
|
16
|
+
* @param {object} options - Validation options including clientCwd
|
|
17
|
+
* @returns {Promise<{valid: boolean, missingPaths: string[], error?: object}>}
|
|
18
|
+
*/
|
|
19
|
+
export async function validateFilePaths(
|
|
20
|
+
filePaths,
|
|
21
|
+
fileType = 'file',
|
|
22
|
+
options = {},
|
|
23
|
+
) {
|
|
24
|
+
if (!Array.isArray(filePaths) || filePaths.length === 0) {
|
|
25
|
+
return { valid: true, missingPaths: [] };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const missingPaths = [];
|
|
29
|
+
|
|
30
|
+
for (const filePath of filePaths) {
|
|
31
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
32
|
+
missingPaths.push(`Invalid path: ${filePath}`);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Skip validation for base64 data URLs
|
|
37
|
+
if (filePath.startsWith('data:')) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Convert to absolute path if needed
|
|
42
|
+
// Use clientCwd if provided (for auto-detected client working directory), otherwise fall back to process.cwd()
|
|
43
|
+
const absolutePath = isAbsolute(filePath)
|
|
44
|
+
? filePath
|
|
45
|
+
: resolve(options.clientCwd || process.cwd(), filePath);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
// Check if file exists and is readable
|
|
49
|
+
await access(absolutePath, constants.R_OK);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// Keep the original path in the error message for clarity
|
|
52
|
+
missingPaths.push(filePath);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (missingPaths.length > 0) {
|
|
57
|
+
const errorMessage = `The following ${fileType}${missingPaths.length > 1 ? 's' : ''} could not be found: ${missingPaths.join(', ')}`;
|
|
58
|
+
return {
|
|
59
|
+
valid: false,
|
|
60
|
+
missingPaths,
|
|
61
|
+
error: createToolError(errorMessage),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { valid: true, missingPaths: [] };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Validate both files and images together
|
|
70
|
+
* @param {object} paths - Object containing files and images arrays
|
|
71
|
+
* @param {object} options - Validation options including clientCwd
|
|
72
|
+
* @returns {Promise<{valid: boolean, errors: string[], errorResponse?: object}>}
|
|
73
|
+
*/
|
|
74
|
+
export async function validateAllPaths(
|
|
75
|
+
{ files = [], images = [] },
|
|
76
|
+
options = {},
|
|
77
|
+
) {
|
|
78
|
+
const errors = [];
|
|
79
|
+
|
|
80
|
+
// Validate regular files
|
|
81
|
+
if (files.length > 0) {
|
|
82
|
+
const fileValidation = await validateFilePaths(files, 'file', options);
|
|
83
|
+
if (!fileValidation.valid) {
|
|
84
|
+
errors.push(`Files not found: ${fileValidation.missingPaths.join(', ')}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Validate image files
|
|
89
|
+
if (images.length > 0) {
|
|
90
|
+
const imageValidation = await validateFilePaths(images, 'image', options);
|
|
91
|
+
if (!imageValidation.valid) {
|
|
92
|
+
errors.push(
|
|
93
|
+
`Images not found: ${imageValidation.missingPaths.join(', ')}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (errors.length > 0) {
|
|
99
|
+
return {
|
|
100
|
+
valid: false,
|
|
101
|
+
errors,
|
|
102
|
+
errorResponse: createToolError(errors.join('. ')),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return { valid: true, errors: [] };
|
|
107
|
+
}
|