pyro-mcp/docs/public-contract.md
Thales Maciel 21a88312b6 Add chat-friendly shell read rendering
Make workspace shell reads usable as direct chat-model input without changing the PTY or cursor model. This adds optional plain rendering and idle-window batching across CLI, SDK, and MCP while keeping raw reads backward-compatible.

Implement the rendering and wait-for-idle logic in the manager layer so the existing guest/backend shell transport stays unchanged. The new helper strips ANSI and other terminal control noise, handles carriage-return overwrite and backspace, and preserves raw cursor semantics even when plain output is requested.

Refresh the stable shell docs/examples to recommend --plain --wait-for-idle-ms 300, mark the 3.5.0 roadmap milestone done, and bump the package/catalog version to 3.5.0.

Validation: uv lock; UV_CACHE_DIR=.uv-cache make check; UV_CACHE_DIR=.uv-cache make dist-check; real guest-backed Firecracker smoke covering shell open/write/read with ANSI plus delayed output.
2026-03-13 01:10:26 -03:00

21 KiB

Public Contract

This document defines the stable public interface for pyro-mcp 3.x.

Package Identity

  • Distribution name: pyro-mcp
  • Public executable: pyro
  • Public Python import: from pyro_mcp import Pyro
  • Public package-level factory: from pyro_mcp import create_server

Stable product framing:

  • pyro run is the stable one-shot entrypoint.
  • pyro workspace ... is the stable persistent workspace contract.

CLI Contract

Top-level commands:

  • pyro env list
  • pyro env pull
  • pyro env inspect
  • pyro env prune
  • pyro mcp serve
  • pyro run
  • pyro workspace create
  • pyro workspace list
  • pyro workspace sync push
  • pyro workspace stop
  • pyro workspace start
  • pyro workspace exec
  • pyro workspace file list
  • pyro workspace file read
  • pyro workspace file write
  • pyro workspace export
  • pyro workspace patch apply
  • pyro workspace disk export
  • pyro workspace disk list
  • pyro workspace disk read
  • pyro workspace diff
  • pyro workspace snapshot create
  • pyro workspace snapshot list
  • pyro workspace snapshot delete
  • pyro workspace reset
  • pyro workspace service start
  • pyro workspace service list
  • pyro workspace service status
  • pyro workspace service logs
  • pyro workspace service stop
  • pyro workspace shell open
  • pyro workspace shell read
  • pyro workspace shell write
  • pyro workspace shell signal
  • pyro workspace shell close
  • pyro workspace status
  • pyro workspace update
  • pyro workspace logs
  • pyro workspace delete
  • pyro doctor
  • pyro demo
  • pyro demo ollama

Stable pyro run interface:

  • positional environment name
  • --vcpu-count
  • --mem-mib
  • --timeout-seconds
  • --ttl-seconds
  • --network
  • --allow-host-compat
  • --json

