SSH MCP Server

offiziell

Führen Sie Befehle aus, verschieben Sie Dateien, durchsuchen Sie Protokolle und prüfen Sie Maschinen über SSH von Ihrem Agenten aus.

Was kann man mit SSH MCP machen?

  • Befehle mit Sicherheitsvorkehrungen ausführen — Bitten Sie Ihren Assistenten, einzelne oder gebündelte Befehle über ssh_exec auszuführen, mit Schutz vor destruktiven Befehlen, der irreversible Operationen blockiert, bevor sie den Server erreichen.
  • Remote-Dateien lesen, schreiben und auflisten — Verwenden Sie ssh_file_read, ssh_file_write und ssh_file_list, um Dateien zu prüfen oder zu ändern, mit atomaren Schreibvorgängen und optionaler SHA-256-Verifizierung.
  • Protokolle durchsuchen und Serverzustand prüfen — Fragen Sie ssh_log_search oder ssh_log_tail über Dateien und Container ab, oder erhalten Sie eine strukturierte Zustandsübersicht mit ssh_snapshot und ssh_audit_baseline.
  • Dateien mit Integritätsprüfungen übertragen — Laden Sie Dateien und Verzeichnisse über ssh_upload und ssh_download hoch oder herunter, mit automatischem Legacy-scp-Fallback für ältere Geräte.
  • Langlaufende Hintergrundjobs verwalten — Trennen Sie langsame Operationen mit ssh_exec ab und verfolgen Sie sie über ssh_job_status, ssh_job_output und ssh_job_kill, die Verbindungsabbrüche überstehen.

Dokumentation

SSH MCP Server — Remote-Server-Tools für KI-Agenten

SSH MCP Server

Ein SSH-MCP-Server – ein Multitool, das dir und deinem KI-Agenten Zeit und Tokens beim Debugging, bei der Entwicklung und bei der Serverwartung spart.

Führe Befehle aus, verschiebe Dateien, lies Logs und prüfe Maschinen über SSH – eine Cloud-VPS, eine Bare-Metal-Box oder der BusyBox-Router in deinem Schrank.

Er nutzt den OpenSSH-Client, der bereits auf deinem Rechner ist: deine Schlüssel, dein ~/.ssh/config, deine Jump-Hosts, dein Agent-Forwarding. Nichts wird mitgeliefert, nichts muss kompiliert werden, keine nativen Bindungen.

Funktioniert mit Claude Code, Codex CLI, Cline, opencode, Gemini CLI, Qwen Code, Hermes und anderen MCP-Clients.

MCP Registry Glama Smithery npm downloads tests

Installation · Tools · Einrichtung · Sicherheit · Roadmap · Dokumentation · Changelog


Installation in 30 Sekunden

Keine globale Installation erforderlich. npx lädt das Paket bei der ersten Verwendung herunter:

npx -y @hypnosis/ssh-mcp-server

Füge es deinem MCP-Client hinzu – Claude Code zum Beispiel – für jedes Projekt:

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

Oder schreibe es von Hand – derselbe Server in der Konfigurationsform, die die meisten Clients teilen:

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

Erstelle dann ~/.claude/ssh-profiles.json mit mindestens einer Maschine:

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

Das reicht, um eine Verbindung herzustellen.

Codex, opencode, Qwen Code und andere Clients werden in Einrichtung des SSH-MCP-Servers behandelt.

Als Plugin installieren

Einige Clients – Claude Code zum Beispiel – können das Ganze stattdessen als Plugin aufnehmen:

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

Das Plugin liest ~/.claude/ssh-profiles.json, sofern SSH_PROFILES_FILE nichts anderes sagt. Erstelle diese Datei also zuerst, und der Server startet mit deinen bereits geladenen Maschinen.

Anforderungen

npm version Node.js TypeScript MCP SDK

Node.js 18+ und ein System-ssh-Client auf PATH. Unter Windows verwende ein schlüsselbasiertes Profil; Passwort- und Passphrase-Profile sind derzeit nicht verfügbar.

Bevorzugst du eine feste Version, Offline-Arbeit oder eine Registry-Prüfung weniger pro Start: npm install -g @hypnosis/ssh-mcp-server, dann verwende ssh-mcp-server als Befehl statt npx.

Für wen das gedacht ist

  • DevOps- und SRE-Teams, die schnellere Audits, Incident-Checks und routinemäßige Serverarbeit wünschen.
  • Vibe-Coder und Indie-Builder, die mit einem KI-Assistenten ausliefern und das, was sie bauen, auf eigenen Servern betreiben.
  • Sysadmins und Plattformingenieure, die strukturierte Tools statt einer uneingeschränkten Roh-Shell wollen.
  • Entwickler und kleine Teams, die ihre eigene VPS ohne dediziertes Operations-Team betreiben.
  • Homelab-, NAS- und Router-Besitzer, deren nützliche Hardware ihre modernen Protokolle überlebt hat.

