terminal_start
Execute terminal commands within an Electron application to manage sessions and retrieve output programmatically.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| command | Yes |
Implementation Reference
- index.js:266-288 (handler)The core handler function for the 'terminal_start' tool. It ensures the Electron-based terminal server is running, executes the provided command via an API call, strips ANSI codes from the output, and returns the session ID, cleaned output, and exit code.async ({ command }) => { try { // Check if server is running, start if not if (!(await isServerRunning())) { await startElectronProcess(); } // Create a new session const response = await axios.post(`${apiBaseUrl}/execute`, { command }); const result = response.data; // Clean up terminal output using strip-ansi const cleanOutput = stripAnsi(result.output); return { content: [{ type: "text", text: `Session ID: ${result.sessionId}\n\n ${cleanOutput}`, exitCode: result.exitCode }], //sessionId: result.sessionId }; } catch (error) { return formatErrorResponse(error); } }
- index.js:263-265 (schema)Input schema for the 'terminal_start' tool, validating a single 'command' parameter as a string using Zod.{ command: z.string() },
- index.js:262-289 (registration)Registration of the 'terminal_start' tool on the MCP server using server.tool(), including the tool name, input schema, and inline handler function."terminal_start", { command: z.string() }, async ({ command }) => { try { // Check if server is running, start if not if (!(await isServerRunning())) { await startElectronProcess(); } // Create a new session const response = await axios.post(`${apiBaseUrl}/execute`, { command }); const result = response.data; // Clean up terminal output using strip-ansi const cleanOutput = stripAnsi(result.output); return { content: [{ type: "text", text: `Session ID: ${result.sessionId}\n\n ${cleanOutput}`, exitCode: result.exitCode }], //sessionId: result.sessionId }; } catch (error) { return formatErrorResponse(error); } } );
- index.js:63-70 (helper)Helper function used by the terminal_start handler to check if the underlying Electron server is running by pinging the /health endpoint.async function isServerRunning() { try { await axios.get(`${apiBaseUrl}/health`); return true; } catch (error) { return false; } }
- index.js:127-224 (helper)Helper function called by the terminal_start handler to start the Electron process (terminal server) if not running, using mutex for single instance, spawning electron with specific env, logging, and waiting for readiness.async function startElectronProcess() { try { // Try to acquire mutex if (!(await acquireMutex())) { logger.error('Electron process is already running or failed to acquire mutex.'); return; } // Get the path to electron from node_modules const electronExecutable = process.platform === 'win32' ? 'electron.cmd' : 'electron'; const electronPath = path.join(__dirname, 'node_modules', '.bin', electronExecutable); // Set up environment variables const env = { ...process.env, ELECTRON_START_URL: 'http://localhost:3000', ELECTRON_ENABLE_LOGGING: 'true', ELECTRON_ENABLE_STACK_DUMPING: 'true', NODE_ENV: 'development' }; logger.error('Starting Electron process'); // Use npx to run electron, hiding the window with windowsHide and shell: true // Corrected spawn call: path.resolve(__dirname) is now an argument to electronPath //const electronProcess = spawn("cmd.exe", ['/c','/s', electronPath, path.resolve(__dirname)], { const electronProcess = spawn(electronPath, [path.resolve(__dirname)], { detached: true, stdio: ['ignore', 'pipe', 'pipe'], // Keep stdio pipes for logging cwd: process.cwd(), env: env, windowsHide: true, // Ensure the window is hidden shell: true // Use shell to execute npx correctly }); // Log any output from the electron process electronProcess.stdout.on('data', (data) => { logger.error('Electron stdout:', data.toString()); }); electronProcess.stderr.on('data', (data) => { logger.error('Electron stderr:', data.toString()); }); electronProcess.on('error', async (error) => { logger.error('Failed to start Electron:', error); await releaseMutex(); }); electronProcess.on('exit', async (code, signal) => { logger.error(`Electron process exited with code ${code} and signal ${signal}`); await releaseMutex(); }); // Don't unref the process immediately to ensure it starts properly setTimeout(() => { electronProcess.unref(); }, 1000); // Wait for server to be ready return new Promise((resolve, reject) => { let attempts = 0; const maxAttempts = 60; const checkServer = async () => { try { if (await isServerRunning()) { logger.error('Server is now running'); resolve(); } else { attempts++; logger.error(`Waiting for server to start... (attempt ${attempts}/${maxAttempts})`); if (attempts >= maxAttempts) { await releaseMutex(); reject(new Error('Server failed to start within timeout period')); return; } setTimeout(checkServer, 1000); } } catch (error) { logger.error('Error checking server status:', error); attempts++; if (attempts >= maxAttempts) { await releaseMutex(); reject(new Error('Server failed to start within timeout period')); return; } setTimeout(checkServer, 1000); } }; checkServer(); }); } catch (error) { logger.error('Failed to start Electron process:', error); await releaseMutex(); throw error; } }