Files
python-mcp/tools/venv.py
AuroraCrimsonRose e471f9bc54 Added many tools
2026-06-03 06:01:06 -05:00

188 lines
5.3 KiB
Python

from __future__ import annotations
from typing import Any
import subprocess
import sys
from pathlib import Path
from core.tools.base import BaseTool, ToolContext
from core.tools.registry import registry
from core.safety import safety
from core.events import bus
class VenvTool(BaseTool):
name = "venv"
description = "Python virtual environment management (create, install, run, list packages)"
# =========================================================
# EXECUTE ROUTER (ONLY ENTRYPOINT WITH ctx)
# =========================================================
def execute(self, payload: dict[str, Any], ctx: ToolContext):
action = str(payload.get("action", "")).strip()
bus.log(
"VENV",
"venv_execute",
"INFO",
{"action": action}
)
match action:
case "create":
return self.create_venv(payload)
case "install":
return self.install_package(payload)
case "run":
return self.run_python(payload)
case "list":
return self.list_packages(payload)
case _:
raise ValueError(f"Unknown venv action: {action}")
# =========================================================
# PATH HELPERS
# =========================================================
def _venv_path(self, path: str) -> Path:
return safety.validate_path(path)
def _python_bin(self, venv: Path) -> Path:
"""Cross-platform python binary resolution."""
if (venv / "bin").exists():
return venv / "bin" / "python"
return venv / "Scripts" / "python.exe"
def _pip_bin(self, venv: Path) -> Path:
"""Cross-platform pip binary resolution."""
if (venv / "bin").exists():
return venv / "bin" / "pip"
return venv / "Scripts" / "pip.exe"
# =========================================================
# CREATE VENV
# =========================================================
def create_venv(self, payload: dict[str, Any]):
path = payload.get("path")
if not isinstance(path, str):
raise ValueError("path must be string")
venv_path = self._venv_path(path)
if venv_path.exists():
return {
"status": "exists",
"path": str(venv_path)
}
subprocess.run(
[sys.executable, "-m", "venv", str(venv_path)],
check=True
)
return {
"status": "created",
"path": str(venv_path)
}
# =========================================================
# INSTALL PACKAGE
# =========================================================
def install_package(self, payload: dict[str, Any]):
path = payload.get("path")
package = payload.get("package")
if not isinstance(path, str):
raise ValueError("path must be string")
if not isinstance(package, str):
raise ValueError("package must be string")
venv_path = self._venv_path(path)
pip = self._pip_bin(venv_path)
if not pip.exists():
raise ValueError("pip not found in virtual environment")
result = subprocess.run(
[str(pip), "install", package],
capture_output=True,
text=True
)
return {
"status": "ok" if result.returncode == 0 else "error",
"stdout": result.stdout,
"stderr": result.stderr
}
# =========================================================
# RUN PYTHON CODE
# =========================================================
def run_python(self, payload: dict[str, Any]):
path = payload.get("path")
code = payload.get("code")
if not isinstance(path, str):
raise ValueError("path must be string")
if not isinstance(code, str):
raise ValueError("code must be string")
venv_path = self._venv_path(path)
python_bin = self._python_bin(venv_path)
if not python_bin.exists():
raise ValueError("python executable not found in venv")
result = subprocess.run(
[str(python_bin), "-c", code],
capture_output=True,
text=True
)
return {
"status": "ok" if result.returncode == 0 else "error",
"stdout": result.stdout,
"stderr": result.stderr
}
# =========================================================
# LIST PACKAGES
# =========================================================
def list_packages(self, payload: dict[str, Any]):
path = payload.get("path")
if not isinstance(path, str):
raise ValueError("path must be string")
venv_path = self._venv_path(path)
pip = self._pip_bin(venv_path)
result = subprocess.run(
[str(pip), "list"],
capture_output=True,
text=True
)
return {
"status": "ok",
"output": result.stdout
}
# =========================================================
# REGISTER TOOL
# =========================================================
registry.register(VenvTool())