SSH MCP Server

официальный

Выполняйте команды, перемещайте файлы, ищите логи и проверяйте машины через SSH с помощью вашего агента.

Что можно делать с SSH MCP?

  • Выполнение команд с защитными ограничениями — Попросите ассистента выполнить одиночные или пакетные команды через ssh_exec, с защитой от разрушительных операций, которая блокирует необратимые действия до их отправки на сервер.
  • Чтение, запись и список удаленных файлов — Используйте ssh_file_read, ssh_file_write и ssh_file_list для просмотра или изменения файлов, с атомарной записью и опциональной проверкой SHA-256.
  • Поиск в журналах и проверка состояния сервера — Запросите ssh_log_search или ssh_log_tail по файлам и контейнерам, или получите структурированный снимок состояния с помощью ssh_snapshot и ssh_audit_baseline.
  • Передача файлов с проверкой целостности — Загружайте или скачивайте файлы и каталоги через ssh_upload и ssh_download, с автоматическим резервным переходом на устаревший scp для старых устройств.
  • Управление длительными фоновыми задачами — Отключайте медленные операции с помощью ssh_exec и отслеживайте их через ssh_job_status, ssh_job_output и ssh_job_kill, с сохранением работы при разрыве соединения.

Документация

SSH MCP Server — Инструменты для удалённых серверов для AI-агентов

SSH MCP Server

SSH MCP сервер — мультиинструмент, который экономит ваше время и токены вашего AI-агента на отладке, разработке и обслуживании серверов.

Выполняйте команды, перемещайте файлы, читайте журналы и проверяйте машины по SSH — облачный VPS, физический сервер или роутер с BusyBox, стоящий в вашем шкафу.

Он использует уже установленный на вашей машине OpenSSH-клиент: ваши ключи, ваш ~/.ssh/config, ваши jump-хосты, ваш агентский форвардинг. Ничего встроенного, ничего компилировать, никаких нативных привязок.

Работает с Claude Code, Codex CLI, Cline, opencode, Gemini CLI, Qwen Code, Hermes и другими MCP-клиентами.

MCP Registry Glama Smithery npm downloads tests

Установка · Инструменты · Настройка · Безопасность · Дорожная карта · Документация · Журнал изменений


Установка за 30 секунд

Глобальная установка не требуется. npx загружает пакет при первом использовании:

npx -y @hypnosis/ssh-mcp-server

Добавьте его в ваш MCP-клиент — например, Claude Code — для каждого проекта:

claude mcp add ssh -s user \
  -e SSH_PROFILES_FILE="$HOME/.claude/ssh-profiles.json" \
  -- npx -y @hypnosis/ssh-mcp-server

Или напишите вручную — тот же сервер в формате конфигурации, который используют большинство клиентов:

{
  "mcpServers": {
    "ssh": {
      "command": "npx",
      "args": ["-y", "@hypnosis/ssh-mcp-server"],
      "env": {
        "SSH_PROFILES_FILE": "~/.claude/ssh-profiles.json"
      }
    }
  }
}

Затем создайте ~/.claude/ssh-profiles.json хотя бы с одной машиной:

{
  "profiles": {
    "production": {
      "host": "server.example.com",
      "username": "admin",
      "privateKeyPath": "~/.ssh/your_private_key"
    }
  }
}

Этого достаточно для подключения.

Codex, opencode, Qwen Code и другие клиенты описаны в Настройка SSH MCP сервера.

Установка как плагина

Некоторые клиенты — например, Claude Code — могут принять всё целиком как плагин:

/plugin marketplace add hypnosis/ssh-mcp-server
/plugin install ssh-mcp-server@ssh-mcp-server

Плагин читает ~/.claude/ssh-profiles.json, если SSH_PROFILES_FILE не указывает иное, поэтому создайте этот файл сначала, и сервер запустится с уже загруженными машинами.

Требования

npm version Node.js TypeScript MCP SDK

Node.js 18+ и системный ssh клиент на PATH. В Windows используйте профиль на основе ключей; профили с паролем и парольной фразой в настоящее время недоступны.

Предпочитаете закреплённую версию, офлайн-работу или одну лишнюю проверку реестра при каждом запуске: npm install -g @hypnosis/ssh-mcp-server, затем используйте ssh-mcp-server в качестве команды вместо npx.

Для кого это

  • DevOps и SRE-инженеры, которым нужны более быстрые аудиты, проверки инцидентов и рутинная работа с серверами.
  • Vibe-кодеры и независимые разработчики, которые создают продукты с помощью AI-ассистента и запускают их на собственных серверах.
  • Системные администраторы и платформенные инженеры, которым нужны структурированные инструменты вместо неограниченной сырой оболочки.
  • Разработчики и небольшие команды, управляющие собственным VPS без выделенной команды эксплуатации.
  • Владельцы домашних серверов, NAS и роутеров, чьё полезное оборудование пережило свои современные протоколы.