Warum ein SSH-MCP-Server statt einer Roh-Shell

Weniger Tokens, geringere KI-Kosten

Eine Roh-Shell gibt einem KI-Agenten einen Feuerwehrschlauch: wiederholte Befehle, ASCII-Tabellen und Log-Dumps. Es verbrennt Tokens, um dieses Rauschen in ein Bild des Servers zu verwandeln – dein Geld.

Schnelleres Server-Debugging

Speziell entwickelte Tools bündeln Routineprüfungen, begrenzen lautes Output und liefern den Teil zurück, der zählt. Der Agent verbringt weniger Zeit damit, Terminalausgaben zu übersetzen, und kommt schneller zur Lösung.

Weniger Rätselraten, weniger KI-Fehler

Strukturierte Antworten sagen, was gefunden wurde, was nicht gemessen werden konnte und was abgeschnitten wurde. Das lässt dem Agenten weniger Raum, Lücken mit einer Halluzination zu füllen – und gibt dir weniger schlechte Fixes, ruhigere Deploys und zuverlässigeren Code.

SSH-Kompatibilität: moderne Server, Altgeräte und Windows

Nutze dein bestehendes OpenSSH-Setup

Keine gebündelte SSH-Implementierung, keine nativen Bindungen, kein Neuaufbau pro Plattform. Befehle nutzen den System-ssh-Client, sodass deine Schlüssel, dein ~/.ssh/config, deine Jump-Hosts und dein Agent-Forwarding genau so weiter funktionieren wie im Terminal. Wo unterstützt, bedeutet eine gemeinsame multiplexte Verbindung pro Ziel, dass du dich einmal authentifizierst, nicht einmal pro Befehl.

SSH-Unterstützung für ältere Server, Router und NAS-Geräte

Sende eine Datei an einen Router mit modernem scp und du erhältst Folgendes:

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

Nichts ist kaputt – ein aktueller scp spricht das neue Protokoll, und der Router kennt es nicht. Im Terminal müsstest du jetzt ein Forum lesen und mit einem zusätzlichen Flag zurückkommen. Hier tust du nichts: Die Übertragung wird versucht, die Ablehnung erkannt, das alte Protokoll stattdessen verwendet, und diese Maschine wird gemerkt, sodass die nächste Datei direkt dorthin geht.

Fallbacks für ältere SSH-Clients und fehlende Tools

Alte Geräte bekommen einen Fallback, keine Sackgasse. Wenn ein modernes Feature fehlt, nimmt der Server, wo möglich, den älteren Weg:

Deine MaschineWas du bekommst
Ein Router oder NAS, zu klein für moderne DateiübertragungDie Datei kommt trotzdem an – das alte Protokoll wird automatisch verwendet
Ein Server von vor zehn JahrenDer Workflow funktioniert weiter; er öffnet nur eine neue Verbindung pro Befehl statt eine wiederzuverwenden
Ein abgespecktes Image ohne Möglichkeit, eine Datei zu hashenDer Upload sagt „konnte nicht verifiziert werden“ statt eine Übereinstimmung zu behaupten, die niemand geprüft hat
Eine Box, auf der ein Tool einfach nicht installiert istDie Antwort sagt „nicht gemessen“ – nie eine Null, die wie „nichts da“ liest

Gebaut für das Model Context Protocol

Gebaut auf dem offiziellen MCP-SDK, durchgehend TypeScript, über 2500 Unit-Tests plus eine Live-Suite, die gegen echte Container statt Mocks läuft.


Roh-SSH vs. SSH-MCP-Server: dieselbe Aufgabe, beide Wege

SSH-Server-Gesundheitscheck

Situation: Ein Deploy ist gerade raus. Der Server fühlt sich langsam an, und du weißt nicht, ob Festplatte, Speicher, Dienste, Container oder Fehler schuld sind.

Frage: „Ist diese Box gesund?“

Roh-SSH

$ 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'.

Das ist immer noch ein gekürztes Ergebnis. Ein vollständiger Check braucht mehr Befehle für CPU, Dienstzustände, Containeranzahlen und aktuelle Fehler, jeweils mit eigenem Ausgabeformat. Schlimmer noch: Eine Box ohne ss kann wie null Listener aussehen, wenn der Port-Check nie lief.

Strukturiertes MCP-Ergebnis

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": []
}

Was der Agent gewinnt

Roh-SSHStrukturiertes MCPDein Gewinn
Mehrere Befehle und ASCII-TabellenBenannte Felder in einem ErgebnisEin Aufruf, benannte Felder und weniger Roundtrips
Ein fehlendes Tool kann wie leere Ausgabe aussehenunavailable benennt, was nicht gemessen wurdeWeniger Rätselraten und weniger schlechte Fixes
Du sortierst durch Festplatten, Dienste und FehlerDie Problemsignale sind bereits sichtbarSchnelleres Debugging

