Reframe pyro around the chat-host path

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 =======================.
This commit is contained in:
Thales Maciel 2026-03-13 15:03:20 -03:00
parent 6433847185
commit 999fe1b23a
No known key found for this signature in database
GPG key ID: 33112E6833C34679
15 changed files with 608 additions and 1613 deletions

View file

@ -1,164 +1,141 @@
# Integration Targets
# Chat Host Integrations
These are the main ways to integrate `pyro-mcp` into an LLM application.
This page documents the intended product path for `pyro-mcp`:
Use this page after you have already validated the host and guest execution through the
CLI path in [install.md](install.md) or [first-run.md](first-run.md).
- validate the host with the CLI
- run `pyro mcp serve`
- connect a chat host
- let the agent work inside disposable workspaces
`pyro-mcp` currently has no users. Expect breaking changes while this chat-host
path is still being shaped.
Use this page after you have already validated the host and guest execution
through [install.md](install.md) or [first-run.md](first-run.md).
## Recommended Default
Bare `pyro mcp serve` now starts `workspace-core`. Use `vm_run` only for one-shot
integrations, and promote the chat surface to `workspace-full` only when it
truly needs shells, services, snapshots, secrets, network policy, or disk
tools.
Bare `pyro mcp serve` starts `workspace-core`. That is the product path.
That keeps the model-facing contract small:
```bash
pyro mcp serve
```
- one tool
- one command
- one ephemeral VM
- automatic cleanup
Use `--profile workspace-full` only when the chat truly needs shells, services,
snapshots, secrets, network policy, or disk tools.
Profile progression:
## Claude Code
- `workspace-core`: default and recommended first profile for persistent chat editing
- `vm-run`: one-shot only
- `workspace-full`: the full stable workspace surface, including shells, services, snapshots, secrets, network policy, and disk tools
Package without install:
## OpenAI Responses API
```bash
claude mcp add pyro -- uvx --from pyro-mcp pyro mcp serve
claude mcp list
```
Best when:
Already installed:
- your agent already uses OpenAI models directly
- you want a normal tool-calling loop instead of MCP transport
- you want the smallest amount of integration code
```bash
claude mcp add pyro -- pyro mcp serve
claude mcp list
```
Recommended surface:
Reference:
- `vm_run` for one-shot loops
- the `workspace-core` tool set for the normal persistent chat loop
- the `workspace-full` tool set only when the host explicitly needs advanced workspace capabilities
- [claude_code_mcp.md](../examples/claude_code_mcp.md)
Canonical example:
## Codex
- [examples/openai_responses_vm_run.py](../examples/openai_responses_vm_run.py)
- [examples/openai_responses_workspace_core.py](../examples/openai_responses_workspace_core.py)
- [docs/use-cases/repro-fix-loop.md](use-cases/repro-fix-loop.md)
Package without install:
## MCP Clients
```bash
codex mcp add pyro -- uvx --from pyro-mcp pyro mcp serve
codex mcp list
```
Best when:
Already installed:
- your host application already supports MCP
- you want `pyro` to run as an external stdio server
- you want tool schemas to be discovered directly from the server
```bash
codex mcp add pyro -- pyro mcp serve
codex mcp list
```
Recommended entrypoint:
Reference:
- `pyro mcp serve`
- [codex_mcp.md](../examples/codex_mcp.md)
Profile progression:
## OpenCode
- `pyro mcp serve --profile vm-run` for the smallest one-shot surface
- `pyro mcp serve` for the normal persistent chat loop
- `pyro mcp serve --profile workspace-full` only when the model truly needs advanced workspace tools
Use the local MCP config shape from:
Host-specific onramps:
- [opencode_mcp_config.json](../examples/opencode_mcp_config.json)
- 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 Desktop fallback: [examples/claude_desktop_mcp_config.json](../examples/claude_desktop_mcp_config.json)
- Cursor fallback: [examples/cursor_mcp_config.json](../examples/cursor_mcp_config.json)
- Use-case recipes: [docs/use-cases/README.md](use-cases/README.md)
Minimal `opencode.json` snippet:
## Direct Python SDK
```json
{
"mcp": {
"pyro": {
"type": "local",
"enabled": true,
"command": ["uvx", "--from", "pyro-mcp", "pyro", "mcp", "serve"]
}
}
}
```
Best when:
If `pyro-mcp` is already installed, replace `uvx --from pyro-mcp pyro` with
`pyro` in the same config shape.
- your application owns orchestration itself
- you do not need MCP transport
- you want direct access to `Pyro`
## Generic MCP Fallback
Recommended default:
Use this only when the host expects a plain `mcpServers` JSON config and does
not already have a dedicated example in the repo:
- `Pyro.run_in_vm(...)`
- `Pyro.create_server()` for most chat hosts now that `workspace-core` is the default profile
- `Pyro.create_workspace(name=..., labels=...)` + `Pyro.list_workspaces()` + `Pyro.update_workspace(...)` when repeated workspaces need human-friendly discovery metadata
- `Pyro.create_workspace(seed_path=...)` + `Pyro.push_workspace_sync(...)` + `Pyro.exec_workspace(...)` when repeated workspace commands are required
- `Pyro.list_workspace_files(...)` / `Pyro.read_workspace_file(...)` / `Pyro.write_workspace_file(...)` / `Pyro.apply_workspace_patch(...)` when the agent needs model-native file inspection and text edits inside one live workspace
- `Pyro.create_workspace(..., secrets=...)` + `Pyro.exec_workspace(..., secret_env=...)` when the workspace needs private tokens or authenticated setup
- `Pyro.create_workspace(..., network_policy="egress+published-ports")` + `Pyro.start_service(..., published_ports=[...])` when the host must probe one workspace service
- `Pyro.diff_workspace(...)` + `Pyro.export_workspace(...)` when the agent needs baseline comparison or host-out file transfer
- `Pyro.start_service(..., secret_env=...)` + `Pyro.list_services(...)` + `Pyro.logs_service(...)` when the agent needs long-running background processes in one workspace
- `Pyro.open_shell(..., secret_env=...)` + `Pyro.write_shell(...)` + `Pyro.read_shell(..., plain=True, wait_for_idle_ms=300)` when the agent needs an interactive PTY inside the workspace
- [mcp_client_config.md](../examples/mcp_client_config.md)
Lifecycle note:
Generic `mcpServers` shape:
- `Pyro.exec_vm(...)` runs one command and auto-cleans the VM afterward
- use `create_vm(...)` + `start_vm(...)` only when you need pre-exec inspection or status before
that final exec
- use `create_workspace(seed_path=...)` when the agent needs repeated commands in one persistent
`/workspace` that starts from host content
- use `create_workspace(name=..., labels=...)`, `list_workspaces()`, and `update_workspace(...)`
when the agent or operator needs to rediscover the right workspace later without external notes
- use `push_workspace_sync(...)` when later host-side changes need to be imported into that
running workspace without recreating it
- use `list_workspace_files(...)`, `read_workspace_file(...)`, `write_workspace_file(...)`, and
`apply_workspace_patch(...)` when the agent should inspect or edit workspace files without shell
quoting tricks
- use `create_workspace(..., secrets=...)` plus `secret_env` on exec, shell, or service start when
the agent needs private tokens or authenticated startup inside that workspace
- use `create_workspace(..., network_policy="egress+published-ports")` plus
`start_service(..., published_ports=[...])` when the host must probe one service from that
workspace
- use `diff_workspace(...)` when the agent needs a structured comparison against the immutable
create-time baseline
- use `export_workspace(...)` when the agent needs one file or directory copied back to the host
- use `stop_workspace(...)` plus `list_workspace_disk(...)`, `read_workspace_disk(...)`, or
`export_workspace_disk(...)` when the agent needs offline inspection or one raw ext4 copy from
a stopped guest-backed workspace
- use `start_service(...)` when the agent needs long-running processes and typed readiness inside
one workspace
- use `open_shell(...)` when the agent needs interactive shell state instead of one-shot execs
```json
{
"mcpServers": {
"pyro": {
"command": "uvx",
"args": ["--from", "pyro-mcp", "pyro", "mcp", "serve"]
}
}
}
```
Examples:
## When To Use `workspace-full`
- [examples/python_run.py](../examples/python_run.py)
- [examples/python_lifecycle.py](../examples/python_lifecycle.py)
- [examples/python_workspace.py](../examples/python_workspace.py)
- [examples/python_shell.py](../examples/python_shell.py)
- [docs/use-cases/README.md](use-cases/README.md)
Stay on bare `pyro mcp serve` unless the chat host truly needs:
## Agent Framework Wrappers
- persistent PTY shell sessions
- long-running services and readiness probes
- secrets
- guest networking and published ports
- stopped-workspace disk inspection or raw ext4 export
Examples:
When that is necessary:
- LangChain tools
- PydanticAI tools
- custom in-house orchestration layers
```bash
pyro mcp serve --profile workspace-full
```
Best when:
## Recipe-Backed Workflows
- you already have an application framework that expects a Python callable tool
- you want to wrap `vm_run` behind framework-specific abstractions
Once the host is connected, move to the five real workflows in
[use-cases/README.md](use-cases/README.md):
Recommended pattern:
- cold-start repo validation
- repro plus fix loops
- parallel isolated workspaces
- unsafe or untrusted code inspection
- review and evaluation workflows
- keep the framework wrapper thin
- map one-shot framework tool input directly onto `vm_run`
- expose `workspace_*` only when the framework truly needs repeated commands in one workspace
Validate the whole story with:
Concrete example:
- [examples/langchain_vm_run.py](../examples/langchain_vm_run.py)
## Selection Rule
Choose the narrowest integration that matches the host environment:
1. OpenAI Responses API if you want a direct provider tool loop.
2. MCP if your host already speaks MCP.
3. Python SDK if you own orchestration and do not need transport.
4. Framework wrappers only as thin adapters over the same `vm_run` contract.
```bash
make smoke-use-cases
```