@iflow-mcp/shell-command-mcp 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,96 +1,38 @@
1
- # *OBSOLETE*
1
+ # shell-command-mcp
2
2
 
3
- I recommend using Claude Code by running `claude mcp serve` instead of this MCP server.
4
- I have created [ai-agent-workspace](https://github.com/kaznak/container-images/tree/main/ai-agent-workspace) as a container to run Claude Code.
5
- Please use it as needed.
3
+ MCP server for executing shell commands.
6
4
 
7
- # Shell Command MCP Server
5
+ This project is sponsored by [ChatWise](https://chatwise.app), an all-in-one LLM chatbot with first-class MCP support.
8
6
 
9
- This is an MCP (Model Context Protocol) server that allows executing shell commands within a Docker container. It provides a secure and isolated workspace for running commands without giving access to the host Docker daemon.
7
+ ## Usage
10
8
 
11
- ## Features
9
+ ### Configure manually
12
10
 
13
- - Run shell scripts through a simple MCP interface
14
- - synchronous execution
15
- - asynchronous execution with 4 different modes
16
- - complete: notify when the command is completed
17
- - line: notify on each line of output
18
- - chunk: notify on each chunk of output
19
- - character: notify on each character of output
20
- - Kubernetes tools included: kubectl, helm, kustomize, hemfile
21
- - Isolated Docker container environment with non-root user
22
- - host-container userid/groupid mapping implemented. this allows the container to run as the same user as the host, ensuring that files created by the container have the same ownership and permissions as those created by the host.
23
- - mount a host directory to the container /home/mcp directory for persistence. it become the home directory the AI works in.
24
- - if the host directory is empty, the initial files will be copied form the backup in the container.
25
-
26
- ## Design Philosophy
27
-
28
- This MCP server provides AI with a workspace similar to that of a human.
29
- Authorization is limited not by MCP functions, but by container isolation and external authorization restrictions.
30
-
31
- It provides more general tools such as shell script execution, so that they can be used without specialized knowledge of tool use.
32
-
33
- The server implementation is kept as simple as possible to facilitate code auditing.
34
-
35
- ## Getting Started
36
-
37
- ### Prerequisites
38
-
39
- - Docker
40
-
41
- ### Usage with Claude for Desktop
42
-
43
- Add the following configuration to your Claude for Desktop configuration file.
44
-
45
- MacOS:
46
-
47
- ```json
48
- "shell-command": {
49
- "command": "docker",
50
- "args": [
51
- "run",
52
- "--rm",
53
- "-i",
54
- "--mount",
55
- "type=bind,src=/Users/user-name/MCPHome,dst=/home/mcp",
56
- "ghcr.io/kaznak/shell-command-mcp:latest"
57
- ]
58
- }
11
+ ```bash
12
+ # stdio server
13
+ npx -y shell-command-mcp
59
14
  ```
60
15
 
61
- Replace `/Users/user-name/ClaudeWorks` with the directory you want to make available to the container.
62
-
63
- Windows:
16
+ ### JSON config
64
17
 
65
18
  ```json
66
- "shell-command": {
67
- "command": "docker",
68
- "args": [
69
- "run",
70
- "--rm",
71
- "-i",
72
- "--mount",
73
- "type=bind,src=\\\\wsl.localhost\\Ubuntu\\home\\user-name\\MCPHome,dst=/home/mcp",
74
- "ghcr.io/kaznak/shell-command-mcp:latest"
75
- ]
19
+ {
20
+ "mcpServers": {
21
+ "shell-command": {
22
+ "command": "npx",
23
+ "args": ["-y", "shell-command-mcp"],
24
+ "env": {
25
+ "ALLOWED_COMMANDS": "cat,ls,echo"
26
+ }
27
+ }
28
+ }
76
29
  }
77
30
  ```
78
31
 
79
- ### Feed some prompts
80
-
81
- To Operate the files in the mounted directory.
82
-
83
- ## Available MCP Tools
84
-
85
- - [execute-bash-script-sync](./src/execute-bash-script-sync.ts)
86
- - [execute-bash-script-async](./src/execute-bash-script-async.ts)
87
-
88
- ## Security Considerations
32
+ ### Allowed commands
89
33
 
90
- - The MCP server runs as a non-root user within the container
91
- - The container does not have access to the host Docker daemon
92
- - User workspace is mounted from the host for persistence
34
+ 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.
93
35
 
94
36
  ## License
95
37
 
96
- MIT
38
+ MIT.
package/cli.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "./dist/index.js"
package/dist/index.js ADDED
@@ -0,0 +1,56 @@
1
+ // index.ts
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { dump } from "js-yaml";
6
+ // package.json
7
+ var version = "1.0.1";
8
+
9
+ // index.ts
10
+ import { x } from "tinyexec";
11
+ import { tokenizeArgs } from "args-tokenizer";
12
+ var allowedCommands = process.env.ALLOWED_COMMANDS?.split(",").map((cmd) => cmd.trim()) || [];
13
+ var server = new McpServer({
14
+ name: "shell-command-mcp",
15
+ version
16
+ }, {
17
+ capabilities: {
18
+ logging: {},
19
+ tools: {}
20
+ }
21
+ });
22
+ server.tool("execute_command", "Execute a shell command", {
23
+ command: z.string().describe("The shell command to execute")
24
+ }, async (args) => {
25
+ const [bin, ...commandArgs] = tokenizeArgs(args.command);
26
+ try {
27
+ if (!allowedCommands.includes("*") && !allowedCommands.includes(bin)) {
28
+ throw new Error(`Command "${bin}" is not allowed, allowed commands: ${allowedCommands.length > 0 ? allowedCommands.join(", ") : "(none)"}`);
29
+ }
30
+ const result = await x(bin, commandArgs);
31
+ return {
32
+ content: [
33
+ {
34
+ type: "text",
35
+ text: dump({
36
+ exit_code: result.exitCode,
37
+ stdout: result.stdout,
38
+ stderr: result.stderr
39
+ })
40
+ }
41
+ ]
42
+ };
43
+ } catch (error) {
44
+ return {
45
+ content: [
46
+ {
47
+ type: "text",
48
+ text: `Error executing command: ${error instanceof Error ? error.message : String(error)}`
49
+ }
50
+ ],
51
+ isError: true
52
+ };
53
+ }
54
+ });
55
+ var transport = new StdioServerTransport;
56
+ await server.connect(transport);
package/package.json CHANGED
@@ -1,43 +1,27 @@
1
1
  {
2
2
  "name": "@iflow-mcp/shell-command-mcp",
3
- "version": "1.0.0",
4
- "description": "MCP server for executing shell commands in a Docker container",
5
- "main": "build/index.js",
3
+ "description": "MCP server for running shell commands",
6
4
  "type": "module",
7
- "bin": {
8
- "shell-command-mcp": "build/index.js"
9
- },
5
+ "version": "1.0.1",
6
+ "files": [
7
+ "dist",
8
+ "/cli.js"
9
+ ],
10
+ "bin": "./cli.js",
10
11
  "scripts": {
11
- "build": "tsc",
12
- "lint": "eslint src/**/*.ts",
13
- "lint:fix": "eslint src/**/*.ts --fix",
14
- "format": "prettier -uw .",
15
- "start": "node build/index.js",
16
- "dev": "tsx src/index.ts",
17
- "clean": "rm -rf build"
12
+ "build": "bun build ./index.ts --packages external --outdir dist",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "devDependencies": {
16
+ "@types/bun": "latest",
17
+ "@types/js-yaml": "^4.0.9",
18
+ "typescript": "^5"
18
19
  },
19
- "keywords": [
20
- "mcp",
21
- "shell",
22
- "docker",
23
- "kubernetes"
24
- ],
25
- "author": "",
26
- "license": "MIT",
27
20
  "dependencies": {
28
21
  "@modelcontextprotocol/sdk": "^1.8.0",
29
- "execa": "^8.0.1",
30
- "zod": "^3.22.4"
31
- },
32
- "devDependencies": {
33
- "@types/node": "^20.10.3",
34
- "@typescript-eslint/eslint-plugin": "^6.13.2",
35
- "@typescript-eslint/parser": "^6.13.2",
36
- "eslint": "^8.55.0",
37
- "eslint-config-prettier": "^9.1.0",
38
- "eslint-plugin-prettier": "^5.0.1",
39
- "prettier": "^3.1.0",
40
- "tsx": "^4.6.2",
41
- "typescript": "~5.3.3"
22
+ "args-tokenizer": "^0.3.0",
23
+ "js-yaml": "^4.1.0",
24
+ "tinyexec": "^1.0.1",
25
+ "zod": "^3.24.2"
42
26
  }
43
27
  }
package/.dockleignore DELETED
@@ -1,15 +0,0 @@
1
- # Create a user for the container
2
- ## Run as root to map host user to container user.
3
- CIS-DI-0001
4
-
5
- # TODO
6
- # Avoid sudo command
7
- DKL-DI-0001
8
- ## Enable Content trust for Docker
9
- CIS-DI-0005
10
- ## Add HEALTHCHECK instruction to the container image
11
- CIS-DI-0006
12
- ## Confirm safety of setuid/setgid files
13
- CIS-DI-0008
14
- ## Only put necessary files
15
- DKL-LI-0003
package/.eslintrc.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "parser": "@typescript-eslint/parser",
3
- "extends": [
4
- "eslint:recommended",
5
- "plugin:@typescript-eslint/recommended",
6
- "plugin:prettier/recommended"
7
- ],
8
- "plugins": ["@typescript-eslint", "prettier"],
9
- "env": {
10
- "node": true,
11
- "es6": true
12
- },
13
- "parserOptions": {
14
- "ecmaVersion": 2022,
15
- "sourceType": "module"
16
- },
17
- "rules": {
18
- "prettier/prettier": "error",
19
- "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
20
- "@typescript-eslint/explicit-function-return-type": "off",
21
- "@typescript-eslint/explicit-module-boundary-types": "off"
22
- }
23
- }
@@ -1,102 +0,0 @@
1
- name: Build and Push Docker Image
2
-
3
- on:
4
- release:
5
- types: [published]
6
- push:
7
- branches:
8
- - main
9
- workflow_dispatch:
10
- inputs:
11
- tag:
12
- description: 'Git tag to build from'
13
- required: false
14
- default: ''
15
-
16
- env:
17
- REGISTRY: ghcr.io
18
- IMAGE_NAME: ${{ github.repository }}
19
-
20
- jobs:
21
- build-and-push:
22
- runs-on: ubuntu-latest
23
- permissions:
24
- contents: read
25
- packages: write
26
- security-events: write
27
-
28
- steps:
29
- - name: Get tag to build
30
- id: get-tag
31
- run: |
32
- if [ "${{ github.event_name }}" = "release" ]; then
33
- echo "tag=${{ github.event.release.tag_name }}"
34
- elif [ -n "${{ github.event.inputs.tag }}" ]; then
35
- echo "tag=${{ github.event.inputs.tag }}"
36
- elif [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/main" ]; then
37
- echo "tag=main"
38
- else
39
- # Get latest release tag if no tag is specified
40
- LATEST_TAG=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name)
41
- echo "tag=${LATEST_TAG}"
42
- fi \
43
- | tee -a $GITHUB_OUTPUT
44
-
45
- - name: Checkout repository
46
- uses: actions/checkout@v4
47
- with:
48
- ref: ${{ steps.get-tag.outputs.tag }}
49
-
50
- - name: Set up Docker Buildx
51
- uses: docker/setup-buildx-action@v3
52
-
53
- - name: Log in to container registry
54
- uses: docker/login-action@v3
55
- with:
56
- registry: ${{ env.REGISTRY }}
57
- username: ${{ github.actor }}
58
- password: ${{ secrets.GITHUB_TOKEN }}
59
-
60
- - name: Extract Docker metadata
61
- id: meta
62
- uses: docker/metadata-action@v5
63
- with:
64
- images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
65
- tags: |
66
- type=raw,value=${{ steps.get-tag.outputs.tag }}
67
- type=raw,value=latest,enable=${{ github.event_name == 'release' || steps.get-tag.outputs.tag == github.event.repository.default_branch }}
68
-
69
- - name: Build Docker image (for scanning)
70
- uses: docker/build-push-action@v5
71
- with:
72
- context: .
73
- push: false
74
- load: true
75
- tags: ${{ env.IMAGE_NAME }}:test
76
- labels: ${{ steps.meta.outputs.labels }}
77
- cache-from: type=gha
78
- cache-to: type=gha,mode=max
79
-
80
- - name: Scan image with Dockle
81
- uses: erzz/dockle-action@v1
82
- with:
83
- image: ${{ env.IMAGE_NAME }}:test
84
- exit-code: 1
85
- failure-threshold: fatal
86
- report-format: sarif
87
-
88
- - name: Upload Dockle scan results
89
- uses: github/codeql-action/upload-sarif@v3
90
- with:
91
- sarif_file: dockle-report.sarif
92
- category: dockle
93
-
94
- - name: Push Docker image
95
- uses: docker/build-push-action@v5
96
- with:
97
- context: .
98
- push: true
99
- tags: ${{ steps.meta.outputs.tags }}
100
- labels: ${{ steps.meta.outputs.labels }}
101
- cache-from: type=gha
102
- cache-to: type=gha,mode=max
package/.prettierignore DELETED
@@ -1,2 +0,0 @@
1
- /client-sequence-example.json
2
- package-lock.json
package/.prettierrc DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "semi": true,
3
- "trailingComma": "all",
4
- "singleQuote": true,
5
- "printWidth": 100,
6
- "tabWidth": 2
7
- }