Make the local chat-host loop explicit and cheap so users can warm the machine once instead of rediscovering environment and guest setup on every session. Add cache-backed daily-loop manifests plus the new `pyro prepare` flow, extend `pyro doctor --environment` with warm/cold/stale readiness reporting, and add `make smoke-daily-loop` to prove the warmed repro-fix reset path end to end. Also fix `python -m pyro_mcp.cli` to invoke `main()` so the new smoke and `dist-check` actually exercise the CLI module, and update the docs/roadmap to present `doctor -> prepare -> connect host -> reset` as the recommended daily path. Validation: `uv lock`, `UV_OFFLINE=1 UV_CACHE_DIR=.uv-cache make check`, `UV_OFFLINE=1 UV_CACHE_DIR=.uv-cache make dist-check`, and `UV_OFFLINE=1 UV_CACHE_DIR=.uv-cache make smoke-daily-loop`.
299 lines
9.8 KiB
Markdown
299 lines
9.8 KiB
Markdown
# 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.
|
|
|
|
[](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
|
|
```
|
|
|
|

|
|
|
|
```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
|
|
```
|