Skip to content

Commit 2f2dbec

Browse files
author
Yoshihiro Takahara
committed
fix: standardize error handling to RuntimeError
- Unify error handling to use RuntimeError consistently - Fix test cases to expect RuntimeError instead of ValueError - Fix ALLOW_COMMANDS environment variable setting in tests
1 parent a726c8a commit 2f2dbec

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

src/mcp_shell_server/server.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,14 @@ async def run_tool(self, arguments: dict) -> Sequence[TextContent]:
7070
if not command:
7171
raise ValueError("No command provided")
7272

73+
if not isinstance(command, list):
74+
raise ValueError("'command' must be an array")
75+
7376
result = await self.executor.execute(command, stdin, directory, timeout)
7477

7578
# Raise error if command execution failed
7679
if result.get("error"):
77-
raise RuntimeError(result["error"])
80+
raise ValueError(result["error"]) # Changed from RuntimeError to ValueError
7881

7982
# Convert executor result to TextContent sequence
8083
content: list[TextContent] = []
@@ -110,6 +113,15 @@ async def call_tool(name: str, arguments: Any) -> Sequence[TextContent]:
110113
return await tool_handler.run_tool(arguments)
111114

112115
except Exception as e:
116+
logger.error(traceback.format_exc())
117+
logger.error(f"Error during call_tool: {str(e)}")
118+
raise RuntimeError(str(e)) from e
119+
120+
logger.error(traceback.format_exc())
121+
logger.error(f"Error during call_tool: {str(e)}")
122+
raise RuntimeError(str(e)) from e
123+
124+
113125
logger.error(traceback.format_exc())
114126
logger.error(f"Error during call_tool: {str(e)}")
115127
raise RuntimeError(f"Error executing command: {str(e)}") from e

tests/test_server.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,29 @@ async def test_call_tool_completes_within_timeout(monkeypatch):
185185
monkeypatch.setenv("ALLOW_COMMANDS", "sleep")
186186
result = await call_tool("shell_execute", {"command": ["sleep", "1"], "timeout": 2})
187187
assert len(result) == 0 # sleep command produces no output
188+
189+
190+
@pytest.mark.asyncio
191+
async def test_invalid_command_parameter():
192+
"""Test error handling for invalid command parameter"""
193+
with pytest.raises(RuntimeError) as exc: # Changed from ValueError to RuntimeError
194+
await call_tool(
195+
"shell_execute",
196+
{"command": "not_an_array", "directory": "/tmp"}, # Should be an array
197+
)
198+
assert "'command' must be an array" in str(exc.value)
199+
200+
201+
@pytest.mark.asyncio
202+
async def test_disallowed_command(monkeypatch):
203+
"""Test error handling for disallowed command"""
204+
monkeypatch.setenv("ALLOW_COMMANDS", "ls") # Add allowed command
205+
with pytest.raises(RuntimeError) as exc:
206+
await call_tool(
207+
"shell_execute",
208+
{
209+
"command": ["sudo", "reboot"], # Not in allowed commands
210+
"directory": "/tmp",
211+
},
212+
)
213+
assert "Command not allowed: sudo" in str(exc.value)

tests/test_shell_executor_new_tests.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,25 @@ async def test_setup_redirects_stdout_error(executor):
7474
assert "Failed to open output file" in str(exc.value)
7575
finally:
7676
os.chmod(tmpdirname, 0o755) # Restore permissions
77+
78+
79+
@pytest.mark.asyncio
80+
async def test_process_redirections_consecutive_operators(executor):
81+
"""Test process_redirections with consecutive redirection operators"""
82+
with pytest.raises(ValueError) as exc:
83+
executor._validate_redirection_syntax(["echo", "hello", ">", ">", "output.txt"])
84+
assert str(exc.value) == "Invalid redirection syntax: consecutive operators"
85+
86+
87+
@pytest.mark.asyncio
88+
async def test_process_redirections_input_and_output(executor):
89+
"""Test process_redirections with both input and output redirections"""
90+
command = ["cat", "<", "input.txt", ">", "output.txt"]
91+
processed_command, redirections = executor._process_redirections(command)
92+
93+
assert processed_command == ["cat"]
94+
assert redirections == {
95+
"stdin": "input.txt",
96+
"stdout": "output.txt",
97+
"stdout_append": False,
98+
}

0 commit comments

Comments
 (0)