MCP server exposing SSH control for Linux servers via Model Context Protocol.
- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Healthy fork ratio
- ✓Clear description
- ✓Mature repo (>1y old)
- ✓Documented (README)
- !Install pipes a remote script into a shell (curl | sh)
git clone https://github.com/tufantunc/ssh-mcp{
"mcpServers": {
"ssh-mcp": {
"command": "node",
"args": ["/path/to/ssh-mcp/dist/index.js"],
"env": {
"SSH_MCP_PASSWORD": "<ssh_mcp_password>"
}
}
}
}SSH_MCP_PASSWORDResumen de MCP Servers
# SSH MCP Server v2
[](https://www.npmjs.com/package/ssh-mcp)
[](https://www.npmjs.com/package/ssh-mcp)
[](https://github.com/tufantunc/ssh-mcp/actions/workflows/ci.yml)
[](https://scorecard.dev/viewer/?uri=github.com/tufantunc/ssh-mcp)
[](https://codecov.io/gh/tufantunc/ssh-mcp)
[](./LICENSE)
[](https://github.com/tufantunc/ssh-mcp/issues)
**SSH MCP Server** is a security-first Model Context Protocol server that gives LLM agents controlled SSH access to remote hosts — with command classification, policy-based authorization, human-in-the-loop approval, and full audit logging.
> **The risk this server exists to manage.** Giving an LLM shell access on a remote host puts private data, untrusted input and network egress in one place — Simon Willison's ["lethal trifecta"](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/). Prompt injection has no general fix, so ssh-mcp assumes any command may be attacker-influenced: it classifies before executing, authorizes against a role × host-group matrix, gates destructive work behind approval, and records the decision either way. That narrows the blast radius; it does not remove the risk. Two things stay yours: **never point it at a root account**, and **never set `auto` approval on a production profile**. [SECURITY.md](./SECURITY.md) has the full threat model.
---
## Quick Start
### 1. Install
```bash
npm install -g ssh-mcp
```
### 2. Configure
Without a config the server still starts, so a client or directory can complete the MCP
handshake and read `tools/list` — but **every tool call is refused** until you configure it,
with a message naming the path below. Nothing runs on a host until this step is done.
Create the config file at the path for your platform:
| Platform | Path |
|---|---|
| Linux | `~/.config/ssh-mcp/config.toml` (or `$XDG_CONFIG_HOME/ssh-mcp/config.toml`) |
| macOS | `~/Library/Application Support/ssh-mcp/config.toml` |
| Windows | `%APPDATA%\ssh-mcp\config.toml` |
```toml
[defaults]
defaultProfile = "dev"
approvalMode = "ask-destructive"
[[profiles]]
name = "dev"
host = "192.168.1.100"
port = 22
user = "deploy" # NOT root!
auth = "key"
keyRef = "~/.ssh/id_ed25519"
role = "admin"
approvalPolicy = "auto" # dev is permissive
```
```bash
chmod 700 ~/.config/ssh-mcp && chmod 600 ~/.config/ssh-mcp/config.toml
```
The config decides which hosts, roles and policy rules this server honours, so it checks
that nobody but you can read it — and treats the two platforms differently, because the
question has a much clearer answer on one of them.
**Linux and macOS: enforced.** The mode check above, on the file *and* the directory —
which is why `chmod 700` is in that command, since `mkdir -p` under the default umask
leaves the directory 0755. The server refuses to start otherwise. "Only the owner" is
unambiguous here and `chmod` is a one-line fix.
**Windows: split by what the ACL actually allows.** There are no mode bits, so the ACL is
read instead — and read exposure and write exposure are not treated alike, because Windows
is much clearer about one of them than the other.
| The ACL lets another account… | Default |
|---|---|
| only **read** the config | reported, and the server starts |
| **change** the config | refused |
| nothing (no ACL at all) | refused — that is full control for everyone |
| …and if the ACL could not be read | refused, except when `icacls` is absent or the check timed out |
A config under `%APPDATA%` inherits access for you, `SYSTEM` and `Administrators` and needs
nothing done to it. One created elsewhere does not: a file under `C:\` inherits *read* for
every local account and *modify* for every authenticated one. The message names the two
`icacls` commands that fix it either way.
Read exposure is reported rather than refused because that is where Windows is genuinely
muddier than POSIX, and refusing over it blocked a config at the documented location
([#138](https://github.com/tufantunc/ssh-mcp/issues/138)). Write exposure is refused
because it is not muddy at all: another account being able to rewrite the file that decides
which hosts, roles and approval policy this server honours is an authorization bypass, not
a disclosure.
Two flags move the whole thing: `--strictConfigAcl` refuses everything the check objects
to, read-only grants included; `--allowUncheckedConfigAcl` reports everything and refuses
nothing. Neither combination leaves you without an exit, which is the lesson of #138.
### Exit statuses
| Status | Meaning |
|---|---|
| `0` | Clean shutdown |
| `1` | A defect in the server — printed with a stack trace; please report it |
| `2` | How it was invoked or configured — printed as a message, no stack |
A supervisor that treats any non-zero status as a failure needs no change. One
that matched on `1` to detect a startup problem should match on `2` as well.
Starting with nothing configured is **not** an exit-2 condition, as of the release that
added introspection without a config: the server starts so it can be described, and
refuses each tool call instead. A supervisor that used a non-zero exit to catch an
unconfigured deployment should watch for `starting unconfigured` on stderr, or read
`configured` from `GET /health` when running the HTTP transport.
### 3. Set credentials via environment variables
```bash
export SSH_MCP_PASSWORD="your-password" # if using auth=password
# OR use SSH agent (recommended):
export SSH_AUTH_SOCK="$SSH_AUTH_SOCK" # already set if agent running
```
### 4. Connect from your MCP client
**Claude Code:**
```bash
claude mcp add --transport stdio ssh-mcp -- ssh-mcp
```
**Claude Desktop / Cursor / Windsurf:**
```json
{
"mcpServers": {
"ssh-mcp": {
"command": "ssh-mcp",
"env": {
"SSH_MCP_PASSWORD": "your-password"
}
}
}
}
```
**Never pass passwords as CLI arguments** — they're visible via `ps aux`. Use env vars, config files, SSH agent, or OS keychain.
---
## Tools (11)
| Tool | Purpose | readOnly | destructive |
|------|---------|:--------:|:----------:|
| `list-connections` | Discover available hosts and connection status | ✅ | — |
| `list-sessions` | List active sessions per host | ✅ | — |
| `open-session` | Create a named interactive (stateful) or background session | — | — |
| `close-session` | Close a session. A background session's command is signalled (INT/TERM/KILL) before its channel is dropped | — | ✅ |
| `read-session-output` | Read output from background sessions (e.g., `tail -f`) | ✅ | — |
| `read-command` | Execute allowlisted read-only commands (`ls`, `cat`, `grep`, ...) | ✅ | — |
| `run-command` | Execute arbitrary commands (destructive ones need approval) | — | — |
| `privileged-command` | Execute with sudo (always requires approval) | — | ✅ |
| `sftp-upload` | Upload a file via SFTP | — | ✅ |
| `sftp-download` | Download a file via SFTP | ✅ | — |
| `signal-process` | Send INT/TERM/KILL to a remote PID | — | ✅ |
### Interactive Sessions
Sessions maintain state (CWD, environment variables) between commands:
```
Agent: open-session(name="deploy", type="interactive")
Agent: run-command(session="deploy", command="cd /opt/myapp")
Agent: run-command(session="deploy", command="git pull") # runs in /opt/myapp
Agent: run-command(session="deploy", command="npm ci") # CWD persists
Agent: close-session(name="deploy")
```
### Background Sessions
Long-running processes (logs, builds):
```
Agent: open-session(name="logs", type="background", command="tail -f /var/log/syslog")
Agent: read-session-output(name="logs", lines=20) # poll
Agent: close-session(name="logs")
```
### Remote host support
Tested against Linux (Debian/bash, Alpine/busybox ash), Dropbear, and Windows
OpenSSH on Windows 11.
| | Linux / BSD / macOS | Windows OpenSSH |
|---|:---:|:---:|
| `read-command`, `run-command`, `privileged-command`, `signal-process` | ✅ | ✅ |
| `sftp-upload`, `sftp-download` | ✅ | ✅ |
| Background sessions | ✅ | ✅ |
| **Interactive sessions** | ✅ | ❌ |
**Interactive sessions require a POSIX shell** (sh, bash, ash, zsh). They work by
bracketing each command with `printf` markers and reading `$?` and `$PWD` from a
trailer — none of which exist in `cmd.exe`, the default shell for Windows
OpenSSH. Opening one against such a host fails immediately with an explicit
error rather than timing out; everything else works normally.
Setting PowerShell as the OpenSSH `DefaultShell` does not help: the protocol is
POSIX-specific, not merely non-`cmd`.
---
## Configuration
### Profile options
```toml
[defaults]
defaultProfile = "dev"
sessionMaxPerConnection = 5
sessionIdleTimeoutMs = 600000 # 10min
sessionBackgroundMaxMs = 3600000 # 1hr
commandTimeoutMs = 60000
commandMaxChars = 5000 # 0 = unlimited, the config spelling of --maxChars=none
commandMaxOutputBytes = 1048576 # 1MB
connectionIdleReapMs = 900000 # 15min
commandQuotaPerDay = 0 # 0 = unlimited; circuit breaker for runaway agents
approvalGrantTtlMs = 0 # 0 = always prompt; see "Approval Grants"
approvalMode = "ask-destructive" # auto | ask-destructive | ask-all | deny
[[profiles]]
name = "prod-web-1"
host = "10.0.1.50"
port = 22
user = "deploy"
auth = "agent" # agent | key | password | keychain
keyRef = "~/.ssh/id_ed25519" # for auth=key
keychainEntry = "ssh-mcp/prod" # for auth=keychain (requires @napi-rs/keyring)
via = "bastion" # ProLo que la gente pregunta sobre ssh-mcp
¿Qué es tufantunc/ssh-mcp?
+
tufantunc/ssh-mcp es mcp servers para el ecosistema de Claude AI. MCP server exposing SSH control for Linux servers via Model Context Protocol. Tiene 655 estrellas en GitHub y su última actualización registrada es del 2026-08-24.
¿Cómo se instala ssh-mcp?
+
Puedes instalar ssh-mcp clonando el repositorio (https://github.com/tufantunc/ssh-mcp) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.
¿Es seguro usar tufantunc/ssh-mcp?
+
Nuestro agente de seguridad ha analizado tufantunc/ssh-mcp y le ha asignado un Trust Score de 94/100 (tier: Verified). Revisa el desglose completo de comprobaciones superadas y flags en esta página.
¿Quién mantiene tufantunc/ssh-mcp?
+
tufantunc/ssh-mcp es mantenido por tufantunc. La última actividad registrada en GitHub es del 2026-08-24, con 5 issues abiertos.
¿Hay alternativas a ssh-mcp?
+
Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.
Despliega ssh-mcp en tu cloud
Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.
¿Mantienes este repo? Añade un badge a tu README
Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.
[](https://claudewave.com/repo/tufantunc-ssh-mcp)<a href="https://claudewave.com/repo/tufantunc-ssh-mcp"><img src="https://claudewave.com/api/badge/tufantunc-ssh-mcp" alt="Featured on ClaudeWave: tufantunc/ssh-mcp" width="320" height="64" /></a>Más MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
The fastest path to AI-powered full stack observability, even for lean teams.
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!