Ein vollständiges ssh_audit_baseline-Ergebnis kann länger sein als ein paar rohe Befehlsausgaben – etwa 1.077 Tokens gegenüber 765 in unserer Labormessung. Die Ersparnis kommt vom vollständigen Workflow, nicht davon, eine Antwort kürzer zu machen.

In einer echten Troubleshooting-Sitzung reduzierten speziell entwickelte Tools 49 einzelne Befehlsaufrufe auf 4 MCP-Aufrufe. Jeder weitere Aufruf startet eine weitere Modellrunde mit dem angesammelten Gespräch. Prompt-Caching kann die Kosten wiederholter Eingaben senken, aber neue Befehle und ihre Ausgaben verbrauchen weiterhin Kontext. Weniger Roundtrips bedeuten weniger Tokens über die Sitzung, weniger wiederholte Analyse und einen schnelleren Weg zur Antwort.

Brauchst du das ganze Bild statt nur den Puls? ssh_audit_baseline bündelt System, Festplatte, Speicher, Ports, sshd, fehlgeschlagene Units, Docker, Firewall und Updates. Ergebnisse erscheinen als KRITISCH / WARNUNG / OK; nicht gemessene Abschnitte werden benannt, statt still als Null zu lesen.

Linux-Server-Logsuche

Situation: Die API läuft in Timeouts, aber dieselbe Meldung kann in nginx, syslog, journald oder einem Anwendungslog sein, das du mit deinem normalen Benutzer nicht lesen kannst.

Frage: „Woher kam dieser Fehler?“

Roh-SSH

$ 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

Der dritte Befehl sieht sauber aus, aber 2>/dev/null hat auch einen Berechtigungsfehler versteckt. „Nichts gefunden“ und „nichts gelesen“ sehen jetzt identisch aus. Ein stark frequentiertes Log kann auch tausende Zeilen zurückgeben und den Rest des Vorfalls aus dem Kontext des Agenten drängen.

Strukturiertes MCP-Ergebnis

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
}

Was der Agent gewinnt

Roh-SSHStrukturiertes MCPDein Gewinn
Vier Suchen und vier AusgabenEine Suche über Dateien und GlobsWeniger Tokens und Roundtrips
Berechtigungsfehler können verschwindenfiles_unreadable benennt jeden verpassten PfadKeine falsche „Logs sind sauber“-Schlussfolgerung
Ausgabe kann ohne nützliche Obergrenze wachsenlimited und truncated legen jeden Cutoff offenSicherere Entscheidungen aus Teilergebnissen

since verwendet die Uhr des Servers, namesOnly: true gibt nur passende Pfade zurück, und ssh_log_tail liest die letzten N Zeilen aus mehreren Logs in einem Aufruf.

Sichere Remote-Konfigurationsbearbeitung

Situation: Du musst eine nginx-Konfiguration auf einem Live-Server ersetzen. Eine abgebrochene Verbindung, falscher Modus oder ein ungeprüfter Kopiervorgang könnten den Dienst mit einer kaputten Datei zurücklassen.

Frage: „Kann ich diese Konfiguration ersetzen, ohne eine Teil-Datei zu hinterlassen?“

Roh-SSH

$ 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

Exit-Code null sagt, dass die Shell fertig wurde. Es beweist nicht, welche Bytes gelandet sind, und > hat die alte Datei vor dem ersten Byte der neuen abgeschnitten. Wenn die Verbindung mitten im Schreiben abbricht, bleibt der Dienst mit einer Teil-Konfiguration zurück.

Strukturiertes MCP-Ergebnis

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 }]
}

Was der Agent gewinnt

Roh-SSHStrukturiertes MCPDein Gewinn
Das Ziel wird abgeschnitten, bevor der Kopiervorgang abgeschlossen istEine vollständige Temp-Datei ersetzt sie mit einer UmbenennungKeine halb geschriebene Konfiguration
Nur Exit-CodeBytes und Verifikationsergebnis sind benanntDu weißt, was tatsächlich gelandet ist
Berechtigungen leben im Shell-Textsudo, mode und verify sind Felder pro DateiVorhersehbare Eigentümerschaft und weniger Zitierfehler

verified hat drei ehrliche Ergebnisse: verified, unavailable, wenn der Server kein Hash-Tool hat, und skipped, wenn keine Verifikation angefordert wurde. Für Lesevorgänge akzeptiert ssh_file_read eine Liste von Pfaden; ssh_file_list behandelt Globs, Rekursion, Größen und Modi.

Batch-SSH-Befehle mit sudo ausführen

