@democratize-quality/mcp-server 1.1.8 → 1.2.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.
Files changed (64) hide show
  1. package/LICENSE +658 -12
  2. package/README.md +11 -6
  3. package/dist/server.d.ts +41 -0
  4. package/dist/server.d.ts.map +1 -0
  5. package/dist/server.js +225 -0
  6. package/dist/server.js.map +1 -0
  7. package/package.json +24 -25
  8. package/browserControl.js +0 -113
  9. package/cli.js +0 -228
  10. package/mcpServer.js +0 -335
  11. package/run-server.js +0 -140
  12. package/src/chatmodes//360/237/214/220 api-generator.chatmode.md" +0 -409
  13. package/src/chatmodes//360/237/214/220 api-healer.chatmode.md" +0 -494
  14. package/src/chatmodes//360/237/214/220 api-planner.chatmode.md" +0 -954
  15. package/src/config/environments/api-only.js +0 -53
  16. package/src/config/environments/development.js +0 -54
  17. package/src/config/environments/production.js +0 -69
  18. package/src/config/index.js +0 -341
  19. package/src/config/server.js +0 -41
  20. package/src/config/tools/api.js +0 -67
  21. package/src/config/tools/browser.js +0 -90
  22. package/src/config/tools/default.js +0 -32
  23. package/src/docs/Agent_README.md +0 -310
  24. package/src/docs/QUICK_REFERENCE.md +0 -111
  25. package/src/services/browserService.js +0 -325
  26. package/src/skills/api-planning/SKILL.md +0 -224
  27. package/src/skills/test-execution/SKILL.md +0 -728
  28. package/src/skills/test-generation/SKILL.md +0 -309
  29. package/src/skills/test-healing/SKILL.md +0 -405
  30. package/src/tools/api/api-generator.js +0 -1865
  31. package/src/tools/api/api-healer.js +0 -617
  32. package/src/tools/api/api-planner.js +0 -2598
  33. package/src/tools/api/api-project-setup.js +0 -313
  34. package/src/tools/api/api-request.js +0 -641
  35. package/src/tools/api/api-session-report.js +0 -1278
  36. package/src/tools/api/api-session-status.js +0 -395
  37. package/src/tools/api/prompts/README.md +0 -293
  38. package/src/tools/api/prompts/generation-prompts.js +0 -703
  39. package/src/tools/api/prompts/healing-prompts.js +0 -195
  40. package/src/tools/api/prompts/index.js +0 -25
  41. package/src/tools/api/prompts/orchestrator.js +0 -334
  42. package/src/tools/api/prompts/validation-rules.js +0 -339
  43. package/src/tools/base/ToolBase.js +0 -230
  44. package/src/tools/base/ToolRegistry.js +0 -269
  45. package/src/tools/browser/advanced/browser-console.js +0 -384
  46. package/src/tools/browser/advanced/browser-dialog.js +0 -319
  47. package/src/tools/browser/advanced/browser-evaluate.js +0 -337
  48. package/src/tools/browser/advanced/browser-file.js +0 -480
  49. package/src/tools/browser/advanced/browser-keyboard.js +0 -343
  50. package/src/tools/browser/advanced/browser-mouse.js +0 -332
  51. package/src/tools/browser/advanced/browser-network.js +0 -421
  52. package/src/tools/browser/advanced/browser-pdf.js +0 -407
  53. package/src/tools/browser/advanced/browser-tabs.js +0 -497
  54. package/src/tools/browser/advanced/browser-wait.js +0 -378
  55. package/src/tools/browser/click.js +0 -168
  56. package/src/tools/browser/close.js +0 -60
  57. package/src/tools/browser/dom.js +0 -70
  58. package/src/tools/browser/launch.js +0 -67
  59. package/src/tools/browser/navigate.js +0 -270
  60. package/src/tools/browser/screenshot.js +0 -351
  61. package/src/tools/browser/type.js +0 -174
  62. package/src/tools/index.js +0 -95
  63. package/src/utils/agentInstaller.js +0 -418
  64. package/src/utils/browserHelpers.js +0 -83
