SSH MCP Server

officiel

Exécutez des commandes, déplacez des fichiers, recherchez des journaux et auditez des machines via SSH depuis votre agent.

Que pouvez-vous faire avec SSH MCP ?

  • Exécuter des commandes avec des garde-fous de sécurité — Demandez à votre assistant d’exécuter des commandes uniques ou groupées via ssh_exec, avec une protection contre les commandes destructrices qui bloque les opérations irréversibles avant qu’elles n’atteignent le serveur.
  • Lire, écrire et lister des fichiers distants — Utilisez ssh_file_read, ssh_file_write et ssh_file_list pour inspecter ou modifier des fichiers, avec des écritures atomiques et une vérification facultative SHA-256.
  • Rechercher dans les journaux et vérifier l’état du serveur — Interrogez ssh_log_search ou ssh_log_tail sur des fichiers et conteneurs, ou obtenez un instantané structuré de l’état avec ssh_snapshot et ssh_audit_baseline.
  • Transférer des fichiers avec vérification d’intégrité — Téléversez ou téléchargez des fichiers et répertoires via ssh_upload et ssh_download, avec repli automatique sur scp hérité pour les appareils plus anciens.
  • Gérer des tâches de fond de longue durée — Détachez les opérations lentes avec ssh_exec et suivez-les via ssh_job_status, ssh_job_output et ssh_job_kill, en survivant aux déconnexions.

Documentation

SSH MCP Server — Outils serveur distant pour agents IA

SSH MCP Server

Un serveur MCP SSH — un outil polyvalent qui vous fait gagner du temps et des tokens, à vous et à votre agent IA, pour le débogage, le développement et la maintenance de serveurs.

Exécutez des commandes, déplacez des fichiers, lisez des journaux et auditez des machines via SSH — un VPS cloud, une machine physique, ou le routeur BusyBox qui dort dans votre placard.

Il utilise le client OpenSSH déjà présent sur votre machine : vos clés, votre ~/.ssh/config, vos hôtes de rebond, votre forwarding d'agent. Rien d'intégré, rien à compiler, aucune liaison native.

Fonctionne avec Claude Code, Codex CLI, Cline, opencode, Gemini CLI, Qwen Code, Hermes et d'autres clients MCP.

MCP Registry Glama Smithery npm downloads tests

Installation · Outils · Configuration · Sécurité · Feuille de route · Documentation · Journal des modifications


Installation en 30 secondes

Aucune installation globale requise. npx télécharge le paquet lors de la première utilisation :

npx -y @hypnosis/ssh-mcp-server

Ajoutez-le à votre client MCP — Claude Code, par exemple — pour chaque projet :

claude mcp add ssh -s user \
  -e SSH_PROFILES_FILE="$HOME/.claude/ssh-profiles.json" \
  -- npx -y @hypnosis/ssh-mcp-server

Ou écrivez-le à la main — le même serveur dans la forme de configuration que partagent la plupart des clients :

{
  "mcpServers": {
    "ssh": {
      "command": "npx",
      "args": ["-y", "@hypnosis/ssh-mcp-server"],
      "env": {
        "SSH_PROFILES_FILE": "~/.claude/ssh-profiles.json"
      }
    }
  }
}

Créez ensuite ~/.claude/ssh-profiles.json avec au moins une machine :

{
  "profiles": {
    "production": {
      "host": "server.example.com",
      "username": "admin",
      "privateKeyPath": "~/.ssh/your_private_key"
    }
  }
}

C'est suffisant pour se connecter.

Codex, opencode, Qwen Code et d'autres clients sont couverts dans Configurer le serveur MCP SSH.

Installation comme plugin

Certains clients — Claude Code, par exemple — peuvent prendre l'ensemble comme plugin à la place :

/plugin marketplace add hypnosis/ssh-mcp-server
/plugin install ssh-mcp-server@ssh-mcp-server

Le plugin lit ~/.claude/ssh-profiles.json sauf si SSH_PROFILES_FILE indique le contraire, donc créez ce fichier d'abord et le serveur démarre avec vos machines déjà chargées.

Prérequis

npm version Node.js TypeScript MCP SDK

Node.js 18+ et un client ssh système sur PATH. Sur Windows, utilisez un profil basé sur une clé ; les profils avec mot de passe et phrase secrète ne sont actuellement pas disponibles.

Vous préférez une version épinglée, un travail hors ligne, ou une vérification de registre en moins à chaque lancement : npm install -g @hypnosis/ssh-mcp-server, puis utilisez ssh-mcp-server comme commande au lieu de npx.

À qui cela s'adresse

  • DevOps et SRE qui veulent des audits plus rapides, des vérifications d'incidents et un travail serveur de routine.
  • Codeurs par vibes et créateurs indépendants qui livrent avec un assistant IA et exécutent ce qu'ils construisent sur leurs propres serveurs.
  • Administrateurs système et ingénieurs plateforme qui veulent des outils structurés au lieu d'un shell brut sans restriction.
  • Développeurs et petites équipes gérant leur propre VPS sans équipe d'exploitation dédiée.
  • Propriétaires de homelab, NAS et routeurs dont le matériel utile a survécu à ses protocoles modernes.

