Skip to main content
ClaudeWave

MCP server exposing SSH control for Linux servers via Model Context Protocol.

MCP ServersOfficial Registry655 stars101 forksTypeScriptMITUpdated today
ClaudeWave Trust Score
94/100
Verified
Passed
  • Open-source license (MIT)
  • Actively maintained (<30d)
  • Healthy fork ratio
  • Clear description
  • Mature repo (>1y old)
  • Documented (README)
Flags
  • !Install pipes a remote script into a shell (curl | sh)
Last scanned: 8/25/2026
Install in Claude Code / Claude Desktop
Method: Manual
Claude Code CLI
git clone https://github.com/tufantunc/ssh-mcp
claude_desktop_config.json (Claude Desktop)
{
  "mcpServers": {
    "ssh-mcp": {
      "command": "node",
      "args": ["/path/to/ssh-mcp/dist/index.js"],
      "env": {
        "SSH_MCP_PASSWORD": "<ssh_mcp_password>"
      }
    }
  }
}
1. Run the command above in your terminal (Claude Code), or paste the JSON config into claude_desktop_config.json (Claude Desktop).
2. Replace any <placeholder> values with your API keys or paths.
3. Restart Claude. The MCP server and its tools appear automatically.
💡 Clone https://github.com/tufantunc/ssh-mcp and follow its README for install instructions.
Detected environment variables
SSH_MCP_PASSWORD
Use cases

MCP Servers overview

# SSH MCP Server v2

[![NPM Version](https://img.shields.io/npm/v/ssh-mcp)](https://www.npmjs.com/package/ssh-mcp)
[![Downloads](https://img.shields.io/npm/dm/ssh-mcp)](https://www.npmjs.com/package/ssh-mcp)
[![CI](https://github.com/tufantunc/ssh-mcp/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/tufantunc/ssh-mcp/actions/workflows/ci.yml)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/tufantunc/ssh-mcp/badge)](https://scorecard.dev/viewer/?uri=github.com/tufantunc/ssh-mcp)
[![codecov](https://codecov.io/gh/tufantunc/ssh-mcp/graph/badge.svg?branch=main)](https://codecov.io/gh/tufantunc/ssh-mcp)
[![License](https://img.shields.io/github/license/tufantunc/ssh-mcp)](./LICENSE)
[![GitHub issues](https://img.shields.io/github/issues/tufantunc/ssh-mcp)](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"                     # Pro

What people ask about ssh-mcp

What is tufantunc/ssh-mcp?

+

tufantunc/ssh-mcp is mcp servers for the Claude AI ecosystem. MCP server exposing SSH control for Linux servers via Model Context Protocol. It has 655 GitHub stars and its last recorded update is dated 2026-08-24.

How do I install ssh-mcp?

+

You can install ssh-mcp by cloning the repository (https://github.com/tufantunc/ssh-mcp) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.

Is tufantunc/ssh-mcp safe to use?

+

Our security agent has analyzed tufantunc/ssh-mcp and assigned a Trust Score of 94/100 (tier: Verified). See the full breakdown of passed checks and flags on this page.

Who maintains tufantunc/ssh-mcp?

+

tufantunc/ssh-mcp is maintained by tufantunc. The last recorded GitHub activity is dated 2026-08-24, with 5 open issues.

Are there alternatives to ssh-mcp?

+

Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.

Deploy ssh-mcp to your cloud

Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.

Maintain this repo? Add a badge to your README

Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.

Featured on ClaudeWave: tufantunc/ssh-mcp
[![Featured on ClaudeWave](https://claudewave.com/api/badge/tufantunc-ssh-mcp)](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>

More MCP Servers

ssh-mcp alternatives