mcp-shell-server 0.1.0__tar.gz

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.
@@ -0,0 +1,13 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ open-pull-requests-limit: 10
8
+
9
+ - package-ecosystem: "github-actions"
10
+ directory: "/"
11
+ schedule:
12
+ interval: "weekly"
13
+ open-pull-requests-limit: 10
@@ -0,0 +1,52 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.11"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install uv
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install uv
28
+
29
+ - name: Install dev/test dependencies
30
+ run: |
31
+ pip install -e ".[dev]"
32
+ pip install -e ".[test]"
33
+
34
+ - name: Run tests
35
+ run: |
36
+ uv run pytest
37
+
38
+ - name: Run ruff
39
+ run: |
40
+ uv run ruff check .
41
+
42
+ - name: Run black
43
+ run: |
44
+ uv run black . --check
45
+
46
+ - name: Run isort
47
+ run: |
48
+ uv run isort . --check-only
49
+
50
+ - name: Run mypy
51
+ run: |
52
+ uv run mypy mcp_shell_server tests
@@ -0,0 +1,78 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ venv/
25
+ env/
26
+ ENV/
27
+ .env
28
+ .venv
29
+
30
+ # IDE
31
+ .idea/
32
+ .vscode/
33
+ *.swp
34
+ *.swo
35
+ .DS_Store
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+ cover/
51
+
52
+ # Jupyter Notebook
53
+ .ipynb_checkpoints
54
+
55
+ # mypy
56
+ .mypy_cache/
57
+ .dmypy.json
58
+ dmypy.json
59
+
60
+ # Distribution / packaging
61
+ .Python
62
+ build/
63
+ develop-eggs/
64
+ dist/
65
+ downloads/
66
+ eggs/
67
+ .eggs/
68
+ lib/
69
+ lib64/
70
+ parts/
71
+ sdist/
72
+ var/
73
+ wheels/
74
+ share/python-wheels/
75
+ *.egg-info/
76
+ .installed.cfg
77
+ *.egg
78
+ MANIFEST
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,23 @@
1
+ .PHONY: test format lint typecheck check
2
+
3
+ test:
4
+ pytest
5
+
6
+ format:
7
+ black .
8
+ isort .
9
+ ruff check --fix .
10
+
11
+
12
+ lint:
13
+ black --check .
14
+ isort --check .
15
+ ruff check .
16
+
17
+ typecheck:
18
+ mypy mcp_shell_server tests
19
+
20
+ # Run all checks required before pushing
21
+ check: lint typecheck test
22
+ fix: check format
23
+ all: check
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.3
2
+ Name: mcp-shell-server
3
+ Version: 0.1.0
4
+ Summary: MCP Shell Server - Execute shell commands via MCP protocol
5
+ Author: tumf
6
+ License: MIT
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: asyncio>=3.4.3
9
+ Requires-Dist: mcp>=1.1.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: black>=23.3.0; extra == 'dev'
12
+ Requires-Dist: isort>=5.12.0; extra == 'dev'
13
+ Requires-Dist: mypy>=1.2.0; extra == 'dev'
14
+ Requires-Dist: pre-commit>=3.2.2; extra == 'dev'
15
+ Requires-Dist: ruff>=0.0.262; extra == 'dev'
16
+ Provides-Extra: test
17
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'test'
18
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'test'
19
+ Requires-Dist: pytest-env>=1.1.0; extra == 'test'
20
+ Requires-Dist: pytest>=7.4.0; extra == 'test'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # MCP Shell Server
24
+
25
+ A secure shell command execution server implementing the Model Context Protocol (MCP). This server allows remote execution of whitelisted shell commands with support for stdin input.
26
+
27
+ ## Features
28
+
29
+ * **Secure Command Execution**: Only whitelisted commands can be executed
30
+ * **Standard Input Support**: Pass input to commands via stdin
31
+ * **Comprehensive Output**: Returns stdout, stderr, exit status, and execution time
32
+ * **Shell Operator Safety**: Validates commands after shell operators (; , &&, ||, |)
33
+
34
+ ## MCP client setting in your Claude.app
35
+
36
+ ```shell
37
+ code ~/Library/Application\ Support/Claude/claude_desktop_config.json
38
+ ```
39
+
40
+ ```json
41
+ {
42
+ "mcpServers": {
43
+ "shell": {
44
+ "command": "uv",
45
+ "args": [
46
+ "--directory",
47
+ ".",
48
+ "run",
49
+ "mcp-shell-server"
50
+ ],
51
+ "env": {
52
+ "ALLOW_COMMANDS": "ls,cat,pwd,grep,wc,touch,find"
53
+ }
54
+ },
55
+ }
56
+ }
57
+ ```
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ pip install mcp-shell-server
63
+ ```
64
+
65
+ ## Usage
66
+
67
+ ### Starting the Server
68
+
69
+ ```bash
70
+ ALLOW_COMMANDS="ls,cat,echo" uvx mcp-shell-server
71
+ ```
72
+
73
+ The `ALLOW_COMMANDS` environment variable specifies which commands are allowed to be executed. Commands can be separated by commas with optional spaces around them.
74
+
75
+ Valid formats for ALLOW_COMMANDS:
76
+
77
+ ```bash
78
+ ALLOW_COMMANDS="ls,cat,echo" # Basic format
79
+ ALLOW_COMMANDS="ls ,echo, cat" # With spaces
80
+ ALLOW_COMMANDS="ls, cat , echo" # Multiple spaces
81
+ ```
82
+
83
+ ### Request Format
84
+
85
+ ```python
86
+ # Basic command execution
87
+ {
88
+ "command": ["ls", "-l", "/tmp"]
89
+ }
90
+
91
+ # Command with stdin input
92
+ {
93
+ "command": ["cat"],
94
+ "stdin": "Hello, World!"
95
+ }
96
+ ```
97
+
98
+ ### Response Format
99
+
100
+ Successful response:
101
+
102
+ ```json
103
+ {
104
+ "stdout": "command output",
105
+ "stderr": "",
106
+ "status": 0,
107
+ "execution_time": 0.123
108
+ }
109
+ ```
110
+
111
+ Error response:
112
+
113
+ ```json
114
+ {
115
+ "error": "Command not allowed: rm",
116
+ "status": 1,
117
+ "stdout": "",
118
+ "stderr": "Command not allowed: rm",
119
+ "execution_time": 0
120
+ }
121
+ ```
122
+
123
+ ## Security
124
+
125
+ The server implements several security measures:
126
+
127
+ 1. **Command Whitelisting**: Only explicitly allowed commands can be executed
128
+ 2. **Shell Operator Validation**: Commands after shell operators (;, &&, ||, |) are also validated against the whitelist
129
+ 3. **No Shell Injection**: Commands are executed directly without shell interpretation
130
+
131
+ ## Development
132
+
133
+ ### Setting up Development Environment
134
+
135
+ 1. Clone the repository
136
+
137
+ ```bash
138
+ git clone https://github.com/yourusername/mcp-shell-server.git
139
+ cd mcp-shell-server
140
+ ```
141
+
142
+ 2. Install dependencies including test requirements
143
+
144
+ ```bash
145
+ pip install -e ".[test]"
146
+ ```
147
+
148
+ ### Running Tests
149
+
150
+ ```bash
151
+ pytest
152
+ ```
153
+
154
+ ## API Reference
155
+
156
+ ### Request Arguments
157
+
158
+ | Field | Type | Required | Description |
159
+ |----------|------------|----------|-----------------------------------------------|
160
+ | command | string[] | Yes | Command and its arguments as array elements |
161
+ | stdin | string | No | Input to be passed to the command |
162
+
163
+ ### Response Fields
164
+
165
+ | Field | Type | Description |
166
+ |----------------|---------|---------------------------------------------|
167
+ | stdout | string | Standard output from the command |
168
+ | stderr | string | Standard error output from the command |
169
+ | status | integer | Exit status code |
170
+ | execution_time | float | Time taken to execute (in seconds) |
171
+ | error | string | Error message (only present if failed) |
172
+
173
+ ## Requirements
174
+
175
+ * Python 3.11 or higher
176
+ * mcp>=1.1.0
177
+
178
+ ## License
179
+
180
+ MIT License - See LICENSE file for details
@@ -0,0 +1,158 @@
1
+ # MCP Shell Server
2
+
3
+ A secure shell command execution server implementing the Model Context Protocol (MCP). This server allows remote execution of whitelisted shell commands with support for stdin input.
4
+
5
+ ## Features
6
+
7
+ * **Secure Command Execution**: Only whitelisted commands can be executed
8
+ * **Standard Input Support**: Pass input to commands via stdin
9
+ * **Comprehensive Output**: Returns stdout, stderr, exit status, and execution time
10
+ * **Shell Operator Safety**: Validates commands after shell operators (; , &&, ||, |)
11
+
12
+ ## MCP client setting in your Claude.app
13
+
14
+ ```shell
15
+ code ~/Library/Application\ Support/Claude/claude_desktop_config.json
16
+ ```
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "shell": {
22
+ "command": "uv",
23
+ "args": [
24
+ "--directory",
25
+ ".",
26
+ "run",
27
+ "mcp-shell-server"
28
+ ],
29
+ "env": {
30
+ "ALLOW_COMMANDS": "ls,cat,pwd,grep,wc,touch,find"
31
+ }
32
+ },
33
+ }
34
+ }
35
+ ```
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install mcp-shell-server
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ### Starting the Server
46
+
47
+ ```bash
48
+ ALLOW_COMMANDS="ls,cat,echo" uvx mcp-shell-server
49
+ ```
50
+
51
+ The `ALLOW_COMMANDS` environment variable specifies which commands are allowed to be executed. Commands can be separated by commas with optional spaces around them.
52
+
53
+ Valid formats for ALLOW_COMMANDS:
54
+
55
+ ```bash
56
+ ALLOW_COMMANDS="ls,cat,echo" # Basic format
57
+ ALLOW_COMMANDS="ls ,echo, cat" # With spaces
58
+ ALLOW_COMMANDS="ls, cat , echo" # Multiple spaces
59
+ ```
60
+
61
+ ### Request Format
62
+
63
+ ```python
64
+ # Basic command execution
65
+ {
66
+ "command": ["ls", "-l", "/tmp"]
67
+ }
68
+
69
+ # Command with stdin input
70
+ {
71
+ "command": ["cat"],
72
+ "stdin": "Hello, World!"
73
+ }
74
+ ```
75
+
76
+ ### Response Format
77
+
78
+ Successful response:
79
+
80
+ ```json
81
+ {
82
+ "stdout": "command output",
83
+ "stderr": "",
84
+ "status": 0,
85
+ "execution_time": 0.123
86
+ }
87
+ ```
88
+
89
+ Error response:
90
+
91
+ ```json
92
+ {
93
+ "error": "Command not allowed: rm",
94
+ "status": 1,
95
+ "stdout": "",
96
+ "stderr": "Command not allowed: rm",
97
+ "execution_time": 0
98
+ }
99
+ ```
100
+
101
+ ## Security
102
+
103
+ The server implements several security measures:
104
+
105
+ 1. **Command Whitelisting**: Only explicitly allowed commands can be executed
106
+ 2. **Shell Operator Validation**: Commands after shell operators (;, &&, ||, |) are also validated against the whitelist
107
+ 3. **No Shell Injection**: Commands are executed directly without shell interpretation
108
+
109
+ ## Development
110
+
111
+ ### Setting up Development Environment
112
+
113
+ 1. Clone the repository
114
+
115
+ ```bash
116
+ git clone https://github.com/yourusername/mcp-shell-server.git
117
+ cd mcp-shell-server
118
+ ```
119
+
120
+ 2. Install dependencies including test requirements
121
+
122
+ ```bash
123
+ pip install -e ".[test]"
124
+ ```
125
+
126
+ ### Running Tests
127
+
128
+ ```bash
129
+ pytest
130
+ ```
131
+
132
+ ## API Reference
133
+
134
+ ### Request Arguments
135
+
136
+ | Field | Type | Required | Description |
137
+ |----------|------------|----------|-----------------------------------------------|
138
+ | command | string[] | Yes | Command and its arguments as array elements |
139
+ | stdin | string | No | Input to be passed to the command |
140
+
141
+ ### Response Fields
142
+
143
+ | Field | Type | Description |
144
+ |----------------|---------|---------------------------------------------|
145
+ | stdout | string | Standard output from the command |
146
+ | stderr | string | Standard error output from the command |
147
+ | status | integer | Exit status code |
148
+ | execution_time | float | Time taken to execute (in seconds) |
149
+ | error | string | Error message (only present if failed) |
150
+
151
+ ## Requirements
152
+
153
+ * Python 3.11 or higher
154
+ * mcp>=1.1.0
155
+
156
+ ## License
157
+
158
+ MIT License - See LICENSE file for details
@@ -0,0 +1,15 @@
1
+ """MCP Shell Server Package."""
2
+
3
+ import asyncio
4
+
5
+ from . import server
6
+
7
+
8
+ def main():
9
+ """Main entry point for the package."""
10
+ asyncio.run(server.main())
11
+
12
+
13
+ # Optionally expose other important items at package level
14
+ __all__ = ["main", "server"]
15
+ __version__ = "0.1.0"
@@ -0,0 +1,123 @@
1
+ import logging
2
+ import traceback
3
+ from collections.abc import Sequence
4
+ from typing import Any
5
+
6
+ from mcp.server import Server
7
+ from mcp.types import TextContent, Tool
8
+
9
+ from .shell_executor import ShellExecutor
10
+
11
+ # Configure logging
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger("mcp-shell-server")
14
+
15
+ app = Server("mcp-shell-server")
16
+
17
+
18
+ class ExecuteToolHandler:
19
+ """Handler for shell command execution"""
20
+
21
+ name = "shell_execute"
22
+ description = "Execute a shell command"
23
+
24
+ def __init__(self):
25
+ self.executor = ShellExecutor()
26
+
27
+ def get_allowed_commands(self) -> list[str]:
28
+ """Get the allowed commands"""
29
+ return self.executor.get_allowed_commands()
30
+
31
+ def get_tool_description(self) -> Tool:
32
+ """Get the tool description for the execute command"""
33
+ return Tool(
34
+ name=self.name,
35
+ description=f"{self.description}\nAllowed commands: {', '.join(self.get_allowed_commands())}",
36
+ inputSchema={
37
+ "type": "object",
38
+ "properties": {
39
+ "command": {
40
+ "type": "array",
41
+ "items": {"type": "string"},
42
+ "description": "Command and its arguments as array",
43
+ },
44
+ "stdin": {
45
+ "type": "string",
46
+ "description": "Input to be passed to the command via stdin",
47
+ },
48
+ "directory": {
49
+ "type": "string",
50
+ "description": "Working directory where the command will be executed",
51
+ },
52
+ },
53
+ "required": ["command"],
54
+ },
55
+ )
56
+
57
+ async def run_tool(self, arguments: dict) -> Sequence[TextContent]:
58
+ """Execute the shell command with the given arguments"""
59
+ command = arguments.get("command", [])
60
+ stdin = arguments.get("stdin")
61
+ directory = arguments.get("directory")
62
+
63
+ if not command:
64
+ raise ValueError("No command provided")
65
+
66
+ result = await self.executor.execute(command, stdin, directory)
67
+
68
+ # Raise error if command execution failed
69
+ if result.get("error"):
70
+ raise RuntimeError(result["error"])
71
+
72
+ # Convert executor result to TextContent sequence
73
+ content: list[TextContent] = []
74
+
75
+ if result.get("stdout"):
76
+ content.append(TextContent(type="text", text=result["stdout"]))
77
+ if result.get("stderr"):
78
+ content.append(TextContent(type="text", text=result["stderr"]))
79
+
80
+ return content
81
+
82
+
83
+ # Initialize tool handlers
84
+ tool_handler = ExecuteToolHandler()
85
+
86
+
87
+ @app.list_tools()
88
+ async def list_tools() -> list[Tool]:
89
+ """List available tools."""
90
+ return [tool_handler.get_tool_description()]
91
+
92
+
93
+ @app.call_tool()
94
+ async def call_tool(name: str, arguments: Any) -> Sequence[TextContent]:
95
+ """Handle tool calls"""
96
+ try:
97
+ if name != tool_handler.name:
98
+ raise ValueError(f"Unknown tool: {name}")
99
+
100
+ if not isinstance(arguments, dict):
101
+ raise ValueError("Arguments must be a dictionary")
102
+
103
+ return await tool_handler.run_tool(arguments)
104
+
105
+ except Exception as e:
106
+ logger.error(traceback.format_exc())
107
+ logger.error(f"Error during call_tool: {str(e)}")
108
+ raise RuntimeError(f"Error executing command: {str(e)}") from e
109
+
110
+
111
+ async def main() -> None:
112
+ """Main entry point for the MCP shell server"""
113
+ logger.info("Starting MCP shell server")
114
+ try:
115
+ from mcp.server.stdio import stdio_server
116
+
117
+ async with stdio_server() as (read_stream, write_stream):
118
+ await app.run(
119
+ read_stream, write_stream, app.create_initialization_options()
120
+ )
121
+ except Exception as e:
122
+ logger.error(f"Server error: {str(e)}")
123
+ raise