#
tokens: 1365/50000 8/8 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .editorconfig
├── .gitignore
├── .prettierrc
├── bun.lock
├── cli.js
├── index.ts
├── package.json
├── README.md
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------

```
{
  "semi": false
}

```

--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------

```
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# dependencies (bun install)
node_modules

# output
out
dist
*.tgz

# code coverage
coverage
*.lcov

# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# caches
.eslintcache
.cache
*.tsbuildinfo

# IntelliJ based IDEs
.idea

# Finder (MacOS) folder config
.DS_Store

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# shell-command-mcp

MCP server for executing shell commands.

This project is sponsored by [ChatWise](https://chatwise.app), an all-in-one LLM chatbot with first-class MCP support.

## Usage

### Configure manually

```bash
# stdio server
npx -y shell-command-mcp
```

### JSON config

```json
{
  "mcpServers": {
    "shell-command": {
      "command": "npx",
      "args": ["-y", "shell-command-mcp"],
      "env": {
        "ALLOWED_COMMANDS": "cat,ls,echo"
      }
    }
  }
}
```

### Allowed commands

Use `ALLOWED_COMMANDS` environment variable to explictly allow the commands that this server can run, separate each command by `,`. You can use `*` to allow any command, but this is potentially dangerous.

## License

MIT.

```

--------------------------------------------------------------------------------
/cli.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node
import "./dist/index.js"

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "shell-command-mcp",
  "description": "MCP server for running shell commands",
  "type": "module",
  "version": "0.0.1",
  "files": [
    "dist",
    "/cli.js"
  ],
  "bin": "./cli.js",
  "scripts": {
    "build": "bun build ./index.ts --packages external --outdir dist",
    "prepublishOnly": "npm run build"
  },
  "devDependencies": {
    "@types/bun": "latest",
    "@types/js-yaml": "^4.0.9",
    "typescript": "^5"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.8.0",
    "args-tokenizer": "^0.3.0",
    "js-yaml": "^4.1.0",
    "tinyexec": "^1.0.1",
    "zod": "^3.24.2"
  }
}

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    // Enable latest features
    "lib": ["ESNext", "DOM"],
    "target": "ESNext",
    "module": "ESNext",
    "moduleDetection": "force",
    "jsx": "react-jsx",
    "allowJs": true,

    // Bundler mode
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "noEmit": true,

    // Best practices
    "strict": true,
    "skipLibCheck": true,
    "noFallthroughCasesInSwitch": true,

    // Some stricter flags (disabled by default)
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noPropertyAccessFromIndexSignature": false
  }
}

```

--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
import { z } from "zod"
import { dump } from "js-yaml"
import { version } from "./package.json"
import { x } from "tinyexec"
import { tokenizeArgs } from "args-tokenizer"

const allowedCommands =
  process.env.ALLOWED_COMMANDS?.split(",").map((cmd) => cmd.trim()) || []

const server = new McpServer(
  {
    name: "shell-command-mcp",
    version,
  },
  {
    capabilities: {
      logging: {},
      tools: {},
    },
  }
)

server.tool(
  "execute_command",
  "Execute a shell command",
  {
    command: z.string().describe("The shell command to execute"),
  },
  async (args) => {
    const [bin, ...commandArgs] = tokenizeArgs(args.command)

    try {
      if (!allowedCommands.includes("*") && !allowedCommands.includes(bin)) {
        throw new Error(
          `Command "${bin}" is not allowed, allowed commands: ${
            allowedCommands.length > 0 ? allowedCommands.join(", ") : "(none)"
          }`
        )
      }

      const result = await x(bin, commandArgs)

      return {
        content: [
          {
            type: "text",
            text: dump({
              exit_code: result.exitCode,
              stdout: result.stdout,
              stderr: result.stderr,
            }),
          },
        ],
      }
    } catch (error) {
      return {
        content: [
          {
            type: "text",
            text: `Error executing command: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
        isError: true,
      }
    }
  }
)

const transport = new StdioServerTransport()
await server.connect(transport)

```