Pourquoi un serveur MCP SSH plutôt qu'un shell brut

Moins de tokens, coûts IA réduits

Un shell brut donne à un agent IA un déluge : commandes répétées, tableaux ASCII et vidages de journaux. Cela brûle des tokens pour transformer ce bruit en image du serveur — votre argent.

Débogage serveur plus rapide

Des outils spécialisés regroupent les vérifications de routine, plafonnent les sorties bruyantes et renvoient la partie qui compte. L'agent passe moins de temps à traduire la sortie du terminal et arrive plus vite à la correction.

Moins de suppositions, moins d'erreurs IA

Les réponses structurées indiquent ce qui a été trouvé, ce qui n'a pas pu être mesuré et ce qui a été tronqué. Cela laisse moins de place à l'agent pour combler les lacunes avec une hallucination — et vous donne moins de mauvaises corrections, des déploiements plus calmes et un code plus fiable.

Compatibilité SSH : serveurs modernes, équipement hérité et Windows

Utilisez votre configuration OpenSSH existante

Aucune implémentation SSH intégrée, aucune liaison native, aucune recompilation par plateforme. Les commandes utilisent le client ssh système, donc vos clés, votre ~/.ssh/config, vos hôtes de rebond et votre forwarding d'agent continuent de fonctionner exactement comme dans un terminal. Lorsque c'est pris en charge, une connexion multiplexée partagée par destination signifie que vous vous authentifiez une fois, pas une fois par commande.

Support SSH pour serveurs hérités, routeurs et NAS

Envoyez un fichier à un routeur avec un scp moderne et vous obtenez ceci :

scp app.conf router:/etc/
# scp: subsystem request failed on channel 0

Rien n'est cassé — un scp actuel parle le nouveau protocole, et le routeur ne le sait pas. Dans un terminal, vous allez maintenant lire un fil de forum et revenir avec un drapeau supplémentaire. Ici, vous ne faites rien : le transfert est tenté, le refus est reconnu, l'ancien protocole est utilisé à la place, et cette machine est mémorisée pour que le prochain fichier y aille directement.

Solutions de repli pour clients SSH plus anciens et outils manquants

Le vieil équipement obtient un repli, pas une impasse. Lorsqu'une fonctionnalité moderne manque, le serveur prend la voie plus ancienne quand c'est possible :

Votre machineCe que vous obtenez
Un routeur ou NAS trop petit pour le transfert de fichiers moderneLe fichier arrive quand même — l'ancien protocole est utilisé automatiquement
Un serveur de dix ansLe flux de travail fonctionne toujours ; il ouvre simplement une nouvelle connexion par commande au lieu d'en réutiliser une
Une image allégée sans moyen de hacher un fichierLe téléversement dit « impossible de vérifier » au lieu de prétendre une correspondance que personne n'a vérifiée
Une machine où un outil n'est simplement pas installéLa réponse dit « non mesuré » — jamais un zéro qui se lit comme « rien là »

Conçu pour le Model Context Protocol

Construit sur le SDK MCP officiel, TypeScript partout, plus de 2500 tests unitaires ainsi qu'une suite en direct qui s'exécute contre de vrais conteneurs plutôt que des simulations.


SSH brut vs serveur MCP SSH : le même travail, des deux manières

Vérification de santé du serveur SSH

Situation : Un déploiement vient de sortir. Le serveur semble lent, et vous ne savez pas si le disque, la mémoire, les services, les conteneurs ou les erreurs sont en cause.

Question : « Cette machine est-elle saine ? »

SSH brut

$ uptime
 10:42:17 up 18 days,  3:21,  2 users,  load average: 0.42, 0.31, 0.28
$ df -hT
Filesystem     Type   Size  Used Avail Use% Mounted on
/dev/sda1      ext4    40G   35G  5.0G  87% /
overlay        overlay  40G   35G  5.0G  87% /var/lib/docker/overlay2/...
$ free -h
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       4.9Gi       612Mi       121Mi       2.2Gi       2.5Gi
$ systemctl --failed
  UNIT              LOAD   ACTIVE SUB    DESCRIPTION
● api-worker.service loaded failed failed API background worker
$ docker ps -a
CONTAINER ID   IMAGE          STATUS                     PORTS
8e14d0b41c2a   api:latest     Up 3 minutes               0.0.0.0:8080->8080/tcp
65b894af2430   worker:latest  Exited (1) 2 minutes ago
$ ss -tulpn
Netid  State   Local Address:Port   Process
tcp    LISTEN  0.0.0.0:22          users:(("sshd",pid=842,fd=3))
tcp    LISTEN  0.0.0.0:8080        users:(("docker-proxy",pid=1942,fd=4))
$ journalctl -p err --since -1h | tail -50
Aug 20 10:39:14 prod api-worker[22104]: database connection timed out
Aug 20 10:39:14 prod systemd[1]: api-worker.service: Failed with result 'exit-code'.

C'est encore un résultat abrégé. Une vérification complète nécessite plus de commandes pour le CPU, les états des services, les nombres de conteneurs et les erreurs récentes, chacune avec son propre format de sortie. Pire, une machine sans ss peut sembler avoir zéro écouteur quand la vérification de port n'a jamais tourné.

