Skip to main content
Glama

Electron Terminal MCP Server

terminal_start

Execute system commands programmatically by sending instructions to a terminal session within an Electron application using the Model Context Protocol (MCP).

Input Schema

NameRequiredDescriptionDefault
commandYes

Input Schema (JSON Schema)

{ "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "properties": { "command": { "type": "string" } }, "required": [ "command" ], "type": "object" }

Implementation Reference

  • The core handler function for the 'terminal_start' MCP tool. It checks if the server is running and starts the Electron process if not, then sends the input 'command' to the local API endpoint /execute, processes the output by stripping ANSI codes, and returns the result with session ID 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:261-289 (registration)
    Registers the 'terminal_start' tool with the MCP server using server.tool(), specifying the tool name, input schema {command: z.string()}, and the inline handler function.
    server.tool( "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); } } );
  • Zod input schema for the terminal_start tool: requires a 'command' string.
    { command: z.string() },
  • Helper function startElectronProcess() that spawns an Electron process if not already running, using mutex locking to prevent multiple instances.
    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; } }
  • Helper function isServerRunning() that checks if the local API server is responsive via /health endpoint.
    async function isServerRunning() { try { await axios.get(`${apiBaseUrl}/health`); return true; } catch (error) { return false; } }

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nexon33/console-terminal-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server