Skip to main content
Glama
nexon33

Electron Terminal MCP Server

terminal_start

Execute terminal commands within an Electron application to manage sessions and retrieve output programmatically.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commandYes

Implementation Reference

  • 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);
      }
    }
  • 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);
        }
      }
    );
  • 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;
      }
    }
  • 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;
      }
    }

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