cli-mcp-server 0.2.1__tar.gz → 0.2.3__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.
- cli_mcp_server-0.2.3/.github/workflows/python-tests.yml +19 -0
- {cli_mcp_server-0.2.1 → cli_mcp_server-0.2.3}/PKG-INFO +43 -33
- {cli_mcp_server-0.2.1 → cli_mcp_server-0.2.3}/README.md +41 -31
- cli_mcp_server-0.2.3/glama.json +6 -0
- {cli_mcp_server-0.2.1 → cli_mcp_server-0.2.3}/pyproject.toml +2 -2
- {cli_mcp_server-0.2.1 → cli_mcp_server-0.2.3}/src/cli_mcp_server/server.py +91 -25
- cli_mcp_server-0.2.3/tests/__init__.py +1 -0
- cli_mcp_server-0.2.3/tests/test_cli_mcp_server.py +98 -0
- cli_mcp_server-0.2.3/uv.lock +345 -0
- cli_mcp_server-0.2.1/uv.lock +0 -295
- {cli_mcp_server-0.2.1 → cli_mcp_server-0.2.3}/.gitignore +0 -0
- {cli_mcp_server-0.2.1 → cli_mcp_server-0.2.3}/.python-version +0 -0
- {cli_mcp_server-0.2.1 → cli_mcp_server-0.2.3}/LICENSE +0 -0
- {cli_mcp_server-0.2.1 → cli_mcp_server-0.2.3}/src/cli_mcp_server/__init__.py +0 -0
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            name: Python Tests
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            on:
         | 
| 4 | 
            +
              push:
         | 
| 5 | 
            +
                branches:
         | 
| 6 | 
            +
                  - main
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            jobs:
         | 
| 9 | 
            +
              test:
         | 
| 10 | 
            +
                runs-on: ubuntu-latest
         | 
| 11 | 
            +
                steps:
         | 
| 12 | 
            +
                  - uses: actions/checkout@v3
         | 
| 13 | 
            +
                  - uses: actions/setup-python@v4
         | 
| 14 | 
            +
                    with:
         | 
| 15 | 
            +
                      python-version: '3.10'
         | 
| 16 | 
            +
                  - run: |
         | 
| 17 | 
            +
                      python -m pip install --upgrade pip
         | 
| 18 | 
            +
                      python -m pip install .
         | 
| 19 | 
            +
                  - run: python -m unittest discover -v
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            Metadata-Version: 2.4
         | 
| 2 2 | 
             
            Name: cli-mcp-server
         | 
| 3 | 
            -
            Version: 0.2. | 
| 3 | 
            +
            Version: 0.2.3
         | 
| 4 4 | 
             
            Summary: Command line interface for MCP clients with secure execution and customizable security policies
         | 
| 5 5 | 
             
            Project-URL: Homepage, https://github.com/MladenSU/cli-mcp-server
         | 
| 6 6 | 
             
            Project-URL: Documentation, https://github.com/MladenSU/cli-mcp-server#readme
         | 
| @@ -9,7 +9,7 @@ Project-URL: Bug Tracker, https://github.com/MladenSU/cli-mcp-server/issues | |
| 9 9 | 
             
            Author-email: Mladen <fangs-lever6n@icloud.com>
         | 
| 10 10 | 
             
            License-File: LICENSE
         | 
| 11 11 | 
             
            Requires-Python: >=3.10
         | 
| 12 | 
            -
            Requires-Dist: mcp>=1. | 
| 12 | 
            +
            Requires-Dist: mcp>=1.6.0
         | 
| 13 13 | 
             
            Description-Content-Type: text/markdown
         | 
| 14 14 |  | 
| 15 15 | 
             
            # CLI MCP Server
         | 
| @@ -17,8 +17,7 @@ Description-Content-Type: text/markdown | |
| 17 17 | 
             
            ---
         | 
| 18 18 |  | 
| 19 19 | 
             
            A secure Model Context Protocol (MCP) server implementation for executing controlled command-line operations with
         | 
| 20 | 
            -
            comprehensive security
         | 
| 21 | 
            -
            features.
         | 
| 20 | 
            +
            comprehensive security features.
         | 
| 22 21 |  | 
| 23 22 | 
             
            
         | 
| 24 23 | 
             
            
         | 
| @@ -53,30 +52,32 @@ features. | |
| 53 52 | 
             
            ## Overview
         | 
| 54 53 |  | 
| 55 54 | 
             
            This MCP server enables secure command-line execution with robust security measures including command whitelisting, path
         | 
| 56 | 
            -
            validation, and
         | 
| 57 | 
            -
            execution controls. Perfect for providing controlled CLI access to LLM applications while maintaining security.
         | 
| 55 | 
            +
            validation, and execution controls. Perfect for providing controlled CLI access to LLM applications while maintaining security.
         | 
| 58 56 |  | 
| 59 57 | 
             
            ## Features
         | 