Résultat MCP structuré

ssh_snapshot({ "profile": "production" })
{
  "disk_pct": 87,
  "mem_pct": 64,
  "cpu_pct": 12,
  "load": "0.42 0.31 0.28",
  "containers": 7,
  "ports": 14,
  "services_running": 3,
  "recent_errors": 21,
  "unavailable": []
}

Ce que l'agent gagne

SSH brutMCP structuréVotre gain
Plusieurs commandes et tableaux ASCIIChamps nommés dans un seul résultatUn appel, champs nommés et moins d'allers-retours
Un outil manquant peut ressembler à une sortie videunavailable nomme ce qui n'a pas été mesuréMoins de suppositions et moins de mauvaises corrections
Vous triez disques, services et erreursLes signaux de problème sont déjà remontésDébogage plus rapide

Un résultat ssh_audit_baseline complet peut être plus long qu'une poignée de sorties de commandes brutes — environ 1 077 tokens contre 765 dans notre mesure en laboratoire. L'économie vient du flux de travail complet, pas du fait de raccourcir une réponse.

Dans une vraie session de dépannage, les outils spécialisés ont réduit 49 appels de commandes séparés à 4 appels MCP. Chaque appel supplémentaire démarre un autre tour de modèle avec la conversation accumulée. La mise en cache des invites peut réduire le coût des entrées répétées, mais les nouvelles commandes et leur sortie consomment toujours du contexte. Moins d'allers-retours signifient moins de tokens sur la session, moins d'analyses répétées et un chemin plus rapide vers la réponse.

Besoin de l'image complète plutôt que du pouls ? ssh_audit_baseline regroupe le système, le disque, la mémoire, les ports, sshd, les unités en échec, Docker, le pare-feu et les mises à jour. Les résultats arrivent en CRITIQUE / AVERTISSEMENT / OK ; les sections non mesurées sont nommées au lieu de se lire silencieusement comme zéro.

Recherche de journal sur serveur Linux

Situation : L'API expire, mais le même message peut être dans nginx, syslog, journald ou un journal d'application que vous ne pouvez pas lire avec votre utilisateur normal.

Question : « D'où vient cette erreur ? »

SSH brut

