Skip to main content
Glama

ssh-mcp

Una puerta de enlace MCP centralizada que otorga a los agentes de IA acceso controlado a la infraestructura SSH a través de Streamable HTTP.

ssh-mcp se ejecuta como un único servicio HTTP. Múltiples clientes de IA — agentes, pipelines de CI, paneles — se conectan a una única puerta de enlace. Las credenciales SSH permanecen en la puerta de enlace. Las políticas de autorización, el registro de auditoría y la limitación de velocidad se aplican de forma centralizada antes de que se ejecute cualquier comando SSH.

License: MIT Docker MCP Security M8ven Live Monitored


Tabla de contenido


Related MCP server: MCP SSH Orchestrator

Arquitectura

MCP stdio local (patrón común)

AI client
   │
   ▼
local MCP process ──► SSH target

Cada agente ejecuta su propio proceso. Las credenciales SSH viven en cada máquina. Sin control centralizado.

ssh-mcp (puerta de enlace HTTP centralizada)

AI clients ───────┐
CI agents ────────┼──► ssh-mcp ──► SSH targets
Dashboards ───────┘      │
                         ├─ API-key authentication
                         ├─ per-client authorization
                         ├─ rate limiting
                         ├─ audit logging
                         └─ connection pooling

Un único despliegue atiende a todos los clientes. Las credenciales, las políticas y los registros viven en un solo lugar.


¿Por qué ssh-mcp?

  • Puerta de enlace HTTP centralizada — Un único despliegue atiende a todos los agentes de IA, pipelines de CI y paneles a través de Streamable HTTP

  • Autorización por cliente — Diferentes claves de API otorgan diferentes conjuntos de comandos en diferentes servidores

  • Políticas de comandos en capas — Los patrones de bloqueo, la detección de shells peligrosos y las listas de permitidos por destino trabajan juntos

  • Acceso SSH centralizado — Las credenciales SSH viven en la puerta de enlace, no en la máquina de cada agente

  • Registro de auditoría — Cada comando, cada cliente, cada resultado — registros JSONL estructurados con trazabilidad de solicitudes

  • Resiliencia operativa — Agrupación de conexiones, interruptores de circuito y reintentos con backoff exponencial

  • Observabilidad — Métricas Prometheus y endpoints de salud para monitorización


Control de acceso multiagente

Diferentes agentes necesitan diferentes permisos. ssh-mcp lo hace cumplir en la puerta de enlace:

monitoring agent  →  API key A  →  read-only commands  →  all servers
deployment agent  →  API key B  →  deploy commands      →  web servers only
database agent    →  API key C  →  db commands           →  database server only
                  ┌─ monitoring agent (read-only, all servers)
                  ├─ deployment agent (deploy commands, web only)
MCP clients ──────┼─ database agent (db commands, db server only)
                  └─ ...
                         │
                         ▼
                      ssh-mcp
                         │
                  centralized policies
                         │
              ┌──────────┼──────────┐
              ▼          ▼          ▼
             web         db      monitoring
           servers    servers     servers

Una configuración mínima que demuestra esta disposición:

{
  "version": 1,
  "ssh_targets": {
    "web-1": { "host": "10.0.1.10", "username": "deploy" },
    "db-1":  { "host": "10.0.1.20", "username": "dbadmin" }
  },
  "allowed_commands": {
    "default": {
      "web-1": { "allow": ["uptime", "df -h", "free -m"] }
    },
    "api_keys": {
      "deploy-key": {
        "web-1": { "allow": ["systemctl restart app", "deploy *"] }
      },
      "db-key": {
        "db-1": { "allow": ["systemctl restart postgres", "pg_dump *"] }
      }
    }
  }
}

El problema

La mayoría de los servidores MCP SSH se ejecutan como procesos stdio locales — uno por cliente, sin estado compartido, sin autorización centralizada y sin registro de auditoría. Cuando múltiples agentes de IA, pipelines de CI o paneles necesitan acceso SSH, cada uno gestiona de forma independiente sus propias claves SSH y ejecuta su propio proceso MCP. Esto crea:

  • Sin control de acceso centralizado — cada cliente decide qué puede ejecutar

  • Sin registro de auditoría — los comandos son invisibles para el equipo de operaciones

  • Proliferación de claves SSH — claves dispersas en cada máquina que ejecuta un agente

  • Sin limitación de velocidad — un agente descontrolado puede saturar un destino

  • Sin agrupación de conexiones — cada cliente abre y cierra sesiones SSH de forma independiente

ssh-mcp resuelve esto desplegando un único servidor MCP como puerta de enlace HTTP. Todos los clientes se conectan a él; él se conecta a tus destinos SSH. La autorización, la autenticación, la limitación de velocidad, la agrupación de conexiones y el registro de auditoría ocurren en un solo lugar.


Casos de uso

Gestión de servidores multiagente

Ejecuta un equipo de agentes de IA con diferentes niveles de acceso. El agente de despliegue puede ejecutar systemctl restart nginx en los servidores web; el agente de monitorización puede ejecutar journalctl en todas partes; el agente de base de datos solo puede ejecutar psql en el servidor de base de datos. Cada agente se autentica con su propia clave de API; cada clave tiene su propio conjunto de permisos.

