@democratize-quality/mcp-server 1.0.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 (48) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +423 -0
  3. package/browserControl.js +113 -0
  4. package/cli.js +187 -0
  5. package/docs/api/tool-reference.md +317 -0
  6. package/docs/api_tools_usage.md +477 -0
  7. package/docs/development/adding-tools.md +274 -0
  8. package/docs/development/configuration.md +332 -0
  9. package/docs/examples/authentication.md +124 -0
  10. package/docs/examples/basic-automation.md +105 -0
  11. package/docs/getting-started.md +214 -0
  12. package/docs/index.md +61 -0
  13. package/mcpServer.js +280 -0
  14. package/package.json +83 -0
  15. package/run-server.js +140 -0
  16. package/src/config/environments/api-only.js +53 -0
  17. package/src/config/environments/development.js +54 -0
  18. package/src/config/environments/production.js +69 -0
  19. package/src/config/index.js +341 -0
  20. package/src/config/server.js +41 -0
  21. package/src/config/tools/api.js +67 -0
  22. package/src/config/tools/browser.js +90 -0
  23. package/src/config/tools/default.js +32 -0
  24. package/src/services/browserService.js +325 -0
  25. package/src/tools/api/api-request.js +641 -0
  26. package/src/tools/api/api-session-report.js +1262 -0
  27. package/src/tools/api/api-session-status.js +395 -0
  28. package/src/tools/base/ToolBase.js +230 -0
  29. package/src/tools/base/ToolRegistry.js +269 -0
  30. package/src/tools/browser/advanced/browser-console.js +384 -0
  31. package/src/tools/browser/advanced/browser-dialog.js +319 -0
  32. package/src/tools/browser/advanced/browser-evaluate.js +337 -0
  33. package/src/tools/browser/advanced/browser-file.js +480 -0
  34. package/src/tools/browser/advanced/browser-keyboard.js +343 -0
  35. package/src/tools/browser/advanced/browser-mouse.js +332 -0
  36. package/src/tools/browser/advanced/browser-network.js +421 -0
  37. package/src/tools/browser/advanced/browser-pdf.js +407 -0
  38. package/src/tools/browser/advanced/browser-tabs.js +497 -0
  39. package/src/tools/browser/advanced/browser-wait.js +378 -0
  40. package/src/tools/browser/click.js +168 -0
  41. package/src/tools/browser/close.js +60 -0
  42. package/src/tools/browser/dom.js +70 -0
  43. package/src/tools/browser/launch.js +67 -0
  44. package/src/tools/browser/navigate.js +270 -0
  45. package/src/tools/browser/screenshot.js +351 -0
  46. package/src/tools/browser/type.js +174 -0
  47. package/src/tools/index.js +95 -0
  48. package/src/utils/browserHelpers.js +83 -0
