Skip to main content
Glama

MCP Shell Server

by mkusaka
index.ts5.49 kB
#!/usr/bin/env node import { McpServer, ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { Command } from "commander"; import { z } from "zod"; import { $, ProcessOutput } from "zx"; import { logger } from "./lib/logger.js"; import os from "os"; import getShell, { isUnderHome, getWorkingDir } from "./shell-config.js"; // CLI configuration is now handled in shell-config.js // Get the shell to use const shell = getShell(); const workingDir = getWorkingDir(); // Display server information logger.info("MCP Shell Server started"); logger.info(`Shell: ${shell}`); logger.info(`Working Directory: ${workingDir}`); logger.info(`Platform: ${os.platform()}`); logger.info(`Hostname: ${os.hostname()}`); logger.info(`Username: ${os.userInfo().username}`); // zx configuration for command execution $.shell = shell; $.verbose = false; // Disable zx debug output // MCP server configuration const server = new McpServer({ name: "@mkusaka/mcp-shell-server", version: "0.1.0", }); // System information resources server.resource( "hostname", new ResourceTemplate("hostname://", { list: undefined }), async (uri) => ({ contents: [ { uri: uri.href, text: os.hostname(), }, ], }), ); server.resource( "platform", new ResourceTemplate("platform://", { list: undefined }), async (uri) => ({ contents: [ { uri: uri.href, text: os.platform(), }, ], }), ); server.resource( "shell", new ResourceTemplate("shell://", { list: undefined }), async (uri) => ({ contents: [ { uri: uri.href, text: shell, }, ], }), ); server.resource( "username", new ResourceTemplate("username://", { list: undefined }), async (uri) => ({ contents: [ { uri: uri.href, text: os.userInfo().username, }, ], }), ); // Combined system information resource server.resource( "system-info", new ResourceTemplate("system-info://", { list: undefined }), async (uri) => ({ contents: [ { uri: uri.href, text: JSON.stringify( { hostname: os.hostname(), platform: os.platform(), shell: shell, username: os.userInfo().username, cpus: os.cpus().length, totalmem: os.totalmem(), freemem: os.freemem(), uptime: os.uptime(), }, null, 2, ), }, ], }), ); // Shell command execution tool configuration server.tool( "shell_exec", "Executes commands in the specified shell with detailed error handling and output capture", { command: z .string() .min(1) .describe( "The shell command to execute in the configured shell environment", ), workingDir: workingDir ? z .string() .optional() .describe( "Optional working directory to execute the command in (must be under $HOME for security)", ) : z .string() .describe( "Working directory to execute the command in (must be under $HOME for security)", ), }, async ({ command, workingDir: cmdWorkingDir }) => { try { logger.info(`Executing command: ${command}`); // Use command-specific working directory or fall back to global setting const execWorkingDir = cmdWorkingDir || workingDir; if (execWorkingDir && !isUnderHome(execWorkingDir)) { logger.error( `Working directory must be under $HOME: ${execWorkingDir}`, ); return { content: [ { type: "text", text: `Error: Working directory must be under $HOME: ${execWorkingDir}`, }, ], isError: true, }; } try { // Execute command using zx // Pass the command to the shell with -c option if (execWorkingDir) { $.cwd = execWorkingDir; } const result = await $`${shell} -c ${command}`; if (result.stderr) { logger.info(`Command warning: ${result.stderr}`); } // Return successful execution result return { content: [ { type: "text", text: result.stdout || "(Command executed successfully but produced no output)", }, ], }; } catch (execError) { // Command execution error (non-zero exit code) const error = execError as ProcessOutput; logger.error( `Command execution error: ${error.stderr || error.message}`, ); return { content: [ { type: "text", text: error.stderr || error.stdout || error.message, }, ], isError: true, }; } } catch (error) { // Other error handling logger.error("Unexpected error:", error); return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); // Start the server const transport = new StdioServerTransport(); await server.connect(transport); logger.info("MCP Shell Server ready");

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/mkusaka/mcp-shell-server'

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