@democratize-quality/mcp-server 1.2.0 → 1.2.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 (56) hide show
  1. package/cli.js +248 -0
  2. package/package.json +7 -5
  3. package/src/chatmodes//360/237/214/220 api-generator.chatmode.md" +409 -0
  4. package/src/chatmodes//360/237/214/220 api-healer.chatmode.md" +494 -0
  5. package/src/chatmodes//360/237/214/220 api-planner.chatmode.md" +954 -0
  6. package/src/config/environments/api-only.js +72 -0
  7. package/src/config/environments/development.js +73 -0
  8. package/src/config/environments/production.js +88 -0
  9. package/src/config/index.js +360 -0
  10. package/src/config/server.js +60 -0
  11. package/src/config/tools/api.js +86 -0
  12. package/src/config/tools/browser.js +109 -0
  13. package/src/config/tools/default.js +51 -0
  14. package/src/docs/Agent_README.md +310 -0
  15. package/src/docs/QUICK_REFERENCE.md +111 -0
  16. package/src/server.ts +234 -0
  17. package/src/services/browserService.js +344 -0
  18. package/src/skills/api-planning/SKILL.md +224 -0
  19. package/src/skills/test-execution/SKILL.md +777 -0
  20. package/src/skills/test-generation/SKILL.md +309 -0
  21. package/src/skills/test-healing/SKILL.md +405 -0
  22. package/src/tools/api/api-generator.js +1884 -0
  23. package/src/tools/api/api-healer.js +636 -0
  24. package/src/tools/api/api-planner.js +2617 -0
  25. package/src/tools/api/api-project-setup.js +332 -0
  26. package/src/tools/api/api-request.js +660 -0
  27. package/src/tools/api/api-session-report.js +1297 -0
  28. package/src/tools/api/api-session-status.js +414 -0
  29. package/src/tools/api/prompts/README.md +293 -0
  30. package/src/tools/api/prompts/generation-prompts.js +722 -0
  31. package/src/tools/api/prompts/healing-prompts.js +214 -0
  32. package/src/tools/api/prompts/index.js +44 -0
  33. package/src/tools/api/prompts/orchestrator.js +353 -0
  34. package/src/tools/api/prompts/validation-rules.js +358 -0
  35. package/src/tools/base/ToolBase.js +249 -0
  36. package/src/tools/base/ToolRegistry.js +288 -0
  37. package/src/tools/browser/advanced/browser-console.js +403 -0
  38. package/src/tools/browser/advanced/browser-dialog.js +338 -0
  39. package/src/tools/browser/advanced/browser-evaluate.js +356 -0
  40. package/src/tools/browser/advanced/browser-file.js +499 -0
  41. package/src/tools/browser/advanced/browser-keyboard.js +362 -0
  42. package/src/tools/browser/advanced/browser-mouse.js +351 -0
  43. package/src/tools/browser/advanced/browser-network.js +440 -0
  44. package/src/tools/browser/advanced/browser-pdf.js +426 -0
  45. package/src/tools/browser/advanced/browser-tabs.js +516 -0
  46. package/src/tools/browser/advanced/browser-wait.js +397 -0
  47. package/src/tools/browser/click.js +187 -0
  48. package/src/tools/browser/close.js +79 -0
  49. package/src/tools/browser/dom.js +89 -0
  50. package/src/tools/browser/launch.js +86 -0
  51. package/src/tools/browser/navigate.js +289 -0
  52. package/src/tools/browser/screenshot.js +370 -0
  53. package/src/tools/browser/type.js +193 -0
  54. package/src/tools/index.js +114 -0
  55. package/src/utils/agentInstaller.js +437 -0
  56. package/src/utils/browserHelpers.js +102 -0