Situation: Ein Deploy ist bereit, aber nginx-Syntax, Dienstzustand und aktuelle Fehler müssen alle geprüft werden, bevor der Traffic umzieht. Ein fehlgeschlagener Check sollte nicht in einem kombinierten Dump verschwinden.

Frage: „Haben alle Preflight-Checks bestanden?“

Roh-SSH

$ 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

Drei Verbindungen geben drei unzusammenhängende Ausgaben zurück. Wenn die Befehle mit ; verbunden werden, meldet die Shell nur den letzten Exit-Code; wenn sie mit && verbunden werden, verschwinden spätere Checks nach dem ersten Fehler.

Strukturiertes MCP-Ergebnis

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
}

Was der Agent gewinnt

Roh-SSHStrukturiertes MCPDein Gewinn
Drei Aufrufe und unzusammenhängende AusgabenEine geordnete BefehlslisteWeniger Roundtrips
Eine kombinierte Shell kann Zwischenstatus versteckenJeder Befehl behält seinen eigenen exit_codeKein übersehener fehlgeschlagener Check
sudo und Zitieren werden im Befehls-Text wiederholtsudo gilt für die gesamte BatchWeniger Zitierfehler

Die Schutzprüfung für destruktive Befehle prüft die vollständige Liste, bevor der erste Befehl läuft. Wenn ein Eintrag abgelehnt wird, wird jeder andere Eintrag als nicht ausgeführt markiert und nichts an den Server gesendet. Jeder Befehl trägt seine eigene stdout und stderr. Ein Befehl, der ausgeführt wurde und nichts ausgegeben hat, hat einen leeren String; ein Befehl, der nie ausgeführt wurde, hat überhaupt kein solches Feld, sodass die beiden nicht verwechselt werden können. Ausgabe über 128 KB pro Befehl behält beide Enden — den Kopf für Tabellen, das Ende für Logs — mit einer Naht dazwischen, die die Menge benennt, und clipped_bytes sagt, wie viel abgeschnitten wurde. Das Abschneiden erfolgt an Byte-Grenzen und tritt zurück an den Rand eines Zeichens, sodass eine abgeschnittene Antwort niemals ein Ersatzzeichen trägt.

sudo erreicht den Server ohne Terminal: Die Antwort des Profils wird sudo auf der Standardeingabe übergeben. Welches Geheimnis das ist, ergibt sich aus sudoPassword, wenn das Profil eines benennt, und andernfalls aus password — ein Profil, das sich per Schlüssel anmeldet, hat überhaupt kein Login-Passwort, und wo eine Maschine die beiden getrennt hält, ist das Login-Passwort die falsche Antwort. Wo es nichts zu antworten gibt, sagt die Antwort dies und nennt die Auswege, anstatt sudos eigenen Hinweis zu -S und Askpass-Helfern zu hinterlassen. Ein Befehl, der seine eigene Standardeingabe liest, bekommt niemals das Passwort, das sonst in die Daten geraten würde.

Lang laufende SSH-Jobs ausführen

Situation: Eine Sicherung oder Migration läuft länger als die Agent-Sitzung. Die Verbindung kann geschlossen werden, aber Sie benötigen später weiterhin ihren Zustand, ihre Ausgabe und ihren Exit-Code.

Frage: „Wird dieser Job die Unterhaltung überleben?“

Rohes SSH

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

Das Terminal ist weg. Sie müssen sich jetzt erneut verbinden, den Prozess finden, die Zieldatei untersuchen und raten, ob die Sicherung abgeschlossen oder auf halbem Weg gestoppt wurde.

Strukturiertes MCP-Ergebnis

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"
}

Was der Agent gewinnt

Rohes SSHStrukturiertes MCPIhr Gewinn
Der Job ist an eine SSH-Sitzung gebundenDer entfernte Job hat eine dauerhafte IDSichere Trennungen und Neustarts
Erneutes Verbinden bedeutet Suchen nach Prozessen und DateienStatus und Exit-Code haben benannte ZuständeKein Raten, ob er abgeschlossen wurde
Erneutes Lesen der Ausgabe wiederholt alten TextAusgabe wird ab einem Byte-Offset fortgesetztGeringerer Token-Verbrauch bei langen Jobs

Der Job-Zustand liegt auf der entfernten Festplatte, nicht im Speicher dieses Servers. ssh_job_status unterscheidet running, finished und lost; ssh_job_output fährt ab dem letzten Byte-Offset fort; und ssh_job_kill signalisiert die gesamte Prozessgruppe statt nur ihrer Shell.

Dateien auf ältere Router und NAS-Geräte übertragen

Situation: Ein aktueller OpenSSH-Client versucht SFTP, aber der Router oder das NAS versteht nur das klassische scp-Protokoll. Die Datei muss dennoch intakt ankommen und ihr Ziel sicher ersetzen.

