MCP server for QNAP Virtualization Station (QVS) — manage virtual machines, snapshots, disks, and more on your QNAP NAS via the QVS REST API.
Note: This is not an official QNAP product. The QVS REST API is undocumented — this project is based on reverse-engineering the web UI and the qnap-qvs-sdk-for-go project.
Before you start, you need:
- QNAP NAS with an x86 CPU (Intel or AMD with VT-x/AMD-V). ARM models do not support Virtualization Station.
- Virtualization Station installed from the QNAP App Center.
- Admin credentials — the username and password you use to log into the QNAP web UI. The server uses these to authenticate with the Virtualization Station API.
- Network access — the machine running the server needs HTTPS access to your NAS (port 443 by default).
- Python 3.10+ on the machine running the server.
If you want the server to report VM IP addresses (via get_vm_ips and get_overview), install the QEMU guest agent inside each VM:
# For Ubuntu/Debian VMs:
sudo apt install qemu-guest-agent
sudo systemctl enable --now qemu-guest-agentWithout the guest agent, IP-related tools will return a helpful message explaining what's needed. Everything else works without it.
| Component | Tested | Expected |
|---|---|---|
| QTS | — | 5.1.0+ |
| QuTS hero | h5.2.8 | h5.1.0+ |
| Virtualization Station | 4.1.x | 3.x+ |
| NAS hardware | x86 (AMD Ryzen) | x86 with VT-x/AMD-V |
39 tools across 7 categories:
- VM lifecycle — start, shutdown, force-stop, reset, suspend, resume
- VM management — create, update settings (CPU, memory, name, auto-start), delete, clone, export, import
- VM info — details, state, IPs, adapters, graphics/VNC, CD-ROMs, USBs
- Disk & ISO — list disks, resize, delete, mount/unmount ISOs, scan for ISOs, download ISOs from URL
- Snapshots — list, create, revert, delete
- Analysis — resource overview dashboard (host CPU/RAM utilization, per-VM summary with networking), QVS audit logs, shutdown progress
- Safety — all destructive operations require explicit
confirm=true
# Via uvx (recommended)
uvx mcp-server-qnap-qvs
# Or via pip
pip install mcp-server-qnap-qvs
# Or via Docker (for remote/NAS deployment)
docker pull ghcr.io/arnstarn/mcp-server-qnap-qvs:latestThere are two separate authentication layers:
The server authenticates to your QNAP's Virtualization Station API using your NAS admin credentials. This happens automatically — you just provide the credentials via environment variables:
| Variable | Description | Required |
|---|---|---|
QNAP_HOST |
NAS hostname or IP address | Yes |
QNAP_PORT |
HTTPS port (default: 443) |
No |
QNAP_USERNAME |
QTS admin username | Yes |
QNAP_PASSWORD |
QTS admin password | Yes |
QNAP_VERIFY_SSL |
Verify TLS certificate (default: false) |
No |
Most QNAP devices use self-signed certificates, so QNAP_VERIFY_SSL=false is typical. Set it to true if you've installed a valid certificate.
When running in SSE mode (remote/Docker), the server requires a Bearer token so only authorized MCP clients can connect:
| Variable | Description | Required |
|---|---|---|
MCP_AUTH_TOKEN |
A secret string you choose (like a password) | No |
- If you set
MCP_AUTH_TOKEN: use that same value as the Bearer token in your MCP client config. - If you don't set it: the server generates a random token on startup and prints it to the log. Copy it from there.
- Stdio mode (local, default): no token needed — the MCP client runs the server as a local process.
The server runs on your machine. Claude Code spawns it as a subprocess — no network, no token needed.
Claude Code (~/.claude.json):
{
"mcpServers": {
"qnap-qvs": {
"command": "uvx",
"args": ["mcp-server-qnap-qvs"],
"env": {
"QNAP_HOST": "your-nas.local",
"QNAP_USERNAME": "admin",
"QNAP_PASSWORD": "your-password",
"QNAP_VERIFY_SSL": "false"
}
}
}
}Claude Desktop (claude_desktop_config.json): same format as above.
The server runs on the NAS (or any Docker host) and MCP clients connect over the network.
Start the server:
# Via Docker (recommended for NAS deployment)
docker run -d \
-p 8445:8445 \
-e QNAP_HOST=localhost \
-e QNAP_USERNAME=admin \
-e QNAP_PASSWORD=your-password \
-e QNAP_VERIFY_SSL=false \
-e MCP_AUTH_TOKEN=your-secret-token \
ghcr.io/arnstarn/mcp-server-qnap-qvs:latest
# Or via Docker Compose
cp .env.example .env # Edit .env with your credentials
docker-compose up -d
# Or directly with env vars
MCP_TRANSPORT=sse MCP_AUTH_TOKEN=your-secret-token mcp-server-qnap-qvsWhen running on the NAS itself, set QNAP_HOST=localhost since the server and the API are on the same machine.
Connect your MCP client:
{
"mcpServers": {
"qnap-qvs": {
"url": "http://your-nas.local:8445/sse",
"headers": {
"Authorization": "Bearer your-secret-token"
},
"transportType": "sse"
}
}
}- Open Container Station on your QNAP NAS
- Pull
ghcr.io/arnstarn/mcp-server-qnap-qvs:latestor import thedocker-compose.yml - Set environment variables (
QNAP_HOST=localhost,QNAP_USERNAME,QNAP_PASSWORD,MCP_AUTH_TOKEN) - The server runs on port 8445 — connect from any MCP client on your network
Install the QPKG directly on your QNAP NAS. It runs as a Docker container via Container Station.
Step 1: Add the repository
- Open App Center on your QNAP
- Click the Settings icon (gear, top-right)
- Go to App Repository
- Add this URL:
https://raw.githubusercontent.com/arnstarn/mcp-server-qnap-qvs/main/qpkg/repo.xml - Click Apply
Step 2: Install
- Search for "MCP QVS Server" in App Center
- Click Install
- Wait for the Docker image to download (first install only)
Step 3: Configure credentials
The QPKG creates a .env file with placeholder values. You need to edit it with your actual QNAP credentials.
SSH into your NAS and edit the .env file:
ssh your-username@your-nas.local
# Find the install path
QPKG_DIR=$(getcfg mcp-server-qnap-qvs Install_Path -f /etc/config/qpkg.conf)
# Edit the .env file (use vi, nano, or echo)
cat > "$QPKG_DIR/.env" << 'EOF'
QNAP_HOST=localhost
QNAP_PORT=443
QNAP_USERNAME=your-admin-username
QNAP_PASSWORD=your-admin-password
QNAP_VERIFY_SSL=false
MCP_AUTH_TOKEN=pick-any-secret-string-here
EOF
# Restart the service to pick up the new config
/etc/init.d/mcp-server-qnap-qvs.sh restartReplace your-admin-username and your-admin-password with the credentials you use to log into the QNAP web UI. The MCP_AUTH_TOKEN is any secret string you choose — you'll use it as the Bearer token in your MCP client config.
Step 4: Connect your MCP client
{
"mcpServers": {
"qnap-qvs": {
"url": "http://your-nas.local:8445/sse",
"headers": {
"Authorization": "Bearer pick-any-secret-string-here"
},
"transportType": "sse"
}
}
}Use the same MCP_AUTH_TOKEN value you set in Step 3.
Updating: When a new version is released, the App Center will show an update. Or pull the latest Docker image manually:
ssh your-username@your-nas.local
CS_DIR=$(getcfg container-station Install_Path -f /etc/config/qpkg.conf)
${CS_DIR}/bin/docker pull ghcr.io/arnstarn/mcp-server-qnap-qvs:latest
/etc/init.d/mcp-server-qnap-qvs.sh restart| Variable | Description | Default | Used In |
|---|---|---|---|
QNAP_HOST |
NAS hostname or IP | — | Both modes |
QNAP_PORT |
NAS HTTPS port | 443 |
Both modes |
QNAP_USERNAME |
QTS admin username | — | Both modes |
QNAP_PASSWORD |
QTS admin password | — | Both modes |
QNAP_VERIFY_SSL |
Verify TLS cert | false |
Both modes |
MCP_TRANSPORT |
Transport mode: stdio or sse |
stdio |
— |
MCP_HOST |
SSE listen address | 0.0.0.0 |
SSE only |
MCP_PORT |
SSE listen port | 8445 |
SSE only |
MCP_AUTH_TOKEN |
Bearer token for SSE auth | (auto-generated) | SSE only |
| Tool | Description |
|---|---|
list_vms |
List all VMs with full details |
get_vm |
Get detailed info for a single VM |
get_vm_states |
Lightweight status overview of all VMs |
get_vm_ips |
Get VM IP addresses (requires QEMU guest agent in the VM) |
list_vm_disks |
List disks attached to a VM |
get_vm_adapters |
Network interfaces — MAC, model, bridge |
get_vm_graphics |
VNC console info — port, password status |
get_vm_cdroms |
CD-ROM drives and mounted ISOs |
get_vm_usbs |
USB passthrough devices |
list_images |
Available ISO images on the NAS |
get_qvs_logs |
QVS audit/event logs (paginated) |
get_overview |
Dashboard — VM count, host resources, utilization %, per-VM summary with networking |
get_stopping_progress |
Monitor bulk shutdown operations |
| Tool | Description | Confirm? |
|---|---|---|
start_vm |
Start a stopped VM | No |
shutdown_vm |
Graceful ACPI shutdown | Yes |
force_shutdown_vm |
Immediate force stop | Yes |
reset_vm |
Hard restart | Yes |
suspend_vm |
Suspend to memory | No |
resume_vm |
Resume suspended VM | No |
| Tool | Description | Confirm? |
|---|---|---|
create_vm |
Create a new VM with CPU, memory, disk, OS type | Yes |
update_vm |
Change name, CPU, memory, auto-start, description | Yes |
delete_vm |
Permanently destroy a VM and its disks | Yes |
clone_vm |
Clone a VM with a new name | Yes |
export_vm |
Export a VM to a NAS path | Yes |
import_vm_file |
Import a VM from an OVA/OVF file on the NAS | Yes |
| Tool | Description | Confirm? |
|---|---|---|
resize_disk |
Expand a virtual disk | Yes |
delete_disk |
Remove and delete a disk | Yes |
mount_iso |
Mount an ISO to a VM's CD-ROM | Yes |
unmount_iso |
Eject an ISO from a VM's CD-ROM | Yes |
check_iso |
Scan a shared folder for available ISO files | — |
download_iso |
Download an ISO from a URL to the NAS | Yes |
| Tool | Description | Confirm? |
|---|---|---|
list_snapshots |
List snapshots for a VM | — |
create_snapshot |
Create a VM snapshot | Yes |
revert_snapshot |
Revert VM to a snapshot | Yes |
delete_snapshot |
Delete a snapshot | Yes |
All destructive operations require confirm=true. Without it, the tool returns a preview of what it would do — no changes are made. This prevents accidental VM deletions, shutdowns, or snapshot reverts.
The get_vm_ips tool and the IP section of get_overview require the QEMU guest agent running inside the VM. Install it:
# Ubuntu/Debian
sudo apt install qemu-guest-agent && sudo systemctl enable --now qemu-guest-agent
# CentOS/RHEL
sudo yum install qemu-guest-agent && sudo systemctl enable --now qemu-guest-agentAll other tools work without the guest agent.
IP addresses can only be retrieved from running VMs. Start the VM first with start_vm.
- Verify the NAS is reachable:
curl -k https://your-nas.local:443 - Check that Virtualization Station is installed and running in the QNAP App Center
- If using a non-default HTTPS port, set
QNAP_PORTaccordingly
- Verify your credentials work on the QNAP web UI
- The username and password are for the QNAP system admin account (the same one you use to log into QTS/QuTS hero)
git clone https://github.com/arnstarn/mcp-server-qnap-qvs.git
cd mcp-server-qnap-qvs
pip install -e ".[dev]"
# Run tests
pytest
# Lint
ruff check src/ tests/
# Build QPKG (requires Docker)
docker build -t qpkg-builder -f qpkg/Dockerfile.builder .
docker run --rm -v "$(pwd)/qpkg:/work" qpkg-builderThe QVS REST API is not officially documented. This project's API knowledge comes from:
- tmeckel/qnap-qvs-sdk-for-go — Go SDK auto-generated from QNAP's internal OpenAPI specs
- QTS HTTP API Authentication v5.1.0 — Official QNAP auth docs
- Browser DevTools inspection of the Virtualization Station web UI
MIT