Behavioral guarantees:

  • pyro run <environment> -- <command> defaults to 1 vCPU / 1024 MiB.
  • pyro run fails if guest boot or guest exec is unavailable unless --allow-host-compat is set.
  • pyro run, pyro env list, pyro env pull, pyro env inspect, pyro env prune, and pyro doctor are human-readable by default and return structured JSON with --json.
  • pyro demo ollama prints log lines plus a final summary line.
  • pyro workspace create auto-starts a persistent workspace.
  • pyro workspace create --seed-path PATH seeds /workspace from a host directory or a local .tar / .tar.gz / .tgz archive before the workspace is returned.
  • pyro workspace create --name NAME --label KEY=VALUE attaches human-oriented discovery metadata without changing the stable workspace_id.
  • pyro workspace create --network-policy {off,egress,egress+published-ports} controls workspace guest networking and whether services may publish localhost ports.
  • pyro mcp serve --profile {vm-run,workspace-core,workspace-full} narrows the model-facing MCP surface without changing runtime behavior.
  • pyro workspace create --secret NAME=VALUE and --secret-file NAME=PATH persist guest-only UTF-8 secrets outside /workspace.
  • pyro workspace list returns persisted workspaces sorted by most recent last_activity_at.
  • pyro workspace sync push WORKSPACE_ID SOURCE_PATH [--dest WORKSPACE_PATH] imports later host-side directory or archive content into a started workspace.
  • pyro workspace stop WORKSPACE_ID stops one persistent workspace without deleting its /workspace, snapshots, or command history.
  • pyro workspace start WORKSPACE_ID restarts one stopped workspace without resetting /workspace.
  • pyro workspace file list WORKSPACE_ID [PATH] [--recursive] returns metadata for one live path under /workspace.
  • pyro workspace file read WORKSPACE_ID PATH [--max-bytes N] reads one regular text file under /workspace.
  • pyro workspace file write WORKSPACE_ID PATH --text TEXT creates or replaces one regular text file under /workspace, creating missing parent directories automatically.
  • pyro workspace export WORKSPACE_ID PATH --output HOST_PATH exports one file or directory from /workspace back to the host.
  • pyro workspace disk export WORKSPACE_ID --output HOST_PATH copies the stopped guest-backed workspace rootfs as raw ext4 to the host.
  • pyro workspace disk list WORKSPACE_ID [PATH] [--recursive] inspects a stopped guest-backed workspace rootfs offline without booting the guest.
  • pyro workspace disk read WORKSPACE_ID PATH [--max-bytes N] reads one regular file from a stopped guest-backed workspace rootfs offline.
  • pyro workspace disk * requires state=stopped and a guest-backed workspace; it fails on host_compat.
  • pyro workspace diff WORKSPACE_ID compares the current /workspace tree to the immutable create-time baseline.
  • pyro workspace snapshot * manages explicit named snapshots in addition to the implicit baseline.
  • pyro workspace reset WORKSPACE_ID [--snapshot SNAPSHOT_NAME|baseline] recreates the full sandbox and restores /workspace from the chosen snapshot.
  • pyro workspace service * manages long-running named services inside one started workspace with typed readiness probes.
  • pyro workspace service start --publish GUEST_PORT or --publish HOST_PORT:GUEST_PORT publishes one guest TCP port to 127.0.0.1 on the host.
  • pyro workspace exec --secret-env SECRET_NAME[=ENV_VAR] maps one persisted secret into one exec call.
  • pyro workspace service start --secret-env SECRET_NAME[=ENV_VAR] maps one persisted secret into one service start call.
  • pyro workspace exec runs in the persistent /workspace for that workspace and does not auto-clean.
  • pyro workspace patch apply WORKSPACE_ID --patch TEXT applies one unified text patch with add/modify/delete operations under /workspace.
  • pyro workspace shell open --secret-env SECRET_NAME[=ENV_VAR] maps one persisted secret into the opened shell environment.
  • pyro workspace shell * manages persistent PTY sessions inside a started workspace.
  • pyro workspace shell read --plain --wait-for-idle-ms 300 is the recommended chat-facing read mode; raw shell reads remain available without --plain.
  • pyro workspace logs returns persisted command history for that workspace until pyro workspace delete.
  • pyro workspace update changes only discovery metadata such as name and key/value labels.
  • Workspace create/status results expose workspace_seed metadata describing how /workspace was initialized.
  • Workspace create/status/reset/update results expose name, labels, and last_activity_at.
  • Workspace create/status/reset results expose network_policy.
  • Workspace create/status/reset results expose reset_count and last_reset_at.
  • Workspace create/status/reset results expose safe secrets metadata with each secret name and source kind, but never the secret values.
  • pyro workspace status includes aggregate service_count and running_service_count fields.
  • pyro workspace list returns one summary row per persisted workspace with workspace_id, name, labels, environment, state, created_at, last_activity_at, expires_at, command_count, service_count, and running_service_count.
  • pyro workspace service start, pyro workspace service list, and pyro workspace service status expose published-port metadata when present.

Python SDK Contract

Primary facade:

  • Pyro

