The unit tests mock QMP and the guest agent, so they cannot catch a wrong argument name, a QEMU option that stopped parsing, or a reply shape that differs from the fake. This suite drives the real thing: the qemu-img toolchain, lifecycle and see-and-drive on a diskless BIOS VM, adopting a forgotten VM through attach_vm, the refusal to reuse a live socket, and the full sandbox journey (guest exec, file round trip, live snapshot create / restore / delete verified by guest state, screenshot) with a check that the base image is never modified. It immediately earned its keep: pid_matches_vm was rejecting attached VMs, because an externally launched QEMU carries whatever -name its launcher chose, so adopting one under a different name made it read as stopped. The identity check now applies only to VMs we spawned.
83 lines
3.1 KiB
Markdown
83 lines
3.1 KiB
Markdown
# mcqemu
|
|
|
|
An MCP server that lets LLM agents manage QEMU virtual machines: launch and
|
|
stop VMs, inspect them over QMP, manage disk images with qemu-img, take live
|
|
snapshots, and run commands inside guests through qemu-guest-agent.
|
|
|
|
## Requirements
|
|
|
|
- Linux with QEMU installed (`qemu-system-*` and `qemu-img` on PATH)
|
|
- `/dev/kvm` access for hardware acceleration (optional — TCG emulation works
|
|
without it, just slower)
|
|
- Python 3.11+ managed with [uv](https://docs.astral.sh/uv/)
|
|
|
|
## Install
|
|
|
|
```bash
|
|
# From this checkout
|
|
uv sync
|
|
|
|
# Add to Claude Code
|
|
claude mcp add mcqemu -- uv run --directory /path/to/mcqemu mcqemu
|
|
```
|
|
|
|
## What it can do
|
|
|
|
| Group | Tools |
|
|
|---|---|
|
|
| Lifecycle | `launch_vm`, `stop_vm`, `pause_vm`, `resume_vm`, `attach_vm`, `forget_vm` |
|
|
| Sandboxes | `sandbox_vm` (overlay + launch + wait-for-agent in one call), `sandbox_destroy` |
|
|
| Inspect | `list_vms`, `vm_info` |
|
|
| Live snapshots | `vm_snapshot_create` / `restore` / `delete` / `list` |
|
|
| See & drive | `vm_screenshot` (PNG), `vm_send_keys`, `vm_type_text`, `vm_click`, `vm_mouse_move` (relative PS/2, for guests without tablet drivers), `vm_serial_read` |
|
|
| Disk images | `image_create`, `image_info`, `image_convert`, `image_resize`, `image_snapshot_*` |
|
|
| Guest agent | `guest_ping`, `guest_info`, `guest_exec`, `guest_file_read`, `guest_file_write` |
|
|
|
|
VMs are daemonized QEMU processes with QMP control sockets, so they survive
|
|
MCP server restarts. The registry lives in `~/.local/share/mcqemu/`, sockets
|
|
in `$XDG_RUNTIME_DIR/mcqemu/`.
|
|
|
|
Guest tools (`guest_*`) need `qemu-guest-agent` installed inside the guest OS;
|
|
the host-side virtio-serial channel is wired on every launch, so installing
|
|
the agent in the guest is the only step.
|
|
|
|
Port forwards accept `"2222:22"` (explicit, collision-checked up front),
|
|
`"auto:22"`, or just `"22"` — auto forms pick a free host port and the launch
|
|
result reports what was chosen.
|
|
|
|
## Quick start
|
|
|
|
Disposable sandbox from any base image with `qemu-guest-agent` inside:
|
|
|
|
```
|
|
sandbox_vm(base_image="~/vms/ubuntu-agent.qcow2")
|
|
# -> overlay created, VM booted, agent waited for, free port forwarded to 22
|
|
guest_exec(name="sandbox", command="uname", args=["-a"])
|
|
sandbox_destroy(name="sandbox") # stops VM, deletes overlay; base untouched
|
|
```
|
|
|
|
Installing an OS from scratch:
|
|
|
|
```
|
|
image_create(path="~/vms/test.qcow2", size="10G")
|
|
launch_vm(name="test", disks=["~/vms/test.qcow2"], iso="~/isos/alpine.iso",
|
|
port_forwards=["auto:22"])
|
|
# ... drive the installer with vm_screenshot / vm_type_text / vm_send_keys ...
|
|
stop_vm(name="test")
|
|
launch_vm(name="test", disks=["~/vms/test.qcow2"])
|
|
guest_exec(name="test", command="uname", args=["-a"])
|
|
```
|
|
|
|
## Development
|
|
|
|
```bash
|
|
uv run pytest # unit tests (QMP and subprocess mocked)
|
|
uv run pytest -m integration # acceptance: every tool group against real QEMU
|
|
uv run ruff check .
|
|
```
|
|
|
|
The acceptance suite boots real VMs. The guest-agent and snapshot journey
|
|
needs a base image with `qemu-guest-agent` installed — it looks for
|
|
`~/vms/ubuntu-agent.qcow2`, overridable with `MCQEMU_TEST_BASE_IMAGE`, and
|
|
skips cleanly when absent.
|