Sysadmin
FreeNot checkedProvider-agnostic MCP server for managing heterogeneous infrastructure (SSH, Proxmox VE, Virtualizor, Hetzner Cloud, Cloudflare) via a JSON inventory, exposing
About
Provider-agnostic MCP server for managing heterogeneous infrastructure (SSH, Proxmox VE, Virtualizor, Hetzner Cloud, Cloudflare) via a JSON inventory, exposing tools for VM/container management, DNS, firewalls, and SSH operations.
README
Servidor MCP agnóstico de proveedor para administrar infraestructura heterogénea: servidores físicos, VPS por SSH, clusters Proxmox VE, paneles Virtualizor, Hetzner Cloud y Cloudflare.
A diferencia de MCPs atados a un hosting (p. ej. Cloudways), este proyecto usa un inventario JSON donde registras cada host con su proveedor y credenciales. Un mismo cliente MCP puede operar Proxmox en tu homelab, Virtualizor en un datacenter y servidores bare-metal en otra ubicación.
Arquitectura
flowchart LR
Client[Cliente MCP / Cursor] --> MCP[mcp-sysadmin]
MCP --> Inv[(inventory.json)]
MCP --> SSH[SSH]
MCP --> PVE[Proxmox API]
MCP --> VZ[Virtualizor API]
MCP --> HZ[Hetzner API]
MCP --> CF[Cloudflare API]
SSH --> Physical[Servidores físicos / VPS]
PVE --> VMs1[VMs KVM / LXC]
VZ --> VMs2[VPS OpenVZ/KVM/Xen]
HZ --> VMs3[Cloud Servers]
CF --> DNS[DNS / CDN / WAF]
Proveedores soportados
| Provider | Uso | Autenticación |
|---|---|---|
ssh |
Servidores físicos, VPS sin API, cualquier Linux | Clave privada o password |
proxmox |
Clusters / nodos Proxmox VE | API Token (PVEAPIToken) |
virtualizor |
Panel Virtualizor (Admin API) | apiKey + apiPass |
hetzner |
Hetzner Cloud (servidores, firewalls, volúmenes) | API Token (Bearer) |
cloudflare |
DNS, CDN, WAF (zonas y registros) | API Token (Bearer) |
Tools incluidos (38)
Inventario
list-hosts,get-host
Nodos / métricas
list-nodes,get-node-status,health-check
Máquinas virtuales / cloud
list-vms,list-containers,get-vm,vm-powerlist-vm-snapshots,create-vm-snapshotlist-proxmox-tasks,get-proxmox-tasklist-storage-usage,list-backups,create-backuplist-hetzner-firewalls,list-hetzner-volumes
Cloudflare (DNS / CDN)
list-zones,list-dns-records,get-dns-recordcreate-dns-record,update-dns-record,delete-dns-record(confirmToken)purge-cache(confirmToken)list-waf-rules
Red
list-network
SSH — operaciones controladas
ssh-exec,ssh-read-file(destructivas / confirmToken)
SSH — diagnóstico read-only
ssh-tail-log— journalctl o tail en/var/log/list-firewall-rules— UFW / nftables / iptableslist-systemd-units— failed / running / allcert-status— certbot / fechas SSLdns-lookup,check-endpointlist-cron,list-timersdocker-compose-ps
Instalación
📖 Manuales operativos: manuales/ — manual general y guías por provider.
Opción A — GitHub Packages + npx (recomendado)
Publicado en GitHub Packages como @kreodevs/mcp-sysadmin. No necesitas clonar el repo.
1. Registry de GitHub (una vez por máquina):
echo "@kreodevs:registry=https://npm.pkg.github.com" >> ~/.npmrc
O copia .npmrc.example. Los paquetes públicos no requieren token para instalar.
2. Inventario — crea tu inventory.json en cualquier ruta (p. ej. ~/mcp/inventory.json). Puedes basarte en config/inventory.example.json.
3. Probar en terminal:
export SYSADMIN_INVENTORY_PATH=~/mcp/inventory.json
export SYSADMIN_PRODUCTION_MODE=true
export SYSADMIN_CONFIRM_TOKEN=$(openssl rand -hex 32)
npx -y @kreodevs/mcp-sysadmin
4. Cliente MCP — configura npx (ver Instalación por cliente MCP abajo).
| Cliente | Botón 1 clic |
|---|---|
| Cursor | Add to Cursor |
| VS Code | Install MCP in VS Code |
⚠️ Tras el 1 clic, edita en el diálogo:
SYSADMIN_INVENTORY_PATH(ruta a tu inventario) ySYSADMIN_CONFIRM_TOKEN.
Generar enlaces personalizados:
SYSADMIN_INVENTORY_PATH=/ruta/a/inventory.json ./scripts/generate-install-links.sh
# Modo desarrollo local (clone): INSTALL_MODE=local ./scripts/generate-install-links.sh
Opción B — Desarrollo desde fuente
git clone https://github.com/kreodevs/mcp-sysadmin.git
cd mcp-sysadmin
npm install
npm run build
cp config/inventory.example.json config/inventory.json
Usa scripts/run-mcp.sh o npm run dev.
Publicar nueva versión (maintainers)
- Sube la versión en
package.jsonysrc/index.ts - Crea un GitHub Release (tag
vX.Y.Z) → el workflow .github/workflows/publish.yml publica en GitHub Packages - Verifica en Packages del repo:
@kreodevs/mcp-sysadmin
Configuración
- Copia el inventario de ejemplo:
cp config/inventory.example.json config/inventory.json
Edita
config/inventory.jsoncon tus hosts reales.Variables de entorno (opcional):
cp .env.example .env
SYSADMIN_INVENTORY_PATH=./config/inventory.json
SYSADMIN_PRODUCTION_MODE=true
SYSADMIN_CONFIRM_TOKEN=un-secreto-largo-que-el-llm-no-conoce
SYSADMIN_READ_ONLY=false
SYSADMIN_REQUIRE_CONFIRM=true
SYSADMIN_RATE_LIMIT_MAX=30
SYSADMIN_HTTP_TIMEOUT_MS=30000
SYSADMIN_SSH_TIMEOUT_MS=30000
ACL por host (inventario)
Cada host puede restringir qué tools puede usar el LLM:
{
"defaults": { "readOnly": false, "requireConfirm": true },
"hosts": [
{
"id": "pve-prod",
"readOnly": false,
"allowedTools": ["list-vms", "get-vm", "vm-power"],
"provider": "proxmox",
"...": "..."
}
]
}
readOnly: true— solo tools de lectura en ese hostallowedTools— lista blanca; si se omite, todas las tools del provider están permitidas
Referencias a secretos en el inventario
Puedes usar ${VAR} para no guardar credenciales en texto plano:
{
"tokenSecret": "${PROXMOX_HOMELAB_TOKEN}",
"apiKey": "${VIRTUALIZOR_API_KEY}",
"apiPass": "${VIRTUALIZOR_API_PASS}"
}
Ejemplo: Proxmox
{
"id": "pve-prod",
"name": "Proxmox Producción",
"provider": "proxmox",
"url": "https://10.0.0.2:8006",
"tokenId": "root@pam!cursor-mcp",
"tokenSecret": "${PROXMOX_TOKEN}",
"verifySsl": false,
"defaultNode": "pve1",
"tags": ["production"]
}
Crea el token en Proxmox: Datacenter → Permissions → API Tokens.
Ejemplo: Virtualizor
{
"id": "vz-panel",
"name": "Virtualizor DC1",
"provider": "virtualizor",
"url": "https://panel.example.com:4085",
"apiKey": "${VIRTUALIZOR_API_KEY}",
"apiPass": "${VIRTUALIZOR_API_PASS}",
"tags": ["vps"]
}
Ejemplo: Servidor físico (SSH)
{
"id": "metal-01",
"name": "Bare Metal Rack A",
"provider": "ssh",
"host": "203.0.113.50",
"port": 22,
"username": "root",
"privateKeyPath": "~/.ssh/id_ed25519",
"tags": ["physical", "production"]
}
Ejemplo: Hetzner Cloud
{
"id": "hz-cloud",
"name": "Hetzner Cloud",
"provider": "hetzner",
"apiToken": "${HETZNER_API_TOKEN}",
"defaultLocation": "fsn1",
"allowedTools": ["list-vms", "get-vm", "vm-power", "list-nodes", "health-check"],
"tags": ["cloud", "hetzner"]
}
Crea el token en Hetzner Cloud Console → Security → API Tokens (permisos Read & Write para power actions).
Ejemplo: Cloudflare
{
"id": "cf-main",
"name": "Cloudflare Production",
"provider": "cloudflare",
"apiToken": "${CLOUDFLARE_API_TOKEN}",
"defaultZoneId": "${CLOUDFLARE_ZONE_ID}",
"readOnly": true,
"allowedTools": ["list-zones", "list-dns-records", "get-dns-record", "list-waf-rules"],
"tags": ["dns", "cdn"]
}
Crea un API Token en Cloudflare con permisos mínimos: Zone → DNS (Read) y, si necesitas escritura, DNS Edit + Cache Purge.
Instalación por cliente MCP
Transporte stdio: el cliente lanza npx @kreodevs/mcp-sysadmin (GitHub Packages) o un script local en desarrollo.
Requisito previo: @kreodevs:registry=https://npm.pkg.github.com en
~/.npmrco--registry=https://npm.pkg.github.comen losargsde npx (incluido en los ejemplos).
Instalación rápida (1 clic)
Los botones de la sección Instalación → Opción A usan npx + GitHub Packages. Solo debes ajustar SYSADMIN_INVENTORY_PATH y SYSADMIN_CONFIRM_TOKEN en el diálogo del IDE.
# Enlaces con tu inventario:
SYSADMIN_INVENTORY_PATH=/ruta/a/inventory.json ./scripts/generate-install-links.sh
Cursor
Archivo: ~/.cursor/mcp.json (global) o .cursor/mcp.json (por proyecto)
UI: Settings → Tools & MCP → New MCP Server
Manual (GitHub Packages):
{
"mcpServers": {
"sysadmin": {
"command": "npx",
"args": ["-y", "--registry=https://npm.pkg.github.com", "@kreodevs/mcp-sysadmin"],
"env": {
"SYSADMIN_INVENTORY_PATH": "/ruta/a/inventory.json",
"SYSADMIN_PRODUCTION_MODE": "true",
"SYSADMIN_CONFIRM_TOKEN": "tu-secreto-humano",
"SYSADMIN_REQUIRE_CONFIRM": "true",
"PROXMOX_HOMELAB_TOKEN": "..."
}
}
}
}
Desarrollo local (clone del repo)
{
"mcpServers": {
"sysadmin": {
"command": "/ruta/absoluta/mcp-sysadmin/scripts/run-mcp.sh",
"env": {
"SYSADMIN_INVENTORY_PATH": "/ruta/absoluta/mcp-sysadmin/config/inventory.json",
"SYSADMIN_PRODUCTION_MODE": "true",
"SYSADMIN_CONFIRM_TOKEN": "tu-secreto-humano"
}
}
}
}
Claude Desktop
Archivo:
| SO | Ruta |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
UI: Settings → Developer → Edit Config
{
"mcpServers": {
"sysadmin": {
"command": "npx",
"args": ["-y", "--registry=https://npm.pkg.github.com", "@kreodevs/mcp-sysadmin"],
"env": {
"SYSADMIN_INVENTORY_PATH": "/ruta/a/inventory.json",
"SYSADMIN_PRODUCTION_MODE": "true",
"SYSADMIN_CONFIRM_TOKEN": "tu-secreto-humano"
}
}
}
}
Reinicia Claude Desktop tras guardar.
Claude Code (CLI)
claude mcp add sysadmin -- npx -y --registry=https://npm.pkg.github.com @kreodevs/mcp-sysadmin
Exporta SYSADMIN_INVENTORY_PATH y SYSADMIN_CONFIRM_TOKEN en el entorno o en la config de Claude Code.
OpenCode
Archivo: opencode.json / opencode.jsonc (proyecto) o ~/.config/opencode/opencode.json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"sysadmin": {
"type": "local",
"command": [
"npx",
"-y",
"--registry=https://npm.pkg.github.com",
"@kreodevs/mcp-sysadmin"
],
"enabled": true,
"environment": {
"SYSADMIN_INVENTORY_PATH": "/ruta/a/inventory.json",
"SYSADMIN_PRODUCTION_MODE": "true",
"SYSADMIN_CONFIRM_TOKEN": "tu-secreto-humano",
"SYSADMIN_REQUIRE_CONFIRM": "true"
}
}
}
}
OpenCode usa
environment, noenv. Elcommanddebe ser un array.
opencode mcp add
opencode mcp list
VS Code
Archivo: .vscode/mcp.json (workspace)
Manual:
{
"servers": {
"sysadmin": {
"type": "stdio",
"command": "npx",
"args": ["-y", "--registry=https://npm.pkg.github.com", "@kreodevs/mcp-sysadmin"],
"env": {
"SYSADMIN_INVENTORY_PATH": "/ruta/a/inventory.json",
"SYSADMIN_PRODUCTION_MODE": "true",
"SYSADMIN_CONFIRM_TOKEN": "tu-secreto-humano"
}
}
}
}
Requiere GitHub Copilot con MCP o extensión compatible.
Windsurf (Cascade)
Archivo: ~/.codeium/windsurf/mcp_config.json
{
"mcpServers": {
"sysadmin": {
"command": "npx",
"args": ["-y", "--registry=https://npm.pkg.github.com", "@kreodevs/mcp-sysadmin"],
"env": {
"SYSADMIN_INVENTORY_PATH": "/ruta/a/inventory.json",
"SYSADMIN_PRODUCTION_MODE": "true",
"SYSADMIN_CONFIRM_TOKEN": "tu-secreto-humano"
}
}
}
}
Pulsa Refresh en MCPs. Límite ~100 tools entre servidores.
Variables de entorno recomendadas (todos los clientes)
SYSADMIN_INVENTORY_PATH=/ruta/a/inventory.json
SYSADMIN_PRODUCTION_MODE=true
SYSADMIN_CONFIRM_TOKEN=<openssl rand -hex 32>
SYSADMIN_READ_ONLY=false
SYSADMIN_REQUIRE_CONFIRM=true
PROXMOX_HOMELAB_TOKEN=...
HETZNER_API_TOKEN=...
CLOUDFLARE_API_TOKEN=...
Desarrollo local del servidor (sin cliente MCP):
npm run dev
Seguridad
Modo producción
Activa siempre en prod:
SYSADMIN_PRODUCTION_MODE=true
SYSADMIN_CONFIRM_TOKEN=<secreto-largo-aleatorio>
Con esto:
- SSH exige
hostKeyFingerprint(anti-MITM) — falla al arrancar si falta SYSADMIN_CONFIRM_TOKENobligatorio — falla al arrancar si falta- SSH usa allowlist estricta (sin
cat/grep; lecturas solo víassh-read-file) - Prohibido
passworden inventario SSH vm-powerrequiere confirmación incluso parastart- Regex custom validadas (sin
.*ni patrones demasiado amplios)
Gate humano: confirmToken
El LLM puede poner confirm: true por prompt injection, pero no conoce SYSADMIN_CONFIRM_TOKEN (solo está en env del MCP, no en el chat):
{
"hostId": "bare-metal-01",
"command": "systemctl status nginx",
"confirm": true,
"confirmToken": "tu-secreto-humano-no-compartir-con-el-modelo"
}
Tú proporcionas el token cuando apruebas la operación.
Token de un solo uso (recomendado)
./scripts/mcp-approve.sh
# Válido 5 minutos; úsalo como confirmToken en la tool call
Alternativa: el token fijo SYSADMIN_CONFIRM_TOKEN en env MCP.
Obtener fingerprint SSH
ssh-keyscan -H 10.0.0.5 | ssh-keygen -lf -
# Copia la línea SHA256:... al inventario como hostKeyFingerprint
Controles implementados
| Control | Descripción |
|---|---|
| confirmToken | Secreto humano en env MCP; el modelo no lo tiene por defecto |
| Modo producción | Allowlist SSH, host key pinning, sin passwords SSH |
| Modo read-only | SYSADMIN_READ_ONLY=true bloquea tools de escritura |
| ACL por host | readOnly, allowedTools, allowedCommandPatterns |
| Allowlist SSH | Solo diagnóstico (systemctl status, journalctl, docker ps, etc.) — sin lectura de archivos |
| Lectura de archivos | Exclusivamente vía ssh-read-file (paths + symlinks + confirmToken) |
| cwd restringido | Solo /tmp, /var/log, /var/www, /home/*, /opt/* en ssh-exec |
| Regex inventario | Patrones custom validados; prohibido .* y regex demasiado amplias |
| Blocklist SSH | Capa extra: rm -rf, pipes a shell, multiline, etc. |
| Paths remotos | readlink -f antes de leer; bloqueo de shadow/symlink bypass |
| Rate limit | 30 req/tool/host/min (configurable) |
| TLS Proxmox | verifySsl default true |
| Redacción | Secretos, configs VM, errores API filtrados |
| Auditoría | JSON en stderr: [mcp-sysadmin:audit] |
Allowlist SSH por defecto
Incluye solo diagnóstico operativo: systemctl status, journalctl, docker ps/logs, kubectl get, ls, df, free, nginx -t, etc.
No incluye cat, grep, head, tail — usa ssh-read-file para leer archivos.
Añade patrones específicos en inventario (sin .*):
{
"allowedCommandPatterns": ["^systemctl restart nginx$"]
}
Tools que requieren confirm + confirmToken
ssh-exec— siempressh-read-file— siemprevm-power— todas las acciones en producción; stop/shutdown/reboot/reset siemprecreate-vm-snapshot— siemprecreate-backup— siemprecreate-dns-record,update-dns-record,delete-dns-record,purge-cache— siempre
Checklist pre-producción
-
SYSADMIN_PRODUCTION_MODE=true -
SYSADMIN_CONFIRM_TOKENgenerado (openssl rand -hex 32) - Fingerprint SSH en cada host
- Tokens Proxmox / Hetzner / Cloudflare con permisos mínimos
-
verifySsl: trueen Proxmox -
allowedToolspor host según necesidad - Inventario sin passwords en texto plano
- Probar una operación destructiva con token manual
CI y publicación
- CI: GitHub Actions ejecuta
typecheck+builden cada push/PR amain(.github/workflows/ci.yml) - Paquete npm: @kreodevs/mcp-sysadmin en GitHub Packages — publicado al crear un GitHub Release
Extensión
Para añadir otro proveedor (oVirt, VMware, AWS, etc.):
- Añade el provider en
src/config/schema.ts - Implementa cliente en
src/providers/<nombre>/client.ts - Regístralo en
src/providers/registry.ts - Expone tools en
src/tools/
La estructura sigue el patrón del cloudways-mcp-server, pero con inventario multi-proveedor en lugar de una API única.
Desarrollo
npm run typecheck
npm run build
Installing Sysadmin
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/kreodevs/mcp-sysadminFAQ
Is Sysadmin MCP free?
Yes, Sysadmin MCP is free — one-click install via Unyly at no cost.
Does Sysadmin need an API key?
No, Sysadmin runs without API keys or environment variables.
Is Sysadmin hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Sysadmin in Claude Desktop, Claude Code or Cursor?
Open Sysadmin 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
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectCompare Sysadmin with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