Integración con pipelines de CI/CD

Apunta tu pipeline de CI a ssh-mcp en lugar de gestionar claves SSH en cada runner. Una única clave de API por pipeline, reglas basadas en red para tu subred de CI y listas de permitidos de comandos garantizan que tus scripts de despliegue ejecuten exactamente lo que deberían — nada más.

Recuperación centralizada de registros y configuración

Usa ssh_download_file para extraer registros, archivos de configuración o volcados de base de datos de servidores remotos sin salir de tu cliente MCP. La validación de rutas en 8 capas y los ajustes de raíz de sandbox garantizan que las transferencias de archivos se mantengan dentro de límites seguros.

Paneles de salud de servidores

Construye un panel impulsado por MCP que consulte uptime, free, df y ps en tu flota. La agrupación de conexiones reutiliza las sesiones SSH, el interruptor de circuito aísla los destinos con fallos y las métricas Prometheus en /metrics alimentan tu stack de monitorización existente.

Cumplimiento y auditoría

Cada comando se registra con JSONL estructurado: quién ejecutó qué, en qué servidor, desde qué IP, si fue permitido y cuánto tardó. El campo matched_via rastrea exactamente qué capa de autorización tomó la decisión. Los cambios de configuración se registran por separado con el estado anterior y posterior.


Modelo de seguridad

ssh-mcp aplica defensa en profundidad en cada capa. El modelo de seguridad completo está documentado en docs/SECURITY.md.

Límite de seguridad: ssh-mcp añade una capa de autorización, autenticación y auditoría delante de SSH. No reemplaza los permisos de las cuentas SSH subyacentes. Si un comando está permitido, el usuario SSH lo ejecuta con los privilegios que tenga esa cuenta. La propia puerta de enlace debe protegerse con TLS y controles de acceso de red. Los registros pueden contener la salida de los comandos y deben tratarse en consecuencia.

Cadena de autorización de comandos

Los comandos se evalúan a través de una cadena ordenada y en capas. Si alguna capa deniega, la solicitud se detiene ahí:

Capa

Qué comprueba

1. Validación de destino

¿Se conoce el nombre del servidor?

2. block_patterns

¿Coincide el comando con una regex bloqueada?

3. Patrones peligrosos

¿Contiene $(), comillas invertidas o nuevas líneas?

4. Guardia de redirección

¿Apuntan las redirecciones del shell a /dev/, /proc/, /sys/?

5. Segmentación