@@ -0,0 +1,426 @@
1
+ /**
2
+ * Copyright (C) 2025 Democratize Quality
3
+ *
4
+ * This file is part of Democratize Quality MCP Server.
5
+ *
6
+ * Democratize Quality MCP Server is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU Affero General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * Democratize Quality MCP Server is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU Affero General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Affero General Public License
17
+ * along with Democratize Quality MCP Server. If not, see <https://www.gnu.org/licenses/>.
18
+ */
19
+
20
+ const ToolBase = require('../../base/ToolBase');
21
+ const browserService = require('../../../services/browserService');
22
+ const fs = require('fs').promises;
23
+ const path = require('path');
24
+
25
+ /**
26
+ * PDF Tool - Generate PDF files from web pages
27
+ * Inspired by Playwright MCP PDF capabilities
28
+ */
29
+ class BrowserPdfTool extends ToolBase {
30
+ static definition = {
31
+ name: "browser_pdf",
32
+ description: "Generate PDF files from web pages with comprehensive formatting options and page control.",
33
+ input_schema: {
34
+ type: "object",
35
+ properties: {
36
+ browserId: {
37
+ type: "string",
38
+ description: "The ID of the browser instance"
39
+ },
40
+ filePath: {
41
+ type: "string",
42
+ description: "Path where the PDF file will be saved"
43
+ },
44
+ options: {
45
+ type: "object",
46
+ properties: {
47
+ format: {
48
+ type: "string",
49
+ enum: ["A3", "A4", "A5", "Legal", "Letter", "Tabloid"],
50
+ default: "A4",
51
+ description: "Paper format"
52
+ },
53
+ width: {
54
+ type: "string",
55
+ description: "Paper width (e.g., '8.5in', '215.9mm')"
56
+ },
57
+ height: {
58
+ type: "string",
59
+ description: "Paper height (e.g., '11in', '279.4mm')"
60
+ },
61
+ landscape: {
62
+ type: "boolean",
63
+ default: false,
64
+ description: "Paper orientation"
65
+ },
66
+ margin: {
67
+ type: "object",
68
+ properties: {
69
+ top: { type: "string", description: "Top margin (e.g., '1in', '2.54cm')" },
70
+ bottom: { type: "string", description: "Bottom margin" },
71
+ left: { type: "string", description: "Left margin" },
72
+ right: { type: "string", description: "Right margin" }
73
+ },
74
+ description: "Page margins"
75
+ },
76
+ printBackground: {
77
+ type: "boolean",
78
+ default: true,
79
+ description: "Whether to print background graphics"
80
+ },
81
+ scale: {
82
+ type: "number",
83
+ minimum: 0.1,
84
+ maximum: 2,
85
+ default: 1,
86
+ description: "Scale factor for the PDF"
87
+ },
88
+ pageRanges: {
89
+ type: "string",
90
+ description: "Paper ranges to print (e.g., '1-5, 8, 11-13')"
91
+ },
92
+ headerTemplate: {
93
+ type: "string",
94
+ description: "HTML template for page header"
95
+ },
96
+ footerTemplate: {
97
+ type: "string",
98
+ description: "HTML template for page footer"
99
+ },
100
+ displayHeaderFooter: {
101
+ type: "boolean",
102
+ default: false,
103
+ description: "Whether to display header and footer"
104
+ },
105
+ preferCSSPageSize: {
106
+ type: "boolean",
107
+ default: false,
108
+ description: "Give preference to CSS page size"
109
+ }
110
+ },
111
+ description: "PDF generation options"
112
+ },
113
+ waitForSelector: {
114
+ type: "string",
115
+ description: "CSS selector to wait for before generating PDF"
116
+ },
117
+ waitForTimeout: {
118
+ type: "number",
119
+ default: 5000,
120
+ description: "Timeout for waiting in milliseconds"
121
+ },
122
+ generateBase64: {
123
+ type: "boolean",
124
+ default: false,
125
+ description: "Whether to return PDF as base64 string"
126
+ }
127
+ },
128
+ required: ["browserId"]
129
+ },
130
+ output_schema: {
131
+ type: "object",
132
+ properties: {
133
+ success: { type: "boolean", description: "Whether PDF generation was successful" },
134
+ filePath: { type: "string", description: "Path where PDF was saved" },
135
+ size: { type: "number", description: "PDF file size in bytes" },
136
+ base64: { type: "string", description: "PDF content as base64 (if requested)" },
137
+ pages: { type: "number", description: "Number of pages in PDF" },
138
+ options: { type: "object", description: "PDF generation options used" },
139
+ browserId: { type: "string", description: "Browser instance ID" }
140
+ },
141
+ required: ["success", "browserId"]
142
+ }
143
+ };
144
+
145
+ async execute(parameters) {
146
+ const {
147
+ browserId,
148
+ filePath,
149
+ options = {},
150
+ waitForSelector,
151
+ waitForTimeout = 5000,
152
+ generateBase64 = false
153
+ } = parameters;
154
+
155
+ const browser = browserService.getBrowserInstance(browserId);
156
+ if (!browser) {
157
+ throw new Error(`Browser instance '${browserId}' not found`);
158
+ }
159
+
160
+ const client = browser.client;
161
+
162
+ // Enable Page domain
163
+ await client.Page.enable();
164
+
165
+ // Wait for specific element if requested
166
+ if (waitForSelector) {
167
+ await this.waitForSelector(client, waitForSelector, waitForTimeout);
168
+ }
169
+
170
+ // Prepare PDF options
171
+ const pdfOptions = this.preparePdfOptions(options);
172
+
173
+ // Generate filename if not provided
174
+ const outputPath = filePath || this.generateDefaultPath();
175
+
176
+ // Ensure output directory exists
177
+ await this.ensureDirectoryExists(outputPath);
178
+
179
+ try {
180
+ // Generate PDF
181
+ const pdfData = await client.Page.printToPDF(pdfOptions);
182
+
183
+ // Save to file
184
+ const buffer = Buffer.from(pdfData.data, 'base64');
185
+ await fs.writeFile(outputPath, buffer);
186
+
187
+ // Get file stats
188
+ const stats = await fs.stat(outputPath);
189
+
190
+ const result = {
191
+ success: true,
192
+ filePath: outputPath,
193
+ size: stats.size,
194
+ options: pdfOptions,
195
+ browserId: browserId
196
+ };
197
+
198
+ // Add base64 if requested
199
+ if (generateBase64) {
200
+ result.base64 = pdfData.data;
201
+ }
202
+
203
+ // Estimate page count (rough calculation)
204
+ result.pages = this.estimatePageCount(stats.size);
205
+
206
+ return result;
207
+ } catch (error) {
208
+ throw new Error(`PDF generation failed: ${error.message}`);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Prepare PDF options for Chrome DevTools Protocol
214
+ */
215
+ preparePdfOptions(options) {
216
+ const pdfOptions = {
217
+ printBackground: options.printBackground !== undefined ? options.printBackground : true,
218
+ landscape: options.landscape || false,
219
+ scale: options.scale || 1,
220
+ preferCSSPageSize: options.preferCSSPageSize || false,
221
+ displayHeaderFooter: options.displayHeaderFooter || false
222
+ };
223
+
224
+ // Handle paper format or custom dimensions
225
+ if (options.format) {
226
+ pdfOptions.format = options.format;
227
+ } else if (options.width || options.height) {
228
+ if (options.width) pdfOptions.paperWidth = this.convertToInches(options.width);
229
+ if (options.height) pdfOptions.paperHeight = this.convertToInches(options.height);
230
+ }
231
+
232
+ // Handle margins
233
+ if (options.margin) {
234
+ if (options.margin.top) pdfOptions.marginTop = this.convertToInches(options.margin.top);
235
+ if (options.margin.bottom) pdfOptions.marginBottom = this.convertToInches(options.margin.bottom);
236
+ if (options.margin.left) pdfOptions.marginLeft = this.convertToInches(options.margin.left);
237
+ if (options.margin.right) pdfOptions.marginRight = this.convertToInches(options.margin.right);
238
+ }
239
+
240
+ // Handle page ranges
241
+ if (options.pageRanges) {
242
+ pdfOptions.pageRanges = options.pageRanges;
243
+ }
244
+
245
+ // Handle header/footer templates
246
+ if (options.headerTemplate) {
247
+ pdfOptions.headerTemplate = options.headerTemplate;
248
+ }
249
+ if (options.footerTemplate) {
250
+ pdfOptions.footerTemplate = options.footerTemplate;
251
+ }
252
+
253
+ return pdfOptions;
254
+ }
255
+
256
+ /**
257
+ * Convert various units to inches for CDP
258
+ */
259
+ convertToInches(value) {
260
+ if (typeof value === 'number') return value;
261
+
262
+ const numValue = parseFloat(value);
263
+ const unit = value.replace(/[\d.-]/g, '').toLowerCase();
264
+
265
+ switch (unit) {
266
+ case 'in':
267
+ case 'inch':
268
+ case 'inches':
269
+ return numValue;
270
+ case 'cm':
271
+ return numValue / 2.54;
272
+ case 'mm':
273
+ return numValue / 25.4;
274
+ case 'pt':
275
+ return numValue / 72;
276
+ case 'px':
277
+ return numValue / 96; // 96 DPI assumption
278
+ default:
279
+ return numValue; // Assume inches if no unit
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Wait for a specific selector to appear
285
+ */
286
+ async waitForSelector(client, selector, timeout) {
287
+ const startTime = Date.now();
288
+
289
+ while (Date.now() - startTime < timeout) {
290
+ const result = await client.Runtime.evaluate({
291
+ expression: `document.querySelector('${selector}') !== null`
292
+ });
293
+
294
+ if (result.result.value === true) {
295
+ return;
296
+ }
297
+
298
+ await new Promise(resolve => setTimeout(resolve, 100));
299
+ }
300
+
301
+ throw new Error(`Selector '${selector}' not found within ${timeout}ms`);
302
+ }
303
+
304
+ /**
305
+ * Generate default PDF filename
306
+ */
307
+ generateDefaultPath() {
308
+ const os = require('os');
309
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
310
+
311
+ // Use output directory from environment or default to user home directory
312
+ const defaultOutputDir = process.env.HOME
313
+ ? path.join(process.env.HOME, '.mcp-browser-control')
314
+ : path.join(os.tmpdir(), 'mcp-browser-control');
315
+ const outputDir = process.env.OUTPUT_DIR || defaultOutputDir;
316
+
317
+ return path.join(outputDir, `page-${timestamp}.pdf`);
318
+ }
319
+
320
+ /**
321
+ * Ensure output directory exists
322
+ */
323
+ async ensureDirectoryExists(filePath) {
324
+ const dir = path.dirname(filePath);
325
+ try {
326
+ await fs.access(dir);
327
+ } catch {
328
+ await fs.mkdir(dir, { recursive: true });
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Estimate page count from file size (rough calculation)
334
+ */
335
+ estimatePageCount(fileSize) {
336
+ // Very rough estimation: ~50KB per page average
337
+ return Math.max(1, Math.round(fileSize / 50000));
338
+ }
339
+
340
+ /**
341
+ * Create PDF with custom CSS for print
342
+ */
343
+ async createPdfWithPrintCSS(client, css, pdfOptions) {
344
+ // Inject print-specific CSS
345
+ await client.Runtime.evaluate({
346
+ expression: `
347
+ (function() {
348
+ const style = document.createElement('style');
349
+ style.textContent = \`@media print { ${css} }\`;
350
+ document.head.appendChild(style);
351
+ })()
352
+ `
353
+ });
354
+
355
+ // Generate PDF
356
+ return await client.Page.printToPDF(pdfOptions);
357
+ }
358
+
359
+ /**
360
+ * Get default header/footer templates
361
+ */
362
+ getDefaultTemplates() {
363
+ return {
364
+ header: `
365
+ <div style="font-size: 10px; width: 100%; text-align: center; margin: 0;">
366
+ <span class="title"></span>
367
+ </div>
368
+ `,
369
+ footer: `
370
+ <div style="font-size: 10px; width: 100%; text-align: center; margin: 0;">
371
+ Page <span class="pageNumber"></span> of <span class="totalPages"></span>
372
+ </div>
373
+ `
374
+ };
375
+ }
376
+
377
+ /**
378
+ * Generate PDF with table of contents
379
+ */
380
+ async generatePdfWithTOC(client, pdfOptions) {
381
+ // Extract headings for TOC
382
+ const headings = await client.Runtime.evaluate({
383
+ expression: `
384
+ Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6')).map(h => ({
385
+ level: parseInt(h.tagName.slice(1)),
386
+ text: h.textContent.trim(),
387
+ id: h.id || ''
388
+ }))
389
+ `
390
+ });
391
+
392
+ // Generate TOC HTML
393
+ const tocHtml = this.generateTOCHtml(headings.result.value);
394
+
395
+ // Insert TOC at beginning of document
396
+ await client.Runtime.evaluate({
397
+ expression: `
398
+ (function() {
399
+ const toc = document.createElement('div');
400
+ toc.innerHTML = \`${tocHtml}\`;
401
+ toc.style.pageBreakAfter = 'always';
402
+ document.body.insertBefore(toc, document.body.firstChild);
403
+ })()
404
+ `
405
+ });
406
+
407
+ return await client.Page.printToPDF(pdfOptions);
408
+ }
409
+
410
+ /**
411
+ * Generate Table of Contents HTML
412
+ */
413
+ generateTOCHtml(headings) {
414
+ let tocHtml = '<div class="table-of-contents"><h2>Table of Contents</h2><ul>';
415
+
416
+ headings.forEach((heading, index) => {
417
+ const indent = ' '.repeat(heading.level - 1);
418
+ tocHtml += `${indent}<li><a href="#${heading.id || `heading-${index}`}">${heading.text}</a></li>`;
419
+ });
420
+
421
+ tocHtml += '</ul></div>';
422
+ return tocHtml;
423
+ }
424
+ }
425
+
426
+ module.exports = BrowserPdfTool;