Frage: „Kann dieses alte Gerät weiterhin eine verifizierte Datei empfangen?“

Rohes SSH

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

Der übliche nächste Schritt ist, sich an das Legacy-Flag zu erinnern, den Kopiervorgang zu wiederholen und dann einen separaten Hash-Befehl auszuführen — falls das Gerät überhaupt ein Hash-Werkzeug hat.

Strukturiertes MCP-Ergebnis

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
  }]
}

Was der Agent gewinnt

Rohes SSHStrukturiertes MCPIhr Gewinn
Der moderne SFTP-Modus stoppt beim ersten FehlerDer klassische scp-Rückfall ist automatisch und wird gemerktAlte Geräte funktionieren weiterhin
Ein erfolgreicher Kopiervorgang beweist keine IntegritätSHA-256-Verifizierung hat ein benanntes ErgebnisKorruption wird nicht mit Erfolg verwechselt
Direktes Ersetzen kann ein teilweises Ziel hinterlassenEine temporäre Datei wird nach der Übertragung an Ort und Stelle verschobenDie Arbeitsdatei überlebt Unterbrechungen

Wenn das Gerät weder sha256sum noch openssl hat, sagt das Ergebnis unavailable und nennt den Grund, anstatt eine falsche Übereinstimmung zu melden. Ganze Verzeichnisse verwenden recursive: true und verifizieren ihre Hashes in einem Stapel.

Schutz vor destruktiven Befehlen für KI-Agenten

Die Wache läuft lokal, bevor ein Befehl SSH erreicht. Sie trennt Operationen, die wiederhergestellt werden können, von solchen, die den Container zerstören, der die Daten enthält, und sie prüft die Befehlsreihenfolge innerhalb von Ketten und Stapeln.

Eine destruktive Kette stoppen, bevor sie beginnt

Eine sichere Sicherungs-und-Ersetzen-Sequenz:

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

Dieselben Operationen in der falschen Reihenfolge:

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

Die Shell würde das Verzeichnis löschen und erst dann feststellen, dass die Sicherungsquelle verschwunden ist. Die Wache sieht, dass spätere Schritte ein Ziel lesen, das bereits durch einen früheren Schritt zerstört wurde, sodass der gesamte Aufruf auf Ihrer Maschine bleibt. Dieselbe Prüfung erfasst dropdb app && pg_dump app > backup.sql.

Unwiderruflichen Verlust ablehnen, vor wiederherstellbaren Änderungen warnen

Abgelehnt — der Container selbstNur gewarnt — sein Inhalt
DROP DATABASE, dropdbDROP TABLE, TRUNCATE, DELETE FROM
docker volume rm, docker compose down -vdocker rm -f <name>
crontab -rBearbeiten eines Jobs
mkfs, wipefs -a, lvremove, zfs destroychmod 777
reboot, shutdown, haltgit reset --hard

docker compose down -v wird abgelehnt, weil -v benannte Docker-Volumes entfernt, einschließlich eines Datenbank-Volumes. Ohne -v wird das Stoppen der Dienste nicht als dieselbe unwiderrufliche Aktion behandelt.

