Skip to main content
Glama
votsie

ssh-mcp

by votsie
README.md
# ssh-mcp

Управление серверами по SSH для любого агента с поддержкой MCP — Claude Code,
Codex, Cursor, Claude Desktop, Windsurf, Zed. MCP-сервер плюс три руководства,
которые учат агента им пользоваться.

Умеет интерактивную консоль с эмуляцией терминала (nano, htop, whiptail,
вендорские панели видны как читаемый экран, а не как каша из ESC-последовательностей),
обмен файлами, туннели (локальный, удалённый, SOCKS5) и память о серверах,
переживающую перезапуск.

## Ключевая идея: работа без конфига

Ничего настраивать заранее не нужно. Пользователь диктует адрес, логин и
пароль — агент подключается, и только **после успешного коннекта** спрашивает,
как назвать сервер, и сохраняет доступы. Дальше сервер адресуется по имени.

```
— подключись к 203.0.113.23, root, пароль XXX
   → подключился, Debian 12, ядро 6.1, отпечаток SHA256:...
   → как назвать этот сервер? [nl4] [vps-nl4] [srv-166]
— nl4
   → сохранено. Дальше просто «nl4».
```

## Установка

Ставится в любой MCP-клиент одной строкой прямо из репозитория — Claude Code,
Codex, Cursor, Claude Desktop, Windsurf, Zed и что угодно ещё, что умеет
stdio-транспорт:

```
uvx --from git+https://github.com/votsie/ssh-mcp ssh-mcp
```

Для Claude Code есть вариант плагином, вместе со скиллами:

```bash
claude plugin marketplace add https://github.com/votsie/ssh-mcp
claude plugin install ssh-mcp@ssh-mcp
```

Готовые блоки конфигурации под каждый клиент, установка без `uv`, переменные
окружения и обновление — в **[INSTALL.md](INSTALL.md)**.