| 60 58 |  | 
| 61 59 | 
             
            - 🔒 Secure command execution with strict validation
         | 
| 62 | 
            -
            - ⚙️ Configurable command and flag whitelisting
         | 
| 63 | 
            -
            - 🛡️ Path traversal prevention
         | 
| 60 | 
            +
            - ⚙️ Configurable command and flag whitelisting with 'all' option
         | 
| 61 | 
            +
            - 🛡️ Path traversal prevention and validation
         | 
| 64 62 | 
             
            - 🚫 Shell operator injection protection
         | 
| 65 63 | 
             
            - ⏱️ Execution timeouts and length limits
         | 
| 66 64 | 
             
            - 📝 Detailed error reporting
         | 
| 67 65 | 
             
            - 🔄 Async operation support
         | 
| 66 | 
            +
            - 🎯 Working directory restriction and validation
         | 
| 68 67 |  | 
| 69 68 | 
             
            ## Configuration
         | 
| 70 69 |  | 
| 71 70 | 
             
            Configure the server using environment variables:
         | 
| 72 71 |  | 
| 73 | 
            -
            | Variable             | Description | 
| 74 | 
            -
             | 
| 75 | 
            -
            | `ALLOWED_DIR` | 
| 76 | 
            -
            | `ALLOWED_COMMANDS` | 
| 77 | 
            -
            | `ALLOWED_FLAGS` | 
| 78 | 
            -
            | `MAX_COMMAND_LENGTH | 
| 79 | 
            -
            | `COMMAND_TIMEOUT` | 
| 72 | 
            +
            | Variable             | Description                                          | Default            |
         | 
| 73 | 
            +
            |---------------------|------------------------------------------------------|-------------------|
         | 
| 74 | 
            +
            | `ALLOWED_DIR`       | Base directory for command execution (Required)      | None (Required)   |
         | 
| 75 | 
            +
            | `ALLOWED_COMMANDS`  | Comma-separated list of allowed commands or 'all'    | `ls,cat,pwd`      |
         | 
| 76 | 
            +
            | `ALLOWED_FLAGS`     | Comma-separated list of allowed flags or 'all'       | `-l,-a,--help`    |
         | 
| 77 | 
            +
            | `MAX_COMMAND_LENGTH`| Maximum command string length                        | `1024`            |
         | 
| 78 | 
            +
            | `COMMAND_TIMEOUT`   | Command execution timeout (seconds)                  | `30`              |
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            Note: Setting `ALLOWED_COMMANDS` or `ALLOWED_FLAGS` to 'all' will allow any command or flag respectively.
         | 
| 80 81 |  | 
| 81 82 | 
             
            ## Installation
         | 
| 82 83 |  | 
| @@ -93,19 +94,28 @@ npx @smithery/cli install cli-mcp-server --client claude | |
| 93 94 | 
             
            Executes whitelisted CLI commands within allowed directories.
         | 
| 94 95 |  | 
| 95 96 | 
             
            **Input Schema:**
         | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             {
         | 
| 97 | 
            +
            ```json
         | 
| 98 | 
            +
            {
         | 
| 99 99 | 
             
              "command": {
         | 
| 100 100 | 
             
                "type": "string",
         | 
| 101 | 
            -
                "description": " | 
| 101 | 
            +
                "description": "Single command to execute (e.g., 'ls -l' or 'cat file.txt')"
         | 
| 102 102 | 
             
              }
         | 
| 103 103 | 
             
            }
         | 
| 104 | 
            -
             | 
| 104 | 
            +
            ```
         | 
| 105 | 
            +
             | 
| 106 | 
            +
            **Security Notes:**
         | 
| 107 | 
            +
            - Shell operators (&&, |, >, >>) are not supported
         | 
| 108 | 
            +
            - Commands must be whitelisted unless ALLOWED_COMMANDS='all'
         | 
| 109 | 
            +
            - Flags must be whitelisted unless ALLOWED_FLAGS='all'
         | 
| 110 | 
            +
            - All paths are validated to be within ALLOWED_DIR
         | 
| 105 111 |  | 
| 106 112 | 
             
            ### show_security_rules
         | 
| 107 113 |  | 
| 108 | 
            -
            Displays current security configuration and restrictions | 
| 114 | 
            +
            Displays current security configuration and restrictions, including:
         | 
| 115 | 
            +
            - Working directory
         | 
| 116 | 
            +
            - Allowed commands
         | 
| 117 | 
            +
            - Allowed flags
         | 
| 118 | 
            +
            - Security limits (max command length and timeout)
         | 
| 109 119 |  | 
| 110 120 | 
             
            ## Usage with Claude Desktop
         | 
| 111 121 |  | 
| @@ -113,7 +123,7 @@ Add to your `~/Library/Application\ Support/Claude/claude_desktop_config.json`: | |
| 113 123 |  | 
| 114 124 | 
             
            > Development/Unpublished Servers Configuration
         | 
| 115 125 |  | 
| 116 | 
            -
             | 
| 126 | 
            +
            ```json
         | 
| 117 127 | 
             
            {
         | 
| 118 128 | 
             
              "mcpServers": {
         | 
| 119 129 | 
             
                "cli-mcp-server": {
         | 
| @@ -134,7 +144,7 @@ Add to your `~/Library/Application\ Support/Claude/claude_desktop_config.json`: | |
| 134 144 | 
             
                }
         | 
| 135 145 | 
             
              }
         | 
| 136 146 | 
             
            }
         | 
| 137 | 
            -
             | 
| 147 | 
            +
            ```
         | 
| 138 148 |  | 
| 139 149 | 
             
            > Published Servers Configuration
         | 
| 140 150 |  | 
| @@ -161,23 +171,25 @@ Add to your `~/Library/Application\ Support/Claude/claude_desktop_config.json`: | |
| 161 171 |  | 
| 162 172 | 
             
            ## Security Features
         | 
| 163 173 |  | 
| 164 | 
            -
            - ✅ Command whitelist enforcement
         | 
| 165 | 
            -
            - ✅ Flag validation
         | 
| 166 | 
            -
            - ✅ Path traversal prevention
         | 
| 174 | 
            +
            - ✅ Command whitelist enforcement with 'all' option
         | 
| 175 | 
            +
            - ✅ Flag validation with 'all' option
         | 
| 176 | 
            +
            - ✅ Path traversal prevention and normalization
         | 
| 167 177 | 
             
            - ✅ Shell operator blocking
         | 
| 168 178 | 
             
            - ✅ Command length limits
         | 
| 169 179 | 
             
            - ✅ Execution timeouts
         | 
| 170 180 | 
             
            - ✅ Working directory restrictions
         | 
| 181 | 
            +
            - ✅ Symlink resolution and validation
         | 
| 171 182 |  | 
| 172 183 | 
             
            ## Error Handling
         | 
| 173 184 |  | 
| 174 185 | 
             
            The server provides detailed error messages for:
         | 
| 175 186 |  | 
| 176 | 
            -
            - Security violations
         | 
| 177 | 
            -
            - Command timeouts
         | 
| 187 | 
            +
            - Security violations (CommandSecurityError)
         | 
| 188 | 
            +
            - Command timeouts (CommandTimeoutError)
         | 
| 178 189 | 
             
            - Invalid command formats
         | 
| 179 190 | 
             
            - Path security violations
         | 
| 180 | 
            -
            - Execution failures
         | 
| 191 | 
            +
            - Execution failures (CommandExecutionError)
         | 
| 192 | 
            +
            - General command errors (CommandError)
         | 
| 181 193 |  | 
| 182 194 | 
             
            ## Development
         | 
| 183 195 |  | 
| @@ -186,8 +198,6 @@ The server provides detailed error messages for: | |
| 186 198 | 
             
            - Python 3.10+
         | 
| 187 199 | 
             
            - MCP protocol library
         | 
| 188 200 |  | 
| 189 | 
            -
            ## Development
         | 
| 190 | 
            -
             | 
| 191 201 | 
             
            ### Building and Publishing
         | 
| 192 202 |  | 
| 193 203 | 
             
            To prepare the package for distribution:
         | 
| @@ -227,6 +237,6 @@ Upon launching, the Inspector will display a URL that you can access in your bro | |
| 227 237 |  | 
| 228 238 | 
             
            This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
         | 
| 229 239 |  | 
| 230 | 
            -
             | 
| 240 | 
            +
            ---
         | 
| 231 241 |  | 
| 232 | 
            -
            For more information or support, please open an issue on the project repository.
         | 
| 242 | 
            +
            For more information or support, please open an issue on the project repository.
         | 
| @@ -3,8 +3,7 @@ | |
| 3 3 | 
             
            ---
         | 
| 4 4 |  | 
| 5 5 | 
             
            A secure Model Context Protocol (MCP) server implementation for executing controlled command-line operations with
         | 
| 6 | 
            -
            comprehensive security
         | 
| 7 | 
            -
            features.
         | 
| 6 | 
            +
            comprehensive security features.
         | 
| 8 7 |  | 
| 9 8 | 
             
            
         | 
| 10 9 | 
             
            
         | 
| @@ -39,30 +38,32 @@ features. | |
| 39 38 | 
             
            ## Overview
         | 
| 40 39 |  | 
| 41 40 | 
             
            This MCP server enables secure command-line execution with robust security measures including command whitelisting, path
         | 
| 42 | 
            -
            validation, and
         | 
| 43 | 
            -
            execution controls. Perfect for providing controlled CLI access to LLM applications while maintaining security.
         | 
| 41 | 
            +
            validation, and execution controls. Perfect for providing controlled CLI access to LLM applications while maintaining security.
         | 
| 44 42 |  | 
| 45 43 | 
             
            ## Features
         | 
| 46 44 |  | 
| 47 45 | 
             
            - 🔒 Secure command execution with strict validation
         | 
| 48 | 
            -
            - ⚙️ Configurable command and flag whitelisting
         | 
| 49 | 
            -
            - 🛡️ Path traversal prevention
         | 
| 46 | 
            +
            - ⚙️ Configurable command and flag whitelisting with 'all' option
         | 
| 47 | 
            +
            - 🛡️ Path traversal prevention and validation
         | 
| 50 48 | 
             
            - 🚫 Shell operator injection protection
         | 
| 51 49 | 
             
            - ⏱️ Execution timeouts and length limits
         | 
| 52 50 | 
             
            - 📝 Detailed error reporting
         | 
| 53 51 | 
             
            - 🔄 Async operation support
         | 
| 52 | 
            +
            - 🎯 Working directory restriction and validation
         | 
| 54 53 |  | 
| 55 54 | 
             
            ## Configuration
         | 
| 56 55 |  | 
| 57 56 | 
             
            Configure the server using environment variables:
         | 
| 58 57 |  | 
| 59 | 
            -
            | Variable             | Description | 
| 60 | 
            -
             | 
| 61 | 
            -
            | `ALLOWED_DIR` | 
| 62 | 
            -
            | `ALLOWED_COMMANDS` | 
| 63 | 
            -
            | `ALLOWED_FLAGS` | 
| 64 | 
            -
            | `MAX_COMMAND_LENGTH | 
| 65 | 
            -
            | `COMMAND_TIMEOUT` | 
| 58 | 
            +
            | Variable             | Description                                          | Default            |
         | 
| 59 | 
            +
            |---------------------|------------------------------------------------------|-------------------|
         | 
| 60 | 
            +
            | `ALLOWED_DIR`       | Base directory for command execution (Required)      | None (Required)   |
         | 
| 61 | 
            +
            | `ALLOWED_COMMANDS`  | Comma-separated list of allowed commands or 'all'    | `ls,cat,pwd`      |
         | 
| 62 | 
            +
            | `ALLOWED_FLAGS`     | Comma-separated list of allowed flags or 'all'       | `-l,-a,--help`    |
         | 
| 63 | 
            +
            | `MAX_COMMAND_LENGTH`| Maximum command string length                        | `1024`            |
         | 
| 64 | 
            +
            | `COMMAND_TIMEOUT`   | Command execution timeout (seconds)                  | `30`              |
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            Note: Setting `ALLOWED_COMMANDS` or `ALLOWED_FLAGS` to 'all' will allow any command or flag respectively.
         | 
| 66 67 |  | 
| 67 68 | 
             
            ## Installation
         | 
| 68 69 |  | 
| @@ -79,19 +80,28 @@ npx @smithery/cli install cli-mcp-server --client claude | |
| 79 80 | 
             
            Executes whitelisted CLI commands within allowed directories.
         | 
