@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,105 @@
1
+ # Basic Browser Automation
2
+
3
+ This example demonstrates basic browser automation tasks using the Democratize Quality MCP Server.
4
+
5
+ ## Complete Workflow
6
+
7
+ ```json
8
+ // 1. Launch browser
9
+ {
10
+ "jsonrpc": "2.0",
11
+ "id": 1,
12
+ "method": "tools/call",
13
+ "params": {
14
+ "name": "browser_launch",
15
+ "arguments": {
16
+ "headless": true,
17
+ "userDataDir": "./browser-session"
18
+ }
19
+ }
20
+ }
21
+
22
+ // Response: { "browserId": "browser-abc123", "port": 9222 }
23
+
24
+ // 2. Navigate to page
25
+ {
26
+ "jsonrpc": "2.0",
27
+ "id": 2,
28
+ "method": "tools/call",
29
+ "params": {
30
+ "name": "browser_navigate",
31
+ "arguments": {
32
+ "browserId": "browser-abc123",
33
+ "url": "https://example.com"
34
+ }
35
+ }
36
+ }
37
+
38
+ // 3. Take screenshot
39
+ {
40
+ "jsonrpc": "2.0",
41
+ "id": 3,
42
+ "method": "tools/call",
43
+ "params": {
44
+ "name": "browser_screenshot",
45
+ "arguments": {
46
+ "browserId": "browser-abc123",
47
+ "fileName": "homepage.png"
48
+ }
49
+ }
50
+ }
51
+
52
+ // 4. Extract page content
53
+ {
54
+ "jsonrpc": "2.0",
55
+ "id": 4,
56
+ "method": "tools/call",
57
+ "params": {
58
+ "name": "browser_dom",
59
+ "arguments": {
60
+ "browserId": "browser-abc123"
61
+ }
62
+ }
63
+ }
64
+
65
+ // 5. Close browser
66
+ {
67
+ "jsonrpc": "2.0",
68
+ "id": 5,
69
+ "method": "tools/call",
70
+ "params": {
71
+ "name": "browser_close",
72
+ "arguments": {
73
+ "browserId": "browser-abc123"
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ ## Error Handling
80
+
81
+ Always handle errors appropriately:
82
+
83
+ ```json
84
+ // If a tool call fails, you'll receive an error response:
85
+ {
86
+ "jsonrpc": "2.0",
87
+ "id": 1,
88
+ "error": {
89
+ "code": -32000,
90
+ "message": "Tool 'browser_navigate' execution failed: Browser instance 'invalid-id' not found",
91
+ "data": {
92
+ "tool_name": "browser_navigate",
93
+ "original_error": "Browser instance 'invalid-id' not found"
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ ## Best Practices
100
+
101
+ 1. **Always close browsers** when done to free resources
102
+ 2. **Use persistent profiles** for authenticated sessions
103
+ 3. **Handle errors gracefully** with retry logic
104
+ 4. **Take screenshots** for debugging and verification
105
+ 5. **Use meaningful file names** for screenshots
@@ -0,0 +1,214 @@
1
+ # Getting Started
2
+
3
+ Quick start guide for the Democratize Quality MCP Server.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ### Prerequisites
10
+
11
+ - Node.js 18+
12
+ - Chrome/Chromium browser
13
+ - Git
14
+
15
+ ### Clone and Setup
16
+
17
+ ```bash
18
+ git clone https://github.com/democratize-quality/mcp-server.git
19
+ cd mcp-server
20
+ npm install
21
+ ```
22
+
23
+ ## Basic Usage
24
+
25
+ ### 1. Start the Server
26
+
27
+ ```bash
28
+ node mcpServer.js
29
+ ```
30
+
31
+ The server will start and display available tools:
32
+
33
+ ```
34
+ [Tools] Tool system initialized successfully:
35
+ [Tools] - Total tools: 7
36
+ [Tools] - Available tools: browser_launch, browser_navigate, browser_screenshot, ...
37
+ ```
38
+
39
+ ### 2. Connect an MCP Client
40
+
41
+ The server communicates via JSON-RPC over stdin/stdout. Example client integration:
42
+
43
+ ```json
44
+ {
45
+ "jsonrpc": "2.0",
46
+ "id": 1,
47
+ "method": "tools/list"
48
+ }
49
+ ```
50
+
51
+ ### 3. Basic Browser Automation
52
+
53
+ Here's a complete example of launching a browser, navigating to a page, and taking a screenshot:
54
+
55
+ ```json
56
+ // 1. Launch browser
57
+ {
58
+ "jsonrpc": "2.0",
59
+ "id": 1,
60
+ "method": "tools/call",
61
+ "params": {
62
+ "name": "browser_launch",
63
+ "arguments": { "headless": true }
64
+ }
65
+ }
66
+
67
+ // 2. Navigate to page
68
+ {
69
+ "jsonrpc": "2.0",
70
+ "id": 2,
71
+ "method": "tools/call",
72
+ "params": {
73
+ "name": "browser_navigate",
74
+ "arguments": {
75
+ "browserId": "browser-123",
76
+ "url": "https://example.com"
77
+ }
78
+ }
79
+ }
80
+
81
+ // 3. Take screenshot
82
+ {
83
+ "jsonrpc": "2.0",
84
+ "id": 3,
85
+ "method": "tools/call",
86
+ "params": {
87
+ "name": "browser_screenshot",
88
+ "arguments": {
89
+ "browserId": "browser-123",
90
+ "fileName": "example.png"
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ ## Configuration
97
+
98
+ ### Environment Modes
99
+
100
+ **Development Mode** (default):
101
+ - Shows browser UI
102
+ - Detailed logging
103
+ - Debug features enabled
104
+
105
+ **Production Mode**:
106
+ ```bash
107
+ NODE_ENV=production node mcpServer.js
108
+ ```
109
+ - Headless browsers only
110
+ - Minimal logging
111
+ - Optimized performance
112
+
113
+ ### Feature Flags
114
+
115
+ Disable specific tool categories:
116
+
117
+ ```bash
118
+ # Disable browser tools
119
+ MCP_FEATURES_ENABLEBROWSERTOOLS=false node mcpServer.js
120
+
121
+ # Run with only specific tools
122
+ MCP_FEATURES_ENABLEFILETOOLS=false \
123
+ MCP_FEATURES_ENABLENETWORKTOOLS=false \
124
+ node mcpServer.js
125
+ ```
126
+
127
+ ### Custom Configuration
128
+
129
+ Create environment-specific configs in `src/config/environments/`:
130
+
131
+ ```javascript
132
+ // src/config/environments/testing.js
133
+ module.exports = {
134
+ tools: {
135
+ browser: {
136
+ browser_launch: {
137
+ defaultHeadless: true,
138
+ maxInstances: 1
139
+ }
140
+ }
141
+ }
142
+ };
143
+ ```
144
+
145
+ Run with custom environment:
146
+ ```bash
147
+ NODE_ENV=testing node mcpServer.js
148
+ ```
149
+
150
+ ## Common Use Cases
151
+
152
+ ### Web Scraping
153
+ 1. Launch browser with persistent profile
154
+ 2. Navigate to login page
155
+ 3. Fill credentials
156
+ 4. Navigate to target pages
157
+ 5. Extract data
158
+ 6. Close browser
159
+
160
+ ### Automated Testing
161
+ 1. Launch browser in headless mode
162
+ 2. Navigate to application
163
+ 3. Perform user interactions
164
+ 4. Take screenshots for verification
165
+ 5. Assert expected outcomes
166
+
167
+ ### Content Generation
168
+ 1. Navigate to webpage
169
+ 2. Take full-page screenshots
170
+ 3. Extract text content
171
+ 4. Generate reports
172
+
173
+ ## Troubleshooting
174
+
175
+ ### Common Issues
176
+
177
+ **Browser fails to launch:**
178
+ - Ensure Chrome/Chromium is installed
179
+ - Check user permissions
180
+ - Try running with `--no-sandbox` flag
181
+
182
+ **Tools not loading:**
183
+ - Check feature flags are enabled
184
+ - Verify file permissions
185
+ - Review server logs for errors
186
+
187
+ **Connection issues:**
188
+ - Ensure proper JSON-RPC formatting
189
+ - Check stdin/stdout communication
190
+ - Verify MCP client implementation
191
+
192
+ ### Debug Mode
193
+
194
+ Enable detailed logging:
195
+ ```bash
196
+ MCP_FEATURES_ENABLEDEBUGMODE=true node mcpServer.js
197
+ ```
198
+
199
+ ### Logs Location
200
+
201
+ - Server logs: stderr output
202
+ - Screenshots: `./output/` directory
203
+ - Browser data: Temporary profiles in system temp
204
+
205
+ ## Next Steps
206
+
207
+ - [Tool Reference](api/tool-reference.md) - Complete tool documentation
208
+ - [Developer Guide](development/adding-tools.md) - Extend the server
209
+ - [Configuration Guide](development/configuration.md) - Advanced configuration
210
+ - [Examples](examples/) - Real-world usage examples
211
+
212
+ ---
213
+
214
+ *For more help, check the documentation or open an issue on GitHub.*
package/docs/index.md ADDED
@@ -0,0 +1,61 @@
1
+ # Documentation Index
2
+
3
+ Welcome to the Democratize Quality MCP Server documentation!
4
+
5
+ ## 📚 Table of Contents
6
+
7
+ ### Getting Started
8
+ - [🚀 Getting Started](getting-started.md) - Quick start guide and basic usage
9
+ - [📦 Installation](getting-started.md#installation) - Setup and prerequisites
10
+
11
+ ### API Reference
12
+ - [🔧 Tool Reference](api/tool-reference.md) - Complete documentation for all tools
13
+ - [⚙️ Feature Flags](api/tool-reference.md#feature-flags) - Tool control and configuration
14
+
15
+ ### Development
16
+ - [👨‍💻 Adding Tools](development/adding-tools.md) - Developer guide for extending the server
17
+ - [🏗️ Architecture](development/adding-tools.md#architecture-overview) - System architecture overview
18
+ - [⚙️ Configuration](development/configuration.md) - Advanced configuration options
19
+ - [🧪 Testing](development/adding-tools.md#testing) - Testing guidelines and examples
20
+
21
+ ### Examples
22
+ - [🤖 Basic Automation](examples/basic-automation.md) - Essential browser automation workflows
23
+ - [🔐 Authentication](examples/authentication.md) - Login flows and session management
24
+
25
+ ### Additional Resources
26
+ - [📄 Main README](../README.md) - Project overview and quick reference
27
+ - [📋 Package Info](../package.json) - Project dependencies and scripts
28
+
29
+ ## 🎯 Quick Navigation
30
+
31
+ ### By Use Case
32
+ - **First-time setup**: [Getting Started](getting-started.md)
33
+ - **Tool usage**: [Tool Reference](api/tool-reference.md)
34
+ - **Extending functionality**: [Adding Tools](development/adding-tools.md)
35
+ - **Configuration tuning**: [Configuration Guide](development/configuration.md)
36
+ - **Real-world examples**: [Examples](examples/)
37
+
38
+ ### By Role
39
+ - **🤖 AI Agent Developers**: Start with [Tool Reference](api/tool-reference.md)
40
+ - **🛠️ Server Developers**: Read [Adding Tools](development/adding-tools.md)
41
+ - **⚙️ System Administrators**: Check [Configuration Guide](development/configuration.md)
42
+ - **📊 Integration Teams**: Review [Examples](examples/)
43
+
44
+ ## 🔄 Keeping Documentation Updated
45
+
46
+ Documentation is automatically generated from the codebase:
47
+
48
+ ```bash
49
+ # Regenerate all documentation
50
+ npm run docs:generate
51
+
52
+ # Watch for changes and regenerate
53
+ npm run docs:watch
54
+
55
+ # Clean and regenerate
56
+ npm run docs:clean && npm run docs:generate
57
+ ```
58
+
59
+ ---
60
+
61
+ *Documentation generated automatically from tool definitions and configuration - Last updated: ${new Date().toISOString().split('T')[0]}*
package/mcpServer.js ADDED
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env node
2
+ // mcpServer.js - Democratize Quality MCP Server entry point
3
+ //
4
+ // Debug/Logging Modes:
5
+ // - Production mode (default): NODE_ENV=production or npm start - Minimal logging
6
+ // - Debug mode: MCP_FEATURES_ENABLEDEBUGMODE=true or npm run dev - Detailed logging
7
+ // - Development mode: NODE_ENV=development - Detailed logging (same as debug)
8
+ //
9
+ // The server automatically detects the mode and adjusts logging verbosity accordingly.
10
+
11
+ const { JSONRPCServer } = require("json-rpc-2.0");
12
+ const browserService = require('./src/services/browserService'); // Keep for shutdown functionality
13
+ const { initializeTools, getToolDefinitions, executeTool } = require('./src/tools');
14
+ const config = require('./src/config');
15
+
16
+ // Initialize JSON-RPC server
17
+ const server = new JSONRPCServer();
18
+
19
+ // Check if debug mode is requested via environment variable first
20
+ const debugFromEnv = process.env.MCP_FEATURES_ENABLEDEBUGMODE === 'true' || process.env.NODE_ENV === 'development';
21
+ const isDebugMode = config.get('features.enableDebugMode', false) || debugFromEnv;
22
+
23
+ // Set quiet mode if not in debug
24
+ config.setQuiet(!isDebugMode);
25
+
26
+ // Global variable to hold tool definitions after initialization
27
+ let toolDefinitions = [];
28
+
29
+ // Helper function for debug logging
30
+ function debugLog(...args) {
31
+ if (isDebugMode) {
32
+ console.error('[Democratize Quality MCP]', ...args);
33
+ }
34
+ }
35
+
36
+ // Helper function for important logs (always shown)
37
+ function log(...args) {
38
+ console.error('[Democratize Quality MCP]', ...args);
39
+ }
40
+
41
+ // Initialize the tool system
42
+ async function initializeServer() {
43
+ try {
44
+ if (isDebugMode) {
45
+ log('Initializing tool system...');
46
+ }
47
+ await initializeTools(isDebugMode);
48
+ toolDefinitions = getToolDefinitions();
49
+ log(`Tool system initialized with ${toolDefinitions.length} tools`);
50
+ } catch (error) {
51
+ log('Failed to initialize tool system:', error.message);
52
+ process.exit(1);
53
+ }
54
+ }
55
+
56
+ // --- Register MCP Standard Methods ---
57
+
58
+ // Initialize method for MCP protocol
59
+ server.addMethod("initialize", async (params) => {
60
+ debugLog("Received 'initialize' request.");
61
+ return {
62
+ protocolVersion: "2024-11-05",
63
+ capabilities: {
64
+ tools: {},
65
+ prompts: {},
66
+ resources: {}
67
+ },
68
+ serverInfo: {
69
+ name: "browser-control-server",
70
+ version: "1.0.0"
71
+ }
72
+ };
73
+ });
74
+
75
+ // The `tools/list` method for tool discovery
76
+ // Replace your tools/list method with this Draft 7 compatible version:
77
+
78
+ server.addMethod("tools/list", async () => {
79
+ debugLog("Received 'tools/list' request.");
80
+
81
+ // Convert all tool definitions to Claude Desktop compatible format
82
+ const compatibleTools = toolDefinitions.map(tool => {
83
+ // Ensure we use the correct property name and clean schema
84
+ return {
85
+ name: tool.name,
86
+ description: tool.description,
87
+ // Claude Desktop expects 'inputSchema', not 'input_schema'
88
+ inputSchema: {
89
+ type: "object",
90
+ properties: {
91
+ // Add minimal properties to avoid empty schema issues
92
+ ...((tool.input_schema && tool.input_schema.properties) || {}),
93
+ // Fallback to ensure there's always at least one property
94
+ ...(Object.keys((tool.input_schema && tool.input_schema.properties) || {}).length === 0 ? {
95
+ _placeholder: { type: "string", description: "Placeholder parameter" }
96
+ } : {})
97
+ },
98
+ // Only add required if it exists and is not empty
99
+ ...(tool.input_schema && tool.input_schema.required && tool.input_schema.required.length > 0 ? {
100
+ required: tool.input_schema.required
101
+ } : {}),
102
+ // Ensure no additionalProperties or other Draft 2020-12 features
103
+ additionalProperties: false
104
+ }
105
+ };
106
+ });
107
+
108
+ debugLog(`Returning ${compatibleTools.length} tools with compatible schemas`);
109
+ debugLog('First tool schema sample:', JSON.stringify(compatibleTools[0]?.inputSchema, null, 2));
110
+
111
+ return { tools: compatibleTools };
112
+ });
113
+
114
+ // The `tools/call` method for tool invocation (note: it's tools/call, not tool/call)
115
+ server.addMethod("tools/call", async ({ name, arguments: parameters }) => {
116
+ debugLog(`Received 'tools/call' for method: ${name} with params: ${JSON.stringify(parameters)}`);
117
+
118
+ try {
119
+ // Use the new tool system to execute the tool
120
+ const result = await executeTool(name, parameters);
121
+ return result;
122
+
123
+ } catch (error) {
124
+ log(`Error executing tool '${name}':`, error.message);
125
+
126
+ // If it's already a properly formatted MCP error, re-throw it
127
+ if (error.code && error.message) {
128
+ throw error;
129
+ }
130
+
131
+ // Otherwise, format it as an MCP error
132
+ throw {
133
+ code: -32000,
134
+ message: `Tool execution failed: ${error.message}`,
135
+ data: { tool_name: name, original_error: error.message }
136
+ };
137
+ }
138
+ });
139
+
140
+ // The `prompts/list` method for prompt discovery
141
+ server.addMethod("prompts/list", async () => {
142
+ debugLog("Received 'prompts/list' request.");
143
+ // This server doesn't provide any prompts, so return an empty array
144
+ return { prompts: [] };
145
+ });
146
+
147
+ // The `resources/list` method for resource discovery
148
+ server.addMethod("resources/list", async () => {
149
+ debugLog("Received 'resources/list' request.");
150
+ // This server doesn't provide any resources, so return an empty array
151
+ return { resources: [] };
152
+ });
153
+
154
+ // Notification methods (these don't return responses)
155
+ server.addMethod("notifications/initialized", async () => {
156
+ debugLog("Received 'notifications/initialized' notification.");
157
+ // Notifications don't return responses
158
+ });
159
+
160
+ server.addMethod("notifications/cancelled", async ({ requestId, reason }) => {
161
+ debugLog(`Received 'notifications/cancelled' for request ${requestId}: ${reason}`);
162
+ // Notifications don't return responses
163
+ });
164
+
165
+ // Add a catch-all method handler for debugging
166
+ server.addMethod("*", async (params, method) => {
167
+ debugLog(`Unknown method called: ${method}`);
168
+ throw {
169
+ code: -32601,
170
+ message: `Method '${method}' not found`
171
+ };
172
+ });
173
+
174
+ // --- STDIO Communication Loop ---
175
+ // This part handles reading JSON-RPC requests from stdin and writing responses to stdout.
176
+
177
+ let inputBuffer = '';
178
+
179
+ process.stdin.on('data', (chunk) => {
180
+ inputBuffer += chunk.toString();
181
+
182
+ // Process messages delimited by newline. This is a common pattern for STDIO RPC.
183
+ // In a production-grade server, you'd want a more robust JSON stream parser
184
+ // to handle partial messages or multiple messages in one chunk.
185
+ let newlineIndex;
186
+ while ((newlineIndex = inputBuffer.indexOf('\n')) !== -1) {
187
+ const messageString = inputBuffer.substring(0, newlineIndex).trim();
188
+ inputBuffer = inputBuffer.substring(newlineIndex + 1);
189
+
190
+ if (messageString) { // Ensure it's not an empty line
191
+ try {
192
+ const jsonRpcRequest = JSON.parse(messageString);
193
+ debugLog(`Received request: ${JSON.stringify(jsonRpcRequest)}`);
194
+
195
+ // Handle notifications (no response expected)
196
+ if (!jsonRpcRequest.id && jsonRpcRequest.method && jsonRpcRequest.method.startsWith('notifications/')) {
197
+ server.receive(jsonRpcRequest).catch(err => {
198
+ log("Error processing notification:", err.message);
199
+ });
200
+ return; // Don't send a response for notifications
201
+ }
202
+
203
+ // The receive method handles processing the request and generating a response
204
+ server.receive(jsonRpcRequest).then((jsonRpcResponse) => {
205
+ if (jsonRpcResponse) {
206
+ debugLog(`Sending response: ${JSON.stringify(jsonRpcResponse)}`);
207
+ // All responses must go to stdout
208
+ process.stdout.write(JSON.stringify(jsonRpcResponse) + '\n');
209
+ }
210
+ }).catch(err => {
211
+ // Catch errors from receive() if it rejects (e.g., malformed JSON-RPC request)
212
+ log("Error processing JSON-RPC request:", err.message);
213
+
214
+ // Send an error response if we have an ID
215
+ if (jsonRpcRequest.id) {
216
+ const errorResponse = {
217
+ jsonrpc: "2.0",
218
+ error: {
219
+ code: -32603,
220
+ message: "Internal error",
221
+ data: err.message
222
+ },
223
+ id: jsonRpcRequest.id
224
+ };
225
+ process.stdout.write(JSON.stringify(errorResponse) + '\n');
226
+ }
227
+ });
228
+ } catch (parseError) {
229
+ log("JSON parse error on input:", messageString, parseError);
230
+ // If the input itself is not valid JSON, we can't get an ID, so ID is null
231
+ const errorResponse = {
232
+ jsonrpc: "2.0",
233
+ error: {
234
+ code: -32700, // Parse error
235
+ message: "Parse error"
236
+ },
237
+ id: null
238
+ };
239
+ process.stdout.write(JSON.stringify(errorResponse) + '\n');
240
+ }
241
+ }
242
+ }
243
+ });
244
+
245
+ // Handle graceful shutdown signals (Ctrl+C, termination)
246
+ process.on('SIGINT', async () => {
247
+ log('\nSIGINT received. Shutting down...');
248
+ try {
249
+ await browserService.shutdownAllBrowsers();
250
+ log('All browser instances closed. Exiting gracefully.');
251
+ } catch (err) {
252
+ log('Error during graceful shutdown:', err.message);
253
+ process.exit(1); // Exit with error code
254
+ }
255
+ process.exit(0); // Exit successfully
256
+ });
257
+
258
+ process.on('SIGTERM', async () => {
259
+ log('\nSIGTERM received. Shutting down...');
260
+ try {
261
+ await browserService.shutdownAllBrowsers();
262
+ log('All browser instances closed. Exiting gracefully.');
263
+ } catch (err) {
264
+ log('Error during graceful shutdown:', err.message);
265
+ process.exit(1);
266
+ }
267
+ process.exit(0);
268
+ });
269
+
270
+ // Initialize the server and start listening
271
+ (async () => {
272
+ await initializeServer();
273
+
274
+ // Initial message to indicate server is ready (to stderr)
275
+ log(`Server started. Waiting for input on stdin.`);
276
+ if (isDebugMode) {
277
+ log(`To integrate with a host, provide the command: node ${__filename}`);
278
+ log(`Debug mode enabled - showing detailed logs`);
279
+ }
280
+ })();