QMP-MCP

by r0guesch0lar

Not rated yet

About

Create, run and manage qemu virtual machines

Explore

The server speaks MCP overstdio(the default — how most clients launch a server directly; no network, no auth) or overHTTP(for a networked deployment), or both at once. The HTTP transport isfail-closed: it refuses to start without authentication — an API key, or a signed HS256 token — unless you explicitly opt into insecure mode for local use. A server that can build and run VMs has no business being reachable unauthenticated. It runs as a non-root user in every mode and never needs--privileged.

The agent's vocabulary — the actions it can take:

For the exact per-implementation tool tables, see theTypeScriptandRustREADMEs.

The server runs wherever QEMU is installed. First get one of the implementations running and point your MCP client at it —run the TypeScript variantorrun the Rust variant— then ask your agent to do something. The scenarios below are what that looks like: each is a Hardware Spec (the arguments tocreate_instance) plus whatever you had to put in place first.

Nothing to set up — just ask for a small machine and drive it.

"Boot a 1 GB Linux VM and tell me its run state."

The agent callscreate_instancewith a minimal spec, thenget_status;destroy_instancecleans up:

{ "machine": "q35", "cpu": "host", "vcpus": 1, "memoryMb": 1024, "accel": "auto" }

(With no disk or ISO there's nothing to boot — perfect for a smoke test; add media for the real thing.)

Put the installer ISO in yourISO Storefolder; the agent creates a blank disk for it and boots from the CD first (boot: "dc").

"Create a 20 GB disk and install Debian from debian-13.iso onto it."

It callscreate_image(into the Image Store), thencreate_instance:

{ "machine": "q35", "cpu": "host", "vcpus": 2, "memoryMb": 2048, "accel": "auto", "disks": [{ "image": "debian.qcow2" }], "cdrom": { "iso": "debian-13.iso" }, "boot": "dc", "display": "vnc" }

Because it asked fordisplay: "vnc", you can watch the installer run — see scenario 4.

Add ahost forwardso a port on your host reaches a port in the guest.

"Run my server image headless and forward host port 2222 to guest 22."

{ "machine": "q35", "cpu": "host", "vcpus": 2, "memoryMb": 2048, "accel": "auto", "disks": [{ "image": "server.qcow2" }], "network": { "hostForwards": [{ "hostPort": 2222, "guestPort": 22 }] } }

Once it's booted,ssh -p 2222 user@localhostfrom the host reaches the guest's SSH.

SetQMP_MCP_VIEWER_PASSWORD, ask for avncdisplay, and open the Viewer. The setup details are in theTypeScript/RustREADMEs; any spec with"display": "vnc"then gets a live, interactive screen athttp://<host>:6080/.

Pick an ARM machine and CPU — theqemu-system-aarch64emulator is chosen automatically from themachine(noQMP_MCP_QEMU_BINARYneeded).

"Bring up an ARM64 virtual machine."

{ "machine": "virt", "cpu": "cortex-a72", "vcpus": 2, "memoryMb": 2048, "accel": "tcg" }

On an x86 hostaccel: autoalready resolves to TCG (an aarch64 guest can't use x86 KVM). On an ARM host it would use KVM, which only accepts ahost/maxCPU — so a named model likecortex-a72there needsaccel: tcg(as above), and theraspiboards always run under TCG (their baked CPU can't be virtualized).

(If you also need tobuildthe Rust binary for a non-x86 host, see itscross-compilation guide.)

QEMU's Raspberry Pi machines boot a kernel directly and render a framebuffer you can watch in the browser Viewer. Put the extracted kernel and device tree in the Image Store (theraspimachines selectqemu-system-aarch64for you), and:

"Boot a Raspberry Pi 3 and show me the console."

{ "machine": "raspi3b", "accel": "tcg", "kernel": "kernel8.img", "dtb": "bcm2710-rpi-3-b.dtb", "appendCmdline": "console=tty1 root=/dev/mmcblk0p2 rootwait rw", "disks": [{ "image": "raspios.img", "interface": "sd", "format": "raw" }], "network": { "model": "usb-net" }, "display": "vnc" }

console=tty1puts the console on the framebuffer, so the noVNC Viewer shows the Pi booting — logos and all. Nocpu/vcpus/memoryMb: the board's hardware is fixed. The Pi has no PCI bus, so the default NIC can't attach — use"network": { "model": "usb-net" }for its USB NIC, or"network": { "mode": "none" }for no networking at all. (On a Pi 3, merge thedisable-btdevice-tree overlay into the dtb first, or the console stays glued to the Bluetooth-shared UART instead of the screen.)

The two are interchangeable — same tools, same specs, same behavior, continuously checked against each other. Pick by ecosystem:

Everything deployment- and usage-specific lives in those two READMEs:

- TypeScript—Run it·Transports & auth·Docker·Browser viewer·Configuration·Developing
- Rust—
Run it·Transports & auth·Docker·KVM acceleration·Browser viewer·Cross-compilation·Configuration·Developing

Both implementations are configured entirely throughQMP_MCP_environment variables —the same names and defaults for each. The fully-commented reference is.env.example, and the command-policy file format ispolicy.example.yaml. The ones you'll reach for:

…plus caps on disk/memory/vCPUs, the host-forward port range, the recording encoder knobs (codec, CRF, max-fps, pixel format), the Command Policy allow/deny lists and policy file, and the Event Buffer size. See.env.examplefor the full list, or each variant's Configuration section in context (TypeScript·Rust).

qmp-mcp/ ├── typescript/ the Node / mcp-framework implementation ├── rust/ the Rust / rmcp implementation ├── testdata/ shared golden fixtures both implementations assert ├── docs/ design notes and rationale ├── CONTEXT.md the domain glossary — the shared vocabulary ├── .env.example every QMP_MCP_ variable, commented └── policy.example.yaml the command-policy file format

The two implementations are independent codebases that share three things at the root: thedomain model(CONTEXT.md— read it first), thegolden fixtures(testdata/), and theconfig surface(.env.example).

Parity here isn't a promise, it's a test.testdata/holds language-neutral golden fixtures that pin the exact QEMU command line each Hardware Spec must produce and the exact verdict the Command Policy must return — andbothimplementations are tested against that same corpus. Change how a spec becomes a command line, or what the policy allows, and you update the shared fixture; the TypeScript suiteandthe Rust suite have to agree, or the build fails. Teach one implementation a new trick and you add the fixture the other has to satisfy.

Working on a variant is self-contained in its folder —developing TypeScript·developing Rust. Thedocs/folder holds the longer-form rationale behind the trickier decisions.

This is a web browser that enables your coding agent, such as Claude Code, to visit websites on your behalf and assist you in identifying bugs or creating UI test cases.

Create crafted UI components inspired by the best 21st.dev design engineers.

Bring agent evaluations, observability, and synthetic test set generation directly into your IDE for free with Galileo's new MCP server

An MCP server to help AI assistants to answer questions and generate AccelByte Extend SDK code more effectively .

MCP server for AI Diagram Maker — generate beautiful software engineering diagrams directly inside Cursor, Claude Desktop, Claude Code, or any MCP-compatible AI agent

ALAPI MCP Tools,Call hundreds of API interfaces via MCP

AI-powered SVG animation generator that transforms static files into animated SVG components using the Allyson platform

MCP server that gives AI assistants on-demand access to 1,500+ amCharts docs, ~300 code examples, and 1000+ class API references.

APIMatic MCP Server is used to validate OpenAPI specifications using APIMatic. The server processes OpenAPI files and returns validation summaries by leveraging APIMatic’s API.

qmp-mcpis aModel Context Protocol(MCP) server that gives an AI agent the controls of a singleQEMUvirtual machine. The agent describes the hardware it wants; the server builds that machine, boots it, and exposes a set of tools to drive it — pause and resume it, reset it, watch its screen, send it low-level QEMU commands, react to its events, and tear it down when it's finished.

The whole design rests on one idea: thetools are the boundary. The agent never hands raw arguments to QEMU or reaches into your filesystem. It fills in a structured, validated description of the machine; the server turns that into a locked-down QEMU command line and mediates every request. Everything the agent can touch — disk images, boot media, the commands it can run against the live VM, the ports it can open — passes through allowlists you control. The VM is the blast radius, and the tools are the walls.

It ships astwo interchangeable implementations— one inTypeScript, one inRust— that behave identically. This page explains what the serverisand how it thinks; the per-implementation READMEs cover installing, running, and deploying each one.

New to the vocabulary?CONTEXT.mdis the one-page glossary. The words below —Instance,Guest,Hardware Spec,Command Policy,Image Store,Viewer— each mean something specific, and this README uses them deliberately.

The server manages exactly oneInstance— the runningqemu-system-process together with its hardware configuration and the live control connection to it. There's never more than one; asking to create another while one exists is refused. An Instance's life is tied to the server's: shut the server down and it tears the VM down with it, so nothing is left orphaned.

An Instance moves through a small lifecycle — from nothing, to starting, torunning, optionallypausedand back, to stopped, and back to nothing:

NONE → STARTING → RUNNING ⇄ PAUSED → STOPPED → NONE

If the underlying QEMU process exits on its own — a guest shutdown, a crash, an external kill — the server notices and reconciles back toNONE, so the next request starts from a clean slate.

The thing runninginsidethe Instance — the operating system or workload — is theGuest. The server manages the machine; what you install and run on it is up to you and your agent.

Describing the machine: the Hardware Spec

The agent doesn't run QEMU. It submits aHardware Spec— a structured, validated description of the machine it wants: machine type and CPU, how many vCPUs and how much memory, which disks and boot media, the network, the display, the accelerator. The server validates every field andgeneratesthe QEMU command line from it. The agent never supplies raw argv.

A spec is just the JSON arguments tocreate_instance:

{ "machine": "q35", "cpu": "host", "vcpus": 2, "memoryMb": 2048, "accel": "auto", "disks": [{ "image": "root.qcow2" }], "cdrom": { "iso": "debian-13.iso" }, "boot": "dc", "display": "vnc" }

Validation isn't a formality — it's the safety boundary. Fields are range- and character-checked, and anything that could smuggle an extra option into the command line (a stray comma in a disk entry, say) is escaped or rejected. Sizes are capped, with ceilings you set on disk, memory, and vCPUs. If a spec is invalid,create_instancefailsbeforeQEMU is launched, with a message that says exactly what was wrong.

There is an escape hatch —extraArgs, which appends raw QEMU flags to the generated command line — but it's off unless you explicitly enable it. It's meant for trusted, single-tenant setups where you've decided the agent can be handed the keys.

Which architecture you emulate falls out of themachine: the server picks the emulator for you —q35/pclaunchqemu-system-x86_64, whilevirtand theraspiboards launchqemu-system-aarch64— so switching architectures is just a differentmachine, no restart.QMP_MCP_QEMU_BINARYoverrides that choice for every Instance (e.g. a custom build orqemu-system-riscv64), andaccel: autoonly uses KVM when the guest arch matches the host, falling back to TCG across architectures (ADR-0013).

Some machines don't boot from a disk at all. QEMU's Raspberry Pi boards (raspi3band friends) have fixed hardware — a set CPU, core count, and RAM — and they expect the kernel handed to them directly rather than read off an SD-card bootloader. For those the spec grows three optional fields:kernelanddtb(a kernel image and device-tree blob, each a name in the Image Store) andappendCmdline(the kernel command line). The server emits-kernel/-dtb/-appendand, because the board's hardware is fixed, omits-cpu/-smp/-m; attach the SD image with"interface": "sd"(sized to a power of two, or QEMU refuses it). These boards also have no PCI bus, so the default NIC can't attach — picknetwork.modelusb-net(their USB NIC) ornetwork.modenone; the server refuses an unattachable NIC up front rather than letting QEMU abort. None of this is Pi-only — any direct-kernel boot (a barevirtmachine, say) can usekernel/appendCmdlinealongside the usual CPU and memory settings.

accel: "auto"(the default) uses hardwareKVMwhen the host can reach a/dev/kvm, and otherwise falls back toTCGsoftware emulation — reporting which it chose. Ask forkvmexplicitly and it fails loudly if KVM isn't available; ask fortcgand you always get portable, zero-privilege emulation. KVM is never required — it's a performance upgrade you opt into, not a privilege the server demands.

Once an Instance is up, the server talks to it over theQMP Session— QEMU's own Machine Protocol, a JSON control channel on a private socket the server owns and never exposes on the network. The server negotiates the session at launch (reads the greeting, sendsqmp_capabilities), and from then on every "drive the VM" tool is a QMP command underneath:pause_instancestops the CPUs,get_statusasks QEMU its run state,screendumpgrabs a framebuffer snapshot, and so on.

For anything without a purpose-built tool, there'sqmp_execute— a generic "run this QMP command" — which brings us to the guardrail on it.

What the agent may command: the Command Policy

qmp_executecould in principle runanyQMP command, which is both powerful and dangerous. TheCommand Policydecides which ones actually go through. Out of the box it's a safe-by-default allowlist; genuinely dangerous commands —migrate,dump-guest-memory,human-monitor-command, and their kin — sit behind ahard denylist that can't be re-enabled. You can widen or narrow the middle ground with an environment variable or a policy file.

One subtlety: the policy gates commands byname, not by their arguments. So a command whoseargumentscould be dangerous — a screen capture that writes to a host file, for instance — isn't exposed through the generic tool at all. It gets a purpose-built tool that validates the arguments for you.

Where files live: the Image Store and ISO Store

The agent refers to disks and boot mediaby name, never by host path — and those names resolve inside two folders you designate:

- TheImage Storeis a single read-write directory for guest disk images. The agent can list what's there and create new blank images in it, and disks in a spec are looked up by name within it.
- TheISO Storeis a separate read-only directory for installation and boot ISOs. Keeping it distinct means install media can never be written to.

…

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.