SSH MCP Server
offiziellFü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_execauszufü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_writeundssh_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_searchoderssh_log_tailüber Dateien und Container ab, oder erhalten Sie eine strukturierte Zustandsübersicht mitssh_snapshotundssh_audit_baseline. - Dateien mit Integritätsprüfungen übertragen — Laden Sie Dateien und Verzeichnisse über
ssh_uploadundssh_downloadhoch oder herunter, mit automatischem Legacy-scp-Fallback für ältere Geräte. - Langlaufende Hintergrundjobs verwalten — Trennen Sie langsame Operationen mit
ssh_execab und verfolgen Sie sie überssh_job_status,ssh_job_outputundssh_job_kill, die Verbindungsabbrüche überstehen.
Dokumentation
SSH MCP Server — Remote-Server-Tools für KI-Agenten
|
|
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.
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
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 Maschine | Was du bekommst |
|---|---|
| Ein Router oder NAS, zu klein für moderne Dateiübertragung | Die Datei kommt trotzdem an – das alte Protokoll wird automatisch verwendet |
| Ein Server von vor zehn Jahren | Der Workflow funktioniert weiter; er öffnet nur eine neue Verbindung pro Befehl statt eine wiederzuverwenden |
| Ein abgespecktes Image ohne Möglichkeit, eine Datei zu hashen | Der 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 ist | Die 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-SSH | Strukturiertes MCP | Dein Gewinn |
|---|---|---|
| Mehrere Befehle und ASCII-Tabellen | Benannte Felder in einem Ergebnis | Ein Aufruf, benannte Felder und weniger Roundtrips |
| Ein fehlendes Tool kann wie leere Ausgabe aussehen | unavailable benennt, was nicht gemessen wurde | Weniger Rätselraten und weniger schlechte Fixes |
| Du sortierst durch Festplatten, Dienste und Fehler | Die Problemsignale sind bereits sichtbar | Schnelleres 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-SSH | Strukturiertes MCP | Dein Gewinn |
|---|---|---|
| Vier Suchen und vier Ausgaben | Eine Suche über Dateien und Globs | Weniger Tokens und Roundtrips |
| Berechtigungsfehler können verschwinden | files_unreadable benennt jeden verpassten Pfad | Keine falsche „Logs sind sauber“-Schlussfolgerung |
| Ausgabe kann ohne nützliche Obergrenze wachsen | limited und truncated legen jeden Cutoff offen | Sicherere 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-SSH | Strukturiertes MCP | Dein Gewinn |
|---|---|---|
| Das Ziel wird abgeschnitten, bevor der Kopiervorgang abgeschlossen ist | Eine vollständige Temp-Datei ersetzt sie mit einer Umbenennung | Keine halb geschriebene Konfiguration |
| Nur Exit-Code | Bytes und Verifikationsergebnis sind benannt | Du weißt, was tatsächlich gelandet ist |
| Berechtigungen leben im Shell-Text | sudo, mode und verify sind Felder pro Datei | Vorhersehbare 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-SSH | Strukturiertes MCP | Dein Gewinn |
|---|---|---|
| Drei Aufrufe und unzusammenhängende Ausgaben | Eine geordnete Befehlsliste | Weniger Roundtrips |
| Eine kombinierte Shell kann Zwischenstatus verstecken | Jeder Befehl behält seinen eigenen exit_code | Kein übersehener fehlgeschlagener Check |
sudo und Zitieren werden im Befehls-Text wiederholt | sudo gilt für die gesamte Batch | Weniger 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 SSH | Strukturiertes MCP | Ihr Gewinn |
|---|---|---|
| Der Job ist an eine SSH-Sitzung gebunden | Der entfernte Job hat eine dauerhafte ID | Sichere Trennungen und Neustarts |
| Erneutes Verbinden bedeutet Suchen nach Prozessen und Dateien | Status und Exit-Code haben benannte Zustände | Kein Raten, ob er abgeschlossen wurde |
| Erneutes Lesen der Ausgabe wiederholt alten Text | Ausgabe wird ab einem Byte-Offset fortgesetzt | Geringerer 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 SSH | Strukturiertes MCP | Ihr Gewinn |
|---|---|---|
| Der moderne SFTP-Modus stoppt beim ersten Fehler | Der klassische scp-Rückfall ist automatisch und wird gemerkt | Alte Geräte funktionieren weiterhin |
| Ein erfolgreicher Kopiervorgang beweist keine Integrität | SHA-256-Verifizierung hat ein benanntes Ergebnis | Korruption wird nicht mit Erfolg verwechselt |
| Direktes Ersetzen kann ein teilweises Ziel hinterlassen | Eine temporäre Datei wird nach der Übertragung an Ort und Stelle verschoben | Die 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 selbst | Nur gewarnt — sein Inhalt |
|---|---|
DROP DATABASE, dropdb | DROP TABLE, TRUNCATE, DELETE FROM |
docker volume rm, docker compose down -v | docker rm -f <name> |
crontab -r | Bearbeiten eines Jobs |
mkfs, wipefs -a, lvremove, zfs destroy | chmod 777 |
reboot, shutdown, halt | git 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.
| Werkzeug | Was es tut |
|---|---|
ssh_exec | Einen Befehl oder einen Stapel ausführen, mit der Wache gegen destruktive Befehle und optionalem Abkoppeln |
ssh_file_read | Eine oder mehrere Dateien lesen, Text oder Binär |
ssh_file_write | Dateien mit atomarem Umbenennen und optionaler SHA-256-Verifizierung schreiben |
ssh_file_list | Ein Verzeichnis auflisten, mit optionalem Glob und Rekursion |
ssh_upload | Eine Datei oder ein Verzeichnis über SSH hochladen, binärsicher mit Integritätsprüfungen; ein Verzeichnis ersetzt das Ziel oder führt darin zusammen |
ssh_download | Eine Datei oder ein Verzeichnis über SSH herunterladen, binärsicher mit Integritätsprüfungen |
ssh_job_status | Zustand eines Hintergrund-Jobs: läuft, abgeschlossen oder verloren |
ssh_job_output | Akkumulierte Ausgabe ab einem Byte-Offset lesen |
ssh_job_list | Jobs auflisten, abgeschlossene über ihre TTL hinaus entfernen |
ssh_job_kill | Die gesamte Prozessgruppe eines Jobs signalisieren |
ssh_log_tail | Letzte N Zeilen eines oder mehrerer Logs, Glob unterstützt; ein Container nach Name |
ssh_log_search | Mustersuche über Logs oder durch das Log eines Containers |
ssh_snapshot | Einmaliger Gesundheits-Schnappschuss: Dienste, Ressourcen, Docker, Netzwerk, Fehler |
ssh_monitor | Transportsteuerung: Statistiken, Neuladen, Test, Auflisten, Schließen |
ssh_audit_baseline | System, Festplatte, Speicher, Netzwerk, ssh, Dienste, Docker, Firewall, Updates |
ssh_tls_check | Zertifikatsablauf, SAN, Kette und Erneuerungs-Hook für eine Domain |
ssh_disk_breakdown | Wohin die Festplatte ging: du Top-N, Docker, journald, Caches |
ssh_service_status | systemctl 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
| Variable | Was sie tut | Standard |
|---|---|---|
SSH_PROFILES_FILE | Pfad zur Profil-JSON — erforderlich | — |
SSH_MCP_LOG_LEVEL | debug, info, warn, error | info |
LOG_LEVEL | Fallback, nur verwendet, wenn SSH_MCP_LOG_LEVEL nicht gesetzt ist | info |
SSH_MCP_LOG_TIMESTAMP | Zeitstempel in Protokollzeilen | true |
SSH_MCP_CONTROL_PERSIST | Sekunden, die eine gemeinsame Verbindung nach dem letzten Befehl aktiv bleibt; 0 schließt sie sofort | 600 |
SSH_MCP_CONTROL_DIR | Wo Steuer-Sockets liegen | ~/.ssh/ssh-mcp |
SSH_MCP_PROFILES_CACHE_TTL | Profil-Cache-TTL, ms | 60000 |
SSH_MCP_PROFILES_WATCH | Profildatei neu laden, wenn sie sich ändert | true |
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
/prochat, wird der Befehl überpsgefunden. FreeBSD ist nicht verifiziert: Korrektes Verhalten dort ist nicht garantiert. Dateiübertragungen undssh_snapshotakzeptieren 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/configimportieren -
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 Shell— ERLEDIGT:ssh_log_tailundssh_log_searchakzeptieren einen Containernamen, fragen Docker, wohin es schreibt, und lesen diese Datei mit derselben Mechanik wie jedes andere Protokoll -
Eine Ablehnung, die dich feststecken lässt— ERLEDIGT: Jede Einschränkung nennt jetztssh_execals Weg durch, sodass das Erreichen der Grenze eines Werkzeugs einen Satz kostet statt eines Ratespiels -
Antworten, die das Modell erreichen— ERLEDIGT: Befehlsausgabe, übereinstimmende Protokollzeilen, Maschinennamen und Snapshot-Abschnitte reisen in den Feldern, nicht nur im Text -
Kleinere MCP-Werkzeugschemata— ERLEDIGT: Die Werkzeugliste wurde um 10 % leichter, und ein getrennter Auftrag zeigt jetzt die letzten geschriebenen Zeilen, statt blind abgefragt zu werden -
Lange Arbeit unter root— ERLEDIGT: Ein getrennter Auftrag läuft mitsudound wird als root verfolgt, und ein schlüsselbasiertes Profil beantwortetsudomit seinem eigenensudoPassword
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.