The caption named an API call, which meant nothing to a first-time reader; it now describes what the picture actually shows, in prose rather than monospace. The cards were offset because CardGrid was using the stagger prop, which deliberately drops alternate columns down the page. Removed, so the four cards sit in an even grid.
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.