Make the docs and help text unapologetically teach as the product path for Claude Code, Codex, and OpenCode on Linux KVM.
Rewrite the README, install/first-run/integration guides, public contract, vision, and use-case docs around the zero-to-hero chat flow, and explicitly note that there are no users yet so breaking changes are acceptable while the interface is still being shaped.
Update package metadata, CLI help, and the docs/help expectation tests to match the new positioning. Validate the reframe with usage: pyro [-h] [--version] COMMAND ...
Validate the host and serve disposable MCP workspaces for chat-based coding agents on supported Linux x86_64 KVM hosts.
positional arguments:
COMMAND
env Inspect and manage curated environments.
mcp Run the MCP server.
run Run one command inside an ephemeral VM.
workspace Manage persistent workspaces.
doctor Inspect runtime and host diagnostics.
demo Run built-in demos.
options:
-h, --help show this help message and exit
--version show program's version number and exit
Suggested zero-to-hero path:
pyro doctor
pyro env list
pyro env pull debian:12
pyro run debian:12 -- git --version
pyro mcp serve
Connect a chat host after that:
claude mcp add pyro -- uvx --from pyro-mcp pyro mcp serve
codex mcp add pyro -- uvx --from pyro-mcp pyro mcp serve
If you want terminal-level visibility into the workspace model:
pyro workspace create debian:12 --seed-path ./repo --id-only
pyro workspace sync push WORKSPACE_ID ./changes
pyro workspace exec WORKSPACE_ID -- cat note.txt
pyro workspace diff WORKSPACE_ID
pyro workspace snapshot create WORKSPACE_ID checkpoint
pyro workspace reset WORKSPACE_ID --snapshot checkpoint
pyro workspace shell open WORKSPACE_ID --id-only
pyro workspace service start WORKSPACE_ID app --ready-file .ready -- sh -lc 'touch .ready && while true; do sleep 60; done'
pyro workspace export WORKSPACE_ID note.txt --output ./note.txt, usage: pyro mcp serve [-h] [--profile {vm-run,workspace-core,workspace-full}]
Expose pyro tools over stdio for an MCP client. Bare `pyro mcp serve` now starts `workspace-core`, the recommended first profile for most chat hosts.
options:
-h, --help show this help message and exit
--profile {vm-run,workspace-core,workspace-full}
Expose only one model-facing tool profile. `workspace-
core` is the default and recommended first profile for
most chat hosts; `workspace-full` is the larger opt-in
profile. (default: workspace-core)
Default and recommended first start:
pyro mcp serve
Profiles:
workspace-core: default for normal persistent chat editing
vm-run: smallest one-shot-only surface
workspace-full: larger opt-in surface for shells, services,
snapshots, secrets, network policy, and disk tools
Use --profile workspace-full only when the host truly needs those
extra workspace capabilities., and uv run ruff check .
All checks passed!
uv run mypy
Success: no issues found in 61 source files
uv run pytest -n auto
============================= test session starts ==============================
platform linux -- Python 3.12.10, pytest-9.0.2, pluggy-1.6.0
rootdir: /home/thales/projects/personal/pyro
configfile: pyproject.toml
testpaths: tests
plugins: anyio-4.12.1, xdist-3.8.0, cov-7.0.0
created: 32/32 workers
32 workers [393 items]
........................................................................ [ 18%]
........................................................................ [ 36%]
........................................................................ [ 54%]
........................................................................ [ 73%]
........................................................................ [ 91%]
................................. [100%]
=============================== warnings summary ===============================
../../../.local/share/uv/python/cpython-3.12.10-linux-x86_64-gnu/lib/python3.12/importlib/metadata/__init__.py:467: 32 warnings
/home/thales/.local/share/uv/python/cpython-3.12.10-linux-x86_64-gnu/lib/python3.12/importlib/metadata/__init__.py:467: DeprecationWarning: Implicit None on return values is deprecated and will raise KeyErrors.
return self.metadata['Version']
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================ tests coverage ================================
_______________ coverage: platform linux, python 3.12.10-final-0 _______________
Name Stmts Miss Cover Missing
-------------------------------------------------------------------------
src/pyro_mcp/__init__.py 25 0 100%
src/pyro_mcp/api.py 307 7 98% 37-38, 63, 69, 72, 75, 548
src/pyro_mcp/cli.py 1132 141 88% 288-289, 332-333, 336, 344, 367-368, 394-395, 398, 406, 450, 460-461, 464, 477, 483-484, 498-499, 502, 566-575, 592-593, 596, 635, 2180, 2182, 2226, 2236, 2280, 2284-2285, 2295, 2302, 2344-2351, 2392, 2409-2414, 2459-2461, 2470-2472, 2483-2485, 2494-2496, 2503-2505, 2510-2512, 2523-2528, 2530, 2541-2546, 2567-2572, 2574, 2589-2594, 2596, 2608, 2623, 2637, 2655-2660, 2669-2674, 2676, 2683-2688, 2690, 2701-2706, 2708, 2719-2724, 2726, 2737-2742, 2764, 2787, 2806, 2824, 2841, 2899, 3017
src/pyro_mcp/contract.py 52 0 100%
src/pyro_mcp/demo.py 16 0 100%
src/pyro_mcp/doctor.py 12 0 100%
src/pyro_mcp/ollama_demo.py 245 6 98% 289, 294, 299, 318, 439, 550
src/pyro_mcp/runtime.py 142 14 90% 80, 84, 88, 92, 120, 130, 144, 173, 182, 194, 230-232, 262
src/pyro_mcp/runtime_boot_check.py 33 0 100%
src/pyro_mcp/runtime_build.py 546 47 91% 92, 127, 181, 189, 238-240, 263-265, 300, 325, 331, 340-341, 343, 392, 396, 413, 416, 492-494, 497-499, 522, 525, 578, 615, 620, 646-647, 649, 686, 688, 694, 697, 725, 765, 779, 791, 805, 808, 1002, 1009, 1198
src/pyro_mcp/runtime_bundle/__init__.py 0 0 100%
src/pyro_mcp/runtime_network_check.py 15 0 100%
src/pyro_mcp/server.py 8 0 100%
src/pyro_mcp/vm_environments.py 386 55 86% 128, 131, 267, 274, 281, 304-306, 329-331, 352-353, 355, 380, 382, 392-394, 415, 418, 421, 429, 431, 436-437, 446-448, 488, 495-496, 502, 515, 526, 539, 546, 549, 570, 596, 599, 608-609, 613, 617, 626, 629, 636, 644, 647, 659, 667, 676, 682, 685
src/pyro_mcp/vm_firecracker.py 47 0 100%
src/pyro_mcp/vm_guest.py 206 22 89% 139, 142, 173, 176, 202, 205, 208, 211, 217, 239, 262-279, 291, 313, 633-634, 643
src/pyro_mcp/vm_manager.py 2846 355 88% 625, 642, 650-657, 677, 684, 688, 712-715, 795-796, 818, 828, 830, 845, 853-855, 858, 870, 872, 881, 889, 892, 901-902, 910, 913, 919, 926, 929, 933, 951-955, 1010-1011, 1050, 1096, 1102, 1114, 1150, 1156, 1159, 1168, 1170, 1173-1177, 1230, 1236, 1239, 1248, 1250, 1253-1257, 1268, 1277, 1280, 1284-1290, 1319, 1322-1324, 1326, 1333, 1335, 1345, 1347, 1349, 1352-1353, 1361, 1377, 1379, 1381, 1391, 1403-1404, 1408, 1424, 1440-1441, 1443, 1447, 1450-1451, 1469, 1476, 1488, 1505, 1508-1509, 1511, 1582-1583, 1586-1588, 1599, 1602, 1605, 1617, 1638, 1649-1650, 1657-1658, 1669-1671, 1781, 1792-1798, 1808, 1860, 1870, 1891, 1894-1895, 1901-1904, 1910, 1922-1962, 1991-1993, 2034, 2046-2047, 2077, 2146, 2175, 2524-2528, 2598-2602, 2614, 2720, 3563, 3577, 3580, 3583, 3648-3653, 3720, 3802, 3842-3843, 3846-3847, 3862-3863, 3914, 4194, 4229, 4232, 4237, 4250, 4254, 4263, 4277, 4316, 4349, 4444, 4472-4473, 4477-4478, 4504, 4530-4531, 4576, 4578, 4600-4601, 4629, 4631, 4661-4662, 4681-4682, 4734, 4738, 4741-4743, 4745, 4747, 4776-4777, 4809-4845, 4863-4864, 4903, 4905, 4934, 4954-4955, 4977, 4988-4990, 5036, 5049-5050, 5059-5061, 5104-5105, 5171-5178, 5189-5192, 5203, 5208, 5216-5230, 5240, 5473-5476, 5485-5490, 5498-5503, 5513, 5557, 5577, 5601-5602, 5678-5680, 5706-5725, 5784, 5789, 5804, 5832, 5836, 5848, 5884-5886, 5946, 5950, 6079, 6111, 6155, 6170, 6189, 6201, 6242, 6251, 6256, 6269, 6274, 6296, 6394, 6422-6423
src/pyro_mcp/vm_network.py 134 22 84% 65-66, 139, 201, 203, 205, 226, 317-331, 350-351, 360, 362, 372-384
src/pyro_mcp/workspace_disk.py 164 0 100%
src/pyro_mcp/workspace_files.py 293 0 100%
src/pyro_mcp/workspace_ports.py 79 1 99% 116
src/pyro_mcp/workspace_shell_output.py 88 2 98% 16, 61
src/pyro_mcp/workspace_shells.py 235 26 89% 105-118, 193-194, 226-227, 230-235, 251, 257-259, 263, 270-271, 299, 301, 303, 306-307
src/pyro_mcp/workspace_use_case_smokes.py 216 8 96% 131, 134-135, 423-426, 490
-------------------------------------------------------------------------
TOTAL 7227 706 90%
Required test coverage of 90% reached. Total coverage: 90.23%
======================= 393 passed, 32 warnings in 5.60s =======================.
234 lines
7.3 KiB
Markdown
234 lines
7.3 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.0.0: [CHANGELOG.md#400](CHANGELOG.md#400)
|
|
- 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 env list
|
|
uvx --from pyro-mcp pyro env pull debian:12
|
|
uvx --from pyro-mcp pyro run debian:12 -- git --version
|
|
```
|
|
|
|

|
|
|
|
```bash
|
|
# Already installed
|
|
pyro doctor
|
|
pyro env list
|
|
pyro env pull 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.0.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.
|
|
|
|
## Chat Host Quickstart
|
|
|
|
After the quickstart works, the intended next step is to connect a chat host.
|
|
Bare `pyro mcp serve` starts `workspace-core`, which is the default product
|
|
path.
|
|
|
|
```bash
|
|
uvx --from pyro-mcp pyro mcp serve
|
|
```
|
|
|
|
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:
|
|
|
|
```bash
|
|
claude mcp add pyro -- uvx --from pyro-mcp pyro mcp serve
|
|
```
|
|
|
|
Codex:
|
|
|
|
```bash
|
|
codex mcp add pyro -- uvx --from pyro-mcp pyro mcp serve
|
|
```
|
|
|
|
OpenCode `opencode.json` snippet:
|
|
|
|
```json
|
|
{
|
|
"mcp": {
|
|
"pyro": {
|
|
"type": "local",
|
|
"enabled": true,
|
|
"command": ["uvx", "--from", "pyro-mcp", "pyro", "mcp", "serve"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
If `pyro-mcp` is already installed, replace `uvx --from pyro-mcp pyro` with
|
|
`pyro` in the same command or config shape.
|
|
|
|
Use `--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. Pull `debian:12` and prove guest execution with `pyro run debian:12 -- git --version`.
|
|
3. Connect Claude Code, Codex, or OpenCode with `pyro mcp serve`.
|
|
4. Start with one recipe from [docs/use-cases/README.md](docs/use-cases/README.md).
|
|
`repro-fix-loop` is the shortest chat-first story.
|
|
5. 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 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
|
|
```
|