@@ -1,617 +0,0 @@
1
- const ToolBase = require('../base/ToolBase');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const { spawn } = require('child_process');
5
-
6
- let z;
7
- try {
8
- const zod = require('zod');
9
- z = zod.z || zod.default?.z || zod;
10
- if (!z || typeof z.object !== 'function') {
11
- throw new Error('Zod not properly loaded');
12
- }
13
- } catch (error) {
14
- console.error('Failed to load Zod:', error.message);
15
- // Fallback: create a simple validation function
16
- z = {
17
- object: (schema) => ({ parse: (data) => data }),
18
- string: () => ({ optional: () => ({}) }),
19
- enum: () => ({ optional: () => ({}) }),
20
- record: () => ({ optional: () => ({}) }),
21
- any: () => ({ optional: () => ({}) }),
22
- number: () => ({ optional: () => ({}) }),
23
- array: () => ({ optional: () => ({}) }),
24
- boolean: () => ({ optional: () => ({}) })
25
- };
26
- }
27
-
28
- // Input schema for the API healer tool
29
- const apiHealerInputSchema = z.object({
30
- testPath: z.string().optional(),
31
- testFiles: z.array(z.string()).optional(),
32
- testType: z.enum(['jest', 'playwright', 'postman', 'auto']).optional(),
33
- sessionId: z.string().optional(),
34
- maxHealingAttempts: z.number().optional(),
35
- autoFix: z.boolean().optional(),
36
- backupOriginal: z.boolean().optional(),
37
- analysisOnly: z.boolean().optional(),
38
- healingStrategies: z.array(z.enum(['schema-update', 'endpoint-fix', 'auth-repair', 'data-correction', 'assertion-update'])).optional()
39
- });
40
-
41
- /**
42
- * API Healer Tool - Debug and fix failing API tests
43
- */
44
- class ApiHealerTool extends ToolBase {
45
- static definition = {
46
- name: "api_healer",
47
- description: "Debug and automatically fix failing API tests by analyzing errors, updating assertions, fixing endpoints, and correcting test data.",
48
- input_schema: {
49
- type: "object",
50
- properties: {
51
- testPath: {
52
- type: "string",
53
- description: "Path to a specific test file to heal"
54
- },
55
- testFiles: {
56
- type: "array",
57
- items: { type: "string" },
58
- description: "Array of test file paths to heal (alternative to testPath)"
59
- },
60
- testType: {
61
- type: "string",
62
- enum: ["jest", "playwright", "postman", "auto"],
63
- description: "Type of tests to heal - auto-detects if not specified"
64
- },
65
- sessionId: {
66
- type: "string",
67
- description: "Session ID for tracking healing process"
68
- },
69
- maxHealingAttempts: {
70
- type: "number",
71
- description: "Maximum number of healing attempts per test (default: 3)"
72
- },
73
- autoFix: {
74
- type: "boolean",
75
- description: "Automatically apply fixes without confirmation (default: true)"
76
- },
77
- backupOriginal: {
78
- type: "boolean",
79
- description: "Create backup of original test files (default: true)"
80
- },
81
- analysisOnly: {
82
- type: "boolean",
83
- description: "Only analyze failures without applying fixes (default: false)"
84
- },
85
- healingStrategies: {
86
- type: "array",
87
- items: {
88
- type: "string",
89
- enum: ["schema-update", "endpoint-fix", "auth-repair", "data-correction", "assertion-update"]
90
- },
91
- description: "Specific healing strategies to apply (default: all strategies)"
92
- }
93
- },
94
- oneOf: [
95
- { required: ["testPath"] },
96
- { required: ["testFiles"] }
97
- ]
98
- }
99
- };
100
-
101
- constructor() {
102
- super();
103
- this.apiRequestTool = null;
104
- }
105
-
106
- async execute(parameters) {
107
- try {
108
- const params = apiHealerInputSchema.parse(parameters);
109
-
110
- // Set defaults
111
- const options = {
112
- testType: params.testType || 'auto',
113
- sessionId: params.sessionId || `api-heal-${Date.now()}`,
114
- maxHealingAttempts: params.maxHealingAttempts || 3,
115
- autoFix: params.autoFix !== false,
116
- backupOriginal: params.backupOriginal !== false,
117
- analysisOnly: params.analysisOnly || false,
118
- healingStrategies: params.healingStrategies || ['schema-update', 'endpoint-fix', 'auth-repair', 'data-correction', 'assertion-update']
119
- };
120
-
121
- // Get test files to heal
122
- let testFiles = [];
123
- if (params.testPath) {
124
- testFiles = [params.testPath];
125
- } else if (params.testFiles) {
126
- testFiles = params.testFiles;
127
- }
128
-
129
- // Validate test files exist
130
- for (const testFile of testFiles) {
131
- if (!fs.existsSync(testFile)) {
132
- throw new Error(`Test file not found: ${testFile}`);
133
- }
134
- }
135
-
136
- // Auto-detect test type if needed
137
- if (options.testType === 'auto') {
138
- options.testType = this._detectTestType(testFiles[0]);
139
- }
140
-
141
- const healingResults = {
142
- sessionId: options.sessionId,
143
- totalFiles: testFiles.length,
144
- processedFiles: 0,
145
- fixedFiles: 0,
146
- failedFiles: 0,
147
- results: []
148
- };
149
-
150
- // Process each test file
151
- for (const testFile of testFiles) {
152
- console.log(`[API Healer] Processing: ${testFile}`);
153
-
154
- try {
155
- const fileResult = await this._healTestFile(testFile, options);
156
- healingResults.results.push(fileResult);
157
- healingResults.processedFiles++;
158
-
159
- if (fileResult.success) {
160
- healingResults.fixedFiles++;
161
- } else {
162
- healingResults.failedFiles++;
163
- }
164
- } catch (error) {
165
- healingResults.results.push({
166
- file: testFile,
167
- success: false,
168
- error: error.message,
169
- healingAttempts: 0,
170
- appliedFixes: []
171
- });
172
- healingResults.failedFiles++;
173
- }
174
- }
175
-
176
- return {
177
- success: true,
178
- message: `API healing completed for ${healingResults.processedFiles} files`,
179
- ...healingResults
180
- };
181
-
182
- } catch (error) {
183
- return {
184
- success: false,
185
- error: error.message,
186
- details: error.stack
187
- };
188
- }
189
- }
190
-
191
- _detectTestType(testFile) {
192
- const content = fs.readFileSync(testFile, 'utf8');
193
-
194
- if (content.includes('@playwright/test') || content.includes('test.describe')) {
195
- return 'playwright';
196
- } else if (content.includes('describe(') && content.includes('jest')) {
197
- return 'jest';
198
- } else if (testFile.endsWith('.postman_collection.json')) {
199
- return 'postman';
200
- } else if (content.includes('describe(') || content.includes('test(')) {
201
- return 'jest'; // Default to Jest for generic test files
202
- }
203
-
204
- throw new Error(`Unable to detect test type for file: ${testFile}`);
205
- }
206
-
207
- async _healTestFile(testFile, options) {
208
- const result = {
209
- file: testFile,
210
- success: false,
211
- healingAttempts: 0,
212
- appliedFixes: [],
213
- errors: [],
214
- finalStatus: 'failed'
215
- };
216
-
217
- // Create backup if requested
218
- if (options.backupOriginal) {
219
- const backupPath = `${testFile}.backup.${Date.now()}`;
220
- fs.copyFileSync(testFile, backupPath);
221
- result.backupPath = backupPath;
222
- }
223
-
224
- // Initial test run to identify failures
225
- let testErrors = await this._runTests(testFile, options.testType);
226
- result.errors = testErrors;
227
-
228
- if (testErrors.length === 0) {
229
- result.success = true;
230
- result.finalStatus = 'passing';
231
- return result;
232
- }
233
-
234
- if (options.analysisOnly) {
235
- result.analysis = await this._analyzeErrors(testErrors, testFile);
236
- return result;
237
- }
238
-
239
- // Healing loop
240
- for (let attempt = 1; attempt <= options.maxHealingAttempts && testErrors.length > 0; attempt++) {
241
- result.healingAttempts = attempt;
242
- console.log(`[API Healer] Healing attempt ${attempt}/${options.maxHealingAttempts} for ${testFile}`);
243
-
244
- const fixes = await this._generateFixes(testErrors, testFile, options);
245
-
246
- if (fixes.length === 0) {
247
- console.log('[API Healer] No fixes available for current errors');
248
- break;
249
- }
250
-
251
- // Apply fixes
252
- for (const fix of fixes) {
253
- try {
254
- await this._applyFix(fix, testFile);
255
- result.appliedFixes.push(fix);
256
- console.log(`[API Healer] Applied fix: ${fix.type} - ${fix.description}`);
257
- } catch (error) {
258
- console.log(`[API Healer] Failed to apply fix: ${fix.type} - ${error.message}`);
259
- }
260
- }
261
-
262
- // Re-run tests to check if issues are resolved
263
- testErrors = await this._runTests(testFile, options.testType);
264
-
265
- if (testErrors.length === 0) {
266
- result.success = true;
267
- result.finalStatus = 'healed';
268
- break;
269
- }
270
- }
271
-
272
- if (testErrors.length > 0) {
273
- result.finalStatus = 'partially-healed';
274
- result.remainingErrors = testErrors;
275
- }
276
-
277
- return result;
278
- }
279
-
280
- async _runTests(testFile, testType) {
281
- return new Promise((resolve) => {
282
- let command, args;
283
-
284
- switch (testType) {
285
- case 'jest':
286
- command = 'npx';
287
- args = ['jest', testFile, '--verbose', '--no-coverage'];
288
- break;
289
- case 'playwright':
290
- command = 'npx';
291
- args = ['playwright', 'test', testFile];
292
- break;
293
- default:
294
- resolve([{ type: 'unknown', message: `Unsupported test type: ${testType}` }]);
295
- return;
296
- }
297
-
298
- const process = spawn(command, args, { cwd: path.dirname(testFile) });
299
- let output = '';
300
- let errorOutput = '';
301
-
302
- process.stdout.on('data', (data) => {
303
- output += data.toString();
304
- });
305
-
306
- process.stderr.on('data', (data) => {
307
- errorOutput += data.toString();
308
- });
309
-
310
- process.on('close', (code) => {
311
- const errors = this._parseTestErrors(output + errorOutput, testType);
312
- resolve(errors);
313
- });
314
-
315
- process.on('error', (error) => {
316
- resolve([{ type: 'execution', message: `Failed to run tests: ${error.message}` }]);
317
- });
318
- });
319
- }
320
-
321
- _parseTestErrors(output, testType) {
322
- const errors = [];
323
-
324
- switch (testType) {
325
- case 'jest':
326
- errors.push(...this._parseJestErrors(output));
327
- break;
328
- case 'playwright':
329
- errors.push(...this._parsePlaywrightErrors(output));
330
- break;
331
- }
332
-
333
- return errors;
334
- }
335
-
336
- _parseJestErrors(output) {
337
- const errors = [];
338
- const lines = output.split('\n');
339
-
340
- for (let i = 0; i < lines.length; i++) {
341
- const line = lines[i];
342
-
343
- // HTTP errors
344
- if (line.includes('Request failed with status code')) {
345
- const statusMatch = line.match(/status code (\d+)/);
346
- if (statusMatch) {
347
- errors.push({
348
- type: 'http-error',
349
- statusCode: parseInt(statusMatch[1]),
350
- message: line.trim(),
351
- line: i + 1
352
- });
353
- }
354
- }
355
-
356
- // Assertion errors
357
- else if (line.includes('expect(') && line.includes('toBe')) {
358
- errors.push({
359
- type: 'assertion-error',
360
- message: line.trim(),
361
- line: i + 1
362
- });
363
- }
364
-
365
- // Network errors
366
- else if (line.includes('ECONNREFUSED') || line.includes('ENOTFOUND')) {
367
- errors.push({
368
- type: 'network-error',
369
- message: line.trim(),
370
- line: i + 1
371
- });
372
- }
373
-
374
- // Authentication errors
375
- else if (line.includes('401') || line.includes('403') || line.includes('Unauthorized')) {
376
- errors.push({
377
- type: 'auth-error',
378
- message: line.trim(),
379
- line: i + 1
380
- });
381
- }
382
-
383
- // Timeout errors
384
- else if (line.includes('timeout') || line.includes('ETIMEDOUT')) {
385
- errors.push({
386
- type: 'timeout-error',
387
- message: line.trim(),
388
- line: i + 1
389
- });
390
- }
391
- }
392
-
393
- return errors;
394
- }
395
-
396
- _parsePlaywrightErrors(output) {
397
- const errors = [];
398
- const lines = output.split('\n');
399
-
400
- for (let i = 0; i < lines.length; i++) {
401
- const line = lines[i];
402
-
403
- // API response errors
404
- if (line.includes('apiRequestContext.get') || line.includes('apiRequestContext.post')) {
405
- if (lines[i + 1] && lines[i + 1].includes('Error:')) {
406
- errors.push({
407
- type: 'api-error',
408
- message: lines[i + 1].trim(),
409
- line: i + 1
410
- });
411
- }
412
- }
413
-
414
- // Assertion errors
415
- else if (line.includes('expect(') && line.includes('toBe')) {
416
- errors.push({
417
- type: 'assertion-error',
418
- message: line.trim(),
419
- line: i + 1
420
- });
421
- }
422
- }
423
-
424
- return errors;
425
- }
426
-
427
- async _analyzeErrors(errors, testFile) {
428
- const analysis = {
429
- totalErrors: errors.length,
430
- errorTypes: {},
431
- recommendations: [],
432
- healable: true
433
- };
434
-
435
- // Categorize errors
436
- errors.forEach(error => {
437
- if (!analysis.errorTypes[error.type]) {
438
- analysis.errorTypes[error.type] = 0;
439
- }
440
- analysis.errorTypes[error.type]++;
441
- });
442
-
443
- // Generate recommendations
444
- Object.keys(analysis.errorTypes).forEach(errorType => {
445
- switch (errorType) {
446
- case 'http-error':
447
- analysis.recommendations.push('Check API endpoint URLs and HTTP methods');
448
- break;
449
- case 'assertion-error':
450
- analysis.recommendations.push('Update test assertions to match current API responses');
451
- break;
452
- case 'auth-error':
453
- analysis.recommendations.push('Update authentication tokens or credentials');
454
- break;
455
- case 'network-error':
456
- analysis.recommendations.push('Verify API server is running and accessible');
457
- analysis.healable = false;
458
- break;
459
- case 'timeout-error':
460
- analysis.recommendations.push('Increase timeout values or check API performance');
461
- break;
462
- }
463
- });
464
-
465
- return analysis;
466
- }
467
-
468
- async _generateFixes(errors, testFile, options) {
469
- const fixes = [];
470
- const testContent = fs.readFileSync(testFile, 'utf8');
471
-
472
- for (const error of errors) {
473
- switch (error.type) {
474
- case 'http-error':
475
- if (error.statusCode === 404) {
476
- fixes.push(await this._generateEndpointFix(error, testContent));
477
- } else if (error.statusCode === 401 || error.statusCode === 403) {
478
- fixes.push(await this._generateAuthFix(error, testContent));
479
- } else if (error.statusCode >= 400 && error.statusCode < 500) {
480
- fixes.push(await this._generateDataFix(error, testContent));
481
- }
482
- break;
483
-
484
- case 'assertion-error':
485
- fixes.push(await this._generateAssertionFix(error, testContent));
486
- break;
487
-
488
- case 'auth-error':
489
- fixes.push(await this._generateAuthFix(error, testContent));
490
- break;
491
-
492
- case 'timeout-error':
493
- fixes.push(this._generateTimeoutFix(error, testContent));
494
- break;
495
- }
496
- }
497
-
498
- return fixes.filter(fix => fix !== null);
499
- }
500
-
501
- async _generateEndpointFix(error, testContent) {
502
- // Try to identify the failing endpoint and suggest alternatives
503
- const urlMatches = testContent.match(/['"`]([^'"`]*\/[^'"`]*)['"`]/g);
504
-
505
- if (urlMatches && urlMatches.length > 0) {
506
- // For now, return a placeholder fix
507
- return {
508
- type: 'endpoint-fix',
509
- description: 'Update endpoint URL to match current API',
510
- pattern: urlMatches[0],
511
- replacement: urlMatches[0], // Would implement actual endpoint discovery
512
- confidence: 0.5
513
- };
514
- }
515
-
516
- return null;
517
- }
518
-
519
- async _generateAuthFix(error, testContent) {
520
- return {
521
- type: 'auth-repair',
522
- description: 'Update authentication method or refresh tokens',
523
- pattern: /Authorization['":\s]+['"`]([^'"`]*)['"`]/,
524
- replacement: 'Authorization: "Bearer {{updated_token}}"',
525
- confidence: 0.8
526
- };
527
- }
528
-
529
- async _generateDataFix(error, testContent) {
530
- return {
531
- type: 'data-correction',
532
- description: 'Update request payload to match API schema',
533
- pattern: /data:\s*{[^}]*}/,
534
- replacement: 'data: { /* Updated payload */ }',
535
- confidence: 0.6
536
- };
537
- }
538
-
539
- async _generateAssertionFix(error, testContent) {
540
- // Extract expected vs actual values from error message
541
- const expectMatch = error.message.match(/expect\(([^)]+)\)\.toBe\(([^)]+)\)/);
542
-
543
- if (expectMatch) {
544
- return {
545
- type: 'assertion-update',
546
- description: 'Update assertion to match actual API response',
547
- pattern: expectMatch[0],
548
- replacement: `expect(${expectMatch[1]}).toBe(/* actual value */)`,
549
- confidence: 0.9
550
- };
551
- }
552
-
553
- return null;
554
- }
555
-
556
- _generateTimeoutFix(error, testContent) {
557
- return {
558
- type: 'timeout-fix',
559
- description: 'Increase timeout values',
560
- pattern: /timeout:\s*\d+/,
561
- replacement: 'timeout: 30000',
562
- confidence: 0.7
563
- };
564
- }
565
-
566
- async _applyFix(fix, testFile) {
567
- if (!fix || !fix.pattern || !fix.replacement) {
568
- throw new Error('Invalid fix object');
569
- }
570
-
571
- let content = fs.readFileSync(testFile, 'utf8');
572
-
573
- if (typeof fix.pattern === 'string') {
574
- content = content.replace(fix.pattern, fix.replacement);
575
- } else if (fix.pattern instanceof RegExp) {
576
- content = content.replace(fix.pattern, fix.replacement);
577
- }
578
-
579
- fs.writeFileSync(testFile, content);
580
- }
581
-
582
- // Initialize API request tool for live API testing
583
- async _initializeApiTool() {
584
- if (!this.apiRequestTool) {
585
- const ApiRequestTool = require('./api-request');
586
- this.apiRequestTool = new ApiRequestTool();
587
- }
588
- return this.apiRequestTool;
589
- }
590
-
591
- // Test API endpoint to verify if it exists and returns expected data
592
- async _testApiEndpoint(method, url, headers = {}, data = null) {
593
- try {
594
- const apiTool = await this._initializeApiTool();
595
- const result = await apiTool.execute({
596
- method: method,
597
- url: url,
598
- headers: headers,
599
- data: data
600
- });
601
-
602
- return {
603
- success: result.success,
604
- statusCode: result.statusCode,
605
- data: result.data,
606
- headers: result.headers
607
- };
608
- } catch (error) {
609
- return {
610
- success: false,
611
- error: error.message
612
- };
613
- }
614
- }
615
- }
616
-
617
- module.exports = ApiHealerTool;