Skip to main content
Glama
thekk1
by thekk1

ssh-mcp

Pro-Benutzer-SSH-Befehlsausführung für LibreChat-Assistenten: ssh_exec(host, port, username, command), authentifiziert mit dem persönlichen SSH-Private-Key des aufrufenden Chat-Benutzers – niemals mit einem gemeinsamen Dienstkonto. Ein zweites Tool, ssh_generate_keypair, erzeugt auf Anfrage ein neues Schlüsselpaar für Benutzer, die noch keines haben. Bereitgestellt als einfacher streamable-HTTP-Docker-Dienst (keine ports: öffentlich exponiert, kein OAuth pro Benutzer auf der MCP-Ebene), gleiche Form wie der Multi-User-Modus von ews-mcp und jenkins-mcp.

ssh_generate_keypair

Erzeugt serverseitig ein neues Ed25519- (Standard) oder RSA-Schlüsselpaar und gibt es in der Tool-Antwort zurück: private_key_base64 – bereit zum direkten Einfügen in das SSH_PRIVATE_KEY-Feld von customUserVars – und public_key, um es zu authorized_keys auf allen Zielhosts hinzuzufügen, die es akzeptieren sollen. Es wird nichts persistiert; der Schlüssel existiert nur für die Dauer dieses einen Tool-Aufrufs und in dem, was der Benutzer danach mit der Antwort macht.

Das ist „Generieren, dann einmal Kopieren-Einfügen“, kein echter One-Click-Flow. LibreChat hat keine API, mit der eine Tool-Antwort in den customUserVars-Speicher eines anderen Feldes zurückschreiben könnte, daher muss der Benutzer den zurückgegebenen privaten Schlüssel weiterhin manuell in die Einstellungen einfügen. Ein echter One-Click-Button würde ein Patchen des LibreChat-eigenen Frontends erfordern, was das „Vanilla-LibreChat + MCP“-Muster bricht, auf das der Rest dieses Stacks angewiesen ist – hier nicht versucht.

Benötigt selbst keine Anmeldeinformationen (reine Schlüsselerzeugung, kein Zielhost beteiligt), funktioniert also sogar, bevor ein Benutzer überhaupt ein SSH_PRIVATE_KEY konfiguriert hat.

Related MCP server: terminal-mcp-server

Warum das existiert (und warum es eigener Code ist, kein Wrapper)

Im Gegensatz zu Jenkins, wo mcp-jenkins bereits genau die benötigte header-basierte Multi-User-Authentifizierung mitbringt, unterstützt keiner der geprüften Open-Source-SSH-MCP-Server (vignitin/multi-ssh-mcp, giuliolibrando/ssh-mcp-server, tufantunc/ssh-mcp) überhaupt Anmeldeinformationen pro Anfrage – jeder von ihnen integriert einen einzelnen Host/Benutzer/Anmeldedaten in Umgebungsvariablen oder eine Konfigurationsdatei beim Start. Das ist also ein kleiner (~150 Zeilen) zweckgebundener Server gegen asyncssh, kein Wrapper um die CLI eines anderen.

So funktioniert es

LibreChat --(streamable-http, /mcp, per-user headers)--> ssh-mcp
                                                              |
                                                              | asyncssh,
                                                              | one connection
                                                              | per tool call
                                                              v
                                                        arbitrary target host

Anmeldeinformationen, über die customUserVars von LibreChat -> Header pro Anfrage (gleicher Mechanismus wie ews-mcp/jenkins-mcp):

  • x-ssh-private-key – der persönliche private Schlüssel des Benutzers, base64-kodiert (ein roher mehrzeiliger PEM-Block kann als HTTP-Header-Wert nicht überleben)

  • x-ssh-key-passphrase – optional, falls dieser Schlüssel passphrasengeschützt ist

Einmal pro Tool-Aufruf dekodiert, direkt an asyncssh übergeben, nie auf die Festplatte geschrieben, nie über Anfragen hinweg zwischengespeichert.

