Skip to main content
Glama
bash.ts4.45 kB
import type {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; import {spawn, type ChildProcess} from 'node:child_process'; import {z} from 'zod'; import {jsonResult} from '../utils/response.js'; const DEFAULT_TIMEOUT = 5000; // 5 seconds // Background job tracking type BackgroundJob = { process: ChildProcess; stdout: string; stderr: string; exitCode: number | null; startedAt: Date; }; const backgroundJobs = new Map<string, BackgroundJob>(); function generateJobId(): string { return Math.random().toString(36).substring(2, 10); } const executeDescription = `Run a command in bash. Returns stdout, stderr, and exit code. Default timeout is 5 seconds. For long-running commands, set 'background: true' to run in background and get a job ID. Then use get_status to check on it. Tips: - Use background mode for commands that take more than a few seconds - For file searches, use 'find' or 'grep' - For file operations, use 'mv', 'rm', 'mkdir -p', 'stat' etc.`; const getStatusDescription = `Check status of a background job. Returns stdout/stderr collected so far, exit code (null if still running), and whether the job is still running. Completed jobs are cleaned up after their status is read.`; export function registerBash(server: McpServer): void { // Tool 1: execute server.registerTool( 'execute', { title: 'Execute', description: executeDescription, inputSchema: { command: z.string().describe('The bash command to run'), timeout: z.number().optional().describe(`Timeout in milliseconds (default: ${DEFAULT_TIMEOUT})`), background: z.boolean().optional().describe('Run in background and return job ID'), }, }, async (args) => { const {command} = args; const timeout = args.timeout ?? DEFAULT_TIMEOUT; // Background mode if (args.background) { const jobId = generateJobId(); const child = spawn('bash', ['-c', command], { stdio: ['ignore', 'pipe', 'pipe'], }); const job: BackgroundJob = { process: child, stdout: '', stderr: '', exitCode: null, startedAt: new Date(), }; child.stdout.on('data', (data: Buffer) => { job.stdout += data.toString(); }); child.stderr.on('data', (data: Buffer) => { job.stderr += data.toString(); }); child.on('close', (code) => { job.exitCode = code ?? 1; }); child.on('error', (err) => { job.stderr += `\nProcess error: ${err.message}`; job.exitCode = 1; }); backgroundJobs.set(jobId, job); return jsonResult({ jobId, pid: child.pid ?? 0, }); } // Foreground mode with timeout return new Promise((resolve) => { const child = spawn('bash', ['-c', command], { stdio: ['ignore', 'pipe', 'pipe'], }); let stdout = ''; let stderr = ''; let timedOut = false; const timeoutId = setTimeout(() => { timedOut = true; child.kill('SIGTERM'); // Give it a moment to die gracefully, then force kill setTimeout(() => child.kill('SIGKILL'), 1000); }, timeout); child.stdout.on('data', (data: Buffer) => { stdout += data.toString(); }); child.stderr.on('data', (data: Buffer) => { stderr += data.toString(); }); child.on('close', (code) => { clearTimeout(timeoutId); if (timedOut) { stderr += `\nProcess timed out after ${timeout}ms`; } resolve(jsonResult({ stdout, stderr, exitCode: timedOut ? 124 : (code ?? 1), // 124 is conventional timeout exit code })); }); child.on('error', (err) => { clearTimeout(timeoutId); resolve(jsonResult({ stdout, stderr: `${stderr}\nProcess error: ${err.message}`, exitCode: 1, })); }); }); }, ); // Tool 2: get_job_status server.registerTool( 'get_job_status', { title: 'Get Job Status', description: getStatusDescription, inputSchema: { jobId: z.string().describe('The job ID returned from execute with background: true'), }, }, async (args) => { const job = backgroundJobs.get(args.jobId); if (!job) { throw new Error(`Job not found: ${args.jobId}`); } const running = job.exitCode === null; // Clean up completed jobs after reading if (!running) { backgroundJobs.delete(args.jobId); } return jsonResult({ stdout: job.stdout, stderr: job.stderr, exitCode: job.exitCode, running, }); }, ); }

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/domdomegg/shell-exec-mcp'

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