Почему SSH MCP сервер вместо сырой оболочки

Меньше токенов, ниже затраты на AI

Сырая оболочка даёт AI-агенту поток данных: повторяющиеся команды, ASCII-таблицы и дампы журналов. Он тратит токены, превращая этот шум в картину состояния сервера — ваши деньги.

Более быстрая отладка серверов

Специализированные инструменты группируют рутинные проверки, ограничивают шумный вывод и возвращают только важное. Агент тратит меньше времени на перевод вывода терминала и быстрее добирается до исправления.

Меньше догадок, меньше ошибок AI

Структурированные ответы сообщают, что было найдено, что не удалось измерить и что было обрезано. Это оставляет агенту меньше места для заполнения пробелов галлюцинациями — и даёт вам меньше плохих исправлений, спокойнее деплои и более надёжный код.

SSH-совместимость: современные серверы, устаревшее оборудование и Windows

Используйте вашу существующую настройку OpenSSH

Никакой встроенной реализации SSH, никаких нативных привязок, никакой пересборки под каждую платформу. Команды используют системный ssh клиент, поэтому ваши ключи, ваш ~/.ssh/config, ваши jump-хосты и ваш агентский форвардинг продолжают работать точно так же, как в терминале. Когда поддерживается, одно общее мультиплексированное соединение на пункт назначения означает, что вы аутентифицируетесь один раз, а не для каждой команды.

Поддержка SSH для устаревших серверов, роутеров и NAS-устройств

Отправьте файл на роутер с современным scp и получите вот это:

scp app.conf router:/etc/
# scp: subsystem request failed on channel 0

Ничего не сломано — текущий scp говорит на новом протоколе, а роутер его не знает. В терминале вам пришлось бы читать форумный тред и возвращаться с дополнительным флагом. Здесь вы не делаете ничего: передача пробуется, отказ распознаётся, вместо него используется старый протокол, и эта машина запоминается, чтобы следующий файл пошёл туда напрямую.

Запасные варианты для старых SSH-клиентов и отсутствующих инструментов

Старое оборудование получает запасной путь, а не тупик. Когда современная функция отсутствует, сервер идёт более старым путём, где может:

Ваша машинаЧто вы получаете
Роутер или NAS слишком маленький для современной передачи файловФайл всё равно доставляется — старый протокол используется автоматически
Сервер десятилетней давностиРабочий процесс всё равно работает; просто открывается новое соединение на команду вместо повторного использования
Урезанный образ без возможности хеширования файлаЗагрузка сообщает «не удалось проверить» вместо заявления о совпадении, которое никто не проверял
Машина, где инструмент просто не установленОтвет сообщает «не измерено» — никогда не ноль, который читается как «ничего нет»

Создан для Model Context Protocol

Построен на официальном MCP SDK, полностью на TypeScript, более 2500 модульных тестов плюс живой набор, который работает с реальными контейнерами, а не с моками.


Сырой SSH против SSH MCP сервера: та же задача, оба способа

Проверка здоровья SSH-сервера

Ситуация: Только что прошёл деплой. Сервер работает медленно, и вы не знаете, виноваты ли диск, память, службы, контейнеры или ошибки.

Вопрос: «Здоров ли этот сервер?»

Сырой SSH

$ uptime
 10:42:17 up 18 days,  3:21,  2 users,  load average: 0.42, 0.31, 0.28
$ df -hT
Filesystem     Type   Size  Used Avail Use% Mounted on
/dev/sda1      ext4    40G   35G  5.0G  87% /
overlay        overlay  40G   35G  5.0G  87% /var/lib/docker/overlay2/...
$ free -h
               total        used        free      shared  buff/cache   available
Mem:           7.7Gi       4.9Gi       612Mi       121Mi       2.2Gi       2.5Gi
$ systemctl --failed
  UNIT              LOAD   ACTIVE SUB    DESCRIPTION
● api-worker.service loaded failed failed API background worker
$ docker ps -a
CONTAINER ID   IMAGE          STATUS                     PORTS
8e14d0b41c2a   api:latest     Up 3 minutes               0.0.0.0:8080->8080/tcp
65b894af2430   worker:latest  Exited (1) 2 minutes ago
$ ss -tulpn
Netid  State   Local Address:Port   Process
tcp    LISTEN  0.0.0.0:22          users:(("sshd",pid=842,fd=3))
tcp    LISTEN  0.0.0.0:8080        users:(("docker-proxy",pid=1942,fd=4))
$ journalctl -p err --since -1h | tail -50
Aug 20 10:39:14 prod api-worker[22104]: database connection timed out
Aug 20 10:39:14 prod systemd[1]: api-worker.service: Failed with result 'exit-code'.

Это всё ещё сокращённый результат. Полная проверка требует больше команд для CPU, состояний служб, количества контейнеров и недавних ошибок, каждая со своим форматом вывода. Хуже того, сервер без ss может выглядеть так, будто у него ноль слушателей, когда проверка портов вообще не выполнялась.