Keine Host-Allowlist, keine Befehls-Whitelist. ssh_exec akzeptiert jeden Host/Port/Benutzernamen/Befehl, den das Modell übergibt. Das ist eine bewusste Abwägung, kein Versehen: Anders als bei jenkins-mcp mit „alles außer der Groovy-Konsole“ gibt es keine äquivalente eingebaute Berechtigungsmatrix, auf die man sich für ein beliebiges SSH-Ziel stützen könnte. Die einzigen zwei Dinge, die zwischen einer Chat-Nachricht und einer echten Shell stehen, sind:

  1. Welche LibreChat-Benutzer diesen Server überhaupt sehen können – wird von diesem Code überhaupt nicht erzwungen, siehe „Sichtbarkeit einschränken“ unten.

  2. Die tatsächlichen Unix-Berechtigungen des Schlüssels, den ein Benutzer mitbringt.

Wenn eine dieser Bedingungen nicht tatsächlich erfüllt ist, ist dieses Tool genauso gefährlich, als würde man diesem Benutzer auf jedem Host, den sein Schlüssel öffnet, ein nacktes Terminal übergeben.

Host-Schlüssel verwenden echtes TOFU (ssh_mcp/hostkeys.py), nicht „alles immer akzeptieren“: Die erste Verbindung zu einem bestimmten host:port pinnt dessen Schlüssel-Fingerabdruck in eine JSON-Datei auf einem Volume; jede spätere Verbindung muss exakt mit diesem Pin übereinstimmen oder wird mit host_key_mismatch abgelehnt. Das kann einen MITM beim allerersten Kontakt mit einem Host nicht verhindern, aber es verwandelt eine spätere unangekündigte Schlüsseländerung – Rotation oder ein echter MITM – in einen lauten, expliziten Fehler statt in ein stilles Loch.

Kein MCP_API_KEY, kein Auth-Gate auf der MCP-Verbindung selbst – bewusst, aus demselben Grund wie bei jedem anderen BOS-MCP: Ein 401 von einem Gate bringt den Nicht-OAuth-MCP-Client von LibreChat dazu, OAuth zu versuchen (und dabei hängen zu bleiben). Die Docker-Netzwerkisolation ist die Grenze auf Transportebene; die echte Zugriffskontrolle lebt eine Ebene höher, in LibreChat.

Sichtbarkeit auf bestimmte Benutzer einschränken

Keine SSO/Bearer-Validierung innerhalb dieses MCP erforderlich. LibreChat selbst (0.8.5+) hat ein DB-gestütztes Konfigurations-Override-System (Admin-Panel -> Konfigurationsverwaltung), das einen zusätzlichen mcpServers-Eintrag auf eine bestimmte Rolle oder Gruppe eingrenzt – beim Login ist die effektive Konfiguration eines Benutzers die Basiskonfiguration, zusammengeführt mit allen Overrides, die für ihn gelten. Ein Benutzer außerhalb der Gruppe hat den ssh-Eintrag in seiner aufgelösten Konfiguration einfach nicht; es ist keine versteckte UI, es ist abwesend. Keycloak-Gruppen/-Rollen können das direkt über OPENID_SYNC_GROUPS_FROM_TOKEN + OPENID_GROUPS_CLAIM_PATH=realm_access.roles + OPENID_TREAT_ROLES_AS_GROUPS speisen.

Zwei Vorbehalte, die man live überprüfen sollte, bevor man sich darauf verlässt, statt es anzunehmen:

  • Die Funktion ist als „in der Vorschau“ dokumentiert, nicht als GA.

  • Es gab einen echten Bug (#13172, Mai 2026), bei dem gruppenbezogene Overrides stillschweigend nicht angewendet wurden, während rollenbezogene es taten; behoben über PR #13176, aber bestätige, dass deine laufende LibreChat-Version den Fix tatsächlich enthält – setze einen Testbenutzer in/aus der Gruppe und prüfe, ob ssh tatsächlich (nicht) erscheint, vertraue nicht nur dem Changelog.

Ausführen

docker build -t ssh-mcp .
docker run --rm -p 8080:8080 -v ssh-mcp-hostkeys:/data ssh-mcp

Das /data-Volume ist es, das TOFU-Pins ein Container-Neu erstellen überleben lässt – ohne es vergisst jeder erneute Deploy jeden zuvor gesehenen Host-Schlüssel und pinnt beim nächsten Kontakt neu (kein Sicherheitsloch, verliert nur die Eigenschaft „spätere Änderung erkennen“, bis die Flotte einmal erneut kontaktiert wurde).

Bereitstellung (BOS-Muster)

docker-compose.yml:

  ssh-mcp:
    build: /home/bos/ssh-mcp   # git clone https://github.com/thekk1/ssh-mcp
    container_name: ssh-mcp
    volumes:
      - ssh-mcp-hostkeys:/data
    restart: always

volumes:
  ssh-mcp-hostkeys:

librechat.yaml – beide customUserVars-Einträge benötigen sowohl title als auch description, sonst schlägt das Zod-Schema der LibreChat-Konfiguration beim Start mit einem verwirrenden Multi-Branch-invalid_union-Fehler fehl (siehe den jenkins-mcp-Bericht von ews-mcp für die genaue Fehlerform):

mcpSettings:
  allowedAddresses:
    - 'ssh-mcp:8080'

mcpServers:
  ssh:
    type: streamable-http
    url: http://ssh-mcp:8080/mcp
    serverInstructions: true
    headers:
      X-SSH-Private-Key: '{{SSH_PRIVATE_KEY}}'
      X-SSH-Key-Passphrase: '{{SSH_KEY_PASSPHRASE}}'
    customUserVars:
      SSH_PRIVATE_KEY:
        title: "SSH-Private-Key (Base64)"
        description: "Dein persoenlicher SSH-Private-Key, Base64-kodiert: `base64 -w0 ~/.ssh/id_ed25519`"
      SSH_KEY_PASSPHRASE:
        title: "SSH-Key-Passphrase (optional)"
        description: "Nur ausfuellen, falls dein privater Key passphrase-geschuetzt ist"

Starte LibreChat nach der Bearbeitung neu – librechat.yaml wird beim Containerstart nur einmal gelesen, kein Hot-Reload.

Verifizieren

curl -s http://127.0.0.1:8080/readyz   # "ok" once the session manager is up

Manuell Ende-zu-Ende verifiziert (nicht nur Unit-getestet): Image gebaut, ausgeführt, einen echten MCP-Client über streamable-http mit den Anmelde-Headern verbunden, tools/list zeigte ssh_exec, tools/call gegen einen Live-Wegwerf-SSH-Server auf Basis von asyncssh führte einen echten Befehl über einen echten SSH-Handshake aus und gab dessen tatsächliches stdout zurück. Auch direkt (unter Umgehung der HTTP-Ebene) gegen denselben Wegwerf-Server getestet: TOFU-Pin beim ersten Kontakt, Akzeptanz bei einem passenden zweiten Kontakt, harte Ablehnung bei einem geänderten/nicht übereinstimmenden Host-Schlüssel, ein Müll-Private-Key als invalid_key abgelehnt, ein nicht autorisierter Schlüssel als connection_failed abgelehnt und ein Nicht-Null-Remote-Exit-Code als ok: true mit diesem Exit-Code durchgereicht (nicht als Tool-Fehler behandelt). ssh_generate_keypair wurde auf die gleiche Weise geprüft: einen Schlüssel genau so erzeugt, wie das Tool es tut, dann denselben Schlüssel (nichts anderes) verwendet, um ssh_exec gegen den Wegwerf-Server zu authentifizieren – bestätigt, dass ein erzeugter Schlüssel ein echter, nutzbarer Schlüssel ist, nicht nur ein plausibel aussehender.

Test

pip install -e '.[dev]'
pytest

Unit-Tests (Parsen der Anmelde-Header, TOFU-Pin/Akzeptieren/Ablehnen-Logik, Tool-Schemas, Schlüsselpaar-Erzeugung einschließlich des passphrasenverschlüsselten Falls) – asyncssh[bcrypt] ist für den letzten Fall erforderlich; einfaches asyncssh wirft KeyExportError bei jedem passphrasengeschützten OpenSSH-Format-Export. Kein echtes Netzwerk oder Subprozess in der automatisierten Suite; die oben genannten Szenarien mit echtem Handshake (einschließlich des Generate -> Exec-Roundtrips) wurden manuell gegen einen Wegwerf-SSH-Server vor Ort ausgeführt.

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    Enables secure SSH connections to remote servers for executing shell commands and managing active sessions. It supports authentication via passwords or private keys and provides optional host-based access control.
    4
    207
    MIT

View all related MCP servers

Related MCP Connectors

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

  • Issue, rotate and revoke scoped API-key passes for 25+ providers — the agent never sees a real key

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

View all MCP Connectors

Latest Blog Posts

MCP directory API

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

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

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