Tras eliminar redirecciones y dividir por &&, `

, ;, |\, cada segmento recorre toda la cadena

6. Reglas default

Reglas de permitir/denegar para todos los clientes

7. Reglas api_keys

Reglas de permitir/denegar por clave

8. Reglas networks

Reglas de permitir/denegar por CIDR

9. Denegar

Respaldo implícito

Autenticación

Las claves de API se envían mediante las cabeceras X-API-Key o Authorization: Bearer. Las claves se procesan mediante hash PBKDF2-HMAC-SHA256 (100 000 iteraciones, sal aleatoria de 16 bytes) y se verifican con comparación en tiempo constante. Las claves en bruto nunca se almacenan.

Saneamiento de entradas

Los comandos, los nombres de destino y las cadenas de registro se sanean antes de procesarse: se eliminan los bytes nulos, se quitan los caracteres de control, se normalizan con NFKC y se someten a la protección ReDoS para block_patterns.

Prevención de recorrido de rutas

Las transferencias SFTP pasan por una validación de rutas en 8 capas que incluye comprobación de bytes nulos, eliminación de caracteres de control, normalización de segmentos de punto, resolución de enlaces simbólicos y aplicación de la raíz de sandbox.

Limitación de velocidad

Limitador de velocidad de ventana deslizante por IP de cliente (60 solicitudes / 60 segundos, /health exento). Las infracciones devuelven HTTP 429 con Retry-After.

La limitación de velocidad se puede configurar mediante settings.rate_limit:

"settings": {
  "rate_limit": {
    "enabled": true,                        // set false to disable entirely
    "max_requests_per_minute": 60,          // max requests per client IP in the window
    "window_seconds": 60.0,                 // sliding-window duration
    "cleanup_interval_seconds": 300.0       // expired-entry GC interval
  }
}

Nota: el limitador de velocidad se construye una vez al iniciar el contenedor a partir de la configuración inicial y no se reconstruye al recargar la configuración en caliente. Para deshabilitar la limitación de velocidad debes establecer settings.rate_limit.enabled en false en la configuración presente al arrancar (por ejemplo, config/ssh-mcp-config.json en el volumen montado). Esto es útil para clientes de alto volumen o suites de pruebas que emiten muchas solicitudes desde una sola IP.


Inicio rápido

Requisitos previos

  • Docker con Docker Compose

  • Un par de claves SSH (o contraseñas por destino) para los servidores a los que quieras acceder

1. Configura el directorio

mkdir -p config logs
ssh-keygen -t ed25519 -f ssh_key -N ""
cp default-config.json config/ssh-mcp-config.json

2. Añade un destino SSH

Abre config/ssh-mcp-config.json y añade un destino:

{
  "version": 1,
  "ssh_targets": {
    "web-server": {
      "host": "192.168.1.10",
      "port": 22,
      "username": "deploy",
      "private_key": "/app/ssh_key"
    }
  },
  "block_patterns": [ "\\brm\\s+-rf\\b", "\\bdd\\s+if=" ],
  "allowed_commands": {
    "default": [
      { "targets": ["*"], "commands": ["hostname", "uptime", "free", "df", "ps", "ls", "cat"] }
    ]
  },
  "settings": {}
}

3. Inicia el servidor

docker compose up -d --build

4. Verifica que está en ejecución

curl http://localhost:9080/health
# {"status": "ok", "connection_pool": {...}}

5. Conecta un cliente MCP

Cualquier cliente MCP compatible con Streamable HTTP puede conectarse. Apúntalo a http://localhost:9080/mcp con una cabecera de clave de API. Consulta Configuración del cliente MCP para más detalles.

6. Lista servidores y ejecuta un comando

curl -X POST http://localhost:9080/mcp \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "ssh_list_servers",
      "arguments": {}
    }
  }'

curl -X POST http://localhost:9080/mcp \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "ssh_execute_command",
      "arguments": {"server_name": "web-server", "command": "uptime"}
    }
  }'

Configuración del cliente MCP

Cualquier cliente MCP compatible con el transporte Streamable HTTP puede conectarse. El formato de configuración varía según el cliente: usa la URL y las cabeceras siguientes.

Parámetro

Valor

Transporte

Streamable HTTP

URL

https://ssh-mcp.example.com/mcp

Autenticación

Cabecera X-API-Key o Authorization: Bearer

Configuración genérica de Streamable HTTP

{
  "mcpServers": {
    "ssh": {
      "url": "http://localhost:9080/mcp",
      "headers": {
        "Authorization": "Bearer <your-api-key>"
      }
    }
  }
}

Cliente Python

import requests

MCP_URL = "https://ssh-mcp.example.com/mcp"
API_KEY = "your-api-key"


def call_tool(name: str, arguments: dict) -> dict:
    response = requests.post(
        MCP_URL,
        headers={
            "Content-Type": "application/json",
            "X-API-Key": API_KEY,
        },
        json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/call",
            "params": {"name": name, "arguments": arguments},
        },
    )
    response.raise_for_status()
    return response.json()


print(call_tool("ssh_list_servers", {}))
print(call_tool("ssh_execute_command", {
    "server_name": "web-server",
    "command": "uptime",
}))

JSON-RPC en bruto

Envía las llamadas a herramientas como solicitudes JSON-RPC tools/call a /mcp:

curl -X POST http://localhost:9080/mcp \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "ssh_execute_command",
      "arguments": {"server_name": "web-server", "command": "uptime"}
    }
  }'

Herramientas

Todas las llamadas a herramientas son solicitudes JSON-RPC tools/call a /mcp. Todas las herramientas devuelven una cadena (JSON o texto plano).

Tool

Parameters

Description

ssh_list_servers

(none)

Lista los destinos SSH configurados (host, puerto, usuario — sin secretos)

ssh_list_allowed_commands

server_name (str)

Lista los comandos que el cliente actual puede ejecutar en un destino (unión de reglas default + api_key + network)

ssh_execute_command

server_name (str), command (str), timeout (int, default 30), sudo (bool, default false)

Ejecuta un comando por SSH; devuelve stdout (stderr añadido como [STDERR], código de salida como [EXIT: n])

ssh_download_file

server_name (str), remote_path (str)

Descarga un archivo vía SFTP; autorización equivalente a cat <path>

ssh_upload_file

server_name (str), remote_path (str), content (str), permissions (str, default "0644")

Sube un archivo vía SFTP; autorización equivalente a tee <path>

ssh_check_connection

server_name (str), timeout (int, default 10)

Comprueba la conectividad SSH ejecutando el checkcommand del destino; devuelve indicador de éxito, salida y código de salida

Ejemplos

# List available servers
call_tool("ssh_list_servers", {})
# {"web-server": {"host": "192.168.1.10", "port": 22, "username": "deploy"}}

# List what this client can run on web-server
call_tool("ssh_list_allowed_commands", {"server_name": "web-server"})
# ["cat", "df", "du", "free", "grep", "head", "hostname", ...]

# Execute a command
call_tool("ssh_execute_command", {
    "server_name": "web-server",
    "command": "uptime",
})
# " 07:12:33 up 10 days,  2:15,  1 user,  load average: 0.08, 0.03, 0.01"

# Download a file
call_tool("ssh_download_file", {
    "server_name": "web-server",
    "remote_path": "/etc/hostname",
})
# "web-server\n"

# Upload a file
call_tool("ssh_upload_file", {
    "server_name": "web-server",
    "remote_path": "/tmp/backup.sql",
    "content": "CREATE TABLE ...;\n",
    "permissions": "0640",
})
# "OK: Uploaded 19 bytes to /tmp/backup.sql"

# Check SSH connectivity
call_tool("ssh_check_connection", {"server_name": "web-server"})
# {"success": true, "output": "ping", "error": null, "exit_code": 0, "checkcommand": "echo ping"}

# Check with custom timeout
call_tool("ssh_check_connection", {"server_name": "web-server", "timeout": 5})

Nota sobre sudo: No existe el parámetro sudo_password. Si sudo requiere una contraseña, esta se toma del campo password del destino en la configuración. La opción sudo envuelve el comando con sudo -S -p '' (contraseña desde la configuración) o sudo -n (sin contraseña).

Respuestas de Error

En caso de error, una herramienta devuelve:

{
  "error": true,
  "error_type": "AuthorizationError",
  "message": "Command rejected: target 'foo' not found",
  "retryable": false,
  "request_id": "abc-123"
}

Valores comunes de error_type: AuthorizationError, PathValidationError, FileTransferError, SSHAuthenticationError, SSHTimeoutError, MCPSSHError. La marca retryable es true para SSHTimeoutError. Las violaciones del límite de tasa devuelven HTTP 429 en su lugar.


Configuración

Ubicación del Archivo de Configuración

El servidor lee <config_dir>/ssh-mcp-config.json. Establece config_dir mediante la opción de CLI --config o la variable de entorno MCP_SSH_CONFIG_PATH (por defecto: /config). Si el archivo no existe, el servidor escribe un default-config.json incluido.

Estructura de Nivel Superior

{
  "version": 1,
  "ssh_targets": { ... },
  "block_patterns": [ ... ],
  "allowed_commands": {
    "default": [ ... ],
    "api_keys": [ ... ],
    "networks": [ ... ]
  },
  "settings": { ... }
}

La configuración se valida contra config.schema.json (JSON Schema Draft 2020-12) al cargarse. Las claves desconocidas provocan un error grave.

ssh_targets

Un objeto indexado por identificador de servidor. Cada destino requiere host, port, username y al menos uno de private_key o password.

"ssh_targets": {
  "web-server": {
    "host": "192.168.1.10",
    "port": 22,
    "username": "deploy",
    "private_key": "/app/ssh_key",
    "checkcommand": "echo ping"
  }
}

Field

Required

Default

Description

host

Yes

Nombre de host o dirección IP

port

No

22

Puerto SSH

username

Yes

Nombre de usuario SSH

private_key

*

Ruta al archivo de clave privada SSH en el sistema de archivos del servidor

password

*

Contraseña SSH (también puede establecerse mediante secrets.json o variables de entorno)

checkcommand

No

"echo ping"

Comando ejecutado por ssh_check_connection para verificar la conectividad

* Se requiere al menos uno de private_key o password.

private_key es una ruta en el sistema de archivos del servidor (en Docker, montada dentro del contenedor), no una clave integrada.

block_patterns

Una lista de patrones de regex. Cualquier comando que coincida con un patrón es denegado independientemente de las demás capas de lista de permitidos. Los patrones se examinan en busca de construcciones de retroceso catastrófico al cargarse (protección ReDoS) y se compilan con guardas de tiempo de espera en tiempo de ejecución.

allowed_commands

Tres subobjetos controlan qué comandos puede ejecutar cada cliente:

  • default — reglas para todos los clientes (a menos que una capa más específica decida primero)

  • api_keys — reglas por clave, coinciden con key_hash

  • networks — reglas por CIDR, coinciden con la IP de origen del cliente

Cada regla tiene una lista targets (identificadores de servidor o "*" para todos) y una lista commands (nombres de comando base o "*" para cualquier comando).

"allowed_commands": {
  "default": [
    { "targets": ["*"], "commands": ["hostname", "uptime", "free", "df", "ps"] }
  ],
  "api_keys": [
    {
      "name": "ci-bot",
      "key_hash": "pbkdf2:sha256:100000$<salt>$<hash>",
      "rules": [
        { "targets": ["web-server"], "commands": ["systemctl", "journalctl"] }
      ]
    }
  ],
  "networks": [
    {
      "name": "home-lan",
      "range": "192.168.1.0/24",
      "rules": [
        { "targets": ["*"], "commands": ["*"] }
      ]
    }
  ]
}

settings

Setting

Default

Description

max_output_length

50000

Máx. de bytes de salida de comando devueltos al cliente (int o cadena de tamaño)

command_timeout_max

120

Límite máximo del tiempo de espera del comando (segundos)

retry_max_attempts

3

Intentos de reintento para fallos SSH transitorios

retry_backoff_base_seconds

1.0

Retroceso exponencial base (segundos)

circuit_breaker_failure_threshold

5

Fallos antes de que se abra el circuito por destino

circuit_breaker_timeout_seconds

60.0

Tiempo de espera de recuperación para un circuito abierto (segundos)

log_level

"INFO"

Nivel de registro: DEBUG, INFO, WARNING, ERROR

max_log_output

4096

Máx. de caracteres de salida almacenados en las entradas de registro

compress_rotated

true

Comprimir con gzip los archivos de registro rotados

pool_max_connections_per_target

5

Máx. de conexiones SSH agrupadas por destino

pool_idle_timeout_seconds

300.0

Tiempo de espera de conexión inactiva (segundos)

pool_cleanup_interval_seconds

60.0

Intervalo de limpieza del grupo (segundos)

max_concurrent_ssh_connections

20

Límite global en todos los destinos; el exceso devuelve HTTP 503

watcher_debounce_seconds

2.0

Intervalo mínimo entre recargas de configuración; 0 lo desactiva

trusted_proxies

[]

IPs de proxy inverso de confianza (IPv4/IPv6)

Ajustes de SFTP (settings.sftp)

Setting

Default

Description

sftp.sandbox_root

"/"

Directorio raíz para la validación de rutas SFTP

sftp.max_path_length

4096

Longitud máxima permitida de ruta SFTP (bytes); 0 la desactiva

Secretos

Las contraseñas de los destinos SSH y los hashes de claves de API pueden separarse de la configuración principal en <config_dir>/secrets.json o en variables de entorno MCP_SSH_SECRET_*. Precedencia:

environment variables  >  secrets.json  >  ssh-mcp-config.json

Secret source

Effect

secrets.json

Sobreescrituras de password por destino y de key_hash por clave (coinciden por nombre)

MCP_SSH_SECRET_PASSWORD_<TARGET_ID>

Sobreescribe ssh_targets[<TARGET_ID>].password

MCP_SSH_SECRET_API_KEY_<KEY_NAME>

Sobreescribe key_hash para la entrada api_keys <KEY_NAME>

<TARGET_ID> y <KEY_NAME> se convierten a mayúsculas con -_. Los valores de claves de API deben ser cadenas de hash, no claves en bruto.

Variables de Entorno y Opciones de CLI

Environment variable

CLI flag

Default

Legacy fallback

MCP_SSH_CONFIG_PATH

--config

/config

CONFIG_DIR

MCP_SSH_SSH_KEY

--ssh-key

ssh_key

SSH_KEY_PATH

MCP_SSH_LOG_DIR

--log-dir

/logs

LOG_DIR

MAX_OUTPUT_LENGTH

--max-output

50000

CONFIG_API_ENABLED

false

CONFIG_API_TOKEN

(obligatorio cuando la API está habilitada)

--fix-permissions

False

--print-default-config

Las opciones de CLI tienen prioridad sobre las variables de entorno. Cualquier clave de settings puede sobreescribirse en tiempo de ejecución con MCP_SSH_SETTING_<KEY> (en mayúsculas, -_).

Recarga en Caliente

El servidor sondea el archivo de configuración para detectar cambios (intervalo de 15 s, debounce de 2 s). Cuando se detecta un cambio, recarga, valida e intercambia atómicamente la nueva configuración. Las devoluciones de llamada de cambio de configuración (reconstrucción de reglas de autorización, actualización del grupo de conexiones) se ejecutan tras el éxito del intercambio. Cuando está disponible, se utiliza la monitorización de archivos basada en watchdog.


Observabilidad

Comprobación de Salud

GET /health devuelve {"status": "ok"} además de las estadísticas del grupo de conexiones. El HEALTHCHECK del contenedor utiliza este endpoint.

Métricas de Prometheus

GET /metrics expone métricas en un registro dedicado, todas con el prefijo mcpssh_:

Metric

Type

Labels

mcpssh_requests_total

Counter

tool, status (éxito/error/denegado)

mcpssh_ssh_connections_total

Counter

target

mcpssh_ssh_connection_duration_seconds

Histogram

target

mcpssh_auth_denials_total

Counter

reason

mcpssh_command_duration_seconds

Histogram

target

mcpssh_pool_active_connections

Gauge

target

mcpssh_pool_idle_connections

Gauge

target

mcpssh_pool_created_total

Counter

target

Registro Estructurado

El servidor mcp-ssh admite destinos de registro intercambiables configurados mediante settings.logging.log_targets en el archivo de configuración. Cada destino es un controlador independiente que recibe todas las entradas de registro.

Comportamiento Predeterminado

Por defecto, las entradas de registro se escriben en stdout en formato de texto legible por humanos. Esto es adecuado para entornos Docker donde los registros del contenedor son capturados por el runtime.

Tipos de Destinos de Registro

Destino

Valor de configuración

Formato

Descripción

Salida estándar

"stdout"

Texto

Escribe en stdout. Destino por defecto.

Archivo JSON

"jsonfile"

JSONL

Escribe un objeto JSON por línea en un archivo.

Archivo de texto

"file"

Texto

Escribe texto legible en un archivo.

Configuración

{
  "settings": {
    "log_level": "INFO",
    "logging": {
      "log_targets": [
        { "target": "stdout" },
        { "target": "jsonfile", "filepath": "logs/ssh-mcp.log" }
      ],
      "max_log_output": 4096,
      "compress_rotated": true
    }
  }
}

Nivel de registro

  • Archivo de configuración: Establece settings.log_level para controlar el nivel por defecto.

  • Variable de entorno: Establece MCP_SSH_LOG_LEVEL para sobrescribir el valor por defecto del archivo de configuración (p. ej., MCP_SSH_LOG_LEVEL=DEBUG).

  • Por destino: Cada destino de registro puede tener su propio log_level que sobrescribe el valor por defecto.

Configuración heredada

Si settings.logging no está presente, el servidor recurre a un único destino de archivo JSONL en el directorio de registros (/logs por defecto). Esto mantiene la compatibilidad con las configuraciones existentes.

Formato de texto

Los destinos de stdout y de archivo de texto usan el formato:

2025-01-15 10:30:00 INFO ssh_execute_command: Command executed on server1

Formato JSON

Los destinos de archivo JSON escriben un objeto JSON por línea:

{"timestamp": "2025-01-15T10:30:00+00:00", "event": "ssh_execute_command", "level": "INFO", "message": "Command executed on server1", "request_id": "abc-123", "log_level": "INFO", "log_format_version": 1}

Rotación de archivos

Los destinos basados en archivos rotan cuando superan max_file_size_mb (por defecto: 10 MiB), conservando backup_count copias de seguridad (por defecto: 5). Los archivos rotados se comprimen con gzip cuando compress_rotated es true.

Eventos de cambio de configuración

Evento

Significado

config.load

Configuración inicial cargada al inicio

config.reload

Configuración releída desde el disco (con success, changed_keys, targets_added, targets_removed)

config.migrated

Migración de esquema aplicada (from_version, to_version)

config.default_created

Copia de la configuración predeterminada incluida

config.fallback

Reversión a los valores por defecto en memoria

config.callback_error

El callback de cambio de configuración lanzó una excepción


API de configuración y panel web

El contenedor unificado incluye una API de configuración y un panel web opcionales: una capa de administración completa para tu política SSH, destinos, reglas de comandos y copias de seguridad. No se requiere editar el archivo de configuración. Esta función está deshabilitada por defecto.

Qué incluye

  • Panel web — una aplicación de página única adaptable con 5 páginas: Destinos SSH, Patrones de bloqueo, Reglas de comandos, Ajustes y Copias de seguridad. Inicia sesión con tu token de API y adminístralo todo desde el navegador.

  • API REST — CRUD completo para cada sección de configuración, además de validación de configuración, hash de claves de API, gestión de copias de seguridad y pruebas de conectividad SSH integradas.

  • Utilidad de hash de claves de API — convierte claves de API en texto plano en cadenas PBKDF2 listas para la configuración. No más adivinar el formato de hash.

  • Copia de seguridad y restauración — copias de seguridad automáticas de la configuración en cada escritura; lista, restaura o elimina copias de seguridad desde el panel o la API.

  • Escrituras atómicas y seguras para subprocesos — todas las escrituras de configuración se validan, se serializan con un bloqueo de subprocesos y se escriben atómicamente en el disco.

  • Swagger UI y ReDoc — documentación interactiva de API generada automáticamente en /api/docs y /api/redoc.

Habilitar la API de configuración

Establece estas variables de entorno en tu archivo compose.yaml o .env:

Variable

Por defecto

Descripción

CONFIG_API_ENABLED

false

Configúrala en true para habilitar la API de configuración

CONFIG_API_TOKEN

(requerido al habilitarla)

Token Bearer para autenticar solicitudes a la API

services:
  mcp-ssh:
    environment:
      CONFIG_API_ENABLED: "true"
      CONFIG_API_TOKEN: "your-secret-token-here"

Endpoints de la API

Todos los endpoints están montados en /api en la misma aplicación Starlette ASGI que el servidor MCP.

Estado y utilidades

Método

Ruta

Descripción

GET

/api/health

Comprobación de estado de la API de configuración (sin autenticación requerida)

POST

/api/hash-key

Convierte una clave de API en texto plano en una cadena PBKDF2-HMAC-SHA256

GET

/api/config/schema

Devuelve el esquema JSON de configuración (sin autenticación requerida)

POST

/api/config/validate

Valida un diccionario de configuración sin escribirlo en el disco

Configuración

Método

Ruta

Descripción

GET

/api/config

Obtiene la configuración completa (redacta secretos)

PUT

/api/config

Reemplaza la configuración completa

GET

/api/config/{section}

Obtiene una sección de configuración (settings, ssh_targets, allowed_commands, block_patterns)

PUT

/api/config/{section}

Reemplaza una sección de configuración

Destinos SSH

Método

Ruta

Descripción

GET

/api/config/ssh_targets/{name}

Obtiene un destino SSH específico (sin secretos)

PUT

/api/config/ssh_targets/{name}

Crea o reemplaza un destino SSH

DELETE

/api/config/ssh_targets/{name}

Elimina un destino SSH

POST

/api/config/ssh_targets/{name}/check

Prueba la conectividad SSH mediante el checkcommand del destino

Reglas de comandos

Método

Ruta

Descripción

GET

/api/config/allowed_commands

Lista las reglas de comandos permitidos (mediante GET /api/config/{section})

PUT

/api/config/allowed_commands

Reemplaza las reglas de comandos permitidos (mediante PUT /api/config/{section})

Patrones de bloqueo

Método

Ruta

Descripción

GET

/api/config/block_patterns

Lista los patrones de bloqueo (mediante GET /api/config/{section})

PUT

/api/config/block_patterns

Reemplaza todos los patrones de bloqueo

POST

/api/config/block_patterns

Añade un patrón de bloqueo

PUT

/api/config/block_patterns/{index}

Reemplaza un solo patrón de bloqueo por índice

DELETE

/api/config/block_patterns/{index}

Elimina un solo patrón de bloqueo por índice

Copias de seguridad

Método

Ruta

Descripción

GET

/api/backups

Lista las copias de seguridad de configuración (las más recientes primero)

POST

/api/backups/{name}/restore

Restaura la configuración desde una copia de seguridad

DELETE

/api/backups/{name}

Elimina un archivo de copia de seguridad

Autenticación

Todas las solicitudes a la API (excepto /api/health y /api/config/schema) requieren un token Bearer en el encabezado Authorization:

curl -H "Authorization: Bearer your-secret-token-here" http://localhost:9080/api/config

Panel web

Cuando está habilitado, una aplicación de página única adaptable está disponible en http://localhost:9080/ui/: una interfaz de administración completa creada con Tailwind CSS. Sin recargas de página, notificaciones toast para cada operación y diálogos modales para editar.

Página

Capacidades

Destinos SSH

Ver, añadir, editar y eliminar destinos; prueba de conectividad integrada mediante checkcommand; vista de tabla con host/puerto/usuario

Patrones de bloqueo

Añadir, editar (por índice) y eliminar patrones individuales; ver la lista completa de patrones

Reglas de comandos

Editar reglas predeterminadas, de clave de API y de red; editor completo de reglas con listas de destinos y comandos

Ajustes

Editar todos los ajustes del servidor: sandbox SFTP, limitación de velocidad, registro, agrupación de conexiones, disyuntor y más

Copias de seguridad

Listar, restaurar y eliminar copias de seguridad de configuración; marca de tiempo y tamaño de cada copia

Características adicionales:

  • Inicio de sesión basado en token con gestión de sesiones (almacenado en sessionStorage)

  • Validación de configuración — los cambios se validan antes de escribirse

  • Hash de claves de API — convierte claves en texto plano directamente desde el panel

  • Diseño adaptable — funciona en escritorio y móvil

  • Notificaciones toast — retroalimentación de éxito/error para cada operación

Swagger / ReDoc

La documentación interactiva de la API la genera automáticamente FastAPI:

  • Swagger UI: http://localhost:9080/api/docs

  • ReDoc: http://localhost:9080/api/redoc


Despliegue

Docker Compose

El archivo compose.yaml define un único servicio mcp-ssh que aloja tanto el servidor MCP como, opcionalmente, la API de configuración y el panel web. La API de configuración se habilita mediante la variable de entorno CONFIG_API_ENABLED (por defecto: false).

mcp-ssh — Pasarela SSH MCP + API de configuración

Ruta del host

Ruta del contenedor

Modo

./config

/config

rw

./logs

/logs

rw

./ssh_key

/app/ssh_key

ro

./ssh_key.pub

/app/ssh_key.pub

ro

Expuesto en el puerto 9080 del host (asignado al puerto 8080 del contenedor). La imagen en tiempo de ejecución es python:3.13-alpine con un digest fijado por hash. Un usuario no root mcpssh ejecuta el proceso. Se genera un SBOM CycloneDX en tiempo de compilación en la etapa sbom.

API de configuración y panel web (opcional)

Habilita la API de configuración estableciendo CONFIG_API_ENABLED=true en tu archivo .env o en el entorno:

# Generate an auth token
openssl rand -hex 32
CONFIG_API_ENABLED=true
CONFIG_API_TOKEN=<your-token>

Cuando está habilitada, la API de configuración se monta en /api en el mismo servidor HTTP que la pasarela MCP. Ofrece:

  • API REST en http://localhost:9080/api/... — CRUD completo para destinos SSH, patrones de bloqueo, reglas de comandos, copias de seguridad y ajustes

  • Panel web (GUI) en http://localhost:9080/ui/ — una aplicación de página única para la gestión visual de políticas (destinos SSH, patrones de bloqueo, reglas de comandos, ajustes, copias de seguridad)

  • Documentación de la API en http://localhost:9080/api/docs (Swagger UI) y http://localhost:9080/api/redoc (ReDoc)

Makefile

Comando

Descripción

make build

Construye la imagen Docker (ghcr.io/gelse/ssh-mcp:latest)

make up

docker compose up -d

make down

docker compose down

make test

Ejecuta las pruebas unitarias

make config-test

Ejecuta las pruebas unitarias de config-api

make integrationtest

Construye la imagen de prueba y ejecuta las pruebas de integración

make clean-test

Elimina los artefactos y contenedores de prueba

Descargar desde GHCR

La imagen Docker se construye y publica automáticamente en GitHub Container Registry:

docker pull ghcr.io/gelse/ssh-mcp:latest

Limitaciones y modelo de amenazas

Lo que ssh-mcp no es

  • No es una shell. No puedes obtener una sesión de terminal interactiva. Toda la ejecución se realiza mediante llamadas de comando de una sola vez.

  • No es un gestor de archivos. SFTP se limita a la subida/descarga de un solo archivo, con validación de rutas y aplicación del sandbox. No hay listado de directorios ni operaciones recursivas.

  • No es un cortafuegos de red. La limitación de frecuencia se aplica por IP con valores predeterminados fijos. Protege contra clientes descontrolados, no contra atacantes decididos.

Modelo de amenazas

Amenaza

Mitigación

Inyección de comandos mediante encadenamiento (cmd1 && cmd2)

Segmentación de comandos — cada segmento ejecuta la cadena de autorización completa

Redirección de shell a rutas sensibles (> /etc/passwd)

El guardián de los destinos de redirección deniega las redirecciones hacia /dev/, /proc/, /sys/

Path traversal en SFTP

Validación de rutas en 8 capas: comprobación de byte nulo, eliminación de caracteres de control, normalización de segmentos de punto, resolución de enlaces simbólicos, aplicación de la raíz del sandbox

ReDoS mediante block_patterns

Análisis estático en el momento de la carga + protección con tiempos de espera en tiempo de ejecución

Fuerza bruta contra la clave de API

PBKDF2-HMAC-SHA256 con verificación en tiempo constante; limitación de frecuencia por IP

Inyección en logs

Saneamiento de los saltos de línea en todos los campos controlados por el usuario antes de registrarlos en el log

Secretos en la configuración

Separación de secrets.json, variables de entorno MCP_SSH_SECRET_*, permisos de archivo 0600

Fuera de alcance

  • Terminación TLS (gestionada por tu proxy inverso)

  • Autenticación de usuarios más allá de las claves de API (sin OAuth ni mTLS en la capa de aplicación)

  • Multiplexación de sesiones SSH (sin paso a través de tmux/screen)

  • Protección contra la manipulación de los registros de auditoría (los logs son archivos locales; usa tu propio envío de logs para la inmutabilidad)


Desarrollo

Estructura del proyecto

  • server.py — Fábrica de la aplicación FastMCP + punto de entrada CLI

  • lib/ — 30 módulos de responsabilidad única (auth, config, cliente SSH, transferencia de archivos, logging, etc.)

  • config-api/ — API de configuración + panel web (FastAPI, montado en /api cuando CONFIG_API_ENABLED=true)

  • tests/ — 36 archivos de pruebas unitarias + pruebas de integración con contenedores Docker reales

Stack tecnológico

Python 3.13, FastMCP 3.4.x, paramiko 5.0, Starlette 1.4, FastAPI 0.115+, Pydantic 2.10+, httpx 0.28+, uvicorn 0.34+

Ejecutar las pruebas

# Unit tests (fast inner loop)
source .venv/bin/activate
python -m pytest tests/test_<module>.py -x

# Full unit test suite
make test

# Integration tests (requires Docker)
make integrationtest

Añadir una nueva herramienta

El ejemplo práctico en AGENTS.md explica paso a paso cómo añadir un nuevo handler @mcp.tool() de principio a fin: constantes, tipos, reexportaciones, handler, pruebas, commit.

Sin herramientas de lint ni de verificación de tipos

El proyecto no tiene configuración de ruff, mypy, pyright ni flake8. El formato sigue los valores predeterminados de .editorconfig (4 espacios para Python, líneas de 88 caracteres).


Hoja de ruta

  • Interfaz gráfica de configuración para la gestión visual de políticas


Licencia

Licencia MIT — consulta LICENSE para más detalles.

A
license - permissive license
Not graded
quality - not tested
A
maintenance

Maintenance

Maintainers
<1hResponse time
Release cycle
1Releases (12mo)
Commit activity

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables secure remote access operations through SSH, SFTP, rsync, VPN, and tunneling with enterprise-grade policy enforcement and audit logging. Provides AI assistants with secure, policy-driven access to remote systems while maintaining comprehensive audit trails and zero-trust security.
    1
    Apache 2.0
  • A
    license
    B
    quality
    A
    maintenance
    Provides policy-driven, auditable SSH access to server fleets for AI assistants with zero-trust security controls, command whitelisting, and comprehensive audit logging to safely manage infrastructure.
    13
    27
    Apache 2.0
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI assistants to securely execute remote SSH commands, perform file transfers, and monitor system status through a standardized interface. It features robust security controls including command whitelisting, blacklisting, and credential isolation to prevent unauthorized operations.
    10
    22
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to securely execute SSH commands on remote servers with connection pooling, session isolation, and a web audit panel.
    3
    MIT

View all related MCP servers

Related MCP Connectors

  • Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.

  • Let AI operate servers without SSH. Choose actions, approve risky changes, and audit every step.

  • Agent payments, API key vaulting, and governed mandates. Agents spend within user-defined limits.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gelse/ssh-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server