Created
August 14, 2026 14:26
-
-
Save csantanapr/c37920871ea6548b27b1e8a4f5ee31fb to your computer and use it in GitHub Desktop.
exec mcp server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env node | |
| import { Server } from "@modelcontextprotocol/sdk/server/index.js"; | |
| import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; | |
| import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; | |
| import { exec } from "child_process"; | |
| import { promisify } from "util"; | |
| import { writeFileSync, unlinkSync, mkdirSync, existsSync } from "fs"; | |
| import { join } from "path"; | |
| const execAsync = promisify(exec); | |
| const MAX_OUTPUT_LENGTH = 50000; | |
| const DEFAULT_TIMEOUT = 30000; | |
| const SCRIPTS_DIR = join(process.env.HOME || "/tmp", ".quick-shell-mcp"); | |
| if (!existsSync(SCRIPTS_DIR)) mkdirSync(SCRIPTS_DIR, { recursive: true }); | |
| const server = new Server( | |
| { name: "shell-exec-mcp", version: "1.0.0" }, | |
| { capabilities: { tools: {} } } | |
| ); | |
| // Register tools (shell_exec, shell_script, terminal_launch, process_list, env_info) | |
| server.setRequestHandler(ListToolsRequestSchema, async () => ({ | |
| tools: [ | |
| { name: "shell_exec", description: "Execute a shell command", | |
| inputSchema: { type: "object", properties: { | |
| command: { type: "string", description: "Shell command to execute" }, | |
| cwd: { type: "string", description: "Working directory (default: $HOME)" }, | |
| timeout: { type: "number", description: "Timeout in ms (default: 30000)" } | |
| }, required: ["command"] } }, | |
| { name: "shell_script", description: "Execute a multi-line bash script", | |
| inputSchema: { type: "object", properties: { | |
| script: { type: "string", description: "Multi-line bash script" }, | |
| cwd: { type: "string" }, timeout: { type: "number" } | |
| }, required: ["script"] } }, | |
| { name: "terminal_launch", description: "Open a Terminal window with a command", | |
| inputSchema: { type: "object", properties: { | |
| command: { type: "string", description: "Command to run" }, | |
| title: { type: "string" }, | |
| app: { type: "string", enum: ["terminal", "iterm"] } | |
| }, required: ["command"] } }, | |
| { name: "process_list", description: "List running processes", | |
| inputSchema: { type: "object", properties: { | |
| filter: { type: "string" }, limit: { type: "number" } | |
| } } }, | |
| { name: "env_info", description: "Get system environment info", | |
| inputSchema: { type: "object", properties: { | |
| include: { type: "array", items: { type: "string" } } | |
| } } } | |
| ] | |
| })); | |
| // Handle tool calls | |
| server.setRequestHandler(CallToolRequestSchema, async (request) => { | |
| const { name, arguments: args } = request.params; | |
| try { | |
| switch (name) { | |
| case "shell_exec": { | |
| const { command, cwd, timeout = DEFAULT_TIMEOUT } = args; | |
| const result = await execAsync(command, { | |
| cwd: cwd || process.env.HOME, timeout, | |
| maxBuffer: 10*1024*1024, shell: "/bin/zsh" | |
| }); | |
| const output = [result.stdout ? `STDOUT:\n${result.stdout}` : "", | |
| result.stderr ? `STDERR:\n${result.stderr}` : ""].filter(Boolean).join("\n"); | |
| return { content: [{ type: "text", text: output.slice(0, MAX_OUTPUT_LENGTH) }] }; | |
| } | |
| case "shell_script": { | |
| const { script, cwd, timeout = 60000 } = args; | |
| const scriptPath = join(SCRIPTS_DIR, `quick_${Date.now()}.sh`); | |
| writeFileSync(scriptPath, `#!/bin/bash\nset -e\n${script}`, { mode: 0o755 }); | |
| try { | |
| const result = await execAsync(scriptPath, { | |
| cwd: cwd || process.env.HOME, timeout, | |
| maxBuffer: 10*1024*1024, shell: "/bin/bash" | |
| }); | |
| return { content: [{ type: "text", text: result.stdout || "(no output)" }] }; | |
| } finally { try { unlinkSync(scriptPath); } catch {} } | |
| } | |
| case "terminal_launch": { | |
| const { command, title = "Quick Shell", app = "terminal" } = args; | |
| const escaped = command.replace(/"/g, '\\"'); | |
| const script = app === "iterm" | |
| ? `tell application "iTerm"\n activate\n create window with default profile\nend tell` | |
| : `tell application "Terminal"\n activate\n do script "${escaped}"\nend tell`; | |
| await execAsync(`osascript -e '${script}'`); | |
| return { content: [{ type: "text", text: `Launched: ${command}` }] }; | |
| } | |
| case "process_list": { | |
| const { filter, limit = 20 } = args || {}; | |
| const cmd = filter | |
| ? `ps aux | grep -i "${filter}" | grep -v grep | head -${limit}` | |
| : `ps aux -r | head -${limit + 1}`; | |
| const result = await execAsync(cmd); | |
| return { content: [{ type: "text", text: result.stdout }] }; | |
| } | |
| case "env_info": { | |
| const info = []; | |
| info.push((await execAsync("sw_vers")).stdout); | |
| info.push((await execAsync("node -v")).stdout); | |
| return { content: [{ type: "text", text: info.join("\n") }] }; | |
| } | |
| } | |
| } catch (e) { return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true }; } | |
| }); | |
| async function main() { | |
| const transport = new StdioServerTransport(); | |
| await server.connect(transport); | |
| } | |
| main().catch(e => { console.error(e); process.exit(1); }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment