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.
@@ -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.1
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.1.0
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
  ![License](https://img.shields.io/badge/license-MIT-blue.svg)
24
23
  ![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)
@@ -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 | Default |
74
- |----------------------|------------------------------------------|--------------------|
75
- | `ALLOWED_DIR` | Base directory for command execution | Required |
76
- | `ALLOWED_COMMANDS` | Comma-separated list of allowed commands | `ls,cat,pwd` |
77
- | `ALLOWED_FLAGS` | Comma-separated list of allowed flags | `-l,-a,--help` |
78
- | `MAX_COMMAND_LENGTH` | Maximum command string length | `1024` |
79
- | `COMMAND_TIMEOUT` | Command execution timeout (seconds) | `30` |
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
- ```json
98
- {
97
+ ```json
98
+ {
99
99
  "command": {
100
100
  "type": "string",
101
- "description": "Command to execute (e.g., 'ls -l' or 'cat file.txt')"
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
- ```json
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
  ![License](https://img.shields.io/badge/license-MIT-blue.svg)
10
9
  ![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)
@@ -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 | Default |
60
- |----------------------|------------------------------------------|--------------------|
61
- | `ALLOWED_DIR` | Base directory for command execution | Required |
62
- | `ALLOWED_COMMANDS` | Comma-separated list of allowed commands | `ls,cat,pwd` |
63
- | `ALLOWED_FLAGS` | Comma-separated list of allowed flags | `-l,-a,--help` |
64
- | `MAX_COMMAND_LENGTH` | Maximum command string length | `1024` |
65
- | `COMMAND_TIMEOUT` | Command execution timeout (seconds) | `30` |
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
- ```json
84
- {
83
+ ```json
84
+ {
85
85
  "command": {
86
86
  "type": "string",
87
- "description": "Command to execute (e.g., 'ls -l' or 'cat file.txt')"
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
- ```json
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.
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://glama.ai/mcp/schemas/server.json",
3
+ "maintainers": [
4
+ "MladenSU"
5
+ ]
6
+ }
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "cli-mcp-server"
3
- version = "0.2.1"
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.1.0"]
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(os.path.realpath(os.path.join(self.allowed_dir, path)))
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(f"Path '{path}' is outside of allowed directory: {self.allowed_dir}")
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(f"Shell operator '{operator}' is not supported")
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 not self.security_config.allow_all_commands and command not in self.security_config.allowed_commands:
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 not self.security_config.allow_all_flags and arg not in self.security_config.allowed_flags:
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(f"Command exceeds maximum length of {self.security_config.max_command_length}")
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(f"Command timed out after {self.security_config.command_timeout} seconds")
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() == 'all'
242
- allow_all_flags = allowed_flags.lower() == 'all'
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=set() if allow_all_commands else set(allowed_commands.split(",")),
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(allowed_dir=os.getenv("ALLOWED_DIR", ""), security_config=load_security_config())
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 = "all commands" if executor.security_config.allow_all_commands else ", ".join(executor.security_config.allowed_commands)
260
- flags_desc = "all flags" if executor.security_config.allow_all_flags else ", ".join(executor.security_config.allowed_flags)
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=("Show what commands and operations are allowed in this environment.\n"),
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(name: str, arguments: Optional[Dict[str, Any]]) -> List[types.TextContent]:
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 [types.TextContent(type="text", text="No command provided", error=True)]
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(types.TextContent(type="text", text=result.stderr, error=True))
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 [types.TextContent(type="text", text=f"Security violation: {str(e)}", error=True)]
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 = "All commands allowed" if executor.security_config.allow_all_commands else ", ".join(sorted(executor.security_config.allowed_commands))
332
- flags_desc = "All flags allowed" if executor.security_config.allow_all_flags else ", ".join(sorted(executor.security_config.allowed_flags))
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