@@ -0,0 +1,341 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ /**
5
+ * Configuration Management System
6
+ * Loads and manages configuration from multiple sources with environment-based overrides
7
+ */
8
+ class ConfigManager {
9
+ constructor(options = {}) {
10
+ this.config = {};
11
+ this.environment = process.env.NODE_ENV || 'api-only';
12
+ this.configDir = path.join(__dirname);
13
+
14
+ // Check for debug mode early
15
+ const debugFromEnv = process.env.MCP_FEATURES_ENABLEDEBUGMODE === 'true' || this.environment === 'development';
16
+ this.quiet = options.quiet !== undefined ? options.quiet : !debugFromEnv;
17
+
18
+ this.loadConfiguration();
19
+ }
20
+
21
+ /**
22
+ * Load configuration from multiple sources in order of precedence:
23
+ * 1. Environment variables (highest precedence)
24
+ * 2. Environment-specific config files
25
+ * 3. Default configuration files (lowest precedence)
26
+ */
27
+ loadConfiguration() {
28
+ if (!this.quiet) {
29
+ console.error('[Config] Loading configuration...');
30
+ }
31
+
32
+ try {
33
+ // Load base server configuration
34
+ this.config = this.loadConfigFile('server.js', {});
35
+
36
+ // Load tool configurations
37
+ this.config.tools = this.loadToolConfigs();
38
+
39
+ // Load environment-specific overrides
40
+ this.loadEnvironmentConfig();
41
+
42
+ // Apply environment variable overrides (highest precedence)
43
+ this.applyEnvironmentVariables();
44
+
45
+ if (!this.quiet) {
46
+ console.error(`[Config] Configuration loaded for environment: ${this.environment}`);
47
+ }
48
+
49
+ } catch (error) {
50
+ console.error('[Config] Error loading configuration:', error.message);
51
+ // Use defaults if config loading fails
52
+ this.config = this.getDefaultConfig();
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Load a configuration file if it exists
58
+ * @param {string} filename - The config file name
59
+ * @param {object} defaultValue - Default value if file doesn't exist
60
+ * @returns {object} - The loaded configuration
61
+ */
62
+ loadConfigFile(filename, defaultValue = {}) {
63
+ const filePath = path.join(this.configDir, filename);
64
+
65
+ if (fs.existsSync(filePath)) {
66
+ try {
67
+ return require(filePath);
68
+ } catch (error) {
69
+ console.error(`[Config] Error loading ${filename}:`, error.message);
70
+ return defaultValue;
71
+ }
72
+ }
73
+
74
+ return defaultValue;
75
+ }
76
+
77
+ /**
78
+ * Load all tool-specific configurations
79
+ * @returns {object} - Combined tool configurations
80
+ */
81
+ loadToolConfigs() {
82
+ const toolsDir = path.join(this.configDir, 'tools');
83
+ const toolConfigs = {};
84
+
85
+ // Load default tool config
86
+ const defaultToolConfig = this.loadConfigFile('tools/default.js', {});
87
+
88
+ if (fs.existsSync(toolsDir)) {
89
+ const toolConfigFiles = fs.readdirSync(toolsDir).filter(file =>
90
+ file.endsWith('.js') && file !== 'default.js'
91
+ );
92
+
93
+ for (const file of toolConfigFiles) {
94
+ const configName = path.basename(file, '.js');
95
+ toolConfigs[configName] = {
96
+ ...defaultToolConfig,
97
+ ...this.loadConfigFile(`tools/${file}`, {})
98
+ };
99
+ }
100
+ }
101
+
102
+ return toolConfigs;
103
+ }
104
+
105
+ /**
106
+ * Load environment-specific configuration overrides
107
+ */
108
+ loadEnvironmentConfig() {
109
+ const envConfig = this.loadConfigFile(`environments/${this.environment}.js`, {});
110
+
111
+ // Deep merge environment config
112
+ this.config = this.deepMerge(this.config, envConfig);
113
+ }
114
+
115
+ /**
116
+ * Apply environment variable overrides
117
+ * Environment variables follow the pattern: MCP_SECTION_KEY=value
118
+ */
119
+ applyEnvironmentVariables() {
120
+ const envPrefix = 'MCP_';
121
+
122
+ for (const [key, value] of Object.entries(process.env)) {
123
+ if (key.startsWith(envPrefix)) {
124
+ let configPath = key.substring(envPrefix.length).toLowerCase().split('_');
125
+ const parsedValue = this.parseEnvValue(value);
126
+
127
+ // Special handling for feature flags to maintain camelCase
128
+ if (configPath[0] === 'features' && configPath.length > 1) {
129
+ // Convert features_enablebrowsertools to features.enableBrowserTools
130
+ const featureName = configPath.slice(1).join('_');
131
+ const camelCaseFeature = this.toCamelCase(featureName);
132
+ configPath = ['features', camelCaseFeature];
133
+
134
+ // Remove the lowercase version if it exists
135
+ if (this.config.features && this.config.features[featureName]) {
136
+ delete this.config.features[featureName];
137
+ }
138
+ }
139
+
140
+ this.setNestedValue(this.config, configPath, parsedValue);
141
+ }
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Convert snake_case to camelCase
147
+ * @param {string} str - Snake case string
148
+ * @returns {string} - CamelCase string
149
+ */
150
+ toCamelCase(str) {
151
+ // Handle special cases for tool feature flags
152
+ if (str === 'enableapitools') return 'enableApiTools';
153
+ if (str === 'enablebrowsertools') return 'enableBrowserTools';
154
+ if (str === 'enableadvancedtools') return 'enableAdvancedTools';
155
+ if (str === 'enablefiletools') return 'enableFileTools';
156
+ if (str === 'enablenetworktools') return 'enableNetworkTools';
157
+ if (str === 'enableothertools') return 'enableOtherTools';
158
+ if (str === 'enabledebugmode') return 'enableDebugMode';
159
+
160
+ // General snake_case to camelCase conversion
161
+ return str.replace(/_([a-z])/g, (match, letter) => letter.toUpperCase());
162
+ }
163
+
164
+ /**
165
+ * Parse environment variable values to appropriate types
166
+ * @param {string} value - The environment variable value
167
+ * @returns {any} - Parsed value
168
+ */
169
+ parseEnvValue(value) {
170
+ // Boolean values
171
+ if (value.toLowerCase() === 'true') return true;
172
+ if (value.toLowerCase() === 'false') return false;
173
+
174
+ // Number values
175
+ if (/^\d+$/.test(value)) return parseInt(value, 10);
176
+ if (/^\d+\.\d+$/.test(value)) return parseFloat(value);
177
+
178
+ // JSON values
179
+ if (value.startsWith('{') || value.startsWith('[')) {
180
+ try {
181
+ return JSON.parse(value);
182
+ } catch {
183
+ return value;
184
+ }
185
+ }
186
+
187
+ return value;
188
+ }
189
+
190
+ /**
191
+ * Deep merge two objects
192
+ * @param {object} target - Target object
193
+ * @param {object} source - Source object
194
+ * @returns {object} - Merged object
195
+ */
196
+ deepMerge(target, source) {
197
+ const result = { ...target };
198
+
199
+ for (const [key, value] of Object.entries(source)) {
200
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
201
+ result[key] = this.deepMerge(result[key] || {}, value);
202
+ } else {
203
+ result[key] = value;
204
+ }
205
+ }
206
+
207
+ return result;
208
+ }
209
+
210
+ /**
211
+ * Set a nested value in an object using a path array
212
+ * @param {object} obj - Target object
213
+ * @param {Array<string>} path - Path array
214
+ * @param {any} value - Value to set
215
+ */
216
+ setNestedValue(obj, path, value) {
217
+ let current = obj;
218
+
219
+ for (let i = 0; i < path.length - 1; i++) {
220
+ if (!(path[i] in current)) {
221
+ current[path[i]] = {};
222
+ }
223
+ current = current[path[i]];
224
+ }
225
+
226
+ current[path[path.length - 1]] = value;
227
+ }
228
+
229
+ /**
230
+ * Get default configuration
231
+ * @returns {object} - Default configuration
232
+ */
233
+ getDefaultConfig() {
234
+ return {
235
+ server: {
236
+ name: 'democratize-quality-mcp-server',
237
+ version: '1.0.0',
238
+ protocolVersion: '2024-11-05',
239
+ port: process.env.PORT || 3000
240
+ },
241
+ features: {
242
+ // Tool category feature flags with sensible defaults
243
+ enableApiTools: true,
244
+ enableBrowserTools: true,
245
+ enableAdvancedTools: false, // Conservative default for advanced tools
246
+ enableFileTools: false, // Security consideration
247
+ enableNetworkTools: false, // Security consideration
248
+ enableOtherTools: false, // Conservative default
249
+ enableDebugMode: this.environment !== 'production'
250
+ },
251
+ tools: {
252
+ autoDiscovery: true,
253
+ enableCache: true,
254
+ validationLevel: 'strict',
255
+ browser: {
256
+ maxInstances: 10,
257
+ defaultHeadless: true,
258
+ launchTimeout: 30000
259
+ }
260
+ },
261
+ logging: {
262
+ level: this.environment === 'production' ? 'error' : 'debug',
263
+ enableToolDebug: this.environment !== 'production'
264
+ },
265
+ // Legacy compatibility
266
+ PORT: process.env.PORT || 3000,
267
+ OUTPUT_DIR: path.resolve(__dirname, '../../output')
268
+ };
269
+ }
270
+
271
+ /**
272
+ * Get configuration value by path
273
+ * @param {string} path - Dot-separated path (e.g., 'tools.browser.maxInstances')
274
+ * @param {any} defaultValue - Default value if path doesn't exist
275
+ * @returns {any} - Configuration value
276
+ */
277
+ get(path, defaultValue = undefined) {
278
+ const keys = path.split('.');
279
+ let current = this.config;
280
+
281
+ for (const key of keys) {
282
+ if (current && typeof current === 'object' && key in current) {
283
+ current = current[key];
284
+ } else {
285
+ return defaultValue;
286
+ }
287
+ }
288
+
289
+ return current;
290
+ }
291
+
292
+ /**
293
+ * Check if a feature is enabled
294
+ * @param {string} featureName - Name of the feature
295
+ * @returns {boolean} - True if enabled
296
+ */
297
+ isFeatureEnabled(featureName) {
298
+ return this.get(`features.${featureName}`, false);
299
+ }
300
+
301
+ /**
302
+ * Get tool-specific configuration
303
+ * @param {string} toolName - Name of the tool
304
+ * @param {string} configKey - Configuration key (optional)
305
+ * @returns {any} - Tool configuration
306
+ */
307
+ getToolConfig(toolName, configKey = null) {
308
+ const toolConfig = this.get(`tools.${toolName}`, {});
309
+
310
+ if (configKey) {
311
+ return toolConfig[configKey];
312
+ }
313
+
314
+ return toolConfig;
315
+ }
316
+
317
+ /**
318
+ * Set quiet mode for logging
319
+ * @param {boolean} quiet - Whether to suppress non-essential logs
320
+ */
321
+ setQuiet(quiet) {
322
+ this.quiet = quiet;
323
+ }
324
+
325
+ /**
326
+ * Get all configuration
327
+ * @returns {object} - Complete configuration
328
+ */
329
+ getAll() {
330
+ return { ...this.config };
331
+ }
332
+
333
+ // Legacy compatibility methods
334
+ get PORT() { return this.get('PORT', 3000); }
335
+ get OUTPUT_DIR() { return this.get('OUTPUT_DIR'); }
336
+ }
337
+
338
+ // Create singleton instance
339
+ const configManager = new ConfigManager();
340
+
341
+ module.exports = configManager;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Server-level configuration
3
+ * Core server settings and MCP protocol configuration
4
+ */
5
+ module.exports = {
6
+ server: {
7
+ name: 'democratize-quality-mcp-server',
8
+ version: '1.0.0',
9
+ protocolVersion: '2024-11-05',
10
+ port: process.env.PORT || 3000
11
+ },
12
+
13
+ features: {
14
+ enableBrowserTools: true,
15
+ enableFileTools: false,
16
+ enableNetworkTools: false,
17
+ enableOtherTools: true,
18
+ enableDebugMode: process.env.NODE_ENV !== 'production'
19
+ },
20
+
21
+ tools: {
22
+ autoDiscovery: true,
23
+ enableCache: true,
24
+ validationLevel: 'strict' // 'strict', 'loose', 'none'
25
+ },
26
+
27
+ logging: {
28
+ level: process.env.NODE_ENV === 'production' ? 'error' : 'debug',
29
+ enableToolDebug: process.env.NODE_ENV !== 'production'
30
+ },
31
+
32
+ security: {
33
+ enableInputValidation: true,
34
+ maxRequestSize: '10MB',
35
+ rateLimiting: false // Disabled by default for MCP
36
+ },
37
+
38
+ // Legacy compatibility
39
+ PORT: process.env.PORT || 3000,
40
+ OUTPUT_DIR: require('path').resolve(__dirname, '../../output')
41
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * API Tools Configuration
3
+ * Configuration specific to API testing tools
4
+ */
5
+ module.exports = {
6
+ // API Request Tool
7
+ api_request: {
8
+ // Session management
9
+ maxSessions: 50,
10
+ sessionTimeout: 600000, // 10 minutes
11
+ enableSessionPersistence: true,
12
+
13
+ // Request settings
14
+ defaultTimeout: 30000,
15
+ maxRetries: 3,
16
+ retryDelay: 1000,
17
+ enableRedirects: true,
18
+ maxRedirects: 5,
19
+
20
+ // Validation settings
21
+ enableResponseValidation: true,
22
+ enableBodyValidation: true,
23
+ strictContentTypeCheck: true,
24
+
25
+ // Logging settings
26
+ enableRequestLogging: true,
27
+ enableResponseLogging: true,
28
+ logLevel: 'info',
29
+
30
+ // Rate limiting
31
+ rateLimitEnabled: false,
32
+ maxRequestsPerSecond: 10
33
+ },
34
+
35
+ // API Session Status Tool
36
+ api_session_status: {
37
+ enableRealTimeUpdates: true,
38
+ maxHistoryEntries: 1000,
39
+ includeDetailedLogs: true,
40
+ enableSessionMetrics: true
41
+ },
42
+
43
+ // API Session Report Tool
44
+ api_session_report: {
45
+ defaultTheme: 'light',
46
+ includeRequestData: true,
47
+ includeResponseData: true,
48
+ includeTiming: true,
49
+ includeValidationResults: true,
50
+
51
+ // Report generation settings
52
+ maxReportSize: 10485760, // 10MB
53
+ enableCompression: true,
54
+ compressionLevel: 6,
55
+
56
+ // HTML report settings
57
+ enableInteractiveReports: true,
58
+ includeCharts: true,
59
+ enableSyntaxHighlighting: true,
60
+
61
+ // Output settings
62
+ defaultOutputDir: process.env.API_REPORTS_DIR || 'output/reports',
63
+ enableTimestampInFilename: true,
64
+ enableAutoCleanup: true,
65
+ maxReportsToKeep: 100
66
+ }
67
+ };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Browser tool-specific configuration
3
+ * Settings for all browser automation tools
4
+ */
5
+ module.exports = {
6
+ // Browser launch settings
7
+ browser_launch: {
8
+ defaultHeadless: true,
9
+ defaultPort: null, // Let chrome-launcher choose
10
+ maxInstances: 10,
11
+ launchTimeout: 30000,
12
+ chromeFlags: [
13
+ '--disable-gpu',
14
+ '--disable-setuid-sandbox',
15
+ '--no-sandbox',
16
+ '--disable-dev-shm-usage', // Prevent crashes in containerized environments
17
+ '--disable-background-timer-throttling',
18
+ '--disable-backgrounding-occluded-windows',
19
+ '--disable-renderer-backgrounding'
20
+ ],
21
+ userDataDirPrefix: 'browser-session-',
22
+ cleanupOnExit: true
23
+ },
24
+
25
+ // Navigation settings
26
+ browser_navigate: {
27
+ pageLoadTimeout: 30000,
28
+ allowedProtocols: ['http:', 'https:'],
29
+ maxRedirects: 5,
30
+ waitForNetworkIdle: true,
31
+ networkIdleTimeout: 2000
32
+ },
33
+
34
+ // Screenshot settings
35
+ browser_screenshot: {
36
+ defaultQuality: 80,
37
+ defaultFormat: 'png',
38
+ maxFileSize: '10MB',
39
+ allowedFormats: ['png', 'jpeg', 'webp'],
40
+ outputDirectory: require('path').resolve(__dirname, '../../../output'),
41
+ enableTimestamps: true,
42
+ compressionLevel: 6
43
+ },
44
+
45
+ // DOM interaction settings
46
+ browser_dom: {
47
+ defaultWaitTimeout: 5000,
48
+ elementVisibilityTimeout: 3000,
49
+ scrollIntoView: true,
50
+ highlightElements: false, // Useful for debugging
51
+ enableRetries: true,
52
+ maxRetryAttempts: 3,
53
+ retryDelay: 500
54
+ },
55
+
56
+ // Click settings
57
+ browser_click: {
58
+ waitForElement: true,
59
+ scrollIntoView: true,
60
+ doubleClickDelay: 100,
61
+ enableCoordinateValidation: true,
62
+ clickOffset: { x: 0, y: 0 } // Offset from element center
63
+ },
64
+
65
+ // Type settings
66
+ browser_type: {
67
+ typingDelay: 10, // Milliseconds between keystrokes
68
+ clearBeforeType: true,
69
+ waitForFocus: true,
70
+ enableNaturalTyping: true, // Simulate human-like typing
71
+ maxTextLength: 10000
72
+ },
73
+
74
+ // Close settings
75
+ browser_close: {
76
+ gracefulShutdown: true,
77
+ shutdownTimeout: 5000,
78
+ forceKillOnTimeout: true,
79
+ cleanupUserData: false // Keep user data by default
80
+ },
81
+
82
+ // Global browser settings
83
+ global: {
84
+ maxConcurrentOperations: 5,
85
+ enableScreenshotOnError: false,
86
+ autoRecovery: true,
87
+ healthCheckInterval: 60000, // 1 minute
88
+ enablePerformanceMetrics: false
89
+ }
90
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Default tool configuration
3
+ * These settings apply to all tools unless overridden by specific tool configs
4
+ */
5
+ module.exports = {
6
+ // Common tool settings
7
+ timeout: 30000,
8
+ retryAttempts: 3,
9
+ retryDelay: 1000,
10
+
11
+ // Validation settings
12
+ enableInputValidation: true,
13
+ enableOutputValidation: false,
14
+ strictMode: true,
15
+
16
+ // Performance settings
17
+ enableCaching: false,
18
+ maxCacheSize: 100,
19
+ cacheTimeout: 300000, // 5 minutes
20
+
21
+ // Error handling
22
+ enableDetailedErrors: process.env.NODE_ENV !== 'production',
23
+ logErrors: true,
24
+ throwOnValidationError: true,
25
+
26
+ // Rate limiting (per tool)
27
+ rateLimit: {
28
+ enabled: false,
29
+ maxRequests: 100,
30
+ windowMs: 60000 // 1 minute
31
+ }
32
+ };