Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Libvirt

FreeNot checked

Enables management of KVM/QEMU virtual machines on remote libvirt hosts via SSH, with tools for inspection, lifecycle management, snapshots, and cloning.

GitHubEmbed

About

Enables management of KVM/QEMU virtual machines on remote libvirt hosts via SSH, with tools for inspection, lifecycle management, snapshots, and cloning.

README

A Model Context Protocol (MCP) server for managing KVM/QEMU virtual machines on remote libvirt hosts. Talks libvirt RPC over SSH — no agent on the hypervisor, no need to install anything on the target host beyond the standard libvirtd.

Functionally a "virt-manager for LLMs": inspect, start/stop, snapshot, clone, and delete KVM/QEMU domains through 13 MCP tools.

Tools

Group Tools
Inspection list_domains, domain_info, snapshot_list, screenshot
Lifecycle start_domain, shutdown_domain (ACPI + poll), reboot_domain, force_destroy_domain
Snapshots snapshot_create, snapshot_revert, snapshot_delete
Management clone_from_template (linked clone), delete_domain (with optional wipe_disks)

Destructive operations (force_destroy_domain, snapshot_revert, snapshot_delete, delete_domain) require an explicit confirm=True argument so an LLM can't fire them by accident.

Architecture

┌──────────────┐                          ┌──────────────────────┐
│ MCP client   │  ── stdio (JSON-RPC) ──▶ │ libvirt-mcp          │
│ Claude Code, │                          │ (Docker container)   │
│ Desktop, …   │                          │  • libvirt-python    │
└──────────────┘                          │  • mcp SDK           │
                                          └──────────┬───────────┘
                                                     │
                                                     │ qemu+ssh://user@host/system
                                                     │ (uses host's ~/.ssh/)
                                                     ▼
                                          ┌──────────────────────┐
                                          │ Hypervisor           │
                                          │  • libvirtd          │
                                          │  • qemu-kvm          │
                                          └──────────────────────┘

The MCP server runs inside a Docker container (so libvirt-dev headers don't need to be installed on your machine). It bind-mounts your ~/.ssh read-only and uses standard libvirt SSH transport, inheriting ~/.ssh/config, agent forwarding, jump hosts, etc.

Setup

1. Pull the image

The container image is published as ghcr.io/rzippert/libvirt-mcp (public — no auth required, even though the source repo is private):

docker pull ghcr.io/rzippert/libvirt-mcp:latest

Tags published:

  • latest — head of main
  • X.Y.Z and X.Y — from git tags vX.Y.Z (the v prefix is dropped per Docker convention)
  • main, sha-<short> — for traceability

2. Create your config

mkdir -p ~/.config/libvirt-mcp
cat > ~/.config/libvirt-mcp/config.toml <<'EOF'
default_profile = "primary"

[profiles.primary]
uri = "qemu+ssh://[email protected]/system"
description = "Primary KVM hypervisor"
EOF
$EDITOR ~/.config/libvirt-mcp/config.toml

The libvirt URI is standard. SSH keys come from your host's ~/.ssh/. Make sure you can already connect:

ssh [email protected] virsh -c qemu:///system list --all

3. Wire it up to an MCP client

Claude Code

claude mcp add libvirt --scope user -- \
  docker run -i --rm \
    -v "$HOME/.ssh:/home/ubuntu/.ssh:ro" \
    -v "$HOME/.config/libvirt-mcp/config.toml:/etc/libvirt-mcp/config.toml:ro" \
    --network host \
    -e LIBVIRT_MCP_CONFIG=/etc/libvirt-mcp/config.toml \
    ghcr.io/rzippert/libvirt-mcp:latest

Verify with claude mcp list. Remove with claude mcp remove libvirt --scope user.

Claude Desktop

Edit claude_desktop_config.json:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "libvirt": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-v", "/Users/youruser/.ssh:/home/ubuntu/.ssh:ro",
        "-v", "/Users/youruser/.config/libvirt-mcp/config.toml:/etc/libvirt-mcp/config.toml:ro",
        "--network", "host",
        "-e", "LIBVIRT_MCP_CONFIG=/etc/libvirt-mcp/config.toml",
        "ghcr.io/rzippert/libvirt-mcp:latest"
      ]
    }
  }
}

