@democratize-quality/mcp-server 1.1.9 → 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 (62) hide show
  1. package/dist/server.d.ts +41 -0
  2. package/dist/server.d.ts.map +1 -0
  3. package/dist/server.js +225 -0
  4. package/dist/server.js.map +1 -0
  5. package/package.json +23 -24
  6. package/browserControl.js +0 -113
  7. package/cli.js +0 -228
  8. package/mcpServer.js +0 -335
  9. package/run-server.js +0 -140
  10. package/src/chatmodes//360/237/214/220 api-generator.chatmode.md" +0 -409
  11. package/src/chatmodes//360/237/214/220 api-healer.chatmode.md" +0 -494
  12. package/src/chatmodes//360/237/214/220 api-planner.chatmode.md" +0 -954
  13. package/src/config/environments/api-only.js +0 -53
  14. package/src/config/environments/development.js +0 -54
  15. package/src/config/environments/production.js +0 -69
  16. package/src/config/index.js +0 -341
  17. package/src/config/server.js +0 -41
  18. package/src/config/tools/api.js +0 -67
  19. package/src/config/tools/browser.js +0 -90
  20. package/src/config/tools/default.js +0 -32
  21. package/src/docs/Agent_README.md +0 -310
  22. package/src/docs/QUICK_REFERENCE.md +0 -111
  23. package/src/services/browserService.js +0 -325
  24. package/src/skills/api-planning/SKILL.md +0 -224
  25. package/src/skills/test-execution/SKILL.md +0 -777
  26. package/src/skills/test-generation/SKILL.md +0 -309
  27. package/src/skills/test-healing/SKILL.md +0 -405
  28. package/src/tools/api/api-generator.js +0 -1865
  29. package/src/tools/api/api-healer.js +0 -617
  30. package/src/tools/api/api-planner.js +0 -2598
  31. package/src/tools/api/api-project-setup.js +0 -313
  32. package/src/tools/api/api-request.js +0 -641
  33. package/src/tools/api/api-session-report.js +0 -1278
  34. package/src/tools/api/api-session-status.js +0 -395
  35. package/src/tools/api/prompts/README.md +0 -293
  36. package/src/tools/api/prompts/generation-prompts.js +0 -703
  37. package/src/tools/api/prompts/healing-prompts.js +0 -195
  38. package/src/tools/api/prompts/index.js +0 -25
  39. package/src/tools/api/prompts/orchestrator.js +0 -334
  40. package/src/tools/api/prompts/validation-rules.js +0 -339
  41. package/src/tools/base/ToolBase.js +0 -230
  42. package/src/tools/base/ToolRegistry.js +0 -269
  43. package/src/tools/browser/advanced/browser-console.js +0 -384
  44. package/src/tools/browser/advanced/browser-dialog.js +0 -319
  45. package/src/tools/browser/advanced/browser-evaluate.js +0 -337
  46. package/src/tools/browser/advanced/browser-file.js +0 -480
  47. package/src/tools/browser/advanced/browser-keyboard.js +0 -343
  48. package/src/tools/browser/advanced/browser-mouse.js +0 -332
  49. package/src/tools/browser/advanced/browser-network.js +0 -421
  50. package/src/tools/browser/advanced/browser-pdf.js +0 -407
  51. package/src/tools/browser/advanced/browser-tabs.js +0 -497
  52. package/src/tools/browser/advanced/browser-wait.js +0 -378
  53. package/src/tools/browser/click.js +0 -168
  54. package/src/tools/browser/close.js +0 -60
  55. package/src/tools/browser/dom.js +0 -70
  56. package/src/tools/browser/launch.js +0 -67
  57. package/src/tools/browser/navigate.js +0 -270
  58. package/src/tools/browser/screenshot.js +0 -351
  59. package/src/tools/browser/type.js +0 -174
  60. package/src/tools/index.js +0 -95
  61. package/src/utils/agentInstaller.js +0 -418
  62. package/src/utils/browserHelpers.js +0 -83
