DevOps MCP — Secure MCP Server for Linux Server Automation
Un servidor MCP de control de acceso de tres niveles que permite a asistentes de IA (Claude Code, Cursor, Windsurf) escanear, planificar y operar servidores Linux de forma segura a través de SSH sin acceso completo de escritura. Incluye una puerta de token de consentimiento humano fuera de banda, escaneo automatizado de conflictos de puertos y un modo seguro predeterminado completamente de solo lectura para eliminar comandos destructivos accidentales en entornos de producción.
Documentación
devops-mcp
Un servidor MCP (Model Context Protocol) basado en modos que permite a los asistentes de IA (Claude Desktop, Cursor, Windsurf, …) operar realmente servidores Linux sin entregarles las llaves del reino.
El modelo puede conectarse, escanear, planificar y desplegar — pero cada paso que cambia el estado en un servidor tipo producción pasa por una puerta de consentimiento que la IA no puede auto-aprobar. El descubrimiento es de solo lectura por diseño.
┌─────────────────┐ MCP / stdio ┌────────────────────┐
│ AI client │ ───────────────────────────► │ devops-mcp │
│ (Claude / │ │ │
│ Cursor / …) │ ◄─────────────────────────── │ ssh2 / docker / │
└─────────────────┘ │ child_process │
└────────┬───────────┘
│ SSH
▼
┌────────────────┐
│ Your VPS │
└────────────────┘
⚡ Configuración inicial (léelo una vez, hazlo una vez)
Hay exactamente cuatro pasos. No te saltes el paso 2.
1. Instalación
git clone <your-fork-url>.git devops-mcp
cd devops-mcp
npm install
npm run build
Requiere Node ≥ 18.
2. Genera tu token de elevación y guárdalo en un lugar donde no lo pierdas
# Linux / macOS
openssl rand -hex 24
# Windows PowerShell
$bytes = New-Object byte[] 24; (New-Object System.Security.Cryptography.RNGCryptoServiceProvider).GetBytes($bytes); [BitConverter]::ToString($bytes).Replace("-","").ToLower()
# Or, via Node
node -e "console.log(require('crypto').randomBytes(24).toString('hex'))"
Obtendrás algo como 6ba329add30b19a5a347178f7e3705fdea0ac1aa66cb9274.
🔑 GUARDA ESTE TOKEN ANTES DEL PASO 3.
Este token es lo único que se interpone entre la IA y el acceso descontrolado a producción. El modelo nunca lo ve. Cada vez que la IA quiera elevar al modo PROVISION/FULL, aprobar una acción destructiva, cambiar el rol de un servidor o escribir en un servidor tipo producción, lo pegas una vez.
Ponlo en un gestor de contraseñas. Si lo pierdes:
- Puedes editar manualmente la configuración de tu cliente MCP para establecer uno nuevo, o
- Puedes pedirle a la IA que llame a
rotate_consent_tokensi aún tienes el anterior (lo cual es circular si has perdido ambos).No hay flujo de recuperación. Esta es la puerta; no incluimos una puerta trasera.
3. Añade devops-mcp a la configuración de tu cliente MCP
Para Claude Desktop, edita claude_desktop_config.json:
- Windows:
%APPDATA%\Claude\claude_desktop_config.json - macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Añade (o fusiona en el mcpServers existente):
{
"mcpServers": {
"devops-mcp": {
"command": "node",
"args": ["/absolute/path/to/devops-mcp/dist/index.js"],
"env": {
"DEVOPS_MCP_ELEVATION_TOKEN": "<paste your token from step 2 here>",
"LOG_LEVEL": "info"
}
}
}
}
La misma estructura funciona para Cursor, Windsurf y cualquier otro cliente MCP: el bloque env es la forma estándar de MCP para pasar secretos.
4. Cierra y vuelve a abrir completamente tu cliente MCP
No "cierra la ventana". En Windows, eso significa bandeja del sistema → Salir. El token de elevación se lee al inicio; el cliente debe reiniciarse para que surta efecto.
Has terminado. La próxima vez que hables con la IA, di "añade mi servidor en …" y te guiará.
Por qué existe esto
Los servidores MCP genéricos de "ejecutar cualquier comando" son peligrosos en máquinas de producción. Un
modelo con shell completo en un servidor en vivo puede — y lo hará — reiniciar el
servicio equivocado, desplegar en un puerto en uso, docker prune un volumen de base de datos, o
escalarse a root porque nada le dijo que no lo hiciera.
devops-mcp traza una línea dura entre leer y cambiar:
- Leer siempre está permitido (dentro de una lista blanca SAFE de solo lectura).
- Cambiar en un servidor tipo producción requiere el token del humano — pasado fuera de banda, invisible para el modelo.
- Desplegar un nuevo proyecto pasa por una verificación de conflicto de puertos y un script revisable, no 40 comandos ad-hoc.
Características
Control de acceso
- Modo de tres niveles:
SAFE(predeterminado, lista blanca de solo lectura),PROVISION(instalaciones del sistema, caducidad predeterminada de 1 h),FULL(root, caducidad predeterminada de 30 min). - Token de consentimiento fuera de banda — la elevación y las aprobaciones requieren una cadena que solo el usuario tiene. El modelo literalmente no puede leerla.
- Puerta de escritura en producción — en servidores
role: production(o cualquier servidor que el escáner marque comoproductionLikely), cualquier comando no-SAFE requiereconsentToken+acknowledgeProductionWrite: true. Los comandos catastróficos — aquellos que son irrecuperables sin una copia de seguridad (rmde una ruta no temporal,rm -rf /…,dd of=/dev/…,mkfs, SQLDROP TABLE/DATABASE,docker rmi,docker volume rm,docker rm -v,docker system prune) — además requierenbackupVerified: true. Las escrituras ordinarias y las operaciones recuperables (editar un archivo,rm /tmp/scratch,docker rmsimple de un contenedor que puede recrearse desde su imagen) no necesitanbackupVerified. Los rechazos muestran el comando resuelto exacto. - Política por servidor —
allowedModes,blockedCommands,allowedPaths,requireApprovalviven enconfig/<server-id>/server.jsony se aplican en cada comando SSH. roleobligatorio —add_serverno permitirá que la IA establezca silenciosamente el rol por defecto; debe preguntar al usuario, y la respuesta incluye un bloqueroleConsequencesque la IA te lee.- Rotación de token —
rotate_consent_tokengenera un token nuevo (por defecto en modo de prueba;apply: trueactualiza atómicamente la configuración de tu cliente MCP). - Rotación de credenciales —
update_server_credentialsrota la contraseña, intercambia la clave SSH (incluidas claves cifradas mediantekeyPassphrase), o migra host/usuario/puerto sin volver a añadir el servidor. El rol, las restricciones y el perfil de escaneo permanecen intactos. Cierra cualquier sesión activa a ese servidor primero, valida las nuevas credenciales con una conexión de prueba, y está sujeto a consentimiento en producción. - Listo para AWS / EC2
.pem— incorpora con el archivo.pem+ nombre de usuario + IP. Refiérelo en su lugar (externalKeyPath) o cópialo en el paquete de configuración (keyFilePath); añadekeyPassphrasesolo si la clave está cifrada. - Múltiples conexiones simultáneas, clave por serverId — el MCP mantiene
una conexión SSH por servidor, no un único espacio global. Claude Desktop ejecuta
un único proceso MCP compartido en todas tus conversaciones; con una conexión
global, dos chats trabajando en dos servidores se pisarían mutuamente
("sesión 1 en servidor A, sesión 2 se conecta a B, ahora los comandos de A golpean
B"). Las conexiones con clave permiten que ambas coexistan.
run_commandtoma unserverId: opcional cuando hay exactamente un servidor conectado, obligatorio cuando hay dos o más — una llamada ambigua se rechaza en lugar de adivinarse. Cada respuesta derun_commandmuestratarget.serverIdyactiveConnections. - Anti-desviación de objetivo — las respuestas de
run_command,set_modeyget_current_modellevan la identidad del servidor conectado, para que una conversación nunca pueda terminar operando silenciosamente la máquina equivocada.disconnect_servertoma unserverIdopcional (o"all"). - Incorporación consciente de sesiones en vivo —
add_servermuestra el/los servidor(es) actualmente conectado(s) en su respuesta y le dice a la IA que no cambie automáticamente al recién añadido sin preguntar.
Descubrimiento y planificación
- Escaneo de descubrimiento del servidor — sonda de solo lectura de SO, hardware, puertos
en escucha, stack instalado (docker / nginx / apache / node / pm2), contenedores
en ejecución, sitios nginx analizados, servicios systemd. La salida se persiste como un
ServerProfile. - Diff de perfil al reconectar —
diff_server_profilevuelve a escanear e informa qué cambió desde la instantánea guardada. - Conciencia de conflicto de puertos —
check_port_conflictdevuelve el proceso en escucha + una sugerencia de puerto libre antes del despliegue. - Planifica, no dispares —
plan_deploymentdevuelve un script bash idempotente que el usuario revisa. El MCP no lo ejecuta.
Endurecimiento de seguridad
- Todos los argumentos de comandos se citan con shell antes de llegar al shell remoto. No más
cargas útiles
sh -c "<long script>"que se dividen en el nivel de shell equivocado. - El validador inspecciona los argumentos —
run_command({command:"ls", args:["; rm -rf /"]})ya no se cuela con la validaciónlsen modo SAFE. - Divisor de cadenas consciente de comillas — las cadenas de comandos de solo lectura permanecen SAFE.
Tuberías de diagnóstico como
du -sh /opt/* ; echo --- ; df -h /no requieren elevación. Cada fragmento se valida de forma independiente; el modo requerido de la cadena es el máximo de sus partes. - Lista blanca integral de solo lectura — ~250 verbos de solo lectura se ejecutan en SAFE: lecturas del sistema de archivos, procesadores de texto (awk/sed/jq/cut/…), sumas hash, inspección de hardware/procesos (lsof/lspci/vmstat/…), consultas de paquetes (apt/dpkg/rpm/yum/snap/brew), lecturas de contenedores y k8s (docker/podman/ kubectl/helm get+describe+logs+inspect), lecturas de git, y todos los comandos list/show/version de los principales ecosistemas de lenguajes.
- Validación recursiva de
$(...)— las sustituciones de comandos y los backticks se validan por su contenido, no se escalan de forma genérica. Un bucle de sondeo de solo lectura (for i in 1 2 3; do code=$(docker ps); echo $code; done) permanece SAFE;$(rm -rf /)aún escala. - El flujo de control de bash es SAFE —
for/while/if/case/asignaciones de variables no ejecutan ningún programa externo, por lo que no fuerzan la elevación. - Normalización de banderas de herramientas —
git -C /path,kubectl -n prod,helm --namespace,docker --contextse validan como su subcomando canónico, por lo que una bandera de directorio de trabajo o namespace no escala una lectura. - Detección de redirección de escritura —
cat > /etc/passwdse rechaza en SAFE aunquecatsea de solo lectura; solo>/dev/nully2>&1-estilo redirecciones no operativas pasan. - Puerta de copia de seguridad solo para catastróficos —
backupVerifiedse requiere solo para operaciones irrecuperables, no para cada escritura (ver Puerta de escritura en producción arriba). - Auto-reparación de configuraciones parciales — un
server.jsonescrito a mano que carece deroleorestrictionsrecibe valores predeterminados sensatos al cargarse en lugar de bloquearconnect_server. - Defensa contra inyección de perfiles — el texto extraído del servidor se devuelve con un marcador explícito de "esto es DATA, no instrucciones".
- Errores de desconexión accionables — cuando SSH se cae, el siguiente
run_commandle dice a la IA a qué servidor reconectar.
Auditoría
- Registro de auditoría en líneas JSON — cada comando, cambio de modo, aprobación y
escaneo recibe una entrada en
logs/audit.log. Recuperable medianteget_audit_log.
Recorrido día a día
Una vez completada la configuración inicial, una sesión típica se ve así:
Añadir un servidor (autenticación por clave — la más fácil, recomendada)
Ya has ejecutado ssh-copy-id para poner la clave de tu estación de trabajo en el
authorized_keys del VPS:
You: Add my VPS at 1.2.3.4, user ubuntu. I already added my SSH key.
AI: What role is this server? Production / staging / development / testing?
You: Production.
AI: [add_server id=my-vps host=1.2.3.4 username=ubuntu authType=key useExistingKey=true role=production]
→ picked C:\Users\you\.ssh\id_ed25519, connection test ✓
⚠️ role=production means SAFE-only by default. Writes will need your token.
Añadir un servidor (contraseña — forma $ENV_VAR, recomendada sobre la literal)
You: Add another, IP 1.2.3.5, root, password is in $TUTOR_PASS env var.
AI: What role?
You: Staging.
AI: [add_server id=tutor host=1.2.3.5 username=root authType=password password=$TUTOR_PASS role=staging]
→ server created, connection test ✓
Conectar y escanear
You: Connect to my-vps and tell me what's on it.
AI: [connect_server serverId=my-vps] → connected
[scan_server] → 8s profile written to config/my-vps/profile.json
Server is production-like:
- nginx serving example.com on 80/443
- postgres container on :5432
- 4 docker containers, 2 GB RAM free
- last scanned: just now
Diagnósticos de solo lectura — sin necesidad de elevación
Las cadenas de comandos de solo lectura se ejecutan en SAFE:
You: How much disk are the projects using?
AI: [run_command "du -sh /opt/* 2>/dev/null ; echo --- ; df -h /" executor=ssh]
→ ran in SAFE mode (read-only chain, no elevation required)
Desplegar un nuevo proyecto
You: Deploy https://github.com/me/newapp on this box, port 8000.
AI: [check_port_conflict port=8000] → in use by "node" (the example.com app)
Port 8000 is taken. Suggested free port: 8001. Use 8001 or stop the existing app?
You: Use 8001.
AI: [plan_deployment port=8001 runtime=node ...] → returns a 26-line bash script
Here's the script. Please review.
You: Looks good. Run it.
AI: [run_command ...] → refused: production write-gate.
To run this I need your elevation token and confirmation that a backup exists.
You: Token is <paste>. Yes, snapshot taken this morning.
AI: [run_command consentToken=<…> acknowledgeProductionWrite=true backupVerified=true]
→ ✓ deployed
Rotar credenciales (la contraseña del VPS cambió, o intercambiaste tu clave SSH)
You: I rotated my-vps's root password. New one is in $MY_VPS_PASS_NEW.
AI: my-vps is role=production — for the rotation I need your elevation token.
You: Token is <paste>.
AI: [update_server_credentials serverId=my-vps authType=password
password=$MY_VPS_PASS_NEW consentToken=<…>]
→ closed active SSH session (was connected), new creds tested ✓
Reconnect with connect_server when ready.
You: Connect.
AI: [connect_server serverId=my-vps] → ✓
El rol, las restricciones, la lista de comandos bloqueados y el perfil de escaneo se conservan. Solo cambian los campos de autenticación.
Actualizar el rol de un servidor más tarde
You: Actually my-vps is staging now, not production.
AI: This is a production-touching change, please confirm with the token.
You: Token is <paste>.
AI: [update_server serverId=my-vps role=staging applyRoleDefaults=true consentToken=<…>]
→ role changed; allowedModes now [SAFE, PROVISION].
Rotar el token (cuando el anterior se ha filtrado, p. ej., en un chat)
You: Generate a new elevation token and update Claude Desktop's config.
AI: For verification, paste the current token.
You: <paste current>
AI: [rotate_consent_token consentToken=<current> apply=true]
→ 🔑 NEW TOKEN: <new> ← SAVE THIS NOW, in a password manager.
Claude Desktop must be fully restarted for the new token to take effect.
Until then, the OLD token still works on this running session.
Modos de acceso
| Modo | Caducidad predeterminada | Qué permite |
|---|---|---|
SAFE | sin caducidad | Lista blanca de solo lectura: ls, cat, df, ss, docker ps, nginx -T, etc. Las cadenas de comandos todos-SAFE también funcionan. |
PROVISION | 1 hora | apt/yum, docker run/build/stop, systemctl start/stop, nginx, ufw, operaciones de archivos |
FULL | 30 minutos | Cualquier cosa, incluyendo fdisk, dd, shutdown, rm -rf / |
La elevación requiere:
acknowledgeRisk: true(la IA lo establece)consentToken: "<your token>"(solo tú lo tienes)
La degradación siempre está permitida y es instantánea. Las sesiones caducan automáticamente de vuelta a SAFE.
Referencia de herramientas (32 herramientas)
Ciclo de vida del servidor
| Herramienta | Modo | Qué hace |
|---|---|---|
add_server | SAFE | Onboarding de una sola vez. Cinco rutas de autenticación: password (literal o $ENV_VAR), keyFilePath (copiar una clave en la configuración), privateKey (pegar en línea), externalKeyPath (apuntar a una clave existente sin copiarla), useExistingKey (selección automática de ~/.ssh/id_*). Requiere role. Prueba automáticamente la conexión. Devuelve roleConsequences. |
update_server | SAFE | Cambiar rol, allowedModes, blockedCommands, allowedPaths, requireApproval, nombre o descripción. Tocar producción requiere consentToken. Los campos de autenticación NO son mutables aquí — ver update_server_credentials. |
update_server_credentials | SAFE | Rotar contraseña, intercambiar clave SSH, migrar host/usuario/puerto. Cierra primero cualquier sesión SSH activa a este servidor. Prueba las nuevas credenciales por defecto. Rol/restricciones/profile.json preservados. Los servidores de producción requieren consentToken. |
setup_server_config | SAFE | Nivel inferior: init / add / status. Misma primitiva que usa add_server. |
list_servers | SAFE | Listar todos los servidores configurados |
test_connection | SAFE | Intentar conectarse por SSH a un servidor configurado (no ejecuta comandos) |
connect_server | SAFE | Abrir la sesión SSH de trabajo para comandos posteriores |
disconnect_server | SAFE | Cerrar la sesión SSH |
Descubrimiento (todo solo lectura)
| Herramienta | Modo | Qué hace |
|---|---|---|
scan_server | SAFE | Sondear SO / hardware / puertos / stack / cargas de trabajo. Persiste config/<id>/profile.json. Sin escrituras en el objetivo. |
get_server_profile | SAFE | Leer el perfil guardado sin volver a escanear |
diff_server_profile | SAFE | Volver a escanear e informar qué cambió. No sobrescribe el perfil guardado a menos que accept: true |
check_port_conflict | SAFE | ¿Está el puerto X en uso? Devuelve el listener + una sugerencia de puerto libre |
list_containers | SAFE | Listar contenedores Docker en el servidor conectado |
list_playbooks | SAFE | Listar playbooks de aprovisionamiento disponibles |
Ejecución y despliegue
| Herramienta | Modo | Qué hace |
|---|---|---|
run_command | varía | Ejecutar un comando (local / ssh / docker). Los argumentos se citan con shell; las cadenas se dividen y cada fragmento se valida; se aplica la puerta de escritura de producción. |
plan_deployment | SAFE | Generar un script bash idempotente (clonar + compilar + pm2/docker). Rechaza en conflicto de puerto a menos que acknowledgeConflict: true. SIN EJECUCIÓN. |
run_playbook | PROVISION | Ejecutar un playbook de aprovisionamiento predefinido |
install_docker | PROVISION | Instalar Docker + Compose |
install_nginx | PROVISION | Instalar Nginx |
configure_nginx | PROVISION | Generar configuración de proxy inverso nginx + recargar. Usa heredoc para evitar errores de citado de shell. |
deploy_app | varía | Primitiva de despliegue de nivel inferior (git clone + compilar + iniciar). Todos los valores interpolados se citan con shell. |
container_action | SAFE / PROVISION | start / stop / restart / logs / inspect |
transfer_files | SAFE (descarga) / varía (subida) | Subida/descarga SFTP de archivos, carpetas (recursivo) o archivos comprimidos. extract: true desempaqueta un .zip/.tar.gz/.tgz/.tar/.tar.bz2/.tar.xz/.gz subido en el servidor; verifyChecksum: true hace sha256 de extremo a extremo en archivos individuales. Las subidas a un servidor tipo producción pasan por la puerta de escritura. |
Modo, consentimiento, auditoría
| Herramienta | Qué hace |
|---|---|
get_current_mode | Modo actual + permisos + tiempo restante |
set_mode | Cambiar modo. La elevación requiere acknowledgeRisk + consentToken |
approve_action | Aprobar una acción pendiente de alto riesgo. Requiere consentToken |
list_pending_approvals | Listar solicitudes de aprobación en cola |
rotate_consent_token | Generar un nuevo token de elevación. apply: true reescribe atómicamente la configuración del cliente MCP. Requiere el token actual. Lea las advertencias de la respuesta antes de reiniciar el cliente. |
generate_ssh_key | Generar un par de claves SSH de sesión con caducidad automática |
revoke_ssh_key | Revocar una clave SSH de sesión |
get_audit_log | Seguir / filtrar logs/audit.log (analiza líneas JSON, filtra por since y action) |
health_check | Vitalidad + versión + modo actual |
Opciones de autenticación (add_server)
Cinco formas de autenticarse, elegidas por authType + qué campo de clave/contraseña se establece:
| Opción | Campos de esquema | Cuándo usar |
|---|---|---|
| Contraseña | authType:"password" + password (literal o $ENV_VAR) | Cuando la autenticación por clave no está configurada. Prefiere $ENV_VAR para que la contraseña no esté en disco en la configuración. |
| Copiar un archivo de clave | authType:"key" + keyFilePath | Tienes un archivo PEM que quieres almacenar junto con la configuración del servidor (paquete portátil) |
| Pegar clave en línea | authType:"key" + privateKey | Solo tienes el texto de la clave |
| Apuntar a clave existente | authType:"key" + externalKeyPath | Ya tienes ~/.ssh/whatever — no copies, solo referencia. ~ se expande. |
| Auto-buscar tu clave | authType:"key" + useExistingKey: true | Tu ~/.ssh/id_* predeterminado ya está en authorized_keys en el servidor. Lo más fácil. |
El manejador valida exactamente una fuente de clave por llamada. Combinar, por ejemplo, useExistingKey y keyFilePath se rechaza con un error claro.
Cualquier ruta de clave (keyFilePath / externalKeyPath) acepta un keyPassphrase opcional (literal o $ENV_VAR) para claves privadas cifradas.
AWS EC2 (el caso .pem)
Obtienes un archivo .pem, un nombre de usuario (ubuntu, ec2-user, admin, …) y una IP/DNS pública. Dos formas:
// Reference the .pem where it sits (recommended — nothing copied)
{
"id": "my-ec2", "host": "ec2-1-2-3-4.compute.amazonaws.com",
"username": "ec2-user", "authType": "key",
"externalKeyPath": "C:\\Users\\you\\Downloads\\my-key.pem",
"role": "production"
}
// Or copy the .pem into the server's config folder (portable bundle)
{
"id": "my-ec2", "host": "1.2.3.4", "username": "ubuntu",
"authType": "key",
"keyFilePath": "C:\\Users\\you\\Downloads\\my-key.pem",
"role": "staging"
}
La mayoría de las claves AWS no tienen frase de contraseña — omite keyPassphrase. Si la tuya está cifrada, agrega "keyPassphrase": "$MY_PEM_PASS" y establece esa variable de entorno.
sshd moderno + autenticación por contraseña: ssh2 necesita
tryKeyboard: truepara configuraciones de sshd que usan PAM (Ubuntu 22.04+, Debian 12, Amazon Linux 2023, RHEL 9). devops-mcp lo establece automáticamente — las contraseñas funcionan incluso cuando el servidor tienePasswordAuthentication noy solo permitekeyboard-interactive.
Configuración del servidor en disco
Cada servidor vive en su propia carpeta:
config/
├── my-vps/
│ ├── server.json # config (host, user, auth, role, restrictions)
│ ├── key.pem # optional SSH private key (only if you used keyFilePath / privateKey)
│ └── profile.json # written by scan_server
└── _example/
└── server.json # template
Ejemplo de server.json:
{
"name": "Production Web",
"host": "1.2.3.4",
"port": 22,
"username": "ubuntu",
"authType": "key",
"keyFile": "production.pem",
"role": "production",
"restrictions": {
"allowedModes": ["SAFE"],
"blockedCommands": ["rm -rf", "shutdown", "reboot", "dd"],
"requireApproval": true
},
"description": "Main production web server"
}
Para un flujo de trabajo externalKeyPath (la clave permanece en ~/.ssh/):
{
"name": "My VPS",
"host": "1.2.3.4",
"port": 22,
"username": "ubuntu",
"authType": "key",
"externalKeyPath": "C:\\Users\\you\\.ssh\\id_ed25519",
"role": "production"
}
Para autenticación por contraseña (siempre prefiere $ENV_VAR):
{
"authType": "password",
"password": "$MY_VPS_PASS"
}
$NAME se resuelve a process.env.NAME al momento de la conexión. No comprometas contraseñas literales.
Si a un server.json le falta role o tiene restrictions: {}, el MCP completa los valores predeterminados de role: "development" al cargar (y advierte en los registros). Esto evita que connect_server se bloquee con configuraciones escritas a mano.
Variables de entorno
| Variable | Propósito | Predeterminado |
|---|---|---|
DEVOPS_MCP_ELEVATION_TOKEN | Token de consentimiento fuera de banda. Establézcalo. Sin él, set_mode / approve_action / la puerta de escritura de producción aceptan el booleano propio de la IA como consentimiento y el servidor registra una advertencia fuerte. | no establecido (modo consultivo) |
DEVOPS_MCP_NO_CONSOLE_LOG | Establecer a 1 para suprimir los registros de consola stderr (los registros de archivo aún se escriben) | no establecido |
LOG_LEVEL | debug / info / warn / error | info |
LOG_DIR | Dónde escribir combined.log, error.log, audit.log | ./logs |
NODE_ENV | (Informativo; los registros van a stderr de todos modos para que MCP stdio no se corrompa) | development |
Modelo de seguridad
Contra qué protege devops-mcp
- Modelo ejecutándose a ciegas en producción — los comandos de escritura en un servidor con
role: productionoproductionLikely: truese rechazan sin el token de consentimiento + confirmación explícita + (para operaciones catastróficas e irrecuperables)backupVerified. - Aprobaciones autoconcedidas — el modelo no puede fabricar
consentTokenporque nunca veDEVOPS_MCP_ELEVATION_TOKEN. - Inyección de argumentos — cada argumento para
run_commandse cita con shell antes de llegar al shell remoto. Los scripts de varias líneas dentro de las cargas útiles desh -csobreviven intactos. - Comandos de contrabando en argumentos — el validador inspecciona
command + argsjuntos, por lo querun_command({command:"ls", args:["; rm -rf /"]})se eleva correctamente a FULL. - Rechazos de cadenas demasiado amplias — las cadenas de comandos de solo lectura permanecen en SAFE. Cada fragmento se valida de forma independiente; solo gana el peor.
- Inyección de prompt desde contenido escaneado — banners, etiquetas de contenedores, líneas de registro se devuelven con un marcador de 'datos no confiables'. La respuesta de la herramienta le dice al modelo: mostrar, no ejecutar.
- Colisiones de puerto silenciosas —
plan_deploymentycheck_port_conflictmuestran conflictos antes del despliegue. - Inyección de shell en ayudantes de despliegue/configuración — cada valor interpolado se cita con shell; las configuraciones de nginx se escriben mediante heredoc; los nombres de rama y las claves de variables de entorno se validan.
- Los rechazos de la puerta de escritura de producción repiten el comando exacto — para que puedas leer lo que estaba a punto de ejecutarse, no la paráfrasis de la IA.
Lo que devops-mcp no hace
- No aísla el servidor conectado. Una vez que estás en modo FULL con el token, el modelo puede hacer cualquier cosa que el usuario SSH pueda.
- No cifra el token de consentimiento en reposo en tu configuración del cliente MCP.
- No respalda tus datos —
backupVerifiedes una atestación humana, no una verificación.
Ver SECURITY.md para el modelo de amenazas completo.
Gestión de tokens
El token de elevación es una cadena estática almacenada en DEVOPS_MCP_ELEVATION_TOKEN en tu configuración del cliente MCP. No caduca.
Lo que caduca:
- sesión de modo
FULL— 30 min por defecto - sesión de modo
PROVISION— 1 h por defecto - Claves SSH de sesión de
generate_ssh_key— 30 min por defecto
Cuando una sesión de modo se agota, vuelve a SAFE; la IA vuelve a pedir el mismo token para re-elevar.
Rotando el token
You: Rotate the elevation token and update Claude Desktop's config.
AI: For verification, paste the current token.
You: <paste>
AI: [rotate_consent_token consentToken=<current> apply=true]
→ new token: <new>
→ 🔑 SAVE THIS NOW. Without it you're locked out of every write operation.
→ Fully quit and reopen Claude Desktop to activate it.
El MCP escribe el nuevo token atómicamente en tu configuración del cliente (solo la clave DEVOPS_MCP_ELEVATION_TOKEN — todo lo demás en el archivo se preserva). El proceso MCP en ejecución sigue usando el token antiguo hasta que reinicies el cliente.
Si pierdes ambos tokens, el antiguo y el nuevo, entre la rotación y el reinicio, edita manualmente la configuración del cliente para establecer uno nuevo — ese es el flujo de recuperación.
Estructura del proyecto
src/
├── index.ts # MCP entry point (stdio)
├── types/ # TypeScript types
├── core/
│ ├── logger.ts # JSON-lines structured logger + audit logger
│ ├── mode-manager.ts # SAFE / PROVISION / FULL state machine
│ ├── command-validator.ts # Allowlist + quote-aware chain splitter + wrapper-token scan
│ ├── server-config-manager.ts # config/<id>/server.json + profile.json + auto-heal
│ ├── server-scanner.ts # SAFE-mode discovery (read-only by design)
│ ├── ssh-key-manager.ts # Session SSH keys with auto-expiry
│ └── approval-manager.ts # Approval queue
├── executors/ # Local / SSH / Docker — all shell-quote args
├── playbooks/ # Provisioning playbooks (Docker, Nginx, …)
└── tools/
├── tool-schemas.ts # Zod schemas + MCP tool definitions
└── tool-handlers.ts # The actual handlers
Desarrollo
npm run dev # watch mode (tsx)
npm run build # tsc → dist/
npm test # vitest
npm run test:run # vitest run (CI mode)
npm run lint # eslint src/**/*.ts
Contribuciones
PRs bienvenidos. Ver CONTRIBUTING.md.
Al agregar una nueva herramienta que escribe en el servidor conectado, asegúrate de que pase por BaseExecutor.execute() para que se apliquen el validador de modo y la puerta de escritura de producción. No hagas shell out directamente desde un manejador, y si debes interpolar un valor en un comando de shell, usa el ayudante shellQuote en el ejecutor — los errores históricos en este código base han sido todos errores de citado.
Licencia
MIT — ver LICENSE.