8 KiB
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
pyroCLI - 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
- First run transcript: docs/first-run.md
- Host requirements: docs/host-requirements.md
- Integration targets: docs/integrations.md
- Public contract: docs/public-contract.md
- Troubleshooting: docs/troubleshooting.md
- Changelog: CHANGELOG.md
Supported Hosts
Supported today:
- Linux x86_64
- Python 3.12+
uv/dev/kvm
Optional for outbound guest networking:
ipnftoriptables- 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:
python -m pip install uv
5-Minute Evaluation
Use the package directly without a manual install:
1. Check the host
uvx --from pyro-mcp pyro doctor
Expected success signals:
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
uvx --from pyro-mcp pyro env list
Expected output:
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.
uvx --from pyro-mcp pyro env pull debian:12
3. Run one command in a guest
uvx --from pyro-mcp pyro run debian:12 -- git --version
Expected success signals:
git version ...
[run] environment=debian:12 execution_mode=guest_vsock exit_code=0 duration_ms=...
4. Optional demos
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.
Public Interfaces
The public user-facing interface is pyro and Pyro. After the CLI validation path works, you can choose one of three surfaces:
pyrofor direct CLI usagefrom pyro_mcp import Pyrofor Python orchestrationpyro mcp servefor MCP clients
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:12debian:12-basedebian: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:
pyro env list
Prefetch one environment:
pyro env pull debian:12
Run one command in an ephemeral VM:
pyro run debian:12 -- git --version
Run with outbound internet enabled:
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:
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:
pyro mcp serve
Run the deterministic demo:
pyro demo
pyro demo --network
Run the Ollama demo:
ollama serve
ollama pull llama3.2:3b
pyro demo ollama
Python SDK
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:
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:
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
- Python lifecycle example: examples/python_lifecycle.py
- MCP client config example: examples/mcp_client_config.md
- Claude Desktop MCP config: examples/claude_desktop_mcp_config.json
- Cursor MCP config: examples/cursor_mcp_config.json
- OpenAI Responses API example: examples/openai_responses_vm_run.py
- LangChain wrapper example: examples/langchain_vm_run.py
- Agent-ready
vm_runexample: 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:
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:
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:
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.