Rekursives Löschen des Dateisystem-Roots, eines Home-Verzeichnisses oder von Systembäumen wie /etc, /var und /usr wird ebenfalls abgelehnt, auch wenn ein Symlink dorthin führt. Ein unaufgelöstes Ziel wie rm -rf "$DIR"/* wird ebenfalls abgelehnt: „konnte nicht geprüft werden“ wird nicht als „sicher“ behandelt.

Benennen, was Sie stoppen

Ein Befehl, der sein Ziel findet, anstatt es zu benennen, wird nicht gesendet. Der Server erweitert es und antwortet mit dem, was hinter dem Ziel steht:

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

Für einen Prozess fügt die Antwort die Anzeichen hinzu, dass er in Gebrauch ist: wie lange er läuft, welche Ports er für Verbindungen akzeptiert, wie viele Verbindungen er trägt. Benannte Ziele kosten nichts extra und gehen still durch — docker kill web-1, kill 4871, systemctl stop app.

Um fortzufahren, benennen Sie, was gestoppt wird. Die Namen werden gegen das geprüft, was der Befehl tatsächlich erreicht, sodass eine Maske, die auf etwas anderes abgedriftet ist, abgelehnt statt bestätigt wird:

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

Ein Muster über Befehlszeilen ist ein eigener Fall. Es passt auf genau den Befehl, der es trägt, sodass die Shell, die ihn ausführt, vor dem Ziel signalisiert wird und die Antwort in der Mitte abbricht. Ein solcher Treffer wird nicht bestätigt, sondern umgeschrieben — durch Nummer oder mit einem Zeichen, das als Klasse geschrieben wird, sodass das Muster aufhört, sich selbst zu matchen:

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

Drei Ergebnisse bleiben getrennt: Ziele gefunden, die Erweiterung erreichte nichts, und nichts zu fragen — keine Engine auf der Maschine, eine abgeschnittene Antwort, eine fehlgeschlagene Verbindung. Die letzten beiden sind ebenfalls Ablehnungen: Nichtwissen ist kein Grund fortzufahren.

Einen absichtlichen destruktiven Befehl bestätigen

Nichts ist dauerhaft verboten. Fügen Sie # CONFIRMED-DESTRUCTIVE zu einem geprüften Befehl hinzu, und er wird durchgelassen. Wenn die Wache einen Eintrag in einem Stapel ablehnt, stoppt der vollständige Stapel vor der Ausführung, sodass der Server niemals nach einer halb ausgeführten Operation zurückgelassen wird.

Die Wache arbeitet innerhalb eines einzelnen Aufrufs. Sie kann ein Löschen in einer Aufrufung nicht mit einem Lesen in der nächsten verbinden oder über Werkzeuge nachdenken, die sie nicht erkennt. Sie ist ein Sicherheitsgurt, keine Richtlinien-Engine: Wiederherstellbare Operationen bleiben Ihre Entscheidung. Pfadeinschränkungen und Zitierregeln sind in docs/security.md dokumentiert.

Werkzeuge

18 SSH-MCP-Werkzeuge für Serveroperationen. Vollständige Parameter und Beispiele finden Sie in docs/tools.md.

WerkzeugWas es tut
ssh_execEinen Befehl oder einen Stapel ausführen, mit der Wache gegen destruktive Befehle und optionalem Abkoppeln
ssh_file_readEine oder mehrere Dateien lesen, Text oder Binär
ssh_file_writeDateien mit atomarem Umbenennen und optionaler SHA-256-Verifizierung schreiben
ssh_file_listEin Verzeichnis auflisten, mit optionalem Glob und Rekursion
ssh_uploadEine Datei oder ein Verzeichnis über SSH hochladen, binärsicher mit Integritätsprüfungen; ein Verzeichnis ersetzt das Ziel oder führt darin zusammen
ssh_downloadEine Datei oder ein Verzeichnis über SSH herunterladen, binärsicher mit Integritätsprüfungen
ssh_job_statusZustand eines Hintergrund-Jobs: läuft, abgeschlossen oder verloren
ssh_job_outputAkkumulierte Ausgabe ab einem Byte-Offset lesen
ssh_job_listJobs auflisten, abgeschlossene über ihre TTL hinaus entfernen
ssh_job_killDie gesamte Prozessgruppe eines Jobs signalisieren
ssh_log_tailLetzte N Zeilen eines oder mehrerer Logs, Glob unterstützt; ein Container nach Name
ssh_log_searchMustersuche über Logs oder durch das Log eines Containers
ssh_snapshotEinmaliger Gesundheits-Schnappschuss: Dienste, Ressourcen, Docker, Netzwerk, Fehler
ssh_monitorTransportsteuerung: Statistiken, Neuladen, Test, Auflisten, Schließen
ssh_audit_baselineSystem, Festplatte, Speicher, Netzwerk, ssh, Dienste, Docker, Firewall, Updates
ssh_tls_checkZertifikatsablauf, SAN, Kette und Erneuerungs-Hook für eine Domain
ssh_disk_breakdownWohin die Festplatte ging: du Top-N, Docker, journald, Caches
ssh_service_statussystemctl status plus ein journalctl-Ende für eine Einheit

MCP-Werkzeug-Sicherheitsannotationen

Standard-MCP-Annotationen teilen Clients mit, welche Werkzeuge schreibgeschützt, destruktiv, idempotent oder offen sind. Siehe die vollständige Tabelle.

SSH-Befehle ausführen und entfernte Dateien verwalten

Befehle, Dateilesen und -schreiben, Verzeichnisauflistungen — die gewöhnliche Arbeit auf einer Maschine, jede Antwort bereits geparst.

Lang laufende SSH-Jobs überwachen

Langsame Arbeit wird abgekoppelt und verfolgt, statt darauf zu warten: Jeder Blick sagt, wie weit sie gekommen ist.

Logs durchsuchen und Servergesundheit prüfen

Logs von Dateien und Containern sowie ein einmaliges Bild der Maschine, mit begrenzter Ausgabe, sodass ein Ende nicht den Kontextfenster auffrisst.

Dateien über SSH hoch- und herunterladen

Binärsichere Übertragungen mit Integritätsprüfungen. Details in docs/transfer.md.

Für Binärdateien und große Dateien verwenden Sie ssh_upload / ssh_download — Base64-Blöcke und Heredocs sind nicht binärsicher oder atomar.

Linux-Server über SSH prüfen

Schreibgeschützt und in einer einzigen Runde gebündelt. Details in docs/audit.md.

Windows-SSH-Kompatibilitätsmodus

Windows verwendet den Kompatibilitätsmodus automatisch. Wenn Verbindungs-Multiplexing nicht verfügbar ist, wechselt der Server zu einer Verbindung pro Befehl. Dieselben Werkzeuge bleiben über schlüsselbasiertes SSH verfügbar — keine separate Einrichtung oder Windows-spezifische Implementierung.

Die Wache gegen destruktive Befehle wird in Schutz vor destruktiven Befehlen für KI-Agenten behandelt.

SSH-MCP-Server einrichten

Führen Sie zuerst das Paket aus Installation in 30 Sekunden aus, und erstellen Sie dann eine Profildatei.

SSH-Verbindungsprofile erstellen

Legen Sie es dort ab, wo Sie möchten — neben der eigenen Konfiguration Ihres Agenten ist die übliche Wahl. Die Beispiele unten verwenden ~/.claude/ssh-profiles.json; für andere Agenten tauschen Sie das Verzeichnis (~/.codex/, ~/.qwen/, ~/.config/opencode/):

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

Ein SSH-Profil explizit wählen

Es gibt kein Profil, auf das der Server zurückfällt: Jedes ist eine andere Maschine, und ein Befehl, der an die falsche Maschine gesendet wird, ist etwas, das eine Fehlermeldung danach nicht rückgängig machen kann. Fragen Sie ohne Namen, und die Antwort listet die Namen zur Auswahl auf:

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

Ein Profil, das der Server nicht für SSH verwenden kann — kein host, kein username oder mode: "local" — wird ohne Beschwerde übersprungen, und Felder, die es nicht erkennt, werden in Ruhe gelassen, sodass die Datei mit anderen Werkzeugen geteilt werden kann. Ein Profil mit einem defekten Feld ist ein anderer Fall: Es wird zusammen mit dem Feld und dem Wert benannt, und seine gesunden Nachbarn funktionieren weiter.

Jedes Profil nimmt optional einen pathSecurity-Block an, der die Pfade, die Dateiwerkzeuge berühren dürfen, auf die Whitelist oder Blacklist setzt — siehe docs/security.md.

Ein Profil, das sich per Schlüssel anmeldet, aber sudo auf der Gegenseite benötigt, nimmt einen sudoPassword — das Geheimnis, mit dem sudo beantwortet wird, das auf vielen Maschinen nicht das Login-Passwort ist. Bewahren Sie es in der Geheimnisdatei auf, nicht hier.

SSH-Passwörter und Passphrasen aus Profilen heraushalten

Bevorzuge Schlüssel. Wenn ein Passwort oder eine Passphrase für einen verschlüsselten Schlüssel unvermeidbar ist, bewahre sie in einer separaten Geheimnisdatei auf, niemals im Profil selbst:

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

Die Geheimnisdatei ist nach Profilnamen Schlüssel — siehe secrets.json.example:

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

sudoPassword ist das, womit sudo auf dieser Maschine beantwortet wird. Ein Profil, das sich per Schlüssel anmeldet, hat kein Login-Passwort anzubieten, und wo die beiden unterschiedlich sind, ist das Login-Passwort die falsche Antwort; ohne es wird password verwendet.

Die Geheimnisdatei muss nur für dich lesbar sein (chmod 600). Relative Pfade werden von der Profildatei aus aufgelöst; Geheimnisse bleiben außerhalb von argv und werden in Protokollen maskiert. Siehe Sicherheit der Anmeldedaten.

Konfiguriere Claude Code, Codex und andere MCP-Clients

Wähle den Client, den du verwendest, und weise ihn auf dieselbe Profildatei hin.

Claude Code

Ein Befehl; -s user macht den Server in jedem Projekt verfügbar:

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

Füge es in ~/.config/opencode/opencode.json ein:

{
  "$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

Ein Befehl, derselbe wie die anderen:

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

Andere MCP-Clients

Gemini CLI, Hermes, Cline, ein Editor-Plugin oder dein eigener Agent funktionieren auf dieselbe Weise. Alles, was sie brauchen, ist ein auszuführender Befehl und eine Umgebungsvariable.

Starte deinen MCP-Client neu

Starte den Client neu und führe dann ssh_monitor({ action: "list" }) aus, um zu bestätigen, dass das Profil geladen wurde.

SSH-MCP-Server-Konfiguration

VariableWas sie tutStandard
SSH_PROFILES_FILEPfad zur Profil-JSON — erforderlich
SSH_MCP_LOG_LEVELdebug, info, warn, errorinfo
LOG_LEVELFallback, nur verwendet, wenn SSH_MCP_LOG_LEVEL nicht gesetzt istinfo
SSH_MCP_LOG_TIMESTAMPZeitstempel in Protokollzeilentrue
SSH_MCP_CONTROL_PERSISTSekunden, die eine gemeinsame Verbindung nach dem letzten Befehl aktiv bleibt; 0 schließt sie sofort600
SSH_MCP_CONTROL_DIRWo Steuer-Sockets liegen~/.ssh/ssh-mcp
SSH_MCP_PROFILES_CACHE_TTLProfil-Cache-TTL, ms60000
SSH_MCP_PROFILES_WATCHProfildatei neu laden, wenn sie sich änderttrue

Die gemeinsame Verbindung überlebt diesen Prozess absichtlich: Sie beim Beenden zu schließen, würde den Kanal eines anderen Fensters auf derselben Maschine unterbrechen.

Einschränkungen des SSH-MCP-Servers

Jede Einschränkung nennt dir den Weg darum herum. Ein Werkzeug, das etwas nicht kann, sagt das und nennt ssh_exec, das Befehle direkt auf der Maschine ausführt — einen nicht unterstützten Protokolltreiber, ein Dienstprogramm, das die Maschine nicht hat, eine Engine, die dieser Server nicht spricht. Du musst nicht im Voraus wissen, wo die Werkzeuge enden: Die Ablehnung sagt es, in dem Moment, in dem es darauf ankommt.

Drei Ablehnungen bleiben bewusst still über die Shell, weil sie dort die falsche Antwort ist: ein Pfad, den dein Profil verbietet (deine eigene Regel zu umgehen ist keine Lösung), ein fehlerhafter Aufruf (die Lösung liegt im Aufruf) und eine Ablehnung von ssh_exec selbst.

  • Abbruch: Ein abgebrochener Aufruf stoppt jetzt auch den Befehl auf dem Server, gesendet als zweiter Aufruf über dieselbe Verbindung. Wo der Server kein /proc hat, wird der Befehl über ps gefunden. FreeBSD ist nicht verifiziert: Korrektes Verhalten dort ist nicht garantiert. Dateiübertragungen und ssh_snapshot akzeptieren keinen Abbruch.
  • Atomare Schreibvorgänge: BSD und macOS können Umbenennungen über Dateisysteme hinweg nicht vorab prüfen.

Roadmap des SSH-MCP-Servers

  • Vollständiger Testlauf gegen macOS-SSH-Hosts

  • End-to-End-Kompatibilitätslauf auf Windows

  • Multi-Host-Audits — Gesundheit über mehrere SSH-Profile in einem Aufruf vergleichen

  • Profile aus dem vorhandenen ~/.ssh/config importieren

  • Fortsetzbare Übertragungen für große Dateien und instabile Verbindungen

  • Remote-Operations-Zeitachse — Befehle, Übertragungen und Schutzentscheidungen in einer Prüfspur

  • Fertige SSH-Fehlerbehebungs-Playbooks

  • Container-Protokolle ohne Umweg über die ShellERLEDIGT: ssh_log_tail und ssh_log_search akzeptieren einen Containernamen, fragen Docker, wohin es schreibt, und lesen diese Datei mit derselben Mechanik wie jedes andere Protokoll

  • Eine Ablehnung, die dich feststecken lässtERLEDIGT: Jede Einschränkung nennt jetzt ssh_exec als Weg durch, sodass das Erreichen der Grenze eines Werkzeugs einen Satz kostet statt eines Ratespiels

  • Antworten, die das Modell erreichenERLEDIGT: Befehlsausgabe, übereinstimmende Protokollzeilen, Maschinennamen und Snapshot-Abschnitte reisen in den Feldern, nicht nur im Text

  • Kleinere MCP-WerkzeugschemataERLEDIGT: Die Werkzeugliste wurde um 10 % leichter, und ein getrennter Auftrag zeigt jetzt die letzten geschriebenen Zeilen, statt blind abgefragt zu werden

  • Lange Arbeit unter rootERLEDIGT: Ein getrennter Auftrag läuft mit sudo und wird als root verfolgt, und ein schlüsselbasiertes Profil beantwortet sudo mit seinem eigenen sudoPassword

Entwickle und teste den SSH-MCP-Server

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

Die Live-Suite läuft gegen echte Container — einen BusyBox, einen coreutils — weil die beiden sich leise widersprechen, und ein Mock stimmt mit dem überein, der es geschrieben hat. Siehe docs/architecture.md für das Layout.

Gefällt dir SSH MCP Server? ⭐

Wenn dir das Werkzeug gefällt, gib ihm einen Stern auf GitHub — es hilft mehr Menschen, das Projekt zu entdecken.

Trage zum SSH-MCP-Server bei

Issues und Pull-Requests sind willkommen unter github.com/hypnosis/ssh-mcp-server.

Lizenz

MIT — siehe LICENSE.