Supported public entrypoints:

  • create_server(profile="workspace-full")
  • Pyro.create_server(profile="workspace-full")
  • Pyro.list_environments()
  • Pyro.pull_environment(environment)
  • Pyro.inspect_environment(environment)
  • Pyro.prune_environments()
  • Pyro.create_vm(...)
  • Pyro.create_workspace(..., name=None, labels=None, network_policy="off", secrets=None)
  • Pyro.list_workspaces()
  • Pyro.push_workspace_sync(workspace_id, source_path, *, dest="/workspace")
  • Pyro.stop_workspace(workspace_id)
  • Pyro.start_workspace(workspace_id)
  • Pyro.list_workspace_files(workspace_id, path="/workspace", recursive=False)
  • Pyro.read_workspace_file(workspace_id, path, *, max_bytes=65536)
  • Pyro.write_workspace_file(workspace_id, path, *, text)
  • Pyro.export_workspace(workspace_id, path, *, output_path)
  • Pyro.apply_workspace_patch(workspace_id, *, patch)
  • Pyro.export_workspace_disk(workspace_id, *, output_path)
  • Pyro.list_workspace_disk(workspace_id, path="/workspace", recursive=False)
  • Pyro.read_workspace_disk(workspace_id, path, *, max_bytes=65536)
  • Pyro.diff_workspace(workspace_id)
  • Pyro.create_snapshot(workspace_id, snapshot_name)
  • Pyro.list_snapshots(workspace_id)
  • Pyro.delete_snapshot(workspace_id, snapshot_name)
  • Pyro.reset_workspace(workspace_id, *, snapshot="baseline")
  • Pyro.start_service(workspace_id, service_name, *, command, cwd="/workspace", readiness=None, ready_timeout_seconds=30, ready_interval_ms=500, secret_env=None, published_ports=None)
  • Pyro.list_services(workspace_id)
  • Pyro.status_service(workspace_id, service_name)
  • Pyro.logs_service(workspace_id, service_name, *, tail_lines=200, all=False)
  • Pyro.stop_service(workspace_id, service_name)
  • Pyro.open_shell(workspace_id, *, cwd="/workspace", cols=120, rows=30, secret_env=None)
  • Pyro.read_shell(workspace_id, shell_id, *, cursor=0, max_chars=65536, plain=False, wait_for_idle_ms=None)
  • Pyro.write_shell(workspace_id, shell_id, *, input, append_newline=True)
  • Pyro.signal_shell(workspace_id, shell_id, *, signal_name="INT")
  • Pyro.close_shell(workspace_id, shell_id)
  • Pyro.start_vm(vm_id)
  • Pyro.exec_vm(vm_id, *, command, timeout_seconds=30)
  • Pyro.exec_workspace(workspace_id, *, command, timeout_seconds=30, secret_env=None)
  • Pyro.stop_vm(vm_id)
  • Pyro.delete_vm(vm_id)
  • Pyro.delete_workspace(workspace_id)
  • Pyro.status_vm(vm_id)
  • Pyro.status_workspace(workspace_id)
  • Pyro.update_workspace(workspace_id, *, name=None, clear_name=False, labels=None, clear_labels=None)
  • Pyro.logs_workspace(workspace_id)
  • Pyro.network_info_vm(vm_id)
  • Pyro.reap_expired()
  • Pyro.run_in_vm(...)

Stable public method names:

  • create_server(profile="workspace-full")
  • list_environments()
  • pull_environment(environment)
  • inspect_environment(environment)
  • prune_environments()
  • create_vm(...)
  • create_workspace(..., name=None, labels=None, network_policy="off", secrets=None)
  • list_workspaces()
  • push_workspace_sync(workspace_id, source_path, *, dest="/workspace")
  • stop_workspace(workspace_id)
  • start_workspace(workspace_id)
  • list_workspace_files(workspace_id, path="/workspace", recursive=False)
  • read_workspace_file(workspace_id, path, *, max_bytes=65536)
  • write_workspace_file(workspace_id, path, *, text)
  • export_workspace(workspace_id, path, *, output_path)
  • apply_workspace_patch(workspace_id, *, patch)
  • export_workspace_disk(workspace_id, *, output_path)
  • list_workspace_disk(workspace_id, path="/workspace", recursive=False)
  • read_workspace_disk(workspace_id, path, *, max_bytes=65536)
  • diff_workspace(workspace_id)
  • create_snapshot(workspace_id, snapshot_name)
  • list_snapshots(workspace_id)
  • delete_snapshot(workspace_id, snapshot_name)
  • reset_workspace(workspace_id, *, snapshot="baseline")
  • start_service(workspace_id, service_name, *, command, cwd="/workspace", readiness=None, ready_timeout_seconds=30, ready_interval_ms=500, secret_env=None, published_ports=None)
  • list_services(workspace_id)
  • status_service(workspace_id, service_name)
  • logs_service(workspace_id, service_name, *, tail_lines=200, all=False)
  • stop_service(workspace_id, service_name)
  • open_shell(workspace_id, *, cwd="/workspace", cols=120, rows=30, secret_env=None)
  • read_shell(workspace_id, shell_id, *, cursor=0, max_chars=65536, plain=False, wait_for_idle_ms=None)
  • write_shell(workspace_id, shell_id, *, input, append_newline=True)
  • signal_shell(workspace_id, shell_id, *, signal_name="INT")
  • close_shell(workspace_id, shell_id)
  • start_vm(vm_id)
  • exec_vm(vm_id, *, command, timeout_seconds=30)
  • exec_workspace(workspace_id, *, command, timeout_seconds=30, secret_env=None)
  • stop_vm(vm_id)
  • delete_vm(vm_id)
  • delete_workspace(workspace_id)
  • status_vm(vm_id)
  • status_workspace(workspace_id)
  • update_workspace(workspace_id, *, name=None, clear_name=False, labels=None, clear_labels=None)
  • logs_workspace(workspace_id)
  • network_info_vm(vm_id)
  • reap_expired()
  • run_in_vm(...)

