# pyro-mcp `pyro-mcp` is a disposable MCP workspace for chat-based coding agents such as Claude Code, Codex, and OpenCode. It is built for Linux `x86_64` hosts with working KVM. The product path is: 1. prove the host works 2. connect a chat host over MCP 3. let the agent work inside a disposable workspace 4. validate the workflow with the recipe-backed smoke pack `pyro-mcp` currently has no users. Expect breaking changes while this chat-host path is still being shaped. This repo is not trying to be a generic VM toolkit, a CI runner, or an SDK-first platform. [![PyPI version](https://img.shields.io/pypi/v/pyro-mcp.svg)](https://pypi.org/project/pyro-mcp/) ## Start Here - Install and zero-to-hero path: [docs/install.md](docs/install.md) - First run transcript: [docs/first-run.md](docs/first-run.md) - Chat host integrations: [docs/integrations.md](docs/integrations.md) - Use-case recipes: [docs/use-cases/README.md](docs/use-cases/README.md) - Vision: [docs/vision.md](docs/vision.md) - Public contract: [docs/public-contract.md](docs/public-contract.md) - Host requirements: [docs/host-requirements.md](docs/host-requirements.md) - Troubleshooting: [docs/troubleshooting.md](docs/troubleshooting.md) - Stable workspace walkthrough GIF: [docs/assets/workspace-first-run.gif](docs/assets/workspace-first-run.gif) - Terminal walkthrough GIF: [docs/assets/first-run.gif](docs/assets/first-run.gif) - What's new in 4.5.0: [CHANGELOG.md#450](CHANGELOG.md#450) - PyPI package: [pypi.org/project/pyro-mcp](https://pypi.org/project/pyro-mcp/) ## Who It's For - Claude Code users who want disposable workspaces instead of running directly on the host - Codex users who want an MCP-backed sandbox for repo setup, bug fixing, and evaluation loops - OpenCode users who want the same disposable workspace model - people evaluating repo setup, test, and app-start workflows from a chat interface on a clean machine If you want a general VM platform, a queueing system, or a broad SDK product, this repo is intentionally biased away from that story. ## Quickstart Use either of these equivalent quickstart paths: ```bash # Package without install python -m pip install uv uvx --from pyro-mcp pyro doctor uvx --from pyro-mcp pyro prepare debian:12 uvx --from pyro-mcp pyro run debian:12 -- git --version ``` ![Quickstart walkthrough](docs/assets/first-run.gif) ```bash # Already installed pyro doctor pyro prepare debian:12 pyro run debian:12 -- git --version ``` From a repo checkout, replace `pyro` with `uv run pyro`. What success looks like: ```bash Platform: linux-x86_64 Runtime: PASS Catalog version: 4.4.0 ... [pull] phase=install environment=debian:12 [pull] phase=ready environment=debian:12 Pulled: debian:12 ... [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 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. `pyro prepare debian:12` performs that install step automatically, then proves create, exec, reset, and delete on one throwaway workspace so the daily loop is warm before the chat host connects. ## Chat Host Quickstart After the quickstart works, make the daily loop explicit before you connect the chat host: ```bash uvx --from pyro-mcp pyro doctor --environment debian:12 uvx --from pyro-mcp pyro prepare debian:12 ``` Then connect a chat host in one named mode. Use the helper flow first: ```bash uvx --from pyro-mcp pyro host connect codex --mode repro-fix uvx --from pyro-mcp pyro host connect codex --mode inspect uvx --from pyro-mcp pyro host connect claude-code --mode cold-start uvx --from pyro-mcp pyro host connect claude-code --mode review-eval uvx --from pyro-mcp pyro host print-config opencode --mode repro-fix ``` If setup drifts or you want to inspect it first: ```bash uvx --from pyro-mcp pyro host doctor uvx --from pyro-mcp pyro host repair claude-code uvx --from pyro-mcp pyro host repair codex uvx --from pyro-mcp pyro host repair opencode ``` Those helpers wrap the same `pyro mcp serve` entrypoint. Use a named mode when one workflow already matches the job. Fall back to the generic no-mode path when the mode feels too narrow. Mode examples: ```bash uvx --from pyro-mcp pyro mcp serve --mode repro-fix uvx --from pyro-mcp pyro mcp serve --mode inspect uvx --from pyro-mcp pyro mcp serve --mode cold-start uvx --from pyro-mcp pyro mcp serve --mode review-eval ``` Generic escape hatch: ```bash uvx --from pyro-mcp pyro mcp serve ``` From a repo root, the generic path auto-detects the current Git checkout and lets the first `workspace_create` omit `seed_path`. If the host does not preserve the server working directory, use: ```bash uvx --from pyro-mcp pyro host connect codex --project-path /abs/path/to/repo uvx --from pyro-mcp pyro mcp serve --project-path /abs/path/to/repo ``` If you are starting outside a local checkout, use a clean clone source: ```bash uvx --from pyro-mcp pyro host connect codex --repo-url https://github.com/example/project.git uvx --from pyro-mcp pyro mcp serve --repo-url https://github.com/example/project.git ``` Copy-paste host-specific starts: - Claude Code: [examples/claude_code_mcp.md](examples/claude_code_mcp.md) - Codex: [examples/codex_mcp.md](examples/codex_mcp.md) - OpenCode: [examples/opencode_mcp_config.json](examples/opencode_mcp_config.json) - Generic MCP config: [examples/mcp_client_config.md](examples/mcp_client_config.md) Claude Code cold-start or review-eval: ```bash claude mcp add pyro -- uvx --from pyro-mcp pyro mcp serve --mode cold-start ``` Codex repro-fix or inspect: ```bash codex mcp add pyro -- uvx --from pyro-mcp pyro mcp serve --mode repro-fix ``` OpenCode `opencode.json` snippet: ```json { "mcp": { "pyro": { "type": "local", "enabled": true, "command": ["uvx", "--from", "pyro-mcp", "pyro", "mcp", "serve", "--mode", "repro-fix"] } } } ``` If OpenCode launches the server from an unexpected cwd, use `pyro host print-config opencode --project-path /abs/path/to/repo` or add `"--project-path", "/abs/path/to/repo"` after `"serve"` in the same command array. If `pyro-mcp` is already installed, replace `uvx --from pyro-mcp pyro` with `pyro` in the same command or config shape. Use the generic no-mode path when the named mode feels too narrow. Move to `--profile workspace-full` only when the chat truly needs shells, services, snapshots, secrets, network policy, or disk tools. ## Zero To Hero 1. Validate the host with `pyro doctor`. 2. Warm the machine-level daily loop with `pyro prepare debian:12`. 3. Prove guest execution with `pyro run debian:12 -- git --version`. 4. Connect Claude Code, Codex, or OpenCode with one named mode such as `pyro host connect codex --mode repro-fix`, then fall back to raw `pyro mcp serve --mode ...` or the generic no-mode path when needed. 5. Start with one recipe from [docs/use-cases/README.md](docs/use-cases/README.md). `repro-fix` is the shortest chat-first mode and story. 6. Use `workspace reset` as the normal retry step inside that warmed loop. 7. Use `make smoke-use-cases` as the trustworthy guest-backed verification path for the advertised workflows. That is the intended user journey. The terminal commands exist to validate and debug that chat-host path, not to replace it as the main product story. ## Manual Terminal Workspace Flow If you want to understand what the agent gets inside the sandbox, or debug a recipe outside the chat host, use the terminal companion flow below: ```bash 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 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 summary "$WORKSPACE_ID" pyro workspace snapshot create "$WORKSPACE_ID" checkpoint pyro workspace reset "$WORKSPACE_ID" --snapshot checkpoint pyro workspace export "$WORKSPACE_ID" note.txt --output ./note.txt pyro workspace delete "$WORKSPACE_ID" ``` Add `workspace-full` only when the chat or your manual debugging loop really needs: - persistent PTY shells - long-running services and readiness probes - guest networking and published ports - secrets - stopped-workspace disk inspection The five recipe docs show when those capabilities are justified: [docs/use-cases/README.md](docs/use-cases/README.md) ## Official Environments Current official environments in the shipped catalog: - `debian:12` - `debian:12-base` - `debian:12-build` The embedded Firecracker runtime ships with the package. Official environments are pulled as OCI artifacts from public Docker Hub into a local cache on first use or through `pyro env pull`. End users do not need registry credentials to pull or run the official environments. ## Contributor Workflow For work inside this repository: ```bash make help make setup make check make dist-check ``` Contributor runtime sources live under `runtime_sources/`. The packaged runtime bundle under `src/pyro_mcp/runtime_bundle/` contains the embedded boot/runtime assets plus manifest metadata. End-user environment installs pull OCI-published environments by default. Use `PYRO_RUNTIME_BUNDLE_DIR=build/runtime_bundle` only when you are explicitly validating a locally built contributor runtime bundle. Official environment publication is performed locally 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 ``` For a local PyPI publish: ```bash export TWINE_PASSWORD='pypi-...' make pypi-publish ```