Swapped the narration voice after auditioning the American female and male sets side by side. River also reads faster, so the recording is 3:22 rather than 4:37, and the duration label is now a component prop instead of a hardcoded string that could drift from the file again.
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-*andqemu-imgon PATH) /dev/kvmaccess for hardware acceleration (optional — TCG emulation works without it, just slower)- Python 3.11+ managed with uv
Install
# 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
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.