Replace /Users/youruser with your home directory. On Windows use forward slashes or double-escape backslashes. Restart Claude Desktop after editing.

VS Code (GitHub Copilot Chat MCP)

Create .vscode/mcp.json in your workspace (project-scoped) or add to user settings:

{
  "servers": {
    "libvirt": {
      "type": "stdio",
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-v", "${userHome}/.ssh:/home/ubuntu/.ssh:ro",
        "-v", "${userHome}/.config/libvirt-mcp/config.toml:/etc/libvirt-mcp/config.toml:ro",
        "--network", "host",
        "-e", "LIBVIRT_MCP_CONFIG=/etc/libvirt-mcp/config.toml",
        "ghcr.io/rzippert/libvirt-mcp:latest"
      ]
    }
  }
}

VS Code expands ${userHome} and ${workspaceFolder}. Open the Copilot Chat panel → MCP servers → enable libvirt.

Configuration reference

~/.config/libvirt-mcp/config.toml (or override with LIBVIRT_MCP_CONFIG):

default_profile = "primary"   # used when a tool is called without `profile=`

[profiles.<name>]
uri = "qemu+ssh://user@host/system"   # standard libvirt URI
description = "Free-form description for humans"

Config search order:

  1. $LIBVIRT_MCP_CONFIG env var
  2. ~/.config/libvirt-mcp/config.toml
  3. ./config.dev.toml
  4. ./config.toml

Safety model

  • Allow-list validation on domain names (^[a-z0-9][a-z0-9-]{0,30}$), bridge names, and absolute paths (no .. segments).
  • Inspection tools open read-only libvirt connections; state-changing tools open read-write connections only when invoked.
  • Confirmation gates on destructive operations: force_destroy_domain, snapshot_revert, snapshot_delete, delete_domain all require confirm=True.
  • No subprocess on the hypervisor: every operation is pure libvirt RPC (no qemu-img, no virt-install, no virsh shell-out). Disk volumes are created and deleted through libvirt's storage volume API, reusing any active dir-pool that covers the target path or transparently creating a transient one.

Development

git clone [email protected]:rzippert/libvirt-mcp.git
cd libvirt-mcp
cp config.example.toml config.dev.toml && $EDITOR config.dev.toml
docker compose build
docker compose run --rm dev bash

Inside the dev container, source on the host (bind-mounted at /workspace) shadows the installed package via PYTHONPATH, so edits take effect immediately. Exercise tools directly:

python -c "from libvirt_mcp.tools.readonly import list_domains; \
           import json; print(json.dumps(list_domains(), indent=2))"

Project layout:

libvirt_mcp/
├── __main__.py        # python -m libvirt_mcp → mcp.run() (stdio)
├── server.py          # FastMCP instance
├── connections.py     # config loader + connect() context manager
├── states.py          # VIR_DOMAIN_* → string
└── tools/
    ├── readonly.py    # inspection
    ├── lifecycle.py   # start/shutdown/reboot/force_destroy
    ├── snapshot.py    # snapshot CRUD (revert/delete gated)
    └── management.py  # clone_from_template, delete_domain

Publishing happens automatically via .github/workflows/publish.yml on push to main and on v* tags (multi-arch: linux/amd64, linux/arm64).

License

GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). See LICENSE.

from github.com/rzippert/libvirt-mcp

Installing Libvirt

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/rzippert/libvirt-mcp

FAQ

Is Libvirt MCP free?

Yes, Libvirt MCP is free — one-click install via Unyly at no cost.

Does Libvirt need an API key?

No, Libvirt runs without API keys or environment variables.

Is Libvirt hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Libvirt in Claude Desktop, Claude Code or Cursor?

Open Libvirt on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Libvirt with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All development MCPs