Behavioral defaults:

  • Pyro.create_vm(...) and Pyro.run_in_vm(...) default to vcpu_count=1 and mem_mib=1024.
  • Pyro.create_workspace(...) defaults to vcpu_count=1 and mem_mib=1024.
  • allow_host_compat defaults to False on create_vm(...) and run_in_vm(...).
  • allow_host_compat defaults to False on create_workspace(...).
  • Pyro.create_workspace(..., seed_path=...) seeds /workspace from a host directory or a local .tar / .tar.gz / .tgz archive before the workspace is returned.
  • Pyro.create_workspace(..., name=..., labels=...) attaches human-oriented discovery metadata without changing the stable workspace_id.
  • Pyro.create_workspace(..., network_policy="off"|"egress"|"egress+published-ports") controls workspace guest networking and whether services may publish host ports.
  • Pyro.create_workspace(..., secrets=...) persists guest-only UTF-8 secrets outside /workspace.
  • Pyro.list_workspaces() returns persisted workspace summaries sorted by most recent last_activity_at.
  • Pyro.push_workspace_sync(...) imports later host-side directory or archive content into a started workspace.
  • Pyro.stop_workspace(...) stops one persistent workspace without deleting its /workspace, snapshots, or command history.
  • Pyro.start_workspace(...) restarts one stopped workspace without resetting /workspace.
  • Pyro.list_workspace_files(...), Pyro.read_workspace_file(...), and Pyro.write_workspace_file(...) provide structured live /workspace inspection and text edits without shell quoting.
  • Pyro.export_workspace(...) exports one file or directory from /workspace to an explicit host path.
  • Pyro.apply_workspace_patch(...) applies unified text patches for add/modify/delete operations under /workspace.
  • Pyro.export_workspace_disk(...) copies the stopped guest-backed workspace rootfs as raw ext4 to an explicit host path.
  • Pyro.list_workspace_disk(...) inspects a stopped guest-backed workspace rootfs offline without booting the guest.
  • Pyro.read_workspace_disk(...) reads one regular file from a stopped guest-backed workspace rootfs offline.
  • stopped-workspace disk helpers require state=stopped and a guest-backed workspace; they fail on host_compat.
  • Pyro.diff_workspace(...) compares the current /workspace tree to the immutable create-time baseline.
  • Pyro.create_snapshot(...) captures one named /workspace checkpoint.
  • Pyro.list_snapshots(...) lists the implicit baseline plus any named snapshots.
  • Pyro.delete_snapshot(...) deletes one named snapshot while leaving baseline intact.
  • Pyro.reset_workspace(...) recreates the full sandbox from baseline or one named snapshot and clears command, shell, and service history.
  • Pyro.start_service(..., secret_env=...) maps persisted workspace secrets into that service process as environment variables for that start call only.
  • Pyro.start_service(...) starts one named long-running process in a started workspace and waits for its typed readiness probe when configured.
  • Pyro.start_service(..., published_ports=[...]) publishes one or more guest TCP ports to 127.0.0.1 on the host when the workspace network policy is egress+published-ports.
  • Pyro.list_services(...), Pyro.status_service(...), Pyro.logs_service(...), and Pyro.stop_service(...) manage those persisted workspace services.
  • Pyro.exec_vm(...) runs one command and auto-cleans that VM after the exec completes.
  • Pyro.exec_workspace(..., secret_env=...) maps persisted workspace secrets into that exec call as environment variables for that call only.
  • Pyro.exec_workspace(...) runs one command in the persistent workspace and leaves it alive.
  • Pyro.open_shell(..., secret_env=...) maps persisted workspace secrets into the shell environment when that shell opens.
  • Pyro.open_shell(...) opens a persistent PTY shell attached to one started workspace.
  • Pyro.read_shell(...) reads merged text output from that shell by cursor, with optional plain rendering and idle batching for chat-facing consumers.
  • Pyro.write_shell(...), Pyro.signal_shell(...), and Pyro.close_shell(...) operate on that persistent shell session.
  • Pyro.update_workspace(...) changes only discovery metadata such as name and key/value labels.

MCP Contract

