Ship first-class MCP setup examples for Claude Code, Codex, and OpenCode so new users can copy one exact command or config instead of translating the generic MCP template by hand. Reposition the docs to surface those host-specific examples before the generic config fallback, keep workspace-core as the recommended profile everywhere user-facing, and retain Claude Desktop/Cursor as secondary fallback examples. Bump the package and catalog to 3.11.0, mark the roadmap milestone done, and add docs-alignment coverage that pins the new examples to the canonical workspace-core server command and the expected OpenCode config shape. Validation: - uv lock - ./.venv/bin/pytest --no-cov tests/test_cli.py - UV_CACHE_DIR=.uv-cache make check - UV_CACHE_DIR=.uv-cache make dist-check
314 lines
16 KiB
Markdown
314 lines
16 KiB
Markdown
# First Run Transcript
|
|
|
|
This is the intended evaluator path for a first successful run on a supported host.
|
|
Copy the commands as-is. Paths and timing values will differ on your machine.
|
|
The same sequence works with an installed `pyro` binary by dropping the
|
|
`uvx --from pyro-mcp` prefix. If you are running from a source checkout instead
|
|
of the published package, replace `pyro` with `uv run pyro`.
|
|
|
|
## 1. Verify the host
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro doctor
|
|
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
|
|
```
|
|
|
|
## 2. Inspect the catalog
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro env list
|
|
Catalog version: 3.11.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
|
|
|
|
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](host-requirements.md) for the full host requirements.
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro env pull debian:12
|
|
[pull] phase=install environment=debian:12
|
|
[pull] phase=ready environment=debian:12
|
|
Pulled: debian:12
|
|
Version: 1.0.0
|
|
Distribution: debian 12
|
|
Installed: yes
|
|
Cache dir: /home/you/.cache/pyro-mcp/environments
|
|
Default packages: bash, coreutils, git
|
|
Install dir: /home/you/.cache/pyro-mcp/environments/linux-x86_64/debian_12-1.0.0
|
|
Install manifest: /home/you/.cache/pyro-mcp/environments/linux-x86_64/debian_12-1.0.0/environment.json
|
|
Kernel image: /home/you/.cache/pyro-mcp/environments/linux-x86_64/debian_12-1.0.0/vmlinux
|
|
Rootfs image: /home/you/.cache/pyro-mcp/environments/linux-x86_64/debian_12-1.0.0/rootfs.ext4
|
|
OCI source: registry-1.docker.io/thalesmaciel/pyro-environment-debian-12:1.0.0
|
|
```
|
|
|
|
## 4. Run one command in a guest
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro run debian:12 -- git --version
|
|
[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.
|
|
|
|
## 5. Continue into the stable workspace path
|
|
|
|
The commands below use the published-package form. The same stable workspace path works with an
|
|
installed `pyro` binary by dropping the `uvx --from pyro-mcp` prefix, or with `uv run pyro` from
|
|
a source checkout.
|
|
|
|
```bash
|
|
$ export WORKSPACE_ID="$(uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo --name repro-fix --label issue=123 --id-only)"
|
|
$ uvx --from pyro-mcp pyro workspace list
|
|
$ uvx --from pyro-mcp pyro workspace update "$WORKSPACE_ID" --label owner=codex
|
|
$ uvx --from pyro-mcp pyro workspace sync push "$WORKSPACE_ID" ./changes
|
|
$ uvx --from pyro-mcp pyro workspace file read "$WORKSPACE_ID" note.txt --content-only
|
|
$ uvx --from pyro-mcp pyro workspace patch apply "$WORKSPACE_ID" --patch-file fix.patch
|
|
$ uvx --from pyro-mcp pyro workspace exec "$WORKSPACE_ID" -- cat note.txt
|
|
$ uvx --from pyro-mcp pyro workspace snapshot create "$WORKSPACE_ID" checkpoint
|
|
$ uvx --from pyro-mcp pyro workspace service start "$WORKSPACE_ID" web --ready-file .web-ready -- sh -lc 'touch .web-ready && while true; do sleep 60; done'
|
|
$ uvx --from pyro-mcp pyro workspace reset "$WORKSPACE_ID" --snapshot checkpoint
|
|
$ uvx --from pyro-mcp pyro workspace export "$WORKSPACE_ID" note.txt --output ./note.txt
|
|
$ uvx --from pyro-mcp pyro workspace stop "$WORKSPACE_ID"
|
|
$ uvx --from pyro-mcp pyro workspace disk list "$WORKSPACE_ID"
|
|
$ uvx --from pyro-mcp pyro workspace disk read "$WORKSPACE_ID" note.txt --content-only
|
|
$ uvx --from pyro-mcp pyro workspace disk export "$WORKSPACE_ID" --output ./workspace.ext4
|
|
$ uvx --from pyro-mcp pyro workspace start "$WORKSPACE_ID"
|
|
$ uvx --from pyro-mcp pyro workspace delete "$WORKSPACE_ID"
|
|
```
|
|
|
|
## 6. Optional one-shot demo and expanded workspace flow
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro demo
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo --name repro-fix --label issue=123
|
|
$ uvx --from pyro-mcp pyro workspace list
|
|
$ uvx --from pyro-mcp pyro workspace update WORKSPACE_ID --label owner=codex
|
|
$ uvx --from pyro-mcp pyro workspace sync push WORKSPACE_ID ./changes
|
|
$ uvx --from pyro-mcp pyro workspace file list WORKSPACE_ID src --recursive
|
|
$ uvx --from pyro-mcp pyro workspace file read WORKSPACE_ID src/app.py --content-only
|
|
$ uvx --from pyro-mcp pyro workspace file write WORKSPACE_ID src/app.py --text-file ./app.py
|
|
$ uvx --from pyro-mcp pyro workspace patch apply WORKSPACE_ID --patch-file fix.patch
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --network-policy egress
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --secret API_TOKEN=expected --secret-file PIP_TOKEN=./token.txt
|
|
$ uvx --from pyro-mcp pyro workspace exec WORKSPACE_ID --secret-env API_TOKEN -- sh -lc 'test "$API_TOKEN" = "expected"'
|
|
$ uvx --from pyro-mcp pyro workspace diff WORKSPACE_ID
|
|
$ uvx --from pyro-mcp pyro workspace snapshot create WORKSPACE_ID checkpoint
|
|
$ uvx --from pyro-mcp pyro workspace reset WORKSPACE_ID --snapshot checkpoint
|
|
$ uvx --from pyro-mcp pyro workspace export WORKSPACE_ID note.txt --output ./note.txt
|
|
$ uvx --from pyro-mcp pyro workspace shell open WORKSPACE_ID --secret-env API_TOKEN --id-only
|
|
$ uvx --from pyro-mcp pyro workspace service start WORKSPACE_ID app --secret-env API_TOKEN --ready-file .ready -- sh -lc 'touch .ready && while true; do sleep 60; done'
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --network-policy egress+published-ports
|
|
$ uvx --from pyro-mcp pyro workspace service start WORKSPACE_ID app --ready-http http://127.0.0.1:8080/ --publish 18080:8080 -- ./start-app
|
|
$ uvx --from pyro-mcp pyro mcp serve --profile workspace-core
|
|
$ claude mcp add pyro -- uvx --from pyro-mcp pyro mcp serve --profile workspace-core
|
|
$ codex mcp add pyro -- uvx --from pyro-mcp pyro mcp serve --profile workspace-core
|
|
```
|
|
|
|
For most chat hosts, `workspace-core` is the recommended first MCP profile.
|
|
Move to `workspace-full` only when the host truly needs shells, services,
|
|
snapshots, secrets, network policy, or disk tools.
|
|
|
|
Host-specific MCP 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)
|
|
|
|
`pyro demo` proves the one-shot create/start/exec/delete VM lifecycle works end to end.
|
|
|
|
Once that stable workspace flow works, continue with the five recipe docs in
|
|
[use-cases/README.md](use-cases/README.md) or run the real guest-backed smoke packs directly with
|
|
`make smoke-use-cases`. Treat that smoke pack as the trustworthy guest-backed
|
|
verification path for the advertised workspace workflows.
|
|
|
|
When you need repeated commands in one sandbox, switch to `pyro workspace ...`:
|
|
|
|
```bash
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --seed-path ./repo
|
|
Workspace ID: ...
|
|
Environment: debian:12
|
|
State: started
|
|
Workspace: /workspace
|
|
Workspace seed: directory from ...
|
|
Network policy: off
|
|
Execution mode: guest_vsock
|
|
Resources: 1 vCPU / 1024 MiB
|
|
Command count: 0
|
|
|
|
$ uvx --from pyro-mcp pyro workspace sync push WORKSPACE_ID ./changes --dest src
|
|
[workspace-sync] workspace_id=... mode=directory source=... destination=/workspace/src entry_count=... bytes_written=... execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace file list WORKSPACE_ID src --recursive
|
|
Workspace file path: /workspace/src
|
|
- /workspace/src/note.txt [file] bytes=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace file read WORKSPACE_ID src/note.txt
|
|
hello from synced workspace
|
|
[workspace-file-read] workspace_id=... path=/workspace/src/note.txt size_bytes=... truncated=False execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace patch apply WORKSPACE_ID --patch-file fix.patch
|
|
|
|
$ uvx --from pyro-mcp pyro workspace exec WORKSPACE_ID -- cat src/note.txt
|
|
hello from synced workspace
|
|
[workspace-exec] workspace_id=... sequence=1 cwd=/workspace execution_mode=guest_vsock exit_code=0 duration_ms=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace exec WORKSPACE_ID --secret-env API_TOKEN -- sh -lc 'test "$API_TOKEN" = "expected"'
|
|
[workspace-exec] workspace_id=... sequence=2 cwd=/workspace execution_mode=guest_vsock exit_code=0 duration_ms=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace diff WORKSPACE_ID
|
|
[workspace-diff] workspace_id=... total=... added=... modified=... deleted=... type_changed=... text_patched=... non_text=...
|
|
--- a/src/note.txt
|
|
+++ b/src/note.txt
|
|
@@ ...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace snapshot create WORKSPACE_ID checkpoint
|
|
[workspace-snapshot-create] snapshot_name=checkpoint kind=named entry_count=... bytes_written=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace reset WORKSPACE_ID --snapshot checkpoint
|
|
Workspace reset from snapshot: checkpoint (named)
|
|
[workspace-reset] destination=/workspace entry_count=... bytes_written=...
|
|
Workspace ID: ...
|
|
State: started
|
|
Command count: 0
|
|
Reset count: 1
|
|
|
|
$ uvx --from pyro-mcp pyro workspace export WORKSPACE_ID src/note.txt --output ./note.txt
|
|
[workspace-export] workspace_id=... workspace_path=/workspace/src/note.txt output_path=... artifact_type=file entry_count=... bytes_written=... execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace shell open WORKSPACE_ID --secret-env API_TOKEN --id-only
|
|
[workspace-shell-open] workspace_id=... shell_id=... state=running cwd=/workspace cols=120 rows=30 execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace shell write WORKSPACE_ID SHELL_ID --input 'pwd'
|
|
[workspace-shell-write] workspace_id=... shell_id=... state=running cwd=/workspace cols=120 rows=30 execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace shell read WORKSPACE_ID SHELL_ID --plain --wait-for-idle-ms 300
|
|
/workspace
|
|
[workspace-shell-read] workspace_id=... shell_id=... state=running cursor=0 next_cursor=... truncated=False plain=True wait_for_idle_ms=300 execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp 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'
|
|
[workspace-service-start] workspace_id=... service=web state=running cwd=/workspace ready_type=file execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service start WORKSPACE_ID worker --ready-file .worker-ready -- sh -lc 'touch .worker-ready && while true; do sleep 60; done'
|
|
[workspace-service-start] workspace_id=... service=worker state=running cwd=/workspace ready_type=file execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace create debian:12 --network-policy egress+published-ports
|
|
Workspace ID: ...
|
|
Network policy: egress+published-ports
|
|
...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service start WORKSPACE_ID app --ready-http http://127.0.0.1:8080/ --publish 18080:8080 -- ./start-app
|
|
[workspace-service-start] workspace_id=... service=app state=running cwd=/workspace ready_type=http execution_mode=guest_vsock published=127.0.0.1:18080->8080/tcp
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service list WORKSPACE_ID
|
|
Workspace: ...
|
|
Services: 2 total, 2 running
|
|
- web [running] cwd=/workspace readiness=file
|
|
- worker [running] cwd=/workspace readiness=file
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service status WORKSPACE_ID web
|
|
Workspace: ...
|
|
Service: web
|
|
State: running
|
|
Command: sh -lc 'touch .web-ready && while true; do sleep 60; done'
|
|
Cwd: /workspace
|
|
Readiness: file /workspace/.web-ready
|
|
Execution mode: guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service logs WORKSPACE_ID web --tail-lines 50
|
|
Workspace: ...
|
|
Service: web
|
|
State: running
|
|
...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service stop WORKSPACE_ID web
|
|
[workspace-service-stop] workspace_id=... service=web state=stopped execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace service stop WORKSPACE_ID worker
|
|
[workspace-service-stop] workspace_id=... service=worker state=stopped execution_mode=guest_vsock
|
|
|
|
$ uvx --from pyro-mcp pyro workspace stop WORKSPACE_ID
|
|
Workspace ID: ...
|
|
State: stopped
|
|
|
|
$ uvx --from pyro-mcp pyro workspace disk list WORKSPACE_ID src --recursive
|
|
Workspace: ...
|
|
Path: /workspace/src
|
|
- /workspace/src [directory]
|
|
- /workspace/src/note.txt [file] bytes=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace disk read WORKSPACE_ID src/note.txt
|
|
hello from synced workspace
|
|
[workspace-disk-read] workspace_id=... path=/workspace/src/note.txt size_bytes=... truncated=False
|
|
|
|
$ uvx --from pyro-mcp pyro workspace disk export WORKSPACE_ID --output ./workspace.ext4
|
|
[workspace-disk-export] workspace_id=... output_path=... disk_format=ext4 bytes_written=...
|
|
|
|
$ uvx --from pyro-mcp pyro workspace start WORKSPACE_ID
|
|
Workspace ID: ...
|
|
State: started
|
|
```
|
|
|
|
Use `--seed-path` when the workspace should start from a host directory or a local
|
|
`.tar` / `.tar.gz` / `.tgz` archive instead of an empty `/workspace`. Use
|
|
`pyro workspace sync push` when you need to import later host-side changes into a started
|
|
workspace. Sync is non-atomic in `3.11.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 create
|
|
named checkpoints, and `pyro workspace export` to copy one changed file or directory back to the
|
|
host. Use `pyro workspace file *` and `pyro workspace patch apply` for model-native text edits,
|
|
`pyro workspace exec` for one-shot commands, and `pyro workspace shell *` when you
|
|
need a persistent interactive PTY session in that same workspace. Use `pyro workspace service *`
|
|
when the workspace needs long-running background processes with typed readiness checks. Internal
|
|
service state and logs stay outside `/workspace`, so service runtime data does not appear in
|
|
workspace diff or export results. Use `--network-policy egress` for 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. Persisted secret files are
|
|
materialized at `/run/pyro-secrets/<name>`, and `--secret-env SECRET_NAME[=ENV_VAR]` maps one
|
|
secret into one exec, shell, or service call without storing that environment mapping on the
|
|
workspace itself. 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 the same workspace.
|
|
|
|
The stable workspace walkthrough GIF in the README is rendered from
|
|
[docs/assets/workspace-first-run.tape](assets/workspace-first-run.tape) with
|
|
[scripts/render_tape.sh](../scripts/render_tape.sh).
|
|
|
|
Example output:
|
|
|
|
```json
|
|
{
|
|
"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"
|
|
}
|
|
```
|
|
|
|
When you are done evaluating and want to remove stale cached environments, run `pyro env prune`.
|
|
|
|
If `pyro doctor` reports `Runtime: FAIL`, or if the `pyro run` summary does not show
|
|
`execution_mode=guest_vsock`, stop and use [troubleshooting.md](troubleshooting.md).
|