| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 |
- #!/usr/bin/env node
- const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
- const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
- const { CallToolRequestSchema, ListToolsRequestSchema, } = require("@modelcontextprotocol/sdk/types.js");
- const { exec } = require("child_process");
- const util = require("util");
- const execAsync = util.promisify(exec);
- const blocked = ["rm ", "dd ", "mkfs", "format", "shutdown", "reboot", "halt", "sudo", "su", "chmod", "chown", "passwd", "kill ", ">", ">>", "|", "&&", "||", "`", "$("];
- const allowed = ["ls", "cat", "head", "tail", "wc", "find", "grep", "which", "pwd", "whoami", "id", "date", "uname", "df", "du", "ps", "free", "uptime", "env", "echo", "sort", "uniq", "stat", "file", "tree", "docker ps", "docker images", "curl", "ping", "host"];
- function isSafe(cmd) {
- const lower = cmd.trim().toLowerCase();
- for (const b of blocked) if (lower.includes(b)) return false;
- for (const a of allowed) if (lower.startsWith(a)) return true;
- return false;
- }
- const server = new Server({ name: "terminal", version: "1.0.0" }, { capabilities: { tools: {} } });
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
- tools: [{
- name: "run_command",
- description: "Execute a safe read-only shell command",
- inputSchema: {
- type: "object",
- properties: {
- command: { type: "string", description: "Shell command to execute" },
- timeout: { type: "number", description: "Timeout in seconds (max 60)", default: 30 }
- },
- required: ["command"]
- }
- }]
- }));
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
- if (request.params.name !== "run_command") throw new Error(`Unknown tool: ${request.params.name}`);
- const { command, timeout = 30 } = request.params.arguments;
- if (!isSafe(command)) return { content: [{ type: "text", text: `Error: command not allowed` }], isError: true };
- try {
- const { stdout, stderr } = await execAsync(command, { timeout: Math.min(timeout, 60) * 1000, shell: "/bin/sh" });
- let text = stdout;
- if (stderr) text += "\n--- stderr ---\n" + stderr;
- return { content: [{ type: "text", text: text || "(no output)" }] };
- } 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(console.error);
|