Единственная зависимость — [uv](https://docs.astral.sh/uv/): он сам поставит
нужный Python и библиотеки в изолированное окружение, в систему ничего не
попадает.

## Руководства достаются любому агенту

Скиллы понимает только Claude Code, поэтому один и тот же материал раздаётся
тремя способами, и источник у него один — `src/ssh_mcp/guides/`:

| Способ | Кому |
|---|---|
| `skills/*/SKILL.md` | Claude Code (собираются из источника, `scripts/sync_skills.py`) |
| инструкции при подключении | всем клиентам, которые их показывают модели |
| инструмент `ssh_guide(topic)` | **всем без исключения** — инструменты видит любой агент |

Подсказки MCP (`ssh-servers`, `ssh-interactive`, `ssh-ops`) объявлены тоже, но
опираться на них нельзя: многие клиенты их модели не показывают вовсе.

## Состояние

Всё живёт в `~/.ssh-mcp/`, вне любого git-репозитория:

```
servers.env      профили с паролями, права 0600 + icacls
known_hosts      закреплённые ключи хостов
memory/<имя>/    facts.json (проба) и notes.md (заметки)
history.jsonl    журнал всех действий
logs/            технический лог
```

`~/.ssh/known_hosts` читается, но **никогда не переписывается**:
`paramiko.HostKeys.save()` нормализует файл целиком и выбросил бы комментарии и
маркеры `@cert-authority`, на которые опирается настоящий ssh-клиент.

## Модель угроз: скажем прямо

Пароли лежат **открытым текстом** — это осознанное требование, конфиг должен
правиться блокнотом. Файл защищён правами владельца (на Windows именно через
`icacls /inheritance:r`, потому что `chmod` на NTFS не запрещает ничего), но
любой процесс, запущенный от вашего пользователя, прочитает его. Модель угроз
та же, что у `~/.aws/credentials`.

**Блок-листа опасных команд нет и переспроса тоже нет** — так заказано. Агент
выполнит `rm -rf /`, если его об этом попросить. Остаётся два смягчающих
средства: журнал `history.jsonl` (это криминалистика, а не предотвращение) и
рецепт «мёртвой руки» в скилле `ssh-servers` для правок, способных отрезать
собственный доступ.

Секреты вычищаются из всех ответов и логов по набору известных строк. Это
необходимо, а не косметика: `Channel.get_pty()` в paramiko шлёт пустое поле
terminal modes и выключить эхо не позволяет, так что эхо целиком на совести
sshd.

## Настройки

| Переменная | По умолчанию | Смысл |
|---|---|---|
| `SSHM_HOME` | `~/.ssh-mcp` | Каталог состояния |
| `SSHM_CONNECT_TIMEOUT` | 20 | Таймаут коннекта, банера и аутентификации |
| `SSHM_CMD_TIMEOUT` | 300 | Таймаут команды по умолчанию |
| `SSHM_SHELL_TTL` | 1800 | Простой, после которого сессия закрывается |
| `SSHM_KEEPALIVE` | 30 | Период keepalive |
| `SSHM_DEBUG` | — | Подробный лог |

## Разработка

```bash
.venv/Scripts/python.exe -m pytest -q
```

Тесты не ходят в сеть: в `tests/sshd.py` поднимается настоящий SSH-сервер на
paramiko с парольной и ключевой аутентификацией, `exec`, эхо-оболочкой с
альтернативным экраном и реальным пробросом `direct-tcpip`. Это покрывает
коннект, drain-цикл, дедлайны, PTY, SOCKS5 и туннели.

## Что не работает и не будет

* Сессии и туннели не переживают перезапуск MCP-сервера. Оболочка на той
  стороне умирает вместе со своим окружением; воскрешать её означало бы врать.
* Эмуляция терминала — VT100/VT220 на чистом Python. 256 цветов, мышь и
  bracketed paste игнорируются, экран местами отличается от настоящего.
  При странностях помогает `term="linux"`.

## Прыжковые хосты

Если сервер закрыт фаерволом снаружи и виден только из своей же сети, укажите в
профиле `jump` — имя другого сохранённого сервера:

```dotenv
SSHM_RU2_JUMP='ru1'
```

Дальше `ssh_run("ru2", ...)` работает как обычно: соединение идёт каналом
`direct-tcpip`, открытым с промежуточного сервера. Проверка ключа хоста при
этом не ослабевает — сверяется ключ конечного сервера, а не промежуточного.
Кольца в цепочке отлавливаются до попытки подключения.

## Ключи PuTTY

`.ppk` версий 2 и 3 читаются напрямую (`ssh_mcp.ppk`), включая шифрование
Argon2id, — `puttygen` не нужен, а на Windows его обычно и нет. MAC
проверяется всегда: он отличает неверную парольную фразу от повреждённого
файла, а это разные беды с разным лечением.

TDQS

B3.4/5.0

Scored across 29 tools

Disambiguation4/5

Most tools have clearly distinct purposes (run vs shell, upload vs file_write, notes vs facts), but ssh_run and ssh_shell_open could be confused for interactive vs non-interactive execution, and ssh_upload vs ssh_file_write overlap in file transfer. The descriptions help disambiguate, but the boundaries require careful reading.

Naming Consistency4/5

The naming follows a consistent ssh_<verb>_<noun> pattern (ssh_run, ssh_upload, ssh_shell_open, ssh_tunnel_open). Minor deviations like ssh_status, ssh_guide, and ssh_import_legacy break the verb_noun pattern slightly, but the overall convention is predictable.

Tool Count3/5

29 tools is on the heavy side for an SSH server, but the breadth is justified by covering connections, file operations, interactive shells, tunnels, notes, and history. It feels comprehensive rather than bloated, though some tools (ssh_keys_list, ssh_shell_resize) are niche.

Completeness5/5

The tool surface covers the full SSH lifecycle: connection management (connect, disconnect, save, forget), file operations (upload, download, read, write, list, copy), interactive sessions (open, send, read, resize, close), tunnels (open, close), and auxiliary data (notes, facts, history, keys). No obvious dead ends for common workflows.

Maintenance

ActivityMaintained
ResponsivenessNo issues