feat(shell): add --force-shell option to override shell selection

- Add --force-shell CLI argument with choices [bash, zsh, sh]
- Set HANZO_MCP_FORCE_SHELL environment variable when option is used
- Modified BashTool, ZshTool, and ShellTool to respect the env var
- Allows users to force all shell operations to use a specific shell
  even when AI requests a different one

Example usage in ~/.claude.json:
  "args": ["hanzo-mcp", "--force-shell", "zsh"]

Bumps version to 0.9.19
This commit is contained in:
2025-12-24 22:23:15 -08:00
parent b26ad5e88b
commit 5d85bfcb09
4 changed files with 40 additions and 4 deletions
+12
View File
@@ -199,6 +199,14 @@ def main() -> None:
help="Timeout specifically for AST operations. Supports: 2min, 5m, 120s, 30sec, 1.5h",
)
_ = parser.add_argument(
"--force-shell",
dest="force_shell",
choices=["bash", "zsh", "sh"],
default=None,
help="Force all shell tools (bash, zsh, shell) to use this shell (default: use requested shell)",
)
_ = parser.add_argument(
"--disable-write-tools",
dest="disable_write_tools",
@@ -307,6 +315,10 @@ def main() -> None:
ast_timeout = _parse_timeout_arg(args.ast_timeout)
os.environ["HANZO_MCP_AST_TIMEOUT"] = str(ast_timeout)
# Set force shell environment variable
if hasattr(args, "force_shell") and args.force_shell:
os.environ["HANZO_MCP_FORCE_SHELL"] = args.force_shell
# Cast args attributes to appropriate types to avoid 'Any' warnings
name: str = cast(str, args.name)
install: bool = cast(bool, args.install)
@@ -59,7 +59,15 @@ bash "npm run dev" --cwd ./frontend # Auto-backgrounds if needed"""
@override
def get_interpreter(self) -> str:
"""Get the bash interpreter."""
"""Get the bash interpreter.
Respects HANZO_MCP_FORCE_SHELL environment variable to override.
"""
# Check for forced shell override
force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL")
if force_shell:
return force_shell
if platform.system() == "Windows":
# Try to find bash on Windows (Git Bash, WSL, etc.)
bash_paths = [
@@ -66,7 +66,15 @@ zsh "npm run dev" --cwd ./frontend # Auto-backgrounds if needed"""
@override
def get_interpreter(self) -> str:
"""Get the zsh interpreter path."""
"""Get the zsh interpreter path.
Respects HANZO_MCP_FORCE_SHELL environment variable to override.
"""
# Check for forced shell override
force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL")
if force_shell:
return force_shell
if platform.system() == "Windows":
# Try to find zsh on Windows (WSL, Git Bash, etc.)
zsh_paths = [
@@ -144,7 +152,15 @@ class ShellTool(BaseScriptTool):
self._best_shell = self._detect_best_shell()
def _detect_best_shell(self) -> str:
"""Detect the best available shell."""
"""Detect the best available shell.
Respects HANZO_MCP_FORCE_SHELL environment variable to override.
"""
# Check for forced shell override first
force_shell = os.environ.get("HANZO_MCP_FORCE_SHELL")
if force_shell:
return force_shell
# Check for zsh first
if shutil.which("zsh"):
# Also check if .zshrc exists
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "hanzo-mcp"
version = "0.9.18"
version = "0.9.19"
description = "The Zen of Hanzo MCP: One server to rule them all. The ultimate MCP that orchestrates all others."
readme = "README.md"
requires-python = ">=3.12"