Структурированный результат MCP

ssh_snapshot({ "profile": "production" })
{
  "disk_pct": 87,
  "mem_pct": 64,
  "cpu_pct": 12,
  "load": "0.42 0.31 0.28",
  "containers": 7,
  "ports": 14,
  "services_running": 3,
  "recent_errors": 21,
  "unavailable": []
}

Что получает агент

Сырой SSHСтруктурированный MCPВаша выгода
Несколько команд и ASCII-таблицыИменованные поля в одном результатеОдин вызов, именованные поля и меньше往返
Отсутствующий инструмент может выглядеть как пустой выводunavailable называет, что не было измереноМеньше догадок и меньше плохих исправлений
Вы разбираетесь с дисками, службами и ошибкамиСигналы проблем уже выведены на поверхностьБолее быстрая отладка

Полный результат ssh_audit_baseline может быть длиннее, чем несколько сырых выводов команд — около 1077 токенов против 765 в наших лабораторных измерениях. Экономия приходит от полного рабочего процесса, а не от сокращения одного ответа.

В реальной сессии устранения неполадок специализированные инструменты сократили 49 отдельных вызовов команд до 4 вызовов MCP. Каждый дополнительный вызов запускает новый виток модели с накопленным разговором. Кэширование подсказок может снизить стоимость повторного ввода, но новые команды и их вывод всё равно потребляют контекст. Меньше往返 означает меньше токенов за сессию, меньше повторного анализа и более быстрый путь к ответу.

Нужна полная картина, а не только пульс? ssh_audit_baseline собирает систему, диск, память, порты, sshd, упавшие юниты, Docker, файрвол и обновления. Результаты приходят как CRITICAL / WARNING / OK; неизмеренные секции названы, а не молча читаются как ноль.

Поиск по журналам Linux-сервера

Ситуация: API истекает по таймауту, но то же сообщение может быть в nginx, syslog, journald или журнале приложения, который вы не можете прочитать своим обычным пользователем.

Вопрос: «Откуда взялась эта ошибка?»

Сырой SSH

