Ryan Malloy 6fd10bef44 Renarrate the talk with af_river
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.
2026-08-18 15:25:48 -06:00
2026-08-16 22:03:16 -06:00

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

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.

Description
MCP server for managing QEMU virtual machines: lifecycle, disposable sandboxes, live snapshots, guest agent, and see-and-drive screenshots/input
Readme MIT 4.7 MiB
Languages
Python 57.5%
MDX 36.2%
CSS 2.2%
Astro 1.9%
JavaScript 1%
Other 1.2%