@@ -1,339 +0,0 @@
1
- /**
2
- * Validation Rules for Generated Test Code
3
- *
4
- * These rules validate generated test code before writing to files.
5
- * Catches common issues and triggers healing if needed.
6
- */
7
-
8
- module.exports = {
9
- /**
10
- * Validate Playwright test code
11
- */
12
- playwright: {
13
- /**
14
- * Check if code has required imports
15
- */
16
- hasRequiredImports: (code, language) => {
17
- if (language === 'typescript') {
18
- return code.includes('import { test, expect }') || code.includes("import { test, expect }");
19
- }
20
- return code.includes('require(') || !code.includes('import');
21
- },
22
-
23
- /**
24
- * Check if code uses proper async/await
25
- */
26
- hasProperAsync: (code) => {
27
- if (code.includes('request.')) {
28
- return code.includes('await request.');
29
- }
30
- return true;
31
- },
32
-
33
- /**
34
- * Check if code has proper URL construction
35
- */
36
- hasProperUrlConstruction: (code) => {
37
- // Should use template literals with ${baseUrl}, not string concatenation
38
- const hasTemplateUrl = code.includes('`${baseUrl}') || code.includes("'http");
39
- const hasWrongUrl = code.includes("'${baseUrl}"); // This is the escaping bug!
40
-
41
- return hasTemplateUrl && !hasWrongUrl;
42
- },
43
-
44
- /**
45
- * Check if code has assertions
46
- */
47
- hasAssertions: (code) => {
48
- return code.includes('expect(') && code.includes('.toBe(');
49
- },
50
-
51
- /**
52
- * Check for common syntax errors
53
- */
54
- noSyntaxErrors: (code) => {
55
- // Basic checks - full validation would require parsing
56
- const balancedBraces = (code.match(/\{/g) || []).length === (code.match(/\}/g) || []).length;
57
- const balancedParens = (code.match(/\(/g) || []).length === (code.match(/\)/g) || []).length;
58
- const balancedBrackets = (code.match(/\[/g) || []).length === (code.match(/\]/g) || []).length;
59
-
60
- return balancedBraces && balancedParens && balancedBrackets;
61
- },
62
-
63
- /**
64
- * Check if GraphQL request is properly structured
65
- */
66
- hasGraphQLRequestStructure: (code) => {
67
- // If code contains GraphQL query, check structure
68
- if (code.includes('query:') || code.includes('mutation:') || code.includes('subscription:')) {
69
- const hasQueryField = code.includes('query:') || code.includes('mutation:');
70
- const hasDataObject = code.includes('data: {') && hasQueryField;
71
- return hasQueryField && hasDataObject;
72
- }
73
- return true; // Not GraphQL, skip
74
- },
75
-
76
- /**
77
- * Check if GraphQL response validation is present
78
- */
79
- hasGraphQLResponseValidation: (code) => {
80
- // If it's a GraphQL test, must validate data or errors
81
- if (code.includes('query:') || code.includes('mutation:')) {
82
- const hasDataCheck = code.includes('.toHaveProperty(\'data\')');
83
- const hasErrorsCheck = code.includes('.toHaveProperty(\'errors\')');
84
- return hasDataCheck || hasErrorsCheck;
85
- }
86
- return true; // Not GraphQL
87
- },
88
-
89
- /**
90
- * Run all validations
91
- */
92
- validateAll: (code, language, isGraphQL) => {
93
- const issues = [];
94
-
95
- // Note: We're validating generated TEST BODY code, not full files
96
- // So we don't check for imports in the body
97
-
98
- if (!module.exports.playwright.hasProperAsync(code)) {
99
- issues.push({
100
- severity: 'error',
101
- message: 'Missing await keyword for async API calls',
102
- fix: 'Add await before request.* calls'
103
- });
104
- }
105
-
106
- if (!module.exports.playwright.hasProperUrlConstruction(code)) {
107
- issues.push({
108
- severity: 'critical',
109
- message: 'Incorrect URL construction - template literal escaping issue',
110
- fix: 'Use backticks for template literals: `${baseUrl}/endpoint` not \'${baseUrl}/endpoint\''
111
- });
112
- }
113
-
114
- if (!module.exports.playwright.hasAssertions(code)) {
115
- issues.push({
116
- severity: 'warning',
117
- message: 'No assertions found in test',
118
- fix: 'Add expect() assertions to validate response'
119
- });
120
- }
121
-
122
- if (!module.exports.playwright.noSyntaxErrors(code)) {
123
- issues.push({
124
- severity: 'error',
125
- message: 'Possible syntax error - unbalanced brackets/braces/parens',
126
- fix: 'Check code for balanced brackets, braces, and parentheses'
127
- });
128
- }
129
-
130
- // NEW: GraphQL-specific validations
131
- if (isGraphQL) {
132
- if (!module.exports.playwright.hasGraphQLRequestStructure(code)) {
133
- issues.push({
134
- severity: 'error',
135
- message: 'GraphQL request structure incorrect',
136
- fix: 'Use: data: { query: "...", variables: {...} }'
137
- });
138
- }
139
-
140
- if (!module.exports.playwright.hasGraphQLResponseValidation(code)) {
141
- issues.push({
142
- severity: 'warning',
143
- message: 'Missing GraphQL response validation',
144
- fix: 'Add: expect(responseData).toHaveProperty(\'data\') or .toHaveProperty(\'errors\')'
145
- });
146
- }
147
- }
148
-
149
- return {
150
- valid: issues.filter(i => i.severity === 'error' || i.severity === 'critical').length === 0,
151
- issues: issues
152
- };
153
- }
154
- },
155
-
156
- /**
157
- * Validate Jest test code
158
- */
159
- jest: {
160
- /**
161
- * Check if code uses API utils correctly
162
- */
163
- hasProperApiCall: (code) => {
164
- return code.includes('apiUtils.makeRequest') && code.includes('await');
165
- },
166
-
167
- /**
168
- * Check if code has assertions
169
- */
170
- hasAssertions: (code) => {
171
- return code.includes('expect(');
172
- },
173
-
174
- /**
175
- * Check for common syntax errors
176
- */
177
- noSyntaxErrors: (code) => {
178
- const balancedBraces = (code.match(/\{/g) || []).length === (code.match(/\}/g) || []).length;
179
- const balancedParens = (code.match(/\(/g) || []).length === (code.match(/\)/g) || []).length;
180
- const balancedBrackets = (code.match(/\[/g) || []).length === (code.match(/\]/g) || []).length;
181
-
182
- return balancedBraces && balancedParens && balancedBrackets;
183
- },
184
-
185
- /**
186
- * Check if GraphQL request is properly structured (Jest/axios style)
187
- */
188
- hasGraphQLRequestStructure: (code) => {
189
- if (code.includes('query:') || code.includes('mutation:') || code.includes('subscription:')) {
190
- const hasQueryField = code.includes('query:') || code.includes('mutation:');
191
- const hasDataObject = code.includes('data: {') && hasQueryField;
192
- return hasQueryField && hasDataObject;
193
- }
194
- return true;
195
- },
196
-
197
- /**
198
- * Check if GraphQL response validation is present (Jest style)
199
- */
200
- hasGraphQLResponseValidation: (code) => {
201
- if (code.includes('query:') || code.includes('mutation:')) {
202
- const hasDataCheck = code.includes('.toHaveProperty(\'data\')');
203
- const hasErrorsCheck = code.includes('.toHaveProperty(\'errors\')');
204
- return hasDataCheck || hasErrorsCheck;
205
- }
206
- return true;
207
- },
208
-
209
- /**
210
- * Run all validations
211
- */
212
- validateAll: (code, language, isGraphQL) => {
213
- const issues = [];
214
-
215
- if (!module.exports.jest.hasProperApiCall(code)) {
216
- issues.push({
217
- severity: 'error',
218
- message: 'Missing or incorrect API call using apiUtils.makeRequest',
219
- fix: 'Use await apiUtils.makeRequest({ method, url, ... })'
220
- });
221
- }
222
-
223
- if (!module.exports.jest.hasAssertions(code)) {
224
- issues.push({
225
- severity: 'warning',
226
- message: 'No assertions found in test',
227
- fix: 'Add expect() assertions to validate response'
228
- });
229
- }
230
-
231
- if (!module.exports.jest.noSyntaxErrors(code)) {
232
- issues.push({
233
- severity: 'error',
234
- message: 'Possible syntax error - unbalanced brackets/braces/parens',
235
- fix: 'Check code for balanced brackets, braces, and parentheses'
236
- });
237
- }
238
-
239
- // NEW: GraphQL-specific validations for Jest
240
- if (isGraphQL) {
241
- if (!module.exports.jest.hasGraphQLRequestStructure(code)) {
242
- issues.push({
243
- severity: 'error',
244
- message: 'GraphQL request structure incorrect',
245
- fix: 'Use: data: { query: "...", variables: {...} } in makeRequest()'
246
- });
247
- }
248
-
249
- if (!module.exports.jest.hasGraphQLResponseValidation(code)) {
250
- issues.push({
251
- severity: 'warning',
252
- message: 'Missing GraphQL response validation',
253
- fix: 'Add: expect(response.data).toHaveProperty(\'data\') or .toHaveProperty(\'errors\')'
254
- });
255
- }
256
- }
257
-
258
- return {
259
- valid: issues.filter(i => i.severity === 'error' || i.severity === 'critical').length === 0,
260
- issues: issues
261
- };
262
- }
263
- },
264
-
265
- /**
266
- * Validate Postman collection structure
267
- */
268
- postman: {
269
- /**
270
- * Validate collection has required fields
271
- */
272
- hasRequiredFields: (collection) => {
273
- return collection.info &&
274
- collection.info.name &&
275
- collection.info.schema &&
276
- Array.isArray(collection.item);
277
- },
278
-
279
- /**
280
- * Validate request structure
281
- */
282
- hasValidRequests: (collection) => {
283
- if (!collection.item || collection.item.length === 0) return false;
284
-
285
- // Check first item as sample
286
- const firstItem = collection.item[0];
287
- if (firstItem.item && Array.isArray(firstItem.item)) {
288
- // It's a folder, check its items
289
- return firstItem.item.every(req => req.name && req.request);
290
- }
291
-
292
- return firstItem.name && firstItem.request;
293
- },
294
-
295
- /**
296
- * Run all validations
297
- */
298
- validateAll: (collection) => {
299
- const issues = [];
300
-
301
- if (!module.exports.postman.hasRequiredFields(collection)) {
302
- issues.push({
303
- severity: 'error',
304
- message: 'Missing required collection fields (info, name, schema, item)',
305
- fix: 'Ensure collection has proper Postman v2.1 structure'
306
- });
307
- }
308
-
309
- if (!module.exports.postman.hasValidRequests(collection)) {
310
- issues.push({
311
- severity: 'error',
312
- message: 'Invalid request structure in collection',
313
- fix: 'Ensure each request has name and request object'
314
- });
315
- }
316
-
317
- return {
318
- valid: issues.filter(i => i.severity === 'error' || i.severity === 'critical').length === 0,
319
- issues: issues
320
- };
321
- }
322
- },
323
-
324
- /**
325
- * Get validator for output format
326
- */
327
- getValidator: (format) => {
328
- switch(format) {
329
- case 'playwright':
330
- return module.exports.playwright;
331
- case 'jest':
332
- return module.exports.jest;
333
- case 'postman':
334
- return module.exports.postman;
335
- default:
336
- throw new Error(`Unknown format: ${format}`);
337
- }
338
- }
339
- };
@@ -1,230 +0,0 @@
1
- /**
2
- * Base class for all MCP tools
3
- * Provides common functionality and enforces a consistent interface
4
- */
5
- class ToolBase {
6
- constructor() {
7
- if (this.constructor === ToolBase) {
8
- throw new Error("ToolBase is an abstract class and cannot be instantiated directly");
9
- }
10
-
11
- // Validate that required static properties are defined
12
- if (!this.constructor.definition) {
13
- throw new Error(`Tool ${this.constructor.name} must define a static 'definition' property`);
14
- }
15
-
16
- this.validateDefinition(this.constructor.definition);
17
-
18
- // Initialize configuration
19
- this.config = require('../../config');
20
- this.toolConfig = this._getToolConfig();
21
- }
22
-
23
- /**
24
- * Get tool-specific configuration
25
- * @returns {object} - Tool configuration
26
- */
27
- _getToolConfig() {
28
- const toolName = this.constructor.definition.name;
29
- const category = this._getToolCategory(toolName);
30
-
31
- // Get configuration in order of precedence:
32
- // 1. Tool-specific config
33
- // 2. Category config
34
- // 3. Default config
35
- return {
36
- ...this.config.getToolConfig('default', {}),
37
- ...this.config.getToolConfig(category, {}),
38
- ...this.config.getToolConfig(category, toolName)
39
- };
40
- }
41
-
42
- /**
43
- * Get tool category from tool name
44
- * @param {string} toolName - The tool name
45
- * @returns {string} - The tool category
46
- */
47
- _getToolCategory(toolName) {
48
- if (toolName.startsWith('browser_')) return 'browser';
49
- if (toolName.startsWith('file_')) return 'file';
50
- if (toolName.startsWith('network_')) return 'network';
51
- return 'other';
52
- }
53
-
54
- /**
55
- * Get a configuration value for this tool
56
- * @param {string} key - Configuration key
57
- * @param {any} defaultValue - Default value
58
- * @returns {any} - Configuration value
59
- */
60
- getConfig(key, defaultValue = undefined) {
61
- return this.toolConfig[key] !== undefined ? this.toolConfig[key] : defaultValue;
62
- }
63
-
64
- /**
65
- * Validates the tool definition schema
66
- * @param {object} definition - The tool definition object
67
- */
68
- validateDefinition(definition) {
69
- const required = ['name', 'description', 'input_schema'];
70
- for (const field of required) {
71
- if (!definition[field]) {
72
- throw new Error(`Tool definition missing required field: ${field}`);
73
- }
74
- }
75
-
76
- if (!definition.input_schema.type || definition.input_schema.type !== 'object') {
77
- throw new Error("Tool input_schema must be of type 'object'");
78
- }
79
- }
80
-
81
- /**
82
- * Validates input parameters against the tool's schema
83
- * @param {object} parameters - The input parameters to validate
84
- */
85
- validateParameters(parameters) {
86
- const schema = this.constructor.definition.input_schema;
87
-
88
- // Check required parameters
89
- if (schema.required) {
90
- for (const required of schema.required) {
91
- if (parameters[required] === undefined || parameters[required] === null) {
92
- throw new Error(`Missing required parameter: ${required}`);
93
- }
94
- }
95
- }
96
-
97
- // Basic type checking for defined properties
98
- if (schema.properties) {
99
- for (const [key, value] of Object.entries(parameters)) {
100
- if (schema.properties[key]) {
101
- const expectedType = schema.properties[key].type;
102
- const actualType = typeof value;
103
-
104
- if (expectedType === 'string' && actualType !== 'string') {
105
- throw new Error(`Parameter '${key}' must be a string, got ${actualType}`);
106
- }
107
- if (expectedType === 'number' && actualType !== 'number') {
108
- throw new Error(`Parameter '${key}' must be a number, got ${actualType}`);
109
- }
110
- if (expectedType === 'boolean' && actualType !== 'boolean') {
111
- throw new Error(`Parameter '${key}' must be a boolean, got ${actualType}`);
112
- }
113
- if (expectedType === 'object' && (actualType !== 'object' || Array.isArray(value))) {
114
- throw new Error(`Parameter '${key}' must be an object, got ${actualType}`);
115
- }
116
- }
117
- }
118
- }
119
- }
120
-
121
- /**
122
- * Executes the tool with the given parameters
123
- * This method must be implemented by subclasses
124
- * @param {object} parameters - The input parameters
125
- * @returns {Promise<any>} - The tool execution result
126
- */
127
- async execute(parameters) {
128
- throw new Error("execute() method must be implemented by subclasses");
129
- }
130
-
131
- /**
132
- * Wrapper method that handles validation, execution, and error handling
133
- * @param {object} parameters - The input parameters
134
- * @returns {Promise<object>} - Formatted MCP response
135
- */
136
- async run(parameters = {}) {
137
- const toolName = this.constructor.definition.name;
138
- const enableDebug = this.config.isFeatureEnabled('enableDebugMode');
139
-
140
- try {
141
- if (enableDebug) {
142
- console.error(`[Tool:${toolName}] Executing with parameters:`, JSON.stringify(parameters));
143
- }
144
-
145
- // Validate input parameters if validation is enabled
146
- if (this.getConfig('enableInputValidation', true)) {
147
- this.validateParameters(parameters);
148
- }
149
-
150
- // Execute the tool with timeout if configured
151
- const timeout = this.getConfig('timeout', 30000);
152
- const result = await this._executeWithTimeout(parameters, timeout);
153
-
154
- if (enableDebug) {
155
- console.error(`[Tool:${toolName}] Execution successful`);
156
- }
157
-
158
- // Return formatted MCP response
159
- return {
160
- content: [{
161
- type: "text",
162
- text: JSON.stringify(result)
163
- }]
164
- };
165
-
166
- } catch (error) {
167
- const enableDetailedErrors = this.getConfig('enableDetailedErrors', true);
168
-
169
- if (enableDetailedErrors || enableDebug) {
170
- console.error(`[Tool:${toolName}] Error during execution:`, error.message);
171
- }
172
-
173
- // Throw properly formatted MCP error
174
- throw {
175
- code: -32000,
176
- message: `Tool '${toolName}' execution failed: ${error.message}`,
177
- data: enableDetailedErrors ? {
178
- tool_name: toolName,
179
- parameters: parameters,
180
- original_error: error.message,
181
- config: this.toolConfig
182
- } : {
183
- tool_name: toolName,
184
- original_error: error.message
185
- }
186
- };
187
- }
188
- }
189
-
190
- /**
191
- * Execute the tool with a timeout
192
- * @param {object} parameters - The input parameters
193
- * @param {number} timeout - Timeout in milliseconds
194
- * @returns {Promise<any>} - Execution result
195
- */
196
- async _executeWithTimeout(parameters, timeout) {
197
- return new Promise(async (resolve, reject) => {
198
- const timeoutId = setTimeout(() => {
199
- reject(new Error(`Tool execution timed out after ${timeout}ms`));
200
- }, timeout);
201
-
202
- try {
203
- const result = await this.execute(parameters);
204
- clearTimeout(timeoutId);
205
- resolve(result);
206
- } catch (error) {
207
- clearTimeout(timeoutId);
208
- reject(error);
209
- }
210
- });
211
- }
212
-
213
- /**
214
- * Gets the tool definition
215
- * @returns {object} - The tool definition
216
- */
217
- static getDefinition() {
218
- return this.definition;
219
- }
220
-
221
- /**
222
- * Gets the tool name from the definition
223
- * @returns {string} - The tool name
224
- */
225
- static getName() {
226
- return this.definition.name;
227
- }
228
- }
229
-
230
- module.exports = ToolBase;