$ grep -i "timeout" /var/log/nginx/error.log
2026/08/20 10:38:54 [error] upstream timed out while reading response header
$ grep -i "timeout" /var/log/syslog
Aug 20 10:39:14 prod api-worker[22104]: database connection timed out
$ grep -i "timeout" /var/log/app/*.log 2>/dev/null
$ journalctl -u api --since "1 hour ago" | grep -i timeout
Aug 20 10:39:14 prod api[22104]: database connection timed out after 30000ms

Третья команда выглядит чистой, но 2>/dev/null также скрыла ошибку прав доступа. «Ничего не совпало» и «ничего не прочитано» теперь выглядят одинаково. Занятый журнал также может вернуть тысячи строк и вытеснить остальную часть инцидента из контекста агента.

Структурированный результат MCP

ssh_log_search({ "profile": "production",
                 "path": ["/var/log/nginx/error.log", "/var/log/syslog", "/var/log/app/*.log"],
                 "query": "timeout", "context": 2, "since": "1h" })
{
  "matches": 34,
  "lines": [
    { "file": "/var/log/nginx/error.log", "line": 4821,
      "text": "upstream timed out while reading response header", "context": false },
    { "file": "/var/log/nginx/error.log", "line": 4822,
      "text": "client closed connection", "context": true }
  ],
  "files_searched": 6,
  "files_unreadable": ["/var/log/app/private"],
  "files_skipped": 12,
  "files_undated": [],
  "limited": false,
  "truncated": false
}

Что получает агент

Сырой SSHСтруктурированный MCPВаша выгода
Четыре поиска и четыре выводаОдин поиск по файлам и глобамМеньше токенов и往返
Ошибки прав могут исчезатьfiles_unreadable называет каждый пропущенный путьНет ложного вывода «журналы чистые»
Вывод может расти без полезного пределаlimited и truncated раскрывают каждый обрывБолее безопасные решения на основе частичных результатов

since использует часы сервера, namesOnly: true возвращает только совпадающие пути, а ssh_log_tail читает последние N строк из нескольких журналов за один вызов.

Безопасное удалённое редактирование конфигураций

Ситуация: Вам нужно заменить конфиг nginx на живом сервере. Обрыв соединения, неправильный режим или непроверенная копия могут оставить службу со сломанным файлом.

Вопрос: «Могу ли я заменить этот конфиг, не оставив частичный файл?»

Сырой SSH

$ sudo sh -c 'cat > /etc/nginx/conf.d/api.conf' <<'EOF'
server {
    listen 80;
    location / { proxy_pass http://127.0.0.1:8080; }
}
EOF
$ echo $?
0

Код выхода ноль говорит, что оболочка завершилась. Это не доказывает, какие байты приземлились, и > обрезал старый файл до того, как прибыл первый байт нового. Если соединение оборвётся в середине записи, служба останется с частичным конфигом.

Структурированный результат MCP

ssh_file_write({ "profile": "production",
                 "files": [{ "path": "/etc/nginx/conf.d/api.conf",
                             "content": "server {\n    listen 80;\n    location / { proxy_pass http://127.0.0.1:8080; }\n}\n",
                             "mode": "644", "sudo": true, "verify": true }] })
{
  "files": [{ "path": "/etc/nginx/conf.d/api.conf", "written": true,
              "verified": "verified", "reason": null, "bytes": 79 }]
}

Что получает агент

Сырой SSHСтруктурированный MCPВаша выгода
Целевой файл обрезается до завершения копированияПолный временный файл заменяет его одним переименованиемНет наполовину записанного конфига
Только код выходаБайты и результат проверки названыВы знаете, что реально приземлилось
Права живут внутри текста оболочкиsudo, mode и verify — поля на файлПредсказуемое владение и меньше ошибок кавычек

verified имеет три честных исхода: verified, unavailable, когда на сервере нет инструмента хеширования, и skipped, когда проверка не была запрошена. Для чтения ssh_file_read принимает список путей; ssh_file_list обрабатывает глобы, рекурсию, размеры и режимы.

Пакетные SSH-команды с sudo

Ситуация: Деплой готов, но синтаксис nginx, состояние службы и недавние ошибки должны быть проверены до переключения трафика. Одна проваленная проверка не должна исчезнуть внутри объединённого дампа.

Вопрос: «Прошла ли каждая предварительная проверка?»

Сырой SSH

$ ssh admin@server.example.com 'sudo nginx -t'
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ ssh admin@server.example.com 'sudo systemctl is-active nginx'
active
$ ssh admin@server.example.com 'sudo tail -5 /var/log/nginx/error.log'
2026/08/20 10:38:54 [error] upstream timed out while reading response header

Три соединения возвращают три несвязанных вывода. Если команды объединены с ;, оболочка сообщает только последний код выхода; если они объединены с &&, более поздние проверки исчезают после первого сбоя.

Структурированный результат MCP

ssh_exec({ "profile": "production",
           "command": ["nginx -t", "systemctl is-active nginx",
                       "tail -5 /var/log/nginx/error.log"],
           "sudo": true })
{
  "commands": [
    { "command": "nginx -t", "exit_code": 0, "truncated": false, "clipped_bytes": 0,
      "stdout": "", "stderr": "nginx: configuration file /etc/nginx/nginx.conf test is successful\n" },
    { "command": "systemctl is-active nginx", "exit_code": 0, "truncated": false,
      "clipped_bytes": 0, "stdout": "active\n", "stderr": "" },
    { "command": "tail -5 /var/log/nginx/error.log", "exit_code": 0, "truncated": false,
      "clipped_bytes": 0, "stdout": "2026/08/21 09:14:02 [error] upstream timed out\n", "stderr": "" }
  ],
  "job_id": null
}

Что получает агент

Сырой SSHСтруктурированный MCPВаша выгода
Три вызова и несвязанные выводыОдин упорядоченный список командМеньше往返
Объединённая оболочка может скрыть промежуточный статусКаждая команда сохраняет свой exit_codeНи одна проваленная проверка не пропущена
sudo и кавычки повторяются в тексте командыsudo применяется ко всему пакетуМеньше ошибок кавычек

Защита от разрушительных команд проверяет полный список до запуска первой команды. Если одна запись отклонена, каждая другая запись помечается как не выполненная, и на сервер ничего не отправляется. Каждая команда несёт собственные stdout и stderr. Команда, которая выполнилась и ничего не вывела, имеет пустую строку; команда, которая никогда не запускалась, вообще не имеет такого поля, так что их невозможно перепутать. Вывод объёмом более 128 КБ на команду сохраняет оба конца — начало для таблиц, хвост для журналов — со швом посередине, указывающим объём, а clipped_bytes сообщает, сколько было обрезано. Обрезка происходит по границам байтов и отступает к краю символа, поэтому усечённый ответ никогда не несёт знака замены.

sudo достигает сервера без терминала: ответ профиля передаётся sudo через стандартный ввод. Какой именно секрет используется, определяется через sudoPassword, когда профиль указывает его, и через password в противном случае — профиль, входящий по ключу, вообще не имеет пароля для входа, а там, где машина различает эти два пароля, пароль для входа является неверным ответом. Когда отвечать нечем, ответ сообщает об этом и называет пути решения, вместо того чтобы оставлять собственные советы sudo о -S и помощниках askpass. Команде, которая читает собственный стандартный ввод, пароль никогда не передаётся, иначе он оказался бы смешан с данными.

Запуск длительных SSH-задач

Ситуация: Резервное копирование или миграция будет выполняться дольше, чем длится сессия агента. Соединение может закрыться, но вам всё равно понадобятся его состояние, вывод и код завершения позже.

Вопрос: «Переживёт ли эта задача разговор?»

Обычный SSH

$ ssh admin@server.example.com 'pg_dump app | gzip > /srv/backups/app.sql.gz'
client_loop: send disconnect: Broken pipe

Терминал исчез. Теперь вам нужно переподключиться, найти процесс, проверить целевой файл и гадать, завершилось ли резервное копирование или остановилось на полпути.

Структурированный результат MCP

ssh_exec({ "profile": "production",
           "command": "pg_dump app | gzip > /srv/backups/app.sql.gz",
           "detach": true })
{
  "commands": [{
    "command": "pg_dump app | gzip > /srv/backups/app.sql.gz",
    "exit_code": null,
    "truncated": false,
    "timed_out": false,
    "blocked": false,
    "blocked_reason": null,
    "not_run": false,
    "warning": null
  }],
  "job_id": "mst0f2q1-9ab3c4d5"
}

Что получает агент

Обычный SSHСтруктурированный MCPВаша выгода
Задача привязана к одной SSH-сессииУдалённая задача имеет постоянный идентификаторБезопасные обрывы и перезапуски
Переподключение означает поиск процессов и файловСтатус и код завершения имеют именованные состоянияНе нужно гадать, завершилось ли
Повторное чтение вывода повторяет старый текстВывод продолжается с байтового смещенияМеньше токенов на длинных задачах

Состояние задачи хранится на удалённом диске, а не в памяти этого сервера. ssh_job_status различает running, finished и lost; ssh_job_output продолжает с последнего байтового смещения; а ssh_job_kill сигнализирует всей группе процессов, а не только её оболочке.

Передача файлов на устаревшие маршрутизаторы и NAS-устройства

Ситуация: Текущий клиент OpenSSH пытается использовать SFTP, но маршрутизатор или NAS понимает только классический протокол scp. Файл всё равно должен дойти нетронутым и безопасно заменить целевой объект.

Вопрос: «Может ли это старое устройство по-прежнему получать проверенный файл?»

Обычный SSH

$ scp app.conf operator@router:/etc/app.conf
subsystem request failed on channel 0
scp: Connection closed

Обычный следующий шаг — вспомнить устаревший флаг, повторить копирование и затем выполнить отдельную команду хеширования — если на устройстве вообще есть инструмент хеширования.

Структурированный результат MCP

ssh_upload({ "profile": "router", "local_path": "./app.conf",
             "remote_path": "/etc/app.conf", "sudo": true,
             "mode": "644", "owner": "root:root", "verify": true })
{
  "files": [{
    "path": "/etc/app.conf",
    "written": true,
    "verified": "verified",
    "reason": null,
    "bytes": 1284
  }]
}

Что получает агент

Обычный SSHСтруктурированный MCPВаша выгода
Современный режим SFTP останавливается на первой ошибкеАвтоматический откат к классическому scp запоминаетсяСтарое оборудование продолжает работать
Успешное копирование не доказывает целостностьПроверка SHA-256 имеет именованный результатПовреждение не принимается за успех
Прямая замена может оставить частичный целевой объектВременный файл перемещается на место после передачиРабочий файл переживает прерывания

Если на устройстве нет ни sha256sum, ни openssl, результат сообщает unavailable и называет причину, вместо того чтобы сообщать о ложном совпадении. Целые каталоги используют recursive: true и проверяют свои хеши одним пакетом.

Защита от разрушительных команд для ИИ-агентов

Защита работает локально, до того как команда достигнет SSH. Она разделяет операции, которые можно восстановить, и те, что уничтожают контейнер с данными, а также проверяет порядок команд внутри цепочек и пакетов.

Остановка разрушительной цепочки до её запуска

Безопасная последовательность резервного копирования и замены:

cp -r /srv/app /srv/app.bak && mv /srv/app /srv/app-old && rm -rf /srv/app

Те же операции в неправильном порядке:

rm -rf /srv/app && cp -r /srv/app /srv/app.bak && mv /srv/app /srv/app-old
# REFUSED before the first command runs

Оболочка удалила бы каталог и только затем обнаружила, что источник резервной копии исчез. Защита видит, что более поздние шаги читают цель, уже уничтоженную более ранним шагом, поэтому весь вызов остаётся на вашей машине. Та же проверка перехватывает dropdb app && pg_dump app > backup.sql.

Отказ при необратимой потере, предупреждение при восстановимых изменениях

Отказано — сам контейнерТолько предупреждение — его содержимое
DROP DATABASE, dropdbDROP TABLE, TRUNCATE, DELETE FROM
docker volume rm, docker compose down -vdocker rm -f <name>
crontab -rредактирование одной задачи
mkfs, wipefs -a, lvremove, zfs destroychmod 777
reboot, shutdown, haltgit reset --hard

docker compose down -v отклоняется, потому что -v удаляет именованные тома Docker, включая том базы данных. Без -v остановка сервисов не рассматривается как то же необратимое действие.

Рекурсивное удаление корня файловой системы, домашнего каталога или системных деревьев, таких как /etc, /var и /usr, также отклоняется, в том числе когда туда ведёт символическая ссылка. Нераспознанная цель, такая как rm -rf "$DIR"/*, тоже отклоняется: «не удалось проверить» не считается «безопасно».

Называйте то, что останавливаете

Команда, которая находит свою цель вместо того, чтобы называть её, не отправляется. Сервер разворачивает её и отвечает, что стоит за целью:

docker kill $(docker ps -q --filter ancestor=web)
# BLOCKED — would stop:
#   edge — web:latest, Up 34 days, 0.0.0.0:8443->8443/tcp

Для процесса ответ добавляет признаки его использования: как долго он работает, на каких портах принимает соединения, сколько соединений несёт. Именованные цели не требуют дополнительных затрат и проходят молча — docker kill web-1, kill 4871, systemctl stop app.

Чтобы продолжить, назовите то, что останавливается. Имена проверяются на соответствие тому, чего команда действительно достигает, поэтому маска, сместившаяся на что-то другое, отклоняется, а не подтверждается:

docker kill $(docker ps -q --filter ancestor=web) # CONFIRMED-KILL: edge

Шаблон по командным строкам — особый случай. Он совпадает с самой командой, которая его несёт, поэтому оболочка, выполняющая его, получает сигнал раньше цели, и ответ обрывается на середине. Такой удар не подтверждается, а переписывается — по номеру или с одним символом, записанным как класс, чтобы шаблон перестал совпадать с самим собой:

pkill -f relay
# BLOCKED — two ways through:
#   kill 4871
#   pkill -f '[r]elay' # CONFIRMED-KILL: 4871

Три исхода остаются раздельными: цели найдены, разворачивание ничего не достигло, и нечем спрашивать — нет движка на машине, усечённый ответ, неудачное соединение. Последние два также являются отказами: незнание — не причина продолжать.

Подтверждение намеренной разрушительной команды

Ничто не запрещено навсегда. Добавьте # CONFIRMED-DESTRUCTIVE к проверенной команде, и она будет пропущена. Когда защита отклоняет одну запись в пакете, весь пакет останавливается до выполнения, поэтому сервер никогда не остаётся после наполовину выполненной операции.

Защита работает в пределах одного вызова. Она не может связать удаление в одном вызове с чтением в следующем или рассуждать об инструментах, которых не распознаёт. Это ремень безопасности, а не механизм политик: восстановимые операции остаются вашим решением. Ограничения путей и правила кавычек задокументированы в docs/security.md.

Инструменты

18 SSH MCP-инструментов для операций на сервере. Полные параметры и примеры находятся в docs/tools.md.

ИнструментЧто делает
ssh_execВыполняет одну команду или пакет, с защитой от разрушительных команд и опциональным отсоединением
ssh_file_readЧитает один или несколько файлов, текст или бинарные данные
ssh_file_writeЗаписывает файлы с атомарным переименованием и опциональной проверкой SHA-256
ssh_file_listПеречисляет каталог, с опциональным глобом и рекурсией
ssh_uploadЗагружает файл или каталог по SSH, бинарно-безопасно с проверками целостности; каталог заменяет цель или сливается с ней
ssh_downloadСкачивает файл или каталог по SSH, бинарно-безопасно с проверками целостности
ssh_job_statusСостояние фоновой задачи: выполняется, завершена или потеряна
ssh_job_outputЧитает накопленный вывод с байтового смещения
ssh_job_listПеречисляет задачи, вычищая завершённые после истечения TTL
ssh_job_killСигналит всей группе процессов задачи
ssh_log_tailПоследние N строк одного или нескольких журналов, с поддержкой глоба; контейнер по имени
ssh_log_searchПоиск по шаблону в журналах или через журнал контейнера
ssh_snapshotРазовый снимок здоровья: сервисы, ресурсы, Docker, сеть, ошибки
ssh_monitorУправление транспортом: статистика, перезагрузка, тест, список, закрытие
ssh_audit_baselineСистема, диск, память, сеть, ssh, сервисы, Docker, брандмауэр, обновления
ssh_tls_checkСрок действия сертификата, SAN, цепочка и хук обновления для домена
ssh_disk_breakdownКуда ушёл диск: du top-N, Docker, journald, кеши
ssh_service_statussystemctl status плюс хвост journalctl для одного юнита

Аннотации безопасности MCP-инструментов

Стандартные аннотации MCP сообщают клиентам, какие инструменты доступны только для чтения, разрушительны, идемпотентны или открыты для мира. См. полную таблицу.

Выполнение SSH-команд и управление удалёнными файлами

Команды, чтение и запись файлов, списки каталогов — обычная работа на машине, каждый ответ уже разобран.

Мониторинг длительных SSH-задач

Медленная работа отсоединяется и отслеживается, а не ожидается: каждый взгляд сообщает, как далеко она продвинулась.

Поиск по журналам и проверка здоровья сервера

Журналы файлов и контейнеров, а также разовый снимок машины, с ограничением вывода, чтобы хвост не съедал контекстное окно.

Загрузка и скачивание файлов по SSH

Бинарно-безопасные передачи с проверками целостности. Подробности в docs/transfer.md.

Для бинарных файлов и больших файлов используйте ssh_upload / ssh_download — фрагменты base64 и heredoc не являются бинарно-безопасными или атомарными.

Аудит Linux-серверов по SSH

Только чтение и пакетная обработка за один往返. Подробности в docs/audit.md.

Режим совместимости с Windows SSH

Windows автоматически использует режим совместимости. Когда мультиплексирование соединений недоступно, сервер переключается на одно соединение на команду. Те же инструменты остаются доступными через SSH с ключами — без отдельной настройки или реализации, специфичной для Windows.

Защита от разрушительных команд описана в Защита от разрушительных команд для ИИ-агентов.

Настройка SSH MCP-сервера

Сначала запустите пакет из Установка за 30 секунд, затем создайте файл профиля.

Создание профилей SSH-подключений

Поместите его куда угодно — рядом с конфигом вашего агента обычный выбор. Примеры ниже используют ~/.claude/ssh-profiles.json; для других агентов замените каталог (~/.codex/, ~/.qwen/, ~/.config/opencode/):

{
  "profiles": {
    "production": {
      "host": "server.example.com",
      "username": "admin",
      "port": 22,
      "privateKeyPath": "~/.ssh/your_private_key"
    }
  }
}

Выбирайте SSH-профиль явно

У сервера нет профиля по умолчанию: каждый из них — отдельная машина, и команда, отправленная не на ту машину, — это не то, что сообщение об ошибке может исправить задним числом. Спросите без имени, и ответ перечислит имена на выбор:

ssh_exec({ command: "uptime" })
→ No profile specified. Name one explicitly: production

Профиль, который сервер не может использовать для SSH — без host, без username или с mode: "local" — пропускается без жалоб, а поля, которые он не распознаёт, остаются нетронутыми, так что файл можно использовать с другими инструментами. Профиль с битым полем — другой случай: он называется вместе с полем и значением, а его исправные соседи продолжают работать.

Каждый профиль опционально принимает блок pathSecurity, который разрешает или запрещает пути, к которым могут обращаться файловые инструменты — см. docs/security.md.

Профиль, который входит по ключу, но требует sudo на той стороне, принимает sudoPassword — секрет, которым отвечают на sudo, который на многих машинах не является паролем для входа. Храните его в файле секретов, а не здесь.

Хранение SSH-паролей и парольных фраз вне профилей

Предпочитайте ключи. Если пароль или парольная фраза для зашифрованного ключа неизбежны, храните их в отдельном файле секретов, никогда — в самом профиле:

{
  "secretsFile": "~/.config/ssh-mcp/secrets.json",
  "profiles": {
    "production": {
      "host": "server.example.com",
      "username": "admin"
    }
  }
}

Файл секретов привязан к имени профиля — см. secrets.json.example:

{
  "production": { "password": "..." },
  "buildbox": { "sudoPassword": "..." }
}

sudoPassword — это то, чем отвечают на sudo на этой машине. Профиль, входящий по ключу, не имеет пароля для входа, и если они различаются, пароль для входа — неверный ответ; без него используется password.

Файл секретов должен быть доступен для чтения только вам (chmod 600). Относительные пути разрешаются от файла профилей; секреты не попадают в argv и маскируются в журналах. См. безопасность учётных данных.

Настройка Claude Code, Codex и других MCP-клиентов

Выберите используемый клиент и укажите ему тот же файл профилей.

Claude Code

Одна команда; -s user делает сервер доступным в каждом проекте:

claude mcp add ssh -s user \
  -e SSH_PROFILES_FILE="$HOME/.claude/ssh-profiles.json" \
  -- npx -y @hypnosis/ssh-mcp-server

Codex CLI

codex mcp add ssh \
  --env SSH_PROFILES_FILE="$HOME/.codex/ssh-profiles.json" \
  -- npx -y @hypnosis/ssh-mcp-server

opencode

Поместите это в ~/.config/opencode/opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "ssh": {
      "type": "local",
      "command": ["npx", "-y", "@hypnosis/ssh-mcp-server"],
      "enabled": true,
      "environment": {
        "SSH_PROFILES_FILE": "~/.config/opencode/ssh-profiles.json"
      }
    }
  }
}

Qwen Code

Одна команда, как и у остальных:

qwen mcp add ssh \
  -e SSH_PROFILES_FILE="$HOME/.qwen/ssh-profiles.json" \
  npx -y @hypnosis/ssh-mcp-server

Другие MCP-клиенты

Gemini CLI, Hermes, Cline, плагин редактора или ваш собственный агент работают так же. Всё, что им нужно, — это команда для запуска и одна переменная окружения.

Перезапустите ваш MCP-клиент

Перезапустите клиент, затем выполните ssh_monitor({ action: "list" }), чтобы убедиться, что профиль загружен.

Конфигурация SSH MCP server

ПеременнаяЧто она делаетПо умолчанию
SSH_PROFILES_FILEПуть к JSON-файлу профилей — обязательно
SSH_MCP_LOG_LEVELdebug, info, warn, errorinfo
LOG_LEVELЗапасной вариант, используется только когда SSH_MCP_LOG_LEVEL не заданаinfo
SSH_MCP_LOG_TIMESTAMPВременные метки в строках журналаtrue
SSH_MCP_CONTROL_PERSISTСекунды, в течение которых общее соединение остаётся активным после последней команды; 0 закрывает его немедленно600
SSH_MCP_CONTROL_DIRГде хранятся управляющие сокеты~/.ssh/ssh-mcp
SSH_MCP_PROFILES_CACHE_TTLВремя жизни кэша профилей, мс60000
SSH_MCP_PROFILES_WATCHПерезагружать файл профилей при его измененииtrue

Общее соединение намеренно переживает этот процесс: закрытие его при выходе оборвало бы канал, который использует другое окно на той же машине.

Ограничения SSH MCP server

Каждое ограничение подсказывает, как его обойти. Инструмент, который не может что-то сделать, сообщает об этом и называет ssh_exec, который выполняет команды на машине напрямую — неподдерживаемый драйвер журнала, утилита, которой нет на машине, движок, с которым этот сервер не говорит. Вам не нужно знать заранее, где заканчиваются инструменты: отказ сообщает об этом в тот момент, когда это важно.

Три отказа намеренно молчат о оболочке, потому что там это неверный ответ: путь, запрещённый вашим профилем (обход собственного правила — не решение), некорректный вызов (исправление — в самом вызове) и отказ от самого ssh_exec.

  • Отмена: отменённый вызов теперь также останавливает команду на сервере, отправляясь как второй вызов по тому же соединению. Если на сервере нет /proc, команда находится через ps вместо этого. FreeBSD не проверялся: корректное поведение там не гарантируется. Передача файлов и ssh_snapshot вообще не поддерживают отмену.
  • Атомарные записи: BSD и macOS не могут предварительно проверять переименования между файловыми системами.

Дорожная карта SSH MCP server

  • Полный прогон тестов на macOS SSH-хостах

  • Сквозная проверка совместимости на Windows

  • Аудиты нескольких хостов — сравнение состояния нескольких SSH-профилей одним вызовом

  • Импорт профилей из существующего ~/.ssh/config

  • Возобновляемые передачи для больших файлов и нестабильных соединений

  • Хронология удалённых операций — команды, передачи и решения защиты в едином журнале аудита

  • Готовые плейбуки для устранения неполадок SSH

  • Журналы контейнеров без перехода в оболочкуГОТОВО: ssh_log_tail и ssh_log_search принимают имя контейнера, спрашивают docker, куда он пишет, и читают этот файл тем же механизмом, что и любой другой журнал

  • Отказ, оставляющий вас в тупикеГОТОВО: каждое ограничение теперь называет ssh_exec как путь вперёд, так что достижение границы инструмента стоит одного предложения вместо игры в угадайку

  • Ответы, доходящие до моделиГОТОВО: вывод команд, совпавшие строки журналов, имена машин и разделы снимков передаются в полях, а не только в тексте

  • Меньше схем MCP-инструментовГОТОВО: список инструментов стал легче на 10%, а отсоединённая задача теперь показывает последние записанные строки вместо слепого опроса

  • Долгая работа под rootГОТОВО: отсоединённая задача выполняется с sudo и отслеживается как root, а профиль только с ключом отвечает на sudo своим собственным sudoPassword

Разработка и тестирование SSH MCP server

npm install
npm run build           # tsc
npx tsc --noEmit        # types, plus dead declarations
npm run test:unit       # unit tests
npm run lab:up          # start the two test containers
npm run test:live       # live suite against those containers

Живой набор тестов выполняется на реальных контейнерах — один BusyBox, один coreutils — потому что они тихо расходятся во мнениях, а макет соглашается с тем, кто его написал. См. docs/architecture.md для описания структуры.

Нравится SSH MCP Server? ⭐

Если вам нравится инструмент, поставьте ему звезду на GitHub — это поможет большему числу людей узнать о проекте.

Внесите вклад в SSH MCP server

Вопросы и запросы на включение изменений приветствуются на github.com/hypnosis/ssh-mcp-server.

Лицензия

MIT — см. LICENSE.