run_in_env_impl
Execute shell commands in isolated Nix environments for Python, Node.js, Ruby, R, or Lua with specific dependencies, avoiding system clutter.
Instructions
Run a shell command in a disposable Nix shell for the chosen language.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| language | Yes | ||
| command | Yes | ||
| extra_packages | No | ||
| timeout_seconds | No |
Input Schema (JSON Schema)
{
"properties": {
"command": {
"type": "string"
},
"extra_packages": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null
},
"language": {
"type": "string"
},
"timeout_seconds": {
"default": 120,
"type": "integer"
}
},
"required": [
"language",
"command"
],
"type": "object"
}
Implementation Reference
- mcp_omnienv_nix/server.py:113-120 (handler)The core handler function for the 'run_in_env_impl' tool. It validates extra packages, builds the Nix shell command using helper functions, executes the subprocess with a timeout, and serializes the result (exit code, stdout, stderr) to JSON.def run_in_env_impl( language: str, command: str, extra_packages: list[str] | None = None, timeout_seconds: int = 120 ) -> str: """Run a shell command in a disposable Nix shell for the chosen language.""" extras = _validate_packages(extra_packages or []) cmd = _nix_shell_command(language.lower(), extras, command) result = _run(cmd, timeout=timeout_seconds) return json.dumps(result, indent=2)
- mcp_omnienv_nix/server.py:123-124 (registration)Registers the run_in_env_impl function as an MCP tool under the name 'run_in_env' using the FastMCP decorator.# Expose as MCP tool while keeping a plain callable for tests and reuse. run_in_env = mcp.tool()(run_in_env_impl)
- mcp_omnienv_nix/server.py:47-85 (helper)Helper function that constructs the full 'nix shell' command list based on the language, extra packages, and command. Handles different Nix expressions for each supported language (python, ruby, r, lua).def _nix_shell_command(lang: str, packages: list[str], command: str) -> list[str]: if lang not in SUPPORTED_LANGUAGES: raise ValueError(f"Unsupported language '{lang}'. Supported: {', '.join(SUPPORTED_LANGUAGES)}") profile = SUPPORTED_LANGUAGES[lang] # Import nixpkgs via flake to avoid relying on NIX_PATH. pkgs_expr = 'import (builtins.getFlake "nixpkgs") {}' if lang == "python": extra_expr = " ".join([f"ps.{p}" for p in packages]) expr = ( f"let pkgs = {pkgs_expr}; in pkgs.{profile.base_packages[0]}.withPackages (ps: [ {extra_expr} ])" ) elif lang == "ruby": extras = " ".join([f"ps.{p}" for p in packages]) expr = f"let pkgs = {pkgs_expr}; in pkgs.{profile.base_packages[0]}.withPackages (ps: [ {extras} ])" elif lang == "r": extras = " ".join([f"ps.{p}" for p in packages]) expr = f"let pkgs = {pkgs_expr}; in pkgs.rWrapper.override {{ packages = ps: [ {extras} ]; }}" elif lang == "lua": extras = " ".join([f"ps.{p}" for p in packages]) expr = f"let pkgs = {pkgs_expr}; in pkgs.lua5_4.withPackages (ps: [ {extras} ])" else: # Should not happen because of the guard above. raise ValueError(f"Unsupported language '{lang}'") return [ "nix", "shell", "--extra-experimental-features", "nix-command flakes", "--impure", "--expr", expr, "--command", "bash", "-lc", command, ]
- mcp_omnienv_nix/server.py:35-44 (helper)Helper function to validate and clean extra package names using a regex pattern.def _validate_packages(pkgs: Iterable[str]) -> list[str]: validated: list[str] = [] for pkg in pkgs: pkg = pkg.strip() if not pkg: continue if not _PACKAGE_RE.match(pkg): raise ValueError(f"Invalid package name: {pkg}") validated.append(pkg) return validated
- mcp_omnienv_nix/server.py:88-100 (helper)Helper function to execute the Nix shell command as a subprocess with timeout and capture stdout, stderr, and exit code.def _run(cmd: list[str], timeout: int = 120) -> dict[str, str | int]: proc = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=timeout, ) return { "exit_code": proc.returncode, "stdout": proc.stdout, "stderr": proc.stderr, }