Skip to main content
Glama
README.md
# SSH MCP

A cross-platform **MCP (Model Context Protocol) server** that lets Claude Code —
or any MCP client — **read, write, edit, search, run commands, transfer files,
and use git on a remote Linux server over SSH**. The server runs on your local
machine; **nothing is installed on the remote**.

It uses a pure-Python SSH transport ([Paramiko](https://www.paramiko.org/)), so
it runs natively on **Windows, macOS, and Linux** — no WSL, no native `ssh`
client, no SSH ControlMaster. Authentication uses your SSH **key / agent /
`~/.ssh/config`**; credentials never pass through the MCP channel.

---

## Why

Sometimes the remote box is great for the code but can't run the agent — e.g.
the network blocks the model's API, or you don't want to install/authenticate an
agent on a shared server. SSH MCP flips it: **the agent runs locally** (with
internet + your credentials) and reaches into the remote host over plain SSH.

```
┌──────────────┐    MCP (stdio)    ┌──────────────┐   SSH / SFTP    ┌───────────────┐
│  Claude Code  │ ◄───────────────► │  ssh-mcp      │ ◄─────────────► │ Remote server  │
│   (local)     │   tool calls      │  (Paramiko)   │   exec + files  │ (Linux / bash) │
└──────────────┘                   └──────────────┘                 └───────────────┘
```

---

## Features

- **22 MCP tools** — files, line-edits, commands, background jobs, search, git,
  and binary-safe file transfer.
- **Binary-safe upload/download over SFTP** — `.db`, images, archives, etc.,
  streamed (large files don't load into memory).
- **Zero remote install** — uses standard tools already on the remote.
- **Flexible auth** — keys, ssh-agent, `~/.ssh/config`, `ProxyCommand`.
- Atomic writes, output truncation, binary-file detection, background jobs.

---

## Requirements

- Python 3.10+
- An SSH key / agent / `~/.ssh/config` entry that can reach the remote host
- [uv](https://docs.astral.sh/uv/) (recommended) or pip
- Claude Code (optional, to use it as an MCP server)

---

## Install

```bash
git clone <your-repo-url> ssh-mcp
cd ssh-mcp
uv tool install .          # puts the `ssh-mcp` command on your PATH
```

Or with pip:

```bash
python -m venv .venv && . .venv/bin/activate     # Windows: .\.venv\Scripts\activate
pip install -e .
```

### Configure

Create `~/.config/ssh-mcp/config.yaml` from the example and edit it:

```bash
mkdir -p ~/.config/ssh-mcp
cp config.example.yaml ~/.config/ssh-mcp/config.yaml
```

```powershell
# Windows (PowerShell)
mkdir "$env:USERPROFILE\.config\ssh-mcp" -Force
copy config.example.yaml "$env:USERPROFILE\.config\ssh-mcp\config.yaml"
```

Minimal config:

```yaml
default_host: myserver
hosts:
  myserver:
    ssh_host: server.example.com   # hostname/IP or a ~/.ssh/config alias
    user: jdoe
    key_path: "~/.ssh/id_ed25519"  # omit to use agent / default keys / ssh_config
    default_cwd: "~/project"
```

### Register with Claude Code

```bash
claude mcp add --scope user ssh-mcp -- ssh-mcp
```

Then run `claude` and ask: *"List the files in ~/project on the remote server."*

---

## Tools

**Commands:** `ssh_run` · `ssh_run_background` · `ssh_job_status`
**Connection:** `ssh_check_connection`
**Files:** `ssh_read_file` · `ssh_write_file` · `ssh_patch_file` ·
`ssh_append_file` · `ssh_insert_lines` · `ssh_replace_lines` · `ssh_list_dir` ·
`ssh_file_info` · `ssh_mkdir` · `ssh_move` · `ssh_delete`
**Transfer (binary-safe, SFTP):** `ssh_upload` · `ssh_download`
**Search:** `ssh_grep` · `ssh_find_files`
**Git:** `ssh_git_status` · `ssh_git_diff` · `ssh_git_log`

> **Text vs binary:** `ssh_write_file` is for **text** (UTF-8). For binary files
> (`.db`, images, archives) use `ssh_upload` / `ssh_download`, which use **SFTP**
> and preserve bytes exactly.

---

## Configuration reference

| Key | Default | Meaning |
|-----|---------|---------|
| `ssh_host` | — (required) | hostname/IP or `~/.ssh/config` alias |
| `user` | ssh_config / system | SSH username |
| `port` | `22` | SSH port |
| `key_path` | — | private key file (else agent / default keys / ssh_config) |
| `password` | — | password auth (discouraged) |
| `use_ssh_config` | `true` | read `~/.ssh/config` for this host |
| `strict_host_key_checking` | `false` | reject unknown host keys |
| `proxy_command` | — | `ProxyCommand` for bastion / IAP |
| `connect_timeout` | `20` | connect timeout (s) |
| `preamble` | — | shell sourced before each command |
| `default_cwd` | `~` | working directory |
| `command_timeout` / `file_timeout` | `30` / `60` | timeouts (s) |
| `max_file_size` | `10485760` | text read/write cap (binary transfer is unbounded) |
| `max_output_lines` | `5000` | command output truncation |

Config path: `~/.config/ssh-mcp/config.yaml` (override with `SSH_MCP_CONFIG` or
`ssh-mcp --config <path>`).

---

## Development

```bash
uv sync --extra dev
uv run pytest -q
```

---

## License

**AGPL-3.0** — see [LICENSE](LICENSE) and [NOTICE](NOTICE). This project is a
modified version of a third-party AGPL-3.0 work and remains under the same
license.

TDQS

A3.8/5.0

Scored across 22 tools

Disambiguation5/5

Each tool has a clearly distinct purpose. File modification tools (write, append, insert, replace, patch) are well-defined and non-overlapping. Git and command execution tools are similarly distinct.

Naming Consistency5/5

All tools follow a consistent 'ssh_<verb>_<object>' pattern with snake_case. Git tools use 'ssh_git_<verb>', which fits the pattern. No mixing of styles.

Tool Count4/5

22 tools is slightly above the typical 3-15 range but still reasonable for a comprehensive SSH server. Each tool serves a distinct need without being excessive.

Completeness4/5

Core file operations, search, command execution, and git integration are covered. Minor gaps like file copy and permission changes exist, but the surface is largely complete for remote server management.

Maintenance

ActivityInactive
ResponsivenessNo issues