| 80 81 |  | 
| 81 82 | 
             
            **Input Schema:**
         | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             {
         | 
| 83 | 
            +
            ```json
         | 
| 84 | 
            +
            {
         | 
| 85 85 | 
             
              "command": {
         | 
| 86 86 | 
             
                "type": "string",
         | 
| 87 | 
            -
                "description": " | 
| 87 | 
            +
                "description": "Single command to execute (e.g., 'ls -l' or 'cat file.txt')"
         | 
| 88 88 | 
             
              }
         | 
| 89 89 | 
             
            }
         | 
| 90 | 
            -
             | 
| 90 | 
            +
            ```
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            **Security Notes:**
         | 
| 93 | 
            +
            - Shell operators (&&, |, >, >>) are not supported
         | 
| 94 | 
            +
            - Commands must be whitelisted unless ALLOWED_COMMANDS='all'
         | 
| 95 | 
            +
            - Flags must be whitelisted unless ALLOWED_FLAGS='all'
         | 
| 96 | 
            +
            - All paths are validated to be within ALLOWED_DIR
         | 
| 91 97 |  | 
| 92 98 | 
             
            ### show_security_rules
         | 
| 93 99 |  | 
| 94 | 
            -
            Displays current security configuration and restrictions | 
| 100 | 
            +
            Displays current security configuration and restrictions, including:
         | 
| 101 | 
            +
            - Working directory
         | 
| 102 | 
            +
            - Allowed commands
         | 
| 103 | 
            +
            - Allowed flags
         | 
| 104 | 
            +
            - Security limits (max command length and timeout)
         | 
| 95 105 |  | 
| 96 106 | 
             
            ## Usage with Claude Desktop
         | 
| 97 107 |  | 
| @@ -99,7 +109,7 @@ Add to your `~/Library/Application\ Support/Claude/claude_desktop_config.json`: | |
| 99 109 |  | 
| 100 110 | 
             
            > Development/Unpublished Servers Configuration
         | 
| 101 111 |  | 
| 102 | 
            -
             | 
| 112 | 
            +
            ```json
         | 
| 103 113 | 
             
            {
         | 
| 104 114 | 
             
              "mcpServers": {
         | 
| 105 115 | 
             
                "cli-mcp-server": {
         | 
| @@ -120,7 +130,7 @@ Add to your `~/Library/Application\ Support/Claude/claude_desktop_config.json`: | |
| 120 130 | 
             
                }
         | 
| 121 131 | 
             
              }
         | 
| 122 132 | 
             
            }
         | 
| 123 | 
            -
             | 
| 133 | 
            +
            ```
         | 
| 124 134 |  | 
| 125 135 | 
             
            > Published Servers Configuration
         | 
| 126 136 |  | 
| @@ -147,23 +157,25 @@ Add to your `~/Library/Application\ Support/Claude/claude_desktop_config.json`: | |
| 147 157 |  | 
| 148 158 | 
             
            ## Security Features
         | 
| 149 159 |  | 
| 150 | 
            -
            - ✅ Command whitelist enforcement
         | 
| 151 | 
            -
            - ✅ Flag validation
         | 
| 152 | 
            -
            - ✅ Path traversal prevention
         | 
| 160 | 
            +
            - ✅ Command whitelist enforcement with 'all' option
         | 
| 161 | 
            +
            - ✅ Flag validation with 'all' option
         | 
| 162 | 
            +
            - ✅ Path traversal prevention and normalization
         | 
| 153 163 | 
             
            - ✅ Shell operator blocking
         | 
| 154 164 | 
             
            - ✅ Command length limits
         | 
| 155 165 | 
             
            - ✅ Execution timeouts
         | 
| 156 166 | 
             
            - ✅ Working directory restrictions
         | 
| 167 | 
            +
            - ✅ Symlink resolution and validation
         | 
| 157 168 |  | 
| 158 169 | 
             
            ## Error Handling
         | 
| 159 170 |  | 
| 160 171 | 
             
            The server provides detailed error messages for:
         | 
| 161 172 |  | 
| 162 | 
            -
            - Security violations
         | 
| 163 | 
            -
            - Command timeouts
         | 
| 173 | 
            +
            - Security violations (CommandSecurityError)
         | 
| 174 | 
            +
            - Command timeouts (CommandTimeoutError)
         | 
| 164 175 | 
             
            - Invalid command formats
         | 
| 165 176 | 
             
            - Path security violations
         | 
| 166 | 
            -
            - Execution failures
         | 
| 177 | 
            +
            - Execution failures (CommandExecutionError)
         | 
| 178 | 
            +
            - General command errors (CommandError)
         | 
| 167 179 |  | 
| 168 180 | 
             
            ## Development
         | 
| 169 181 |  | 
| @@ -172,8 +184,6 @@ The server provides detailed error messages for: | |
| 172 184 | 
             
            - Python 3.10+
         | 
| 173 185 | 
             
            - MCP protocol library
         | 
| 174 186 |  | 
| 175 | 
            -
            ## Development
         | 
| 176 | 
            -
             | 
| 177 187 | 
             
            ### Building and Publishing
         | 
| 178 188 |  | 
| 179 189 | 
             
            To prepare the package for distribution:
         | 
| @@ -213,6 +223,6 @@ Upon launching, the Inspector will display a URL that you can access in your bro | |
| 213 223 |  | 
| 214 224 | 
             
            This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
         | 
| 215 225 |  | 
| 216 | 
            -
             | 
| 226 | 
            +
            ---
         | 
| 217 227 |  | 
| 218 | 
            -
            For more information or support, please open an issue on the project repository.
         | 
| 228 | 
            +
            For more information or support, please open an issue on the project repository.
         | 
| @@ -1,10 +1,10 @@ | |
| 1 1 | 
             
            [project]
         | 
| 2 2 | 
             
            name = "cli-mcp-server"
         | 
| 3 | 
            -
            version = "0.2. | 
| 3 | 
            +
            version = "0.2.3"
         | 
| 4 4 | 
             
            description = "Command line interface for MCP clients with secure execution and customizable security policies"
         | 
| 5 5 | 
             
            readme = "README.md"
         | 
| 6 6 | 
             
            requires-python = ">=3.10"
         | 
| 7 | 
            -
            dependencies = ["mcp>=1. | 
| 7 | 
            +
            dependencies = ["mcp>=1.6.0"]
         | 
| 8 8 | 
             
            authors = [
         | 
| 9 9 | 
             
                { name = "Mladen", email = "fangs-lever6n@icloud.com" },
         | 
| 10 10 | 
             
            ]
         | 
| @@ -68,10 +68,14 @@ class CommandExecutor: | |
| 68 68 | 
             
                            real_path = os.path.abspath(os.path.realpath(path))
         | 
| 69 69 | 
             
                        else:
         | 
| 70 70 | 
             
                            # If relative path, combine with allowed_dir first
         | 
| 71 | 
            -
                            real_path = os.path.abspath( | 
| 71 | 
            +
                            real_path = os.path.abspath(
         | 
| 72 | 
            +
                                os.path.realpath(os.path.join(self.allowed_dir, path))
         | 
| 73 | 
            +
                            )
         | 
| 72 74 |  | 
| 73 75 | 
             
                        if not self._is_path_safe(real_path):
         | 
| 74 | 
            -
                            raise CommandSecurityError( | 
| 76 | 
            +
                            raise CommandSecurityError(
         | 
| 77 | 
            +
                                f"Path '{path}' is outside of allowed directory: {self.allowed_dir}"
         | 
| 78 | 
            +
                            )
         | 
| 75 79 |  | 
| 76 80 | 
             
                        return real_path
         | 
| 77 81 | 
             
                    except CommandSecurityError:
         | 
| @@ -102,7 +106,9 @@ class CommandExecutor: | |
| 102 106 | 
             
                    shell_operators = ["&&", "||", "|", ">", ">>", "<", "<<", ";"]
         | 
| 103 107 | 
             
                    for operator in shell_operators:
         | 
| 104 108 | 
             
                        if operator in command_string:
         | 
| 105 | 
            -
                            raise CommandSecurityError( | 
| 109 | 
            +
                            raise CommandSecurityError(
         | 
| 110 | 
            +
                                f"Shell operator '{operator}' is not supported"
         | 
| 111 | 
            +
                            )
         | 
| 106 112 |  | 
| 107 113 | 
             
                    try:
         | 
| 108 114 | 
             
                        parts = shlex.split(command_string)
         | 
| @@ -112,20 +118,31 @@ class CommandExecutor: | |
| 112 118 | 
             
                        command, args = parts[0], parts[1:]
         | 
| 113 119 |  | 
| 114 120 | 
             
                        # Validate command if not in allow-all mode
         | 
| 115 | 
            -
                        if  | 
| 121 | 
            +
                        if (
         | 
| 122 | 
            +
                            not self.security_config.allow_all_commands
         | 
| 123 | 
            +
                            and command not in self.security_config.allowed_commands
         | 
| 124 | 
            +
                        ):
         | 
| 116 125 | 
             
                            raise CommandSecurityError(f"Command '{command}' is not allowed")
         | 
| 117 126 |  | 
| 118 127 | 
             
                        # Process and validate arguments
         | 
| 119 128 | 
             
                        validated_args = []
         | 
| 120 129 | 
             
                        for arg in args:
         | 
| 121 130 | 
             
                            if arg.startswith("-"):
         | 
| 122 | 
            -
                                if  | 
| 131 | 
            +
                                if (
         | 
| 132 | 
            +
                                    not self.security_config.allow_all_flags
         | 
| 133 | 
            +
                                    and arg not in self.security_config.allowed_flags
         | 
| 134 | 
            +
                                ):
         | 
| 123 135 | 
             
                                    raise CommandSecurityError(f"Flag '{arg}' is not allowed")
         | 
| 124 136 | 
             
                                validated_args.append(arg)
         | 
| 125 137 | 
             
                                continue
         | 
| 126 138 |  | 
| 127 139 | 
             
                            # For any path-like argument, validate it
         | 
| 128 140 | 
             
                            if "/" in arg or "\\" in arg or os.path.isabs(arg) or arg == ".":
         | 
| 141 | 
            +
                                if self._is_url_path(arg):
         | 
| 142 | 
            +
                                    # If it's a URL, we don't need to normalize it
         | 
| 143 | 
            +
                                    validated_args.append(arg)
         | 
| 144 | 
            +
                                    continue
         | 
| 145 | 
            +
             | 
| 129 146 | 
             
                                normalized_path = self._normalize_path(arg)
         | 
| 130 147 | 
             
                                validated_args.append(normalized_path)
         | 
| 131 148 | 
             
                            else:
         | 
| @@ -137,6 +154,19 @@ class CommandExecutor: | |
| 137 154 | 
             
                    except ValueError as e:
         | 
| 138 155 | 
             
                        raise CommandSecurityError(f"Invalid command format: {str(e)}")
         | 
| 139 156 |  | 
| 157 | 
            +
                def _is_url_path(self, path: str) -> bool:
         | 
| 158 | 
            +
                    """
         | 
| 159 | 
            +
                    Checks if a given path is a URL of type http or https.
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                    Args:
         | 
| 162 | 
            +
                        path (str): The path to check.
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                    Returns:
         | 
| 165 | 
            +
                        bool: True if the path is a URL, False otherwise.
         | 
| 166 | 
            +
                    """
         | 
| 167 | 
            +
                    url_pattern = re.compile(r"^https?://")
         | 
| 168 | 
            +
                    return bool(url_pattern.match(path))
         | 
| 169 | 
            +
             | 
| 140 170 | 
             
                def _is_path_safe(self, path: str) -> bool:
         | 
| 141 171 | 
             
                    """
         | 
| 142 172 | 
             
                    Checks if a given path is safe to access within allowed directory boundaries.
         | 
| @@ -190,7 +220,9 @@ class CommandExecutor: | |
| 190 220 | 
             
                        - Captures both stdout and stderr
         | 
| 191 221 | 
             
                    """
         | 
| 192 222 | 
             
                    if len(command_string) > self.security_config.max_command_length:
         | 
| 193 | 
            -
                        raise CommandSecurityError( | 
| 223 | 
            +
                        raise CommandSecurityError(
         | 
| 224 | 
            +
                            f"Command exceeds maximum length of {self.security_config.max_command_length}"
         | 
| 225 | 
            +
                        )
         | 
| 194 226 |  | 
| 195 227 | 
             
                    try:
         | 
| 196 228 | 
             
                        command, args = self.validate_command(command_string)
         | 
| @@ -204,7 +236,9 @@ class CommandExecutor: | |
| 204 236 | 
             
                            cwd=self.allowed_dir,
         | 
| 205 237 | 
             
                        )
         | 
| 206 238 | 
             
                    except subprocess.TimeoutExpired:
         | 
| 207 | 
            -
                        raise CommandTimeoutError( | 
| 239 | 
            +
                        raise CommandTimeoutError(
         | 
| 240 | 
            +
                            f"Command timed out after {self.security_config.command_timeout} seconds"
         | 
| 241 | 
            +
                        )
         | 
| 208 242 | 
             
                    except CommandError:
         | 
| 209 243 | 
             
                        raise
         | 
| 210 244 | 
             
                    except Exception as e:
         | 
| @@ -237,12 +271,14 @@ def load_security_config() -> SecurityConfig: | |
| 237 271 | 
             
                """
         | 
| 238 272 | 
             
                allowed_commands = os.getenv("ALLOWED_COMMANDS", "ls,cat,pwd")
         | 
| 239 273 | 
             
                allowed_flags = os.getenv("ALLOWED_FLAGS", "-l,-a,--help")
         | 
| 240 | 
            -
             | 
| 241 | 
            -
                allow_all_commands = allowed_commands.lower() ==  | 
| 242 | 
            -
                allow_all_flags = allowed_flags.lower() ==  | 
| 243 | 
            -
             | 
| 274 | 
            +
             | 
| 275 | 
            +
                allow_all_commands = allowed_commands.lower() == "all"
         | 
| 276 | 
            +
                allow_all_flags = allowed_flags.lower() == "all"
         | 
| 277 | 
            +
             | 
| 244 278 | 
             
                return SecurityConfig(
         | 
| 245 | 
            -
                    allowed_commands= | 
| 279 | 
            +
                    allowed_commands=(
         | 
| 280 | 
            +
                        set() if allow_all_commands else set(allowed_commands.split(","))
         | 
| 281 | 
            +
                    ),
         | 
| 246 282 | 
             
                    allowed_flags=set() if allow_all_flags else set(allowed_flags.split(",")),
         | 
| 247 283 | 
             
                    max_command_length=int(os.getenv("MAX_COMMAND_LENGTH", "1024")),
         | 
| 248 284 | 
             
                    command_timeout=int(os.getenv("COMMAND_TIMEOUT", "30")),
         | 
| @@ -251,14 +287,24 @@ def load_security_config() -> SecurityConfig: | |
| 251 287 | 
             
                )
         | 
| 252 288 |  | 
| 253 289 |  | 
| 254 | 
            -
            executor = CommandExecutor( | 
| 290 | 
            +
            executor = CommandExecutor(
         | 
| 291 | 
            +
                allowed_dir=os.getenv("ALLOWED_DIR", ""), security_config=load_security_config()
         | 
| 292 | 
            +
            )
         | 
| 255 293 |  | 
| 256 294 |  | 
| 257 295 | 
             
            @server.list_tools()
         | 
| 258 296 | 
             
            async def handle_list_tools() -> list[types.Tool]:
         | 
| 259 | 
            -
                commands_desc =  | 
| 260 | 
            -
             | 
| 261 | 
            -
             | 
| 297 | 
            +
                commands_desc = (
         | 
| 298 | 
            +
                    "all commands"
         | 
| 299 | 
            +
                    if executor.security_config.allow_all_commands
         | 
| 300 | 
            +
                    else ", ".join(executor.security_config.allowed_commands)
         | 
| 301 | 
            +
                )
         | 
| 302 | 
            +
                flags_desc = (
         | 
| 303 | 
            +
                    "all flags"
         | 
| 304 | 
            +
                    if executor.security_config.allow_all_flags
         | 
| 305 | 
            +
                    else ", ".join(executor.security_config.allowed_flags)
         | 
| 306 | 
            +
                )
         | 
| 307 | 
            +
             | 
| 262 308 | 
             
                return [
         | 
| 263 309 | 
             
                    types.Tool(
         | 
| 264 310 | 
             
                        name="run_command",
         | 
| @@ -281,7 +327,9 @@ async def handle_list_tools() -> list[types.Tool]: | |
| 281 327 | 
             
                    ),
         | 
| 282 328 | 
             
                    types.Tool(
         | 
| 283 329 | 
             
                        name="show_security_rules",
         | 
| 284 | 
            -
                        description=( | 
| 330 | 
            +
                        description=(
         | 
| 331 | 
            +
                            "Show what commands and operations are allowed in this environment.\n"
         | 
| 332 | 
            +
                        ),
         | 
| 285 333 | 
             
                        inputSchema={
         | 
| 286 334 | 
             
                            "type": "object",
         | 
| 287 335 | 
             
                            "properties": {},
         | 
| @@ -291,10 +339,14 @@ async def handle_list_tools() -> list[types.Tool]: | |
| 291 339 |  | 
| 292 340 |  | 
| 293 341 | 
             
            @server.call_tool()
         | 
| 294 | 
            -
            async def handle_call_tool( | 
| 342 | 
            +
            async def handle_call_tool(
         | 
| 343 | 
            +
                name: str, arguments: Optional[Dict[str, Any]]
         | 
| 344 | 
            +
            ) -> List[types.TextContent]:
         | 
| 295 345 | 
             
                if name == "run_command":
         | 
| 296 346 | 
             
                    if not arguments or "command" not in arguments:
         | 
| 297 | 
            -
                        return [ | 
| 347 | 
            +
                        return [
         | 
| 348 | 
            +
                            types.TextContent(type="text", text="No command provided", error=True)
         | 
| 349 | 
            +
                        ]
         | 
| 298 350 |  | 
| 299 351 | 
             
                    try:
         | 
| 300 352 | 
             
                        result = executor.execute(arguments["command"])
         | 
| @@ -303,7 +355,9 @@ async def handle_call_tool(name: str, arguments: Optional[Dict[str, Any]]) -> Li | |
| 303 355 | 
             
                        if result.stdout:
         | 
| 304 356 | 
             
                            response.append(types.TextContent(type="text", text=result.stdout))
         | 
| 305 357 | 
             
                        if result.stderr:
         | 
| 306 | 
            -
                            response.append( | 
| 358 | 
            +
                            response.append(
         | 
| 359 | 
            +
                                types.TextContent(type="text", text=result.stderr, error=True)
         | 
| 360 | 
            +
                            )
         | 
| 307 361 |  | 
| 308 362 | 
             
                        response.append(
         | 
| 309 363 | 
             
                            types.TextContent(
         | 
| @@ -315,7 +369,11 @@ async def handle_call_tool(name: str, arguments: Optional[Dict[str, Any]]) -> Li | |
| 315 369 | 
             
                        return response
         | 
| 316 370 |  | 
| 317 371 | 
             
                    except CommandSecurityError as e:
         | 
| 318 | 
            -
                        return [ | 
| 372 | 
            +
                        return [
         | 
| 373 | 
            +
                            types.TextContent(
         | 
| 374 | 
            +
                                type="text", text=f"Security violation: {str(e)}", error=True
         | 
| 375 | 
            +
                            )
         | 
| 376 | 
            +
                        ]
         | 
| 319 377 | 
             
                    except subprocess.TimeoutExpired:
         | 
| 320 378 | 
             
                        return [
         | 
| 321 379 | 
             
                            types.TextContent(
         | 
| @@ -328,9 +386,17 @@ async def handle_call_tool(name: str, arguments: Optional[Dict[str, Any]]) -> Li | |
| 328 386 | 
             
                        return [types.TextContent(type="text", text=f"Error: {str(e)}", error=True)]
         | 
| 329 387 |  | 
| 330 388 | 
             
                elif name == "show_security_rules":
         | 
| 331 | 
            -
                    commands_desc =  | 
| 332 | 
            -
             | 
| 333 | 
            -
             | 
| 389 | 
            +
                    commands_desc = (
         | 
| 390 | 
            +
                        "All commands allowed"
         | 
| 391 | 
            +
                        if executor.security_config.allow_all_commands
         | 
| 392 | 
            +
                        else ", ".join(sorted(executor.security_config.allowed_commands))
         | 
| 393 | 
            +
                    )
         | 
| 394 | 
            +
                    flags_desc = (
         | 
| 395 | 
            +
                        "All flags allowed"
         | 
| 396 | 
            +
                        if executor.security_config.allow_all_flags
         | 
| 397 | 
            +
                        else ", ".join(sorted(executor.security_config.allowed_flags))
         | 
| 398 | 
            +
                    )
         | 
| 399 | 
            +
             | 
| 334 400 | 
             
                    security_info = (
         | 
| 335 401 | 
             
                        "Security Configuration:\n"
         | 
| 336 402 | 
             
                        f"==================\n"
         | 
| @@ -364,4 +430,4 @@ async def main(): | |
| 364 430 | 
             
                                experimental_capabilities={},
         | 
| 365 431 | 
             
                            ),
         | 
| 366 432 | 
             
                        ),
         | 
| 367 | 
            -
                    )
         | 
| 433 | 
            +
                    )
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            # tests package
         |