Skip to content

Commit 16d272b

Browse files
author
Yoshihiro Takahara
committed
fix: error handling and remove warnings
- Unify error handling in server.py - Add proper cleanup for async tests - Add pytest warning filters - Move asyncio import inside main function - Add __main__ guard in __init__.py
1 parent a6a3ee6 commit 16d272b

File tree

4 files changed

+28
-13
lines changed

4 files changed

+28
-13
lines changed

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ asyncio_mode = "strict"
4141
testpaths = "tests"
4242
# Set default event loop scope for async tests
4343
asyncio_default_fixture_loop_scope = "function"
44+
filterwarnings = [
45+
"ignore::RuntimeWarning:selectors:",
46+
"ignore::pytest.PytestUnhandledCoroutineWarning:",
47+
]
4448

4549
[tool.ruff]
4650
lint.select = [

src/mcp_shell_server/__init__.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
"""MCP Shell Server Package."""
22

3-
import asyncio
4-
53
from . import server
64

5+
__version__ = "0.1.0"
6+
__all__ = ["main", "server"]
7+
78

89
def main():
910
"""Main entry point for the package."""
11+
import asyncio
12+
1013
asyncio.run(server.main())
1114

1215

13-
# Optionally expose other important items at package level
14-
__all__ = ["main", "server"]
15-
__version__ = "0.1.0"
16+
if __name__ == "__main__":
17+
main()

src/mcp_shell_server/server.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import asyncio
12
import logging
23
import traceback
34
from collections.abc import Sequence
@@ -56,15 +57,15 @@ def get_tool_description(self) -> Tool:
5657
"minimum": 0,
5758
},
5859
},
59-
"required": ["command"],
60+
"required": ["command", "directory"],
6061
},
6162
)
6263

6364
async def run_tool(self, arguments: dict) -> Sequence[TextContent]:
6465
"""Execute the shell command with the given arguments"""
6566
command = arguments.get("command", [])
6667
stdin = arguments.get("stdin")
67-
directory = arguments.get("directory")
68+
directory = arguments.get("directory", "/tmp") # default to /tmp for safety
6869
timeout = arguments.get("timeout")
6970

7071
if not command:
@@ -73,15 +74,21 @@ async def run_tool(self, arguments: dict) -> Sequence[TextContent]:
7374
if not isinstance(command, list):
7475
raise ValueError("'command' must be an array")
7576

76-
result = await self.executor.execute(command, stdin, directory, timeout)
77+
# Make sure directory exists
78+
if not directory:
79+
raise ValueError("Directory is required")
7780

78-
# Raise error if command execution failed
79-
if result.get("error"):
80-
raise ValueError(result["error"]) # Changed from RuntimeError to ValueError
81+
try:
82+
result = await self.executor.execute(command, directory, stdin, timeout)
83+
except asyncio.TimeoutError as e:
84+
raise ValueError(f"Command timed out after {timeout} seconds") from e
8185

8286
# Convert executor result to TextContent sequence
8387
content: list[TextContent] = []
8488

89+
if result.get("error"):
90+
raise ValueError(result["error"])
91+
8592
if result.get("stdout"):
8693
content.append(TextContent(type="text", text=result["stdout"]))
8794
if result.get("stderr"):
@@ -114,7 +121,6 @@ async def call_tool(name: str, arguments: Any) -> Sequence[TextContent]:
114121

115122
except Exception as e:
116123
logger.error(traceback.format_exc())
117-
logger.error(f"Error during call_tool: {str(e)}")
118124
raise RuntimeError(f"Error executing command: {str(e)}") from e
119125

120126

tests/test_init.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@ def test_main(mocker):
1717
# The first argument of the call should be a coroutine object
1818
args = mock_run.call_args[0]
1919
assert len(args) == 1
20-
assert asyncio.iscoroutine(args[0])
20+
coro = args[0]
21+
assert asyncio.iscoroutine(coro)
22+
# Clean up the coroutine
23+
coro.close()

0 commit comments

Comments
 (0)