# pyro-mcp `pyro-mcp` runs commands inside ephemeral Firecracker microVMs using curated Linux environments such as `debian:12`. It exposes the same runtime in three public forms: - the `pyro` CLI - the Python SDK via `from pyro_mcp import Pyro` - an MCP server so LLM clients can call VM tools directly ## Start Here - Install: [docs/install.md](docs/install.md) - First run transcript: [docs/first-run.md](docs/first-run.md) - PyPI package: [pypi.org/project/pyro-mcp](https://pypi.org/project/pyro-mcp/) - Host requirements: [docs/host-requirements.md](docs/host-requirements.md) - Integration targets: [docs/integrations.md](docs/integrations.md) - Public contract: [docs/public-contract.md](docs/public-contract.md) - Troubleshooting: [docs/troubleshooting.md](docs/troubleshooting.md) - Changelog: [CHANGELOG.md](CHANGELOG.md) ## Supported Hosts Supported today: - Linux x86_64 - Python 3.12+ - `uv` - `/dev/kvm` Optional for outbound guest networking: - `ip` - `nft` or `iptables` - privilege to create TAP devices and configure NAT Not supported today: - macOS - Windows - Linux hosts without working KVM at `/dev/kvm` If you do not already have `uv`, install it first: ```bash python -m pip install uv ``` Published package path: ```bash uvx --from pyro-mcp pyro doctor uv tool install pyro-mcp ``` Source checkout path: ```bash uv run pyro doctor ``` ## 5-Minute Evaluation Use the package directly without a manual install: ### 1. Check the host ```bash uvx --from pyro-mcp pyro doctor ``` Expected success signals: ```bash Platform: linux-x86_64 Runtime: PASS KVM: exists=yes readable=yes writable=yes Environment cache: /home/you/.cache/pyro-mcp/environments Capabilities: vm_boot=yes guest_exec=yes guest_network=yes Networking: tun=yes ip_forward=yes ``` ### 2. Inspect the catalog and pull the default environment ```bash uvx --from pyro-mcp pyro env list ``` Expected output: ```bash Catalog version: 2.0.0 debian:12 [installed|not installed] Debian 12 environment with Git preinstalled for common agent workflows. debian:12-base [installed|not installed] Minimal Debian 12 environment for shell and core Unix tooling. debian:12-build [installed|not installed] Debian 12 environment with Git and common build tools preinstalled. ``` ```bash uvx --from pyro-mcp pyro env pull debian:12 ``` The first pull downloads an OCI environment from public Docker Hub, requires outbound HTTPS access to `registry-1.docker.io`, and needs local cache space for the guest image. See [docs/host-requirements.md](docs/host-requirements.md) for the full host requirements. ### 3. Run one command in a guest ```bash uvx --from pyro-mcp pyro run debian:12 -- git --version ``` Expected success signals: ```bash [run] environment=debian:12 execution_mode=guest_vsock exit_code=0 duration_ms=... git version ... ``` The guest command output and the `[run] ...` summary are written to different streams, so they may appear in either order in terminals or capture tools. Use `--json` if you need a deterministic structured result. ### 4. Optional demos ```bash uvx --from pyro-mcp pyro demo uvx --from pyro-mcp pyro demo --network ``` If you prefer a fuller copy-pasteable transcript, see [docs/first-run.md](docs/first-run.md). ## Public Interfaces The public user-facing interface is `pyro` and `Pyro`. After the CLI validation path works, you can choose one of three surfaces: - `pyro` for direct CLI usage - `from pyro_mcp import Pyro` for Python orchestration - `pyro mcp serve` for MCP clients Command forms: - published package without install: `uvx --from pyro-mcp pyro ...` - installed package: `pyro ...` - source checkout: `uv run pyro ...` `Makefile` targets are contributor conveniences for this repository and are not the primary product UX. ## Official Environments Current official environments in the shipped catalog: - `debian:12` - `debian:12-base` - `debian:12-build` The package ships the embedded Firecracker runtime and a package-controlled environment catalog. Official environments are pulled as OCI artifacts from public Docker Hub repositories into a local cache on first use or through `pyro env pull`. End users do not need registry credentials to pull or run official environments. The default cache location is `~/.cache/pyro-mcp/environments`; override it with `PYRO_ENVIRONMENT_CACHE_DIR`. ## CLI List available environments: ```bash pyro env list ``` Prefetch one environment: ```bash pyro env pull debian:12 ``` Run one command in an ephemeral VM: ```bash pyro run debian:12 -- git --version ``` Run with outbound internet enabled: ```bash pyro run debian:12 --network -- \ "git clone --depth 1 https://github.com/octocat/Hello-World.git hello-world && git -C hello-world rev-parse --is-inside-work-tree" ``` Show runtime and host diagnostics: ```bash pyro doctor pyro doctor --json ``` `pyro run` defaults to `1 vCPU / 1024 MiB`. It fails closed when guest boot or guest exec is unavailable. Use `--allow-host-compat` only if you explicitly want host execution. Run the MCP server after the CLI path above works: ```bash pyro mcp serve ``` Run the deterministic demo: ```bash pyro demo pyro demo --network ``` Run the Ollama demo: ```bash ollama serve ollama pull llama3.2:3b pyro demo ollama ``` ## Python SDK ```python from pyro_mcp import Pyro pyro = Pyro() result = pyro.run_in_vm( environment="debian:12", command="git --version", timeout_seconds=30, network=False, ) print(result["stdout"]) ``` Lower-level lifecycle control remains available: ```python from pyro_mcp import Pyro pyro = Pyro() created = pyro.create_vm( environment="debian:12", ttl_seconds=600, network=True, ) vm_id = created["vm_id"] pyro.start_vm(vm_id) result = pyro.exec_vm(vm_id, command="git --version", timeout_seconds=30) print(result["stdout"]) ``` Environment management is also available through the SDK: ```python from pyro_mcp import Pyro pyro = Pyro() print(pyro.list_environments()) print(pyro.inspect_environment("debian:12")) ``` ## MCP Tools Primary agent-facing tool: - `vm_run(environment, command, vcpu_count=1, mem_mib=1024, timeout_seconds=30, ttl_seconds=600, network=false, allow_host_compat=false)` Advanced lifecycle tools: - `vm_list_environments()` - `vm_create(environment, vcpu_count=1, mem_mib=1024, ttl_seconds=600, network=false, allow_host_compat=false)` - `vm_start(vm_id)` - `vm_exec(vm_id, command, timeout_seconds=30)` - `vm_stop(vm_id)` - `vm_delete(vm_id)` - `vm_status(vm_id)` - `vm_network_info(vm_id)` - `vm_reap_expired()` ## Integration Examples - Python one-shot SDK example: [examples/python_run.py](examples/python_run.py) - Python lifecycle example: [examples/python_lifecycle.py](examples/python_lifecycle.py) - MCP client config example: [examples/mcp_client_config.md](examples/mcp_client_config.md) - Claude Desktop MCP config: [examples/claude_desktop_mcp_config.json](examples/claude_desktop_mcp_config.json) - Cursor MCP config: [examples/cursor_mcp_config.json](examples/cursor_mcp_config.json) - OpenAI Responses API example: [examples/openai_responses_vm_run.py](examples/openai_responses_vm_run.py) - LangChain wrapper example: [examples/langchain_vm_run.py](examples/langchain_vm_run.py) - Agent-ready `vm_run` example: [examples/agent_vm_run.py](examples/agent_vm_run.py) ## Runtime The package ships an embedded Linux x86_64 runtime payload with: - Firecracker - Jailer - guest agent - runtime manifest and diagnostics No system Firecracker installation is required. `pyro` installs curated environments into a local cache and reports their status through `pyro env inspect` and `pyro doctor`. The public CLI is human-readable by default; add `--json` for structured output. ## Contributor Workflow For work inside this repository: ```bash make help make setup make check make dist-check ``` Contributor runtime source artifacts are still maintained under `src/pyro_mcp/runtime_bundle/` and `runtime_sources/`. Official environment publication is automated through `.github/workflows/publish-environments.yml`. For a local publish against Docker Hub: ```bash export DOCKERHUB_USERNAME='your-dockerhub-username' export DOCKERHUB_TOKEN='your-dockerhub-token' make runtime-materialize make runtime-publish-official-environments-oci ``` `make runtime-publish-environment-oci` auto-exports the OCI layout for the selected environment if it is missing. The publisher accepts either `DOCKERHUB_USERNAME` and `DOCKERHUB_TOKEN` or `OCI_REGISTRY_USERNAME` and `OCI_REGISTRY_PASSWORD`. Docker Hub uploads are chunked by default for large rootfs layers; if you need to tune a slow link, use `PYRO_OCI_UPLOAD_TIMEOUT_SECONDS`, `PYRO_OCI_UPLOAD_CHUNK_SIZE_BYTES`, and `PYRO_OCI_REQUEST_TIMEOUT_SECONDS`. For a local PyPI publish: ```bash export TWINE_PASSWORD='pypi-...' make pypi-publish ``` `make pypi-publish` defaults `TWINE_USERNAME` to `__token__`. Set `PYPI_REPOSITORY_URL=https://test.pypi.org/legacy/` to publish to TestPyPI instead.