Stable MCP profiles:

  • vm-run: exposes only vm_run
  • workspace-core: exposes vm_run, workspace_create, workspace_list, workspace_update, workspace_status, workspace_sync_push, workspace_exec, workspace_logs, workspace_file_list, workspace_file_read, workspace_file_write, workspace_patch_apply, workspace_diff, workspace_export, workspace_reset, and workspace_delete
  • workspace-full: exposes the complete stable MCP surface below

Behavioral defaults:

  • pyro mcp serve and create_server() default to workspace-full.
  • workspace-core narrows workspace_create by omitting network_policy and secrets.
  • workspace-core narrows workspace_exec by omitting secret_env.

Primary tool:

  • vm_run

Advanced lifecycle tools:

  • vm_list_environments
  • vm_create
  • vm_start
  • vm_exec
  • vm_stop
  • vm_delete
  • vm_status
  • vm_network_info
  • vm_reap_expired

Persistent workspace tools:

  • workspace_create
  • workspace_list
  • workspace_sync_push
  • workspace_stop
  • workspace_start
  • workspace_exec
  • workspace_file_list
  • workspace_file_read
  • workspace_file_write
  • workspace_export
  • workspace_patch_apply
  • workspace_disk_export
  • workspace_disk_list
  • workspace_disk_read
  • workspace_diff
  • snapshot_create
  • snapshot_list
  • snapshot_delete
  • workspace_reset
  • service_start
  • service_list
  • service_status
  • service_logs
  • service_stop
  • shell_open
  • shell_read
  • shell_write
  • shell_signal
  • shell_close
  • workspace_status
  • workspace_update
  • workspace_logs
  • workspace_delete

Behavioral defaults:

  • vm_run and vm_create default to vcpu_count=1 and mem_mib=1024.
  • workspace_create defaults to vcpu_count=1 and mem_mib=1024.
  • vm_run and vm_create expose allow_host_compat, which defaults to false.
  • workspace_create exposes allow_host_compat, which defaults to false.
  • workspace_create accepts optional seed_path and seeds /workspace from a host directory or a local .tar / .tar.gz / .tgz archive before the workspace is returned.
  • workspace_create accepts optional name and labels metadata for human discovery without changing the stable workspace_id.
  • workspace_create accepts network_policy with off, egress, or egress+published-ports to control workspace guest networking and service port publication.
  • workspace_create accepts optional secrets and persists guest-only UTF-8 secret material outside /workspace.
  • workspace_list returns persisted workspace summaries sorted by most recent last_activity_at.
  • workspace_sync_push imports later host-side directory or archive content into a started workspace, with an optional dest under /workspace.
  • workspace_stop stops one persistent workspace without deleting its /workspace, snapshots, or command history.
  • workspace_start restarts one stopped workspace without resetting /workspace.
  • workspace_file_list, workspace_file_read, and workspace_file_write provide structured live /workspace inspection and text edits without shell quoting.
  • workspace_export exports one file or directory from /workspace to an explicit host path.
  • workspace_patch_apply applies unified text patches for add/modify/delete operations under /workspace.
  • workspace_disk_export copies the stopped guest-backed workspace rootfs as raw ext4 to an explicit host path.
  • workspace_disk_list inspects a stopped guest-backed workspace rootfs offline without booting the guest.
  • workspace_disk_read reads one regular file from a stopped guest-backed workspace rootfs offline.
  • stopped-workspace disk tools require state=stopped and a guest-backed workspace; they fail on host_compat.
  • workspace_diff compares the current /workspace tree to the immutable create-time baseline.
  • snapshot_create, snapshot_list, and snapshot_delete manage explicit named snapshots in addition to the implicit baseline.
  • workspace_reset recreates the full sandbox and restores /workspace from baseline or one named snapshot.
  • service_start, service_list, service_status, service_logs, and service_stop manage persistent named services inside a started workspace.
  • service_start accepts optional published_ports to expose guest TCP ports on 127.0.0.1 when the workspace network policy is egress+published-ports.
  • vm_exec runs one command and auto-cleans that VM after the exec completes.
  • workspace_exec accepts optional secret_env mappings for one exec call and leaves the workspace alive.
  • workspace_update changes only discovery metadata such as name and key/value labels.
  • service_start accepts optional secret_env mappings for one service start call.
  • shell_open accepts optional secret_env mappings for the opened shell session.
  • shell_open, shell_read, shell_write, shell_signal, and shell_close manage persistent PTY shells inside a started workspace.

Versioning Rule

  • pyro-mcp uses SemVer.
  • Environment names are stable identifiers in the shipped catalog.
  • Changing a public command name, public flag, public method name, public MCP tool name, or required request field is a breaking change.