$ grep -i "timeout" /var/log/nginx/error.log
2026/08/20 10:38:54 [error] upstream timed out while reading response header
$ grep -i "timeout" /var/log/syslog
Aug 20 10:39:14 prod api-worker[22104]: database connection timed out
$ grep -i "timeout" /var/log/app/*.log 2>/dev/null
$ journalctl -u api --since "1 hour ago" | grep -i timeout
Aug 20 10:39:14 prod api[22104]: database connection timed out after 30000ms

La troisième commande semble propre, mais 2>/dev/null a aussi masqué une erreur de permission. « Rien ne correspond » et « rien n'a été lu » semblent maintenant identiques. Un journal chargé peut aussi renvoyer des milliers de lignes et pousser le reste de l'incident hors du contexte de l'agent.

Résultat MCP structuré

ssh_log_search({ "profile": "production",
                 "path": ["/var/log/nginx/error.log", "/var/log/syslog", "/var/log/app/*.log"],
                 "query": "timeout", "context": 2, "since": "1h" })
{
  "matches": 34,
  "lines": [
    { "file": "/var/log/nginx/error.log", "line": 4821,
      "text": "upstream timed out while reading response header", "context": false },
    { "file": "/var/log/nginx/error.log", "line": 4822,
      "text": "client closed connection", "context": true }
  ],
  "files_searched": 6,
  "files_unreadable": ["/var/log/app/private"],
  "files_skipped": 12,
  "files_undated": [],
  "limited": false,
  "truncated": false
}

Ce que l'agent gagne

SSH brutMCP structuréVotre gain
Quatre recherches et quatre sortiesUne recherche sur fichiers et globsMoins de tokens et d'allers-retours
Les erreurs de permission peuvent disparaîtrefiles_unreadable nomme chaque chemin manquéPas de fausse conclusion « journaux propres »
La sortie peut croître sans plafond utilelimited et truncated exposent chaque coupureDécisions plus sûres à partir de résultats partiels

since utilise l'horloge du serveur, namesOnly: true renvoie uniquement les chemins correspondants, et ssh_log_tail lit les N dernières lignes de plusieurs journaux en un seul appel.

Modifications de configuration distantes sûres

Situation : Vous devez remplacer une configuration nginx sur un serveur en production. Une connexion interrompue, un mauvais mode ou une copie non vérifiée pourrait laisser le service avec un fichier cassé.

Question : « Puis-je remplacer cette configuration sans laisser un fichier partiel ? »

SSH brut

$ sudo sh -c 'cat > /etc/nginx/conf.d/api.conf' <<'EOF'
server {
    listen 80;
    location / { proxy_pass http://127.0.0.1:8080; }
}
EOF
$ echo $?
0

Un code de sortie zéro dit que le shell a terminé. Cela ne prouve pas quels octets ont atterri, et > a tronqué l'ancien fichier avant que le premier octet du nouveau n'arrive. Si la connexion tombe en pleine écriture, le service reste avec une configuration partielle.

Résultat MCP structuré

ssh_file_write({ "profile": "production",
                 "files": [{ "path": "/etc/nginx/conf.d/api.conf",
                             "content": "server {\n    listen 80;\n    location / { proxy_pass http://127.0.0.1:8080; }\n}\n",
                             "mode": "644", "sudo": true, "verify": true }] })
{
  "files": [{ "path": "/etc/nginx/conf.d/api.conf", "written": true,
              "verified": "verified", "reason": null, "bytes": 79 }]
}

Ce que l'agent gagne

SSH brutMCP structuréVotre gain
La cible est tronquée avant que la copie ne se termineUn fichier temporaire complet la remplace avec un seul renommagePas de configuration à moitié écrite
Code de sortie uniquementOctets et résultat de vérification sont nommésVous savez ce qui a réellement atterri
Les permissions vivent dans le texte du shellsudo, mode et verify sont des champs par fichierPropriété prévisible et moins d'erreurs de citation

verified a trois résultats honnêtes : verified, unavailable quand le serveur n'a pas d'outil de hachage, et skipped quand la vérification n'a pas été demandée. Pour les lectures, ssh_file_read accepte une liste de chemins ; ssh_file_list gère les globs, la récursion, les tailles et les modes.

Exécuter des commandes SSH par lots avec sudo

Situation : Un déploiement est prêt, mais la syntaxe nginx, l'état des services et les erreurs récentes doivent tous être vérifiés avant que le trafic ne bouge. Un échec de vérification ne doit pas disparaître dans un vidage combiné.

Question : « Chaque vérification préalable a-t-elle réussi ? »

SSH brut

$ ssh admin@server.example.com 'sudo nginx -t'
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ ssh admin@server.example.com 'sudo systemctl is-active nginx'
active
$ ssh admin@server.example.com 'sudo tail -5 /var/log/nginx/error.log'
2026/08/20 10:38:54 [error] upstream timed out while reading response header

Trois connexions renvoient trois sorties sans rapport. Si les commandes sont jointes avec ;, le shell ne rapporte que le dernier code de sortie ; si elles sont jointes avec &&, les vérifications ultérieures disparaissent après le premier échec.

Résultat MCP structuré

ssh_exec({ "profile": "production",
           "command": ["nginx -t", "systemctl is-active nginx",
                       "tail -5 /var/log/nginx/error.log"],
           "sudo": true })
{
  "commands": [
    { "command": "nginx -t", "exit_code": 0, "truncated": false, "clipped_bytes": 0,
      "stdout": "", "stderr": "nginx: configuration file /etc/nginx/nginx.conf test is successful\n" },
    { "command": "systemctl is-active nginx", "exit_code": 0, "truncated": false,
      "clipped_bytes": 0, "stdout": "active\n", "stderr": "" },
    { "command": "tail -5 /var/log/nginx/error.log", "exit_code": 0, "truncated": false,
      "clipped_bytes": 0, "stdout": "2026/08/21 09:14:02 [error] upstream timed out\n", "stderr": "" }
  ],
  "job_id": null
}

Ce que l'agent gagne

SSH brutMCP structuréVotre gain
Trois appels et sorties sans rapportUne liste de commandes ordonnéeMoins d'allers-retours
Un shell combiné peut masquer le statut intermédiaireChaque commande garde son propre exit_codeAucun échec de vérification manqué
sudo et les citations sont répétés dans le texte de commandesudo s'applique à tout le lotMoins d'erreurs de citation

La protection contre les commandes destructrices vérifie la liste complète avant que la première commande ne s'exécute. Si une entrée est refusée, chaque autre entrée est marquée comme non exécutée et rien n'est envoyé au serveur. Chaque commande porte ses propres stdout et stderr. Une commande qui s'est exécutée sans rien afficher a une chaîne vide ; une commande qui n'a jamais tourné n'a pas du tout ce champ, donc les deux ne peuvent pas être confondus. Une sortie de plus de 128 Ko par commande conserve les deux extrémités — le début pour les tableaux, la fin pour les journaux — avec une jointure au milieu indiquant la quantité, et clipped_bytes précise combien a été coupé. La coupure se fait sur des limites d'octets et recule jusqu'au bord d'un caractère, donc une réponse tronquée ne porte jamais de marque de remplacement.

sudo atteint le serveur sans terminal : la réponse du profil est transmise à sudo sur l'entrée standard. Le secret utilisé provient de sudoPassword lorsque le profil en nomme un et de password sinon — un profil qui se connecte par clé n'a aucun mot de passe de connexion, et là où une machine sépare les deux, celui de connexion est la mauvaise réponse. Quand il n'y a rien avec quoi répondre, la réponse le dit et nomme les solutions, au lieu de laisser les propres conseils de sudo sur -S et les assistants askpass. Une commande qui lit sa propre entrée standard ne reçoit jamais le mot de passe, qui se retrouverait sinon mélangé aux données.

Exécuter des tâches SSH de longue durée

Situation : Une sauvegarde ou une migration durera plus longtemps que la session de l'agent. La connexion peut se fermer, mais vous aurez encore besoin de son état, de sa sortie et de son code de sortie plus tard.

Question : « Cette tâche survivra-t-elle à la conversation ? »

SSH brut

$ ssh admin@server.example.com 'pg_dump app | gzip > /srv/backups/app.sql.gz'
client_loop: send disconnect: Broken pipe

Le terminal a disparu. Vous devez maintenant vous reconnecter, trouver le processus, inspecter le fichier cible et deviner si la sauvegarde s'est terminée ou s'est arrêtée à mi-chemin.

Résultat MCP structuré

ssh_exec({ "profile": "production",
           "command": "pg_dump app | gzip > /srv/backups/app.sql.gz",
           "detach": true })
{
  "commands": [{
    "command": "pg_dump app | gzip > /srv/backups/app.sql.gz",
    "exit_code": null,
    "truncated": false,
    "timed_out": false,
    "blocked": false,
    "blocked_reason": null,
    "not_run": false,
    "warning": null
  }],
  "job_id": "mst0f2q1-9ab3c4d5"
}

Ce que l'agent gagne

SSH brutMCP structuréVotre gain
La tâche est liée à une seule session SSHLa tâche distante a un identifiant persistantDéconnexions et redémarrages sûrs
Se reconnecter signifie chercher processus et fichiersLe statut et le code de sortie ont des états nommésPas de devinette sur la fin
Relire la sortie répète l'ancien texteLa sortie continue depuis un décalage d'octetsMoins de jetons utilisés sur les longues tâches

L'état de la tâche vit sur le disque distant, pas dans la mémoire de ce serveur. ssh_job_status distingue running, finished et lost ; ssh_job_output continue depuis le dernier décalage d'octets ; et ssh_job_kill signale tout le groupe de processus au lieu de seulement son shell.

Transférer des fichiers vers des routeurs et NAS hérités

Situation : Un client OpenSSH actuel essaie SFTP, mais le routeur ou le NAS ne comprend que le protocole scp classique. Le fichier doit quand même arriver intact et remplacer sa cible en toute sécurité.

Question : « Cet ancien appareil peut-il encore recevoir un fichier vérifié ? »

SSH brut

$ scp app.conf operator@router:/etc/app.conf
subsystem request failed on channel 0
scp: Connection closed

L'étape suivante habituelle est de se souvenir du drapeau hérité, de réessayer la copie puis d'exécuter une commande de hachage séparée — si l'appareil dispose d'un outil de hachage.

Résultat MCP structuré

ssh_upload({ "profile": "router", "local_path": "./app.conf",
             "remote_path": "/etc/app.conf", "sudo": true,
             "mode": "644", "owner": "root:root", "verify": true })
{
  "files": [{
    "path": "/etc/app.conf",
    "written": true,
    "verified": "verified",
    "reason": null,
    "bytes": 1284
  }]
}

Ce que l'agent gagne

SSH brutMCP structuréVotre gain
Le mode SFTP moderne s'arrête à la première erreurLe repli scp classique est automatique et mémoriséLe vieux matériel fonctionne encore
Une copie réussie ne prouve pas l'intégritéLa vérification SHA-256 a un résultat nomméLa corruption n'est pas prise pour un succès
Le remplacement direct peut laisser une cible partielleUn fichier temporaire est déplacé en place après le transfertLe fichier de travail survit aux interruptions

Si l'appareil n'a ni sha256sum ni openssl, le résultat dit unavailable et nomme la raison au lieu de signaler une fausse correspondance. Les répertoires entiers utilisent recursive: true et vérifient leurs hachages en un seul lot.

Protection contre les commandes destructrices pour les agents IA

La garde s'exécute localement, avant qu'une commande n'atteigne SSH. Elle sépare les opérations qui peuvent être récupérées de celles qui détruisent le conteneur contenant les données, et elle vérifie l'ordre des commandes dans les chaînes et les lots.

Arrêter une chaîne destructrice avant qu'elle ne commence

Une séquence sûre de sauvegarde-et-remplacement :

cp -r /srv/app /srv/app.bak && mv /srv/app /srv/app-old && rm -rf /srv/app

Les mêmes opérations dans le mauvais ordre :

rm -rf /srv/app && cp -r /srv/app /srv/app.bak && mv /srv/app /srv/app-old
# REFUSED before the first command runs

Le shell supprimerait le répertoire et ne découvrirait qu'ensuite que la source de sauvegarde a disparu. La garde voit que les étapes ultérieures lisent une cible déjà détruite par une étape antérieure, donc tout l'appel reste sur votre machine. La même vérification détecte dropdb app && pg_dump app > backup.sql.

Refuser la perte irréversible, avertir des changements récupérables

Refusé — le conteneur lui-mêmeSeulement averti — son contenu
DROP DATABASE, dropdbDROP TABLE, TRUNCATE, DELETE FROM
docker volume rm, docker compose down -vdocker rm -f <name>
crontab -rmodifier une tâche
mkfs, wipefs -a, lvremove, zfs destroychmod 777
reboot, shutdown, haltgit reset --hard

docker compose down -v est refusé parce que -v supprime les volumes Docker nommés, y compris un volume de base de données. Sans -v, arrêter les services n'est pas traité comme la même action irréversible.

La suppression récursive de la racine du système de fichiers, d'un répertoire personnel ou d'arbres système tels que /etc, /var et /usr est également refusée, y compris lorsqu'un lien symbolique y mène. Une cible non résolue telle que rm -rf "$DIR"/* est aussi refusée : « impossible de vérifier » n'est pas traité comme « sûr ».

Nommer ce que vous arrêtez

Une commande qui trouve sa cible au lieu de la nommer n'est pas envoyée. Le serveur l'expand et répond avec ce qui se cache derrière la cible :

docker kill $(docker ps -q --filter ancestor=web)
# BLOCKED — would stop:
#   edge — web:latest, Up 34 days, 0.0.0.0:8443->8443/tcp

Pour un processus, la réponse ajoute les signes qu'il est en cours d'utilisation : depuis combien de temps il tourne, sur quels ports il accepte les connexions, combien de connexions il porte. Les cibles nommées ne coûtent rien de plus et passent en silence — docker kill web-1, kill 4871, systemctl stop app.

Pour continuer, nommez ce qui est arrêté. Les noms sont vérifiés par rapport à ce que la commande atteint réellement, donc un masque qui a dérivé vers autre chose est refusé plutôt que confirmé :

docker kill $(docker ps -q --filter ancestor=web) # CONFIRMED-KILL: edge

Un motif sur les lignes de commande est un cas particulier. Il correspond à la commande même qui le porte, donc le shell qui l'exécute est signalé avant la cible et la réponse s'interrompt au milieu. Une telle frappe n'est pas confirmée mais réécrite — par numéro, ou avec un caractère écrit comme une classe pour que le motif cesse de se correspondre à lui-même :

pkill -f relay
# BLOCKED — two ways through:
#   kill 4871
#   pkill -f '[r]elay' # CONFIRMED-KILL: 4871

Trois résultats restent distincts : cibles trouvées, l'expansion n'a rien atteint, et rien avec quoi demander — pas de moteur sur la machine, une réponse tronquée, une connexion qui a échoué. Les deux derniers sont aussi des refus : ne pas savoir n'est pas une raison de procéder.

Confirmer une commande destructrice intentionnelle

Rien n'est interdit définitivement. Ajoutez # CONFIRMED-DESTRUCTIVE à une commande examinée et elle est autorisée. Quand la garde refuse une entrée dans un lot, le lot complet s'arrête avant l'exécution, donc le serveur n'est jamais laissé après une opération à moitié exécutée.

La garde fonctionne dans un seul appel. Elle ne peut pas relier une suppression dans une invocation avec une lecture dans la suivante, ni raisonner sur des outils qu'elle ne reconnaît pas. C'est une ceinture de sécurité, pas un moteur de politique : les opérations récupérables restent votre décision. Les restrictions de chemins et les règles de citation sont documentées dans docs/security.md.

Outils

18 outils SSH MCP pour les opérations serveur. Paramètres complets et exemples dans docs/tools.md.

OutilCe qu'il fait
ssh_execExécuter une commande ou un lot, avec la garde anti-destructrice et détachement optionnel
ssh_file_readLire un ou plusieurs fichiers, texte ou binaire
ssh_file_writeÉcrire des fichiers avec renommage atomique et vérification SHA-256 optionnelle
ssh_file_listLister un répertoire, avec glob et récursion optionnels
ssh_uploadTéléverser un fichier ou répertoire via SSH, binaire-sûr avec vérifications d'intégrité ; un répertoire remplace la cible ou fusionne dedans
ssh_downloadTélécharger un fichier ou répertoire via SSH, binaire-sûr avec vérifications d'intégrité
ssh_job_statusÉtat d'une tâche en arrière-plan : en cours, terminée, ou perdue
ssh_job_outputLire la sortie accumulée depuis un décalage d'octets
ssh_job_listLister les tâches, en balayant celles terminées au-delà de leur TTL
ssh_job_killSignaler tout le groupe de processus d'une tâche
ssh_log_tailDernières N lignes d'un ou plusieurs journaux, glob pris en charge ; un conteneur par nom
ssh_log_searchRecherche par motif dans les journaux, ou à travers le journal d'un conteneur
ssh_snapshotInstantané de santé ponctuel : services, ressources, Docker, réseau, erreurs
ssh_monitorContrôle du transport : statistiques, rechargement, test, liste, fermeture
ssh_audit_baselineSystème, disque, mémoire, réseau, ssh, services, Docker, pare-feu, mises à jour
ssh_tls_checkExpiration de certificat, SAN, chaîne et hook de renouvellement pour un domaine
ssh_disk_breakdownOù le disque est parti : du top-N, Docker, journald, caches
ssh_service_statussystemctl status plus une queue journalctl pour une unité

Annotations de sécurité des outils MCP

Les annotations MCP standard indiquent aux clients quels outils sont en lecture seule, destructeurs, idempotents ou monde-ouvert. Voir le tableau complet.

Exécuter des commandes SSH et gérer des fichiers distants

Commandes, lectures et écritures de fichiers, listages de répertoires — le travail ordinaire sur une machine, chaque réponse déjà analysée.

Surveiller les tâches SSH de longue durée

Le travail lent est détaché et suivi au lieu d'être attendu : chaque regard dit jusqu'où il est allé.

Rechercher dans les journaux et vérifier la santé du serveur

Journaux de fichiers et de conteneurs, et une image ponctuelle de la machine, avec sortie plafonnée pour qu'une queue ne dévore pas la fenêtre de contexte.

Téléverser et télécharger des fichiers via SSH

Transferts binaires-sûrs avec vérifications d'intégrité. Détails dans docs/transfer.md.

Pour les binaires et gros fichiers, utilisez ssh_upload / ssh_download — les morceaux base64 et les heredocs ne sont ni binaires-sûrs ni atomiques.

Auditer des serveurs Linux via SSH

En lecture seule et regroupés en un seul aller-retour. Détails dans docs/audit.md.

Mode de compatibilité SSH Windows

Windows utilise le mode de compatibilité automatiquement. Quand le multiplexage de connexion est indisponible, le serveur passe à une connexion par commande. Les mêmes outils restent disponibles via SSH par clé — aucune configuration séparée ni implémentation spécifique à Windows.

La garde anti-commandes destructrices est couverte dans Protection contre les commandes destructrices pour les agents IA.

Configurer le serveur SSH MCP

Exécutez le paquet depuis Installation en 30 secondes d'abord, puis créez un fichier de profil.

Créer des profils de connexion SSH

Placez-le où vous voulez — à côté de la propre configuration de votre agent est le choix habituel. Les exemples ci-dessous utilisent ~/.claude/ssh-profiles.json ; pour d'autres agents, changez le répertoire (~/.codex/, ~/.qwen/, ~/.config/opencode/) :

{
  "profiles": {
    "production": {
      "host": "server.example.com",
      "username": "admin",
      "port": 22,
      "privateKeyPath": "~/.ssh/your_private_key"
    }
  }
}

Choisir un profil SSH explicitement

Il n'y a pas de profil de repli : chacun est une machine différente, et une commande envoyée à la mauvaise machine n'est pas quelque chose qu'un message d'erreur peut défaire ensuite. Demandez sans nom et la réponse liste les noms parmi lesquels choisir :

ssh_exec({ command: "uptime" })
→ No profile specified. Name one explicitly: production

Un profil que le serveur ne peut pas utiliser pour SSH — pas de host, pas de username, ou mode: "local" — est ignoré sans plainte, et les champs qu'il ne reconnaît pas sont laissés tranquilles, donc le fichier peut être partagé avec d'autres outils. Un profil avec un champ cassé est un cas différent : il est nommé avec le champ et la valeur, et ses voisins sains continuent de fonctionner.

Chaque profil prend optionnellement un bloc pathSecurity qui autorise ou bloque les chemins que les outils de fichiers peuvent toucher — voir docs/security.md.

Un profil qui se connecte par clé mais a besoin de sudo côté distant prend un sudoPassword — le secret auquel sudo répond, qui sur beaucoup de machines n'est pas le mot de passe de connexion. Gardez-le dans le fichier de secrets plutôt qu'ici.

Garder les mots de passe SSH et phrases secrètes hors des profils

Privilégiez les clés. Si un mot de passe ou une phrase secrète pour une clé chiffrée est inévitable, conservez-le dans un fichier de secrets séparé, jamais dans le profil lui-même :

{
  "secretsFile": "~/.config/ssh-mcp/secrets.json",
  "profiles": {
    "production": {
      "host": "server.example.com",
      "username": "admin"
    }
  }
}

Le fichier de secrets est indexé par nom de profil — voir secrets.json.example :

{
  "production": { "password": "..." },
  "buildbox": { "sudoPassword": "..." }
}

sudoPassword est la réponse à sudo sur cette machine. Un profil qui se connecte par clé n'a pas de mot de passe de connexion à fournir, et lorsque les deux diffèrent, celui de connexion est la mauvaise réponse ; sans lui, password est utilisé.

Le fichier de secrets doit être lisible uniquement par vous (chmod 600). Les chemins relatifs sont résolus à partir du fichier de profils ; les secrets restent hors de argv et sont masqués dans les journaux. Voir sécurité des identifiants.

Configurer Claude Code, Codex et d'autres clients MCP

Choisissez le client que vous utilisez et pointez-le vers le même fichier de profils.

Claude Code

Une seule commande ; -s user rend le serveur disponible dans chaque projet :

claude mcp add ssh -s user \
  -e SSH_PROFILES_FILE="$HOME/.claude/ssh-profiles.json" \
  -- npx -y @hypnosis/ssh-mcp-server

Codex CLI

codex mcp add ssh \
  --env SSH_PROFILES_FILE="$HOME/.codex/ssh-profiles.json" \
  -- npx -y @hypnosis/ssh-mcp-server

opencode

Placez-le dans ~/.config/opencode/opencode.json :

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "ssh": {
      "type": "local",
      "command": ["npx", "-y", "@hypnosis/ssh-mcp-server"],
      "enabled": true,
      "environment": {
        "SSH_PROFILES_FILE": "~/.config/opencode/ssh-profiles.json"
      }
    }
  }
}

Qwen Code

Une seule commande, comme les autres :

qwen mcp add ssh \
  -e SSH_PROFILES_FILE="$HOME/.qwen/ssh-profiles.json" \
  npx -y @hypnosis/ssh-mcp-server

Autres clients MCP

Gemini CLI, Hermes, Cline, un plugin d'éditeur ou votre propre agent fonctionnent de la même manière. Tout ce dont ils ont besoin, c'est d'une commande à exécuter et d'une variable d'environnement.

Redémarrer votre client MCP

Redémarrez le client, puis exécutez ssh_monitor({ action: "list" }) pour confirmer que le profil est chargé.

Configuration du serveur SSH MCP

VariableRôleDéfaut
SSH_PROFILES_FILEChemin vers le JSON des profils — obligatoire
SSH_MCP_LOG_LEVELdebug, info, warn, errorinfo
LOG_LEVELRepli, utilisé uniquement lorsque SSH_MCP_LOG_LEVEL n'est pas définiinfo
SSH_MCP_LOG_TIMESTAMPHorodatage dans les lignes de journaltrue
SSH_MCP_CONTROL_PERSISTSecondes pendant lesquelles une connexion partagée reste active après la dernière commande ; 0 la ferme immédiatement600
SSH_MCP_CONTROL_DIROù vivent les sockets de contrôle~/.ssh/ssh-mcp
SSH_MCP_PROFILES_CACHE_TTLDurée de vie du cache de profils, ms60000
SSH_MCP_PROFILES_WATCHRecharger le fichier de profils lorsqu'il changetrue

La connexion partagée survit volontairement à ce processus : la fermer à la sortie couperait le canal qu'une autre fenêtre sur la même machine utilise.

Limites du serveur SSH MCP

Chaque limite vous indique le moyen de la contourner. Un outil qui ne peut pas faire quelque chose le dit et nomme ssh_exec, qui exécute des commandes directement sur la machine — un pilote de journal non pris en charge, un utilitaire que la machine ne possède pas, un moteur que ce serveur ne parle pas. Vous n'avez pas besoin de savoir à l'avance où s'arrêtent les outils : le refus le dit, au moment où cela compte.

Trois refus restent délibérément silencieux à propos du shell, car là, c'est la mauvaise réponse : un chemin que votre profil interdit (contourner votre propre règle n'est pas une solution), un appel malformé (la correction est dans l'appel), et un refus de ssh_exec lui-même.

  • Annulation : un appel annulé arrête désormais aussi la commande sur le serveur, envoyé comme un second appel sur la même connexion. Lorsque le serveur n'a pas de /proc, la commande est trouvée via ps à la place. FreeBSD n'est pas vérifié : un comportement correct n'y est pas garanti. Les transferts de fichiers et ssh_snapshot ne prennent pas du tout l'annulation.
  • Écritures atomiques : BSD et macOS ne peuvent pas pré-vérifier les renommages entre systèmes de fichiers.

Feuille de route du serveur SSH MCP

  • Test complet contre des hôtes SSH macOS

  • Test de compatibilité de bout en bout sur Windows

  • Audits multi-hôtes — comparer la santé de plusieurs profils SSH en un seul appel

  • Importer des profils depuis le ~/.ssh/config existant

  • Transferts reprenables pour les gros fichiers et les connexions instables

  • Chronologie des opérations à distance — commandes, transferts et décisions de garde dans une seule piste d'audit

  • Playbooks de dépannage SSH prêts à l'emploi

  • Journaux de conteneurs sans passer par le shellFAIT : ssh_log_tail et ssh_log_search prennent un nom de conteneur, demandent à docker où il écrit et lisent ce fichier avec le même mécanisme que tout autre journal

  • Un refus qui vous laisse bloquéFAIT : chaque limite nomme désormais ssh_exec comme voie de sortie, donc atteindre le bord d'un outil coûte une phrase au lieu d'un jeu de devinettes

  • Des réponses qui atteignent le modèleFAIT : la sortie de commande, les lignes de journal correspondantes, les noms de machines et les sections d'instantané voyagent dans les champs, pas seulement dans le texte

  • Schémas d'outils MCP plus petitsFAIT : la liste d'outils a été allégée de 10 %, et un travail détaché affiche désormais les dernières lignes qu'il a écrites au lieu d'être interrogé à l'aveugle

  • Travail long sous rootFAIT : un travail détaché s'exécute avec sudo et est suivi en tant que root, et un profil à clé seule répond à sudo avec son propre sudoPassword

Développer et tester le serveur SSH MCP

npm install
npm run build           # tsc
npx tsc --noEmit        # types, plus dead declarations
npm run test:unit       # unit tests
npm run lab:up          # start the two test containers
npm run test:live       # live suite against those containers

La suite en conditions réelles s'exécute contre de vrais conteneurs — un BusyBox, un coreutils — car les deux divergent silencieusement, et une maquette est d'accord avec celui qui l'a écrite. Voir docs/architecture.md pour la structure.

Vous aimez SSH MCP Server ? ⭐

Si vous aimez l'outil, donnez-lui une étoile sur GitHub — cela aide plus de personnes à découvrir le projet.

Contribuer au serveur SSH MCP

Les problèmes et les demandes de tirage sont les bienvenus sur github.com/hypnosis/ssh-mcp-server.

Licence

MIT — voir LICENSE.