ssh-mcp
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.
Tabla de contenido
Related MCP server: MCP SSH Orchestrator
Arquitectura
MCP stdio local (patrón común)
AI client
│
▼
local MCP process ──► SSH targetCada 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 poolingUn ú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 serversUna 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. | ¿Coincide el comando con una regex bloqueada? | ||
3. Patrones peligrosos | ¿Contiene | ||
4. Guardia de redirección | ¿Apuntan las redirecciones del shell a | ||
5. Segmentación | Tras eliminar redirecciones y dividir por |
| |
6. Reglas | Reglas de permitir/denegar para todos los clientes | ||
7. Reglas | Reglas de permitir/denegar por clave | ||
8. Reglas | 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.enabledenfalseen la configuración presente al arrancar (por ejemplo,config/ssh-mcp-config.jsonen 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.json2. 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 --build4. 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 |
|
Autenticación | Cabecera |
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 |
| (none) | Lista los destinos SSH configurados (host, puerto, usuario — sin secretos) |
|
| Lista los comandos que el cliente actual puede ejecutar en un destino (unión de reglas default + api_key + network) |
|
| Ejecuta un comando por SSH; devuelve stdout (stderr añadido como |
|
| Descarga un archivo vía SFTP; autorización equivalente a |
|
| Sube un archivo vía SFTP; autorización equivalente a |
|
| Comprueba la conectividad SSH ejecutando el |
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 campopassworddel destino en la configuración. La opciónsudoenvuelve el comando consudo -S -p ''(contraseña desde la configuración) osudo -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 |
| Yes | — | Nombre de host o dirección IP |
| No |
| Puerto SSH |
| Yes | — | Nombre de usuario SSH |
| * | — | Ruta al archivo de clave privada SSH en el sistema de archivos del servidor |
| * | — | Contraseña SSH (también puede establecerse mediante |
| No |
| Comando ejecutado por |
* Se requiere al menos uno de private_key o password.
private_keyes 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 conkey_hashnetworks— 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 |
|
| Máx. de bytes de salida de comando devueltos al cliente (int o cadena de tamaño) |
|
| Límite máximo del tiempo de espera del comando (segundos) |
|
| Intentos de reintento para fallos SSH transitorios |
|
| Retroceso exponencial base (segundos) |
|
| Fallos antes de que se abra el circuito por destino |
|
| Tiempo de espera de recuperación para un circuito abierto (segundos) |
|
| Nivel de registro: DEBUG, INFO, WARNING, ERROR |
|
| Máx. de caracteres de salida almacenados en las entradas de registro |
|
| Comprimir con gzip los archivos de registro rotados |
|
| Máx. de conexiones SSH agrupadas por destino |
|
| Tiempo de espera de conexión inactiva (segundos) |
|
| Intervalo de limpieza del grupo (segundos) |
|
| Límite global en todos los destinos; el exceso devuelve HTTP 503 |
|
| Intervalo mínimo entre recargas de configuración; |
|
| IPs de proxy inverso de confianza (IPv4/IPv6) |
Ajustes de SFTP (settings.sftp)
Setting | Default | Description |
|
| Directorio raíz para la validación de rutas SFTP |
|
| Longitud máxima permitida de ruta SFTP (bytes); |
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.jsonSecret source | Effect |
| Sobreescrituras de |
| Sobreescribe |
| Sobreescribe |
<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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| — |
| — |
| — |
| — | (obligatorio cuando la API está habilitada) | — |
— |
|
| — |
— |
| — | — |
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 |
| Counter |
|
| Counter |
|
| Histogram |
|
| Counter |
|
| Histogram |
|
| Gauge |
|
| Gauge |
|
| Counter |
|
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 |
| Texto | Escribe en stdout. Destino por defecto. |
Archivo JSON |
| JSONL | Escribe un objeto JSON por línea en un archivo. |
Archivo de texto |
| 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_levelpara controlar el nivel por defecto.Variable de entorno: Establece
MCP_SSH_LOG_LEVELpara 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_levelque 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 server1Formato 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 |
| Configuración inicial cargada al inicio |
| Configuración releída desde el disco (con |
| Migración de esquema aplicada ( |
| Copia de la configuración predeterminada incluida |
| Reversión a los valores por defecto en memoria |
| 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/docsy/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úrala en |
| (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 |
|
| Comprobación de estado de la API de configuración (sin autenticación requerida) |
|
| Convierte una clave de API en texto plano en una cadena PBKDF2-HMAC-SHA256 |
|
| Devuelve el esquema JSON de configuración (sin autenticación requerida) |
|
| Valida un diccionario de configuración sin escribirlo en el disco |
Configuración
Método | Ruta | Descripción |
|
| Obtiene la configuración completa (redacta secretos) |
|
| Reemplaza la configuración completa |
|
| Obtiene una sección de configuración ( |
|
| Reemplaza una sección de configuración |
Destinos SSH
Método | Ruta | Descripción |
|
| Obtiene un destino SSH específico (sin secretos) |
|
| Crea o reemplaza un destino SSH |
|
| Elimina un destino SSH |
|
| Prueba la conectividad SSH mediante el |
Reglas de comandos
Método | Ruta | Descripción |
|
| Lista las reglas de comandos permitidos (mediante |
|
| Reemplaza las reglas de comandos permitidos (mediante |
Patrones de bloqueo
Método | Ruta | Descripción |
|
| Lista los patrones de bloqueo (mediante |
|
| Reemplaza todos los patrones de bloqueo |
|
| Añade un patrón de bloqueo |
|
| Reemplaza un solo patrón de bloqueo por índice |
|
| Elimina un solo patrón de bloqueo por índice |
Copias de seguridad
Método | Ruta | Descripción |
|
| Lista las copias de seguridad de configuración (las más recientes primero) |
|
| Restaura la configuración desde una copia de seguridad |
|
| 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/configPanel 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 |
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/docsReDoc:
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 |
|
| rw |
|
| rw |
|
| ro |
|
| 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 32CONFIG_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 ajustesPanel 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) yhttp://localhost:9080/api/redoc(ReDoc)
Makefile
Comando | Descripción |
| Construye la imagen Docker ( |
|
|
|
|
| Ejecuta las pruebas unitarias |
| Ejecuta las pruebas unitarias de config-api |
| Construye la imagen de prueba y ejecuta las pruebas de integración |
| 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:latestLimitaciones 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 ( | Segmentación de comandos — cada segmento ejecuta la cadena de autorización completa |
Redirección de shell a rutas sensibles ( | El guardián de los destinos de redirección deniega las redirecciones hacia |
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 | 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 |
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 CLIlib/— 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/apicuandoCONFIG_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 integrationtestAñ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.
This server cannot be installed
Maintenance
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables 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.1Apache 2.0
- AlicenseBqualityAmaintenanceProvides 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.1327Apache 2.0
- AlicenseAqualityCmaintenanceEnables 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.1022MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to securely execute SSH commands on remote servers with connection pooling, session isolation, and a web audit panel.3MIT
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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