pyro-mcp/docs/install.md
Thales Maciel c00c699a9f Make workspace-core the default MCP profile
Flip bare pyro mcp serve, create_server(), and Pyro.create_server() to default to workspace-core in 4.0.0 while keeping workspace-full as the explicit advanced opt-in surface.

Rewrite the MCP-facing docs and host-specific examples around the bare default command, update package and catalog compatibility to 4.x, and move the public-contract wording from 3.x compatibility guidance to the new stable default.

Adjust the server, API, and contract tests so bare server creation now asserts the workspace-core tool set, while explicit workspace-full coverage continues to prove shells, services, snapshots, and disk tools remain available.

Validation: uv lock; .venv/bin/pytest --no-cov tests/test_cli.py tests/test_api.py tests/test_server.py tests/test_public_contract.py; UV_CACHE_DIR=.uv-cache make check; UV_CACHE_DIR=.uv-cache make dist-check; real guest-backed smoke for bare Pyro.create_server() plus explicit profile="workspace-full".
2026-03-13 14:14:15 -03:00

14 KiB

Install

Support Matrix

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:

python -m pip install uv

Use these command forms consistently:

  • published package without install: uvx --from pyro-mcp pyro ...
  • installed package: pyro ...
  • source checkout: uv run pyro ...

Fastest Evaluation Path

Use either of these equivalent evaluator paths:

# Package without install
uvx --from pyro-mcp pyro doctor
uvx --from pyro-mcp pyro env list
uvx --from pyro-mcp pyro env pull debian:12
uvx --from pyro-mcp pyro run debian:12 -- git --version
# Already installed
pyro doctor
pyro env list
pyro env pull debian:12
pyro run debian:12 -- git --version

If you are running from a repo checkout instead, replace pyro with uv run pyro.

After that one-shot proof works, continue into the stable workspace path with pyro workspace ....

1. Check the host first

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

If Runtime: FAIL, stop here and use troubleshooting.md.

2. Inspect the catalog

uvx --from pyro-mcp pyro env list

Expected output:

Catalog version: 4.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.

3. Pull the default environment

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 host-requirements.md for the full host requirements.

Expected success signals:

[pull] phase=install environment=debian:12
[pull] phase=ready environment=debian:12
Pulled: debian:12
...

4. Run one command in a guest

uvx --from pyro-mcp pyro run debian:12 -- git --version

Expected success signals:

[run] phase=create environment=debian:12
[run] phase=start vm_id=...
[run] phase=execute vm_id=...
[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.

If guest execution is unavailable, the command fails unless you explicitly pass --allow-host-compat.

5. Continue into the stable workspace path

The commands below use plain pyro .... Run the same flow with uvx --from pyro-mcp pyro ... for the published package, or uv run pyro ... from a source checkout.

uv tool install pyro-mcp
WORKSPACE_ID="$(pyro workspace create debian:12 --seed-path ./repo --name repro-fix --label issue=123 --id-only)"
pyro workspace list
pyro workspace update "$WORKSPACE_ID" --label owner=codex
pyro workspace sync push "$WORKSPACE_ID" ./changes
pyro workspace file read "$WORKSPACE_ID" note.txt --content-only
pyro workspace patch apply "$WORKSPACE_ID" --patch-file fix.patch
pyro workspace exec "$WORKSPACE_ID" -- cat note.txt
pyro workspace snapshot create "$WORKSPACE_ID" checkpoint
pyro workspace service start "$WORKSPACE_ID" web --ready-file .web-ready -- sh -lc 'touch .web-ready && while true; do sleep 60; done'
pyro workspace reset "$WORKSPACE_ID" --snapshot checkpoint
pyro workspace export "$WORKSPACE_ID" note.txt --output ./note.txt
pyro workspace delete "$WORKSPACE_ID"

This is the stable persistent-workspace contract:

  • workspace create seeds /workspace
  • workspace create --name/--label, workspace list, and workspace update make workspaces discoverable
  • workspace sync push imports later host-side changes
  • workspace file * and workspace patch apply cover model-native text inspection and edits
  • workspace exec and workspace shell * keep work inside one sandbox
  • workspace service * manages long-running processes with typed readiness
  • workspace snapshot * and workspace reset make reset-over-repair explicit
  • workspace diff compares against the immutable create-time baseline
  • workspace export copies results back to the host
  • workspace stop|start and workspace disk * add secondary stopped-workspace inspection and raw ext4 export

When that stable workspace path is working, continue with the recipe index at use-cases/README.md. It groups the five core workspace stories and the real smoke targets behind them, starting with make smoke-use-cases or one of the per-scenario targets such as make smoke-repro-fix-loop. Treat make smoke-use-cases as the trustworthy guest-backed verification path for the advertised workspace workflows.

6. Optional demo proof point

uvx --from pyro-mcp pyro demo

pyro demo proves the one-shot create/start/exec/delete VM lifecycle works end to end.

Example output:

{
  "cleanup": {
    "deleted": true,
    "reason": "post_exec_cleanup",
    "vm_id": "..."
  },
  "command": "git --version",
  "environment": "debian:12",
  "execution_mode": "guest_vsock",
  "exit_code": 0,
  "stdout": "git version ...\n"
}

For a fuller copy-pasteable transcript, see first-run.md. When you are done evaluating and want to remove stale cached environments, run pyro env prune.

Installed CLI

If you already installed the package, the same evaluator path works with plain pyro ...:

uv tool install pyro-mcp
pyro --version
pyro doctor
pyro env list
pyro env pull debian:12
pyro run debian:12 -- git --version

After the CLI path works, you can move on to:

  • persistent workspaces: pyro workspace create debian:12 --seed-path ./repo
  • workspace discovery metadata: pyro workspace create debian:12 --name repro-fix --label issue=123
  • workspace discovery commands: pyro workspace list and pyro workspace update WORKSPACE_ID --label owner=codex
  • live workspace updates: pyro workspace sync push WORKSPACE_ID ./changes
  • guest networking policy: pyro workspace create debian:12 --network-policy egress
  • workspace secrets: pyro workspace create debian:12 --secret API_TOKEN=expected --secret-file PIP_TOKEN=./token.txt
  • model-native file editing: pyro workspace file read WORKSPACE_ID src/app.py --content-only, pyro workspace file write WORKSPACE_ID src/app.py --text-file ./app.py, and pyro workspace patch apply WORKSPACE_ID --patch-file fix.patch
  • baseline diff: pyro workspace diff WORKSPACE_ID
  • snapshots and reset: pyro workspace snapshot create WORKSPACE_ID checkpoint and pyro workspace reset WORKSPACE_ID --snapshot checkpoint
  • host export: pyro workspace export WORKSPACE_ID note.txt --output ./note.txt
  • stopped-workspace inspection: pyro workspace stop WORKSPACE_ID, pyro workspace disk list WORKSPACE_ID, pyro workspace disk read WORKSPACE_ID note.txt --content-only, and pyro workspace disk export WORKSPACE_ID --output ./workspace.ext4
  • interactive shells: pyro workspace shell open WORKSPACE_ID --id-only
  • long-running services: pyro workspace service start WORKSPACE_ID app --ready-file .ready -- sh -lc 'touch .ready && while true; do sleep 60; done'
  • localhost-published ports: pyro workspace create debian:12 --network-policy egress+published-ports and pyro workspace service start WORKSPACE_ID app --ready-http http://127.0.0.1:8080/ --publish 18080:8080 -- ./start-app
  • MCP: pyro mcp serve
  • Python SDK: from pyro_mcp import Pyro
  • Demos: pyro demo or pyro demo --network

Chat Host Quickstart

For most chat-host integrations, bare pyro mcp serve now starts workspace-core:

uvx --from pyro-mcp pyro mcp serve

Copy-paste host-specific starts:

Claude Code:

claude mcp add pyro -- uvx --from pyro-mcp pyro mcp serve

Codex:

codex mcp add pyro -- uvx --from pyro-mcp pyro mcp serve

OpenCode uses the mcp/type: "local" config shape shown in examples/opencode_mcp_config.json. If pyro-mcp is already installed, replace the uvx --from pyro-mcp pyro command with pyro in the same host-specific command or config shape. Use --profile workspace-full only when the host truly needs the full advanced workspace surface.

Use profile progression like this:

  • workspace-core: default and recommended first profile for normal persistent chat editing
  • vm-run: one-shot-only integrations
  • workspace-full: explicit advanced opt-in when the host truly needs shells, services, snapshots, secrets, network policy, or disk tools

Stable Workspace

Use pyro workspace ... when you need repeated commands in one sandbox instead of one-shot pyro run.

pyro workspace create debian:12 --seed-path ./repo
pyro workspace create debian:12 --network-policy egress
pyro workspace create debian:12 --seed-path ./repo --secret API_TOKEN=expected
pyro workspace create debian:12 --network-policy egress+published-ports
pyro workspace sync push WORKSPACE_ID ./changes --dest src
pyro workspace file list WORKSPACE_ID src --recursive
pyro workspace file read WORKSPACE_ID src/note.txt --content-only
pyro workspace file write WORKSPACE_ID src/app.py --text-file ./app.py
pyro workspace patch apply WORKSPACE_ID --patch-file fix.patch
pyro workspace exec WORKSPACE_ID -- cat src/note.txt
pyro workspace exec WORKSPACE_ID --secret-env API_TOKEN -- sh -lc 'test "$API_TOKEN" = "expected"'
pyro workspace diff WORKSPACE_ID
pyro workspace snapshot create WORKSPACE_ID checkpoint
pyro workspace reset WORKSPACE_ID --snapshot checkpoint
pyro workspace reset WORKSPACE_ID
pyro workspace export WORKSPACE_ID src/note.txt --output ./note.txt
pyro workspace shell open WORKSPACE_ID --secret-env API_TOKEN --id-only
pyro workspace shell write WORKSPACE_ID SHELL_ID --input 'pwd'
pyro workspace shell read WORKSPACE_ID SHELL_ID --plain --wait-for-idle-ms 300
pyro workspace shell close WORKSPACE_ID SHELL_ID
pyro workspace service start WORKSPACE_ID web --secret-env API_TOKEN --ready-file .web-ready -- sh -lc 'touch .web-ready && while true; do sleep 60; done'
pyro workspace service start WORKSPACE_ID worker --ready-file .worker-ready -- sh -lc 'touch .worker-ready && while true; do sleep 60; done'
pyro workspace service start WORKSPACE_ID app --ready-http http://127.0.0.1:8080/ --publish 18080:8080 -- ./start-app
pyro workspace service list WORKSPACE_ID
pyro workspace service status WORKSPACE_ID web
pyro workspace service logs WORKSPACE_ID web --tail-lines 50
pyro workspace service stop WORKSPACE_ID web
pyro workspace service stop WORKSPACE_ID worker
pyro workspace stop WORKSPACE_ID
pyro workspace disk list WORKSPACE_ID
pyro workspace disk read WORKSPACE_ID src/note.txt --content-only
pyro workspace disk export WORKSPACE_ID --output ./workspace.ext4
pyro workspace start WORKSPACE_ID
pyro workspace logs WORKSPACE_ID
pyro workspace delete WORKSPACE_ID

Workspace commands default to the persistent /workspace directory inside the guest. If you need the identifier programmatically, use --id-only for only the identifier or --json for the full workspace payload. Use --seed-path when the workspace should start from a host directory or a local .tar / .tar.gz / .tgz archive. Use pyro workspace sync push for later host-side changes to a started workspace. Sync is non-atomic in 4.0.0; if it fails partway through, prefer pyro workspace reset to recover from baseline or one named snapshot. Use pyro workspace diff to compare the current workspace tree to its immutable create-time baseline, pyro workspace snapshot * to capture named checkpoints, and pyro workspace export to copy one changed file or directory back to the host. Use pyro workspace exec for one-shot commands and pyro workspace shell * when you need an interactive PTY that survives across separate calls. Prefer pyro workspace shell read --plain --wait-for-idle-ms 300 for chat-facing shell loops. Use pyro workspace service * when the workspace needs long-running background processes with typed readiness probes. Service metadata and logs stay outside /workspace, so the service runtime itself does not show up in workspace diff or export results. Use --network-policy egress when the workspace needs outbound guest networking, and --network-policy egress+published-ports plus workspace service start --publish when one service must be reachable from the host on 127.0.0.1. Use --secret and --secret-file at workspace creation when the sandbox needs private tokens or config, and --secret-env SECRET_NAME[=ENV_VAR] when one exec, shell, or service call needs that secret as an environment variable. Persisted secret files are available in the guest at /run/pyro-secrets/<name>. Use pyro workspace stop plus pyro workspace disk list|read|export when you need offline inspection or one raw ext4 copy from a stopped guest-backed workspace, then pyro workspace start to resume it.

Contributor Clone

git lfs install
git clone <repo>
cd pyro
git lfs pull
make setup