SSH MCP Server について
SSH MCP Server は、AI エージェント向けにリモートサーバーの確認、コマンド実行、ファイル操作、ログ検索、監査の構造化ツールを提供します。システムの OpenSSH クライアントを使い、既存の鍵、SSH 設定、踏み台ホスト、エージェント転送を引き継ぎます。
主な機能
- 01リモートマシンでのコマンド実行
- 02ファイル転送と編集
- 03複数パスのログ検索
- 04構造化されたサーバー稼働状況の取得
- 05システムとサービスの監査
- 06既存の OpenSSH 設定の利用
活用シーン
- 01デプロイ後のサーバー稼働状況確認
- 02複数ログから障害のエラーを検索
- 03リモート設定ファイルの安全な置換
- 04VPS や旧式ネットワーク機器の監査
MCP 対応
MCP クライアントに SSH 経由のコマンド、ファイル、ログ、稼働状況確認、監査ツールを公開します。接続にはローカルの OpenSSH クライアントと設定済みのプロファイルを使います。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
npx -y @hypnosis/ssh-mcp-serverSSH_PROFILES_FILEAbsolute path to the JSON file describing the servers this agent may reach.SSH_MCP_LOG_LEVELLog verbosity: error, warn, info or debug. Defaults to info.ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
18掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| cwd | string | いいえ | 開始ディレクトリ。デタッチされたジョブにも適用されます。ディレクトリに移動できない場合、コマンドは停止し、別の場所では実行されません。 |
| sudo | boolean | いいえ | sudo を使用してコマンドを実行します。デフォルト:false |
| detach | boolean | いいえ | サーバー上でバックグラウンドジョブを実行します。ID を直ちに返し、この呼び出しの終了後もジョブは継続します。タイムアウトは適用されません。ssh_job_status / ssh_job_output で確認し、ssh_job_kill で停止します。コマンドは 1 つのみ指定できます。sudo を使用するとジョブは root として実行され、プロファイルにパスワードが設定されているか sudo がパスワードを必要としない場合、その後の呼び出しもすべて root として実行されます。デフォルト:false |
| command | any | はい | 単一のコマンド、またはコマンドのリスト:["hostname", "whoami"]。各コマンドは個別のシェルで実行され、変数や現在のディレクトリは共有されません。cwd はすべてに適用されます。終了コードが 0 以外でも、リストの実行は停止しません。 |
| profile | string | はい | マシン名。 |
| timeout | number | いいえ | ミリ秒単位。コマンドのリストでは、リスト全体ではなく各コマンドに適用されます。デフォルトは 30000 です。数分かかる作業には、この値を大きくするのではなく detach を使用してください。 |
詳細
SSH MCP Server — AI エージェント向けリモートサーバーツール
|
|
SSH MCP サーバー — デバッグ、開発、サーバー保守にかかる時間とトークンを節約するマルチツールです。 SSH 経由でコマンドを実行し、ファイルを移動し、ログを読み、マシンを監査できます。クラウド VPS、ベアメタルサーバー、あるいはクローゼットに置かれた BusyBox ルーターにも対応します。 |
マシンにすでにある OpenSSH クライアントを使用します。鍵、~/.ssh/config、踏み台ホスト、エージェント転送をそのまま利用します。バンドルされたものも、コンパイルも、ネイティブバインディングも不要です。
Claude Code、Codex CLI、Cline、opencode、Gemini CLI、Qwen Code、Hermes、その他の MCP クライアントで動作します。
インストール · ツール · セットアップ · セキュリティ · ロードマップ · ドキュメント · 変更履歴
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"
}
}
}
}
次に、マシンを 1 台以上登録した ~/.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
プラグインは、SSH_PROFILES_FILE で別の場所が指定されていない限り ~/.claude/ssh-profiles.json を読み込みます。そのため、先にこのファイルを作成しておけば、マシンが読み込まれた状態でサーバーが起動します。
要件
Node.js 18+ と、PATH 上にあるシステムの ssh クライアントが必要です。Windows では、鍵ベースのプロファイルを使用してください。パスワードおよびパスフレーズのプロファイルは、現在利用できません。
バージョンを固定したい場合、オフラインで作業する場合、または起動のたびに行われるレジストリ確認を1回減らしたい場合は、次を実行してください。
npm install -g @hypnosis/ssh-mcp-server を実行し、npx の代わりに ssh-mcp-server をコマンドとして使用します。
対象ユーザー
- 監査、インシデント確認、日常的なサーバー作業を迅速化したい DevOps および SRE。
- AI アシスタントを使ってプロダクトをリリースし、自分のサーバーで運用する Vibe コーダーや個人開発者。
- 制限のない生のシェルではなく、構造化されたツールを使いたい システム管理者やプラットフォームエンジニア。
- 専任の運用チームなしで自前の VPS を運用する 開発者や小規模チーム。
- 現代のプロトコルには対応しなくなったものの、今も役立つハードウェアを所有する ホームラボ、NAS、ルーターのオーナー。
生のシェルではなく SSH MCP サーバーを使う理由
トークン消費を抑え、AI のコストを削減
生のシェルを使うと、AI エージェントにはコマンドの繰り返し、ASCII テーブル、ログの大量出力が次々と渡されます。そのノイズからサーバーの状況を把握するためにトークンを消費し、コストがかさみます。
サーバーのデバッグを迅速化
専用ツールは、定型的な確認をまとめて実行し、ノイズの多い出力を制限して、重要な部分を返します。エージェントはターミナル出力の解釈にかける時間を減らし、より早く修正に取りかかれます。
推測を減らし、AI のミスを抑える
構造化された回答では、検出した内容、測定できなかった内容、切り詰められた内容が示されます。これにより、エージェントが不足情報を幻覚で補う余地が減り、不適切な修正が減って、デプロイが安定し、コードの信頼性も高まります。
SSH 互換性:最新のサーバー、レガシー機器、Windows
既存の OpenSSH 設定をそのまま利用
SSH の実装を同梱せず、ネイティブバインディングも不要で、プラットフォームごとの再ビルドもありません。コマンドはシステムの ssh クライアントを使うため、鍵、~/.ssh/config、ジャンプホスト、エージェント転送を、ターミナルとまったく同じように引き続き利用できます。サポートされている場合は、宛先ごとに1つの多重化接続を共有するため、コマンドごとではなく1回だけ認証すれば済みます。
レガシーサーバー、ルーター、NAS デバイス向けの SSH サポート
最新の scp でルーターにファイルを送ると、次のようになります。
scp app.conf router:/etc/
# scp: subsystem request failed on channel 0
何も壊れていません。最新の scp は新しいプロトコルを使いますが、ルーターはそれに対応していないのです。
ターミナルで作業している場合は、フォーラムのスレッドを読んで、追加のフラグを付けて戻ってくることになります。ここではその必要はありません。転送を試み、拒否を認識すると、代わりに古いプロトコルを使い、そのマシンを記憶するので、次のファイルは最初からその方法で送られます。
古い SSH クライアントや不足しているツールへのフォールバック
古い機器でも行き止まりにはならず、フォールバックが用意されています。最新の機能が使えない場合、可能な範囲でサーバーは従来の方法に切り替えます。
| お使いのマシン | 利用できること |
|---|---|
| 最新のファイル転送には小さすぎるルーターや NAS | ファイルは引き続き転送されます。古いプロトコルが自動的に使われます |
| 10 年前のサーバー | ワークフローは引き続き使えます。ただし、接続を再利用せず、コマンドごとに新しい接続を開きます |
| ファイルのハッシュを計算する方法がない最小構成のイメージ | アップロードは、確認していない一致を主張するのではなく、「確認できませんでした」と報告します |
| ツールがインストールされていないマシン | 回答は「測定されていません」と報告します。「何もありません」と受け取られるゼロ値は返しません |
Model Context Protocol 向けに構築
公式 MCP SDK を基盤とし、全編 TypeScript で実装されています。2500 件以上の単体テストに加え、モックではなく実際のコンテナに対して実行するライブテストスイートも備えています。
生の SSH と SSH MCP サーバー:同じ作業を 2 つの方法で
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 テーブル | 1 つの結果にまとめられた名前付きフィールド | 1 回の呼び出しで、名前付きフィールドを取得し、ラウンドトリップを削減 |
| ツールがないと空の出力に見えることがある | unavailable によって測定できなかった項目が分かる | 推測や不適切な修正が減る |
| ディスク、サービス、エラーを自分で確認する | 問題を示す情報がすでに提示されている | デバッグが速くなる |
完全な ssh_audit_baseline の結果は、いくつかの生のコマンド出力より長くなる場合があります。ラボでの測定では、約 1,077 トークンに対して 765 トークンでした。削減されるのは、1 つの応答を短くすることではなく、ワークフロー全体です。
実際のトラブルシューティングでは、専用ツールを使うことで、個別のコマンド呼び出し 49 回を MCP 呼び出し 4 回に減らせました。呼び出しを追加するたびに、これまでの会話を蓄積した状態で新しいモデルのターンが始まります。プロンプトキャッシュにより入力の繰り返しにかかるコストは削減できますが、新しいコマンドとその出力は引き続きコンテキストを消費します。ラウンドトリップが減ると、セッション全体で使うトークンが減り、分析の繰り返しも抑えられ、答えに早くたどり着けます。
状態の概要だけでなく全体像が必要ですか? 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
3 つ目のコマンドでは問題がないように見えますが、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 | メリット |
|---|---|---|
| 4 回の検索と 4 件の出力 | ファイルとグロブをまとめて検索 | トークンと往復回数を削減 |
| 権限エラーが見過ごされることがある | files_unreadable に読み取れなかったパスがすべて表示される | 「ログに問題はない」という誤った結論を防止 |
| 出力が実用的な上限なく増えることがある | limited と truncated ですべての打ち切り箇所がわかる | 一部の結果に基づく、より安全な判断 |
since はサーバーの時計を基準にし、namesOnly: true を指定すると一致するパスのみが返されます。また、ssh_log_tail を使うと、1 回の呼び出しで複数のログの末尾から 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 | メリット |
|---|---|---|
| コピーの完了前に対象ファイルが切り詰められる | 完全な一時ファイルを、1 回の名前変更で置き換える | 中途半端な設定が残らない |
| 終了コードのみ | バイト数と検証結果が明示される | 実際に書き込まれた内容がわかる |
| 権限がシェルのテキスト内に記述される | sudo、mode、verify をファイルごとに指定する | 所有権を予測しやすくなり、クォートミスを削減 |
verified には、正直な結果が 3 種類あります。verified は検証済み、サーバーにハッシュツールがない場合の unavailable、検証をリクエストしていない場合の skipped です。読み取りには、ssh_file_read で複数のパスを指定できます。ssh_file_list はグロブ、再帰、サイズ、モードに対応しています。
sudo で SSH コマンドを一括実行する
状況: デプロイの準備ができましたが、トラフィックを切り替える前に nginx の構文、サービスの状態、直近のエラーをすべて確認する必要があります。まとめて出力すると、失敗したチェックが見えなくなるおそれがあります。
質問: 「デプロイ前のチェックはすべて成功しましたか?」
生の SSH
$ ssh [email protected] 'sudo nginx -t'
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ ssh [email protected] 'sudo systemctl is-active nginx'
active
$ ssh [email protected] 'sudo tail -5 /var/log/nginx/error.log'
2026/08/20 10:38:54 [error] upstream timed out while reading response header
3 つの接続から、互いに関連のない 3 件の出力が返されます。コマンドを ; でつなぐと、シェルが報告するのは最後の終了コードのみです。&& でつなぐと、最初の失敗後に続くチェックが実行されません。
構造化された 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 | 得られるメリット |
|---|---|---|
| 3 回呼び出し、無関係な出力 | 順序付けされた 1 つのコマンドリスト | 往復回数の削減 |
| シェルコマンドをまとめると、中間ステータスが隠れることがある | 各コマンドに個別の exit_code が保持される | チェックの失敗を見落とさない |
コマンドテキストで sudo と引用符を何度も指定する | sudo はバッチ全体に適用される | 引用符のミスを削減 |
破壊的なコマンドを防ぐチェックでは、最初のコマンドを実行する前にリスト全体を確認します。1 つでも拒否された項目があれば、ほかのすべての項目は未実行としてマークされ、サーバーには何も送信されません。
各コマンドにはそれぞれ stdout と stderr が含まれます。実行されて何も出力されなかったコマンドは空文字列になり、実行されなかったコマンドにはそのフィールド自体がないため、両者を混同することはありません。コマンドごとの出力が 128 KB を超える場合は、両端(テーブル用の先頭部分とログ用の末尾部分)を残し、その間に省略量を示す区切りを入れます。また、clipped_bytes で削除されたバイト数が示されます。切り取りはバイト境界で行い、文字の途中にならないよう直前の境界まで戻るため、省略された回答に置換文字が含まれることはありません。
sudo は端末を使わずにサーバーに接続し、プロファイルのパスワードを標準入力から sudo に渡します。どのシークレットを使うかは、プロファイルで指定されている場合は sudoPassword、そうでなければ password によって決まります。キーでログインするプロファイルにはログインパスワードがなく、ログイン用と sudo 用のパスワードを分けているマシンでは、ログイン用のパスワードを渡しても正しくありません。渡せるものがない場合は、その旨と対処方法が返信されるため、-S や askpass ヘルパーに関する sudo 自身の案内がそのまま残ることはありません。標準入力を自ら読み取るコマンドにはパスワードを渡しません。渡すとデータにパスワードが混ざってしまうためです。
長時間実行する SSH ジョブを実行する
状況: バックアップや移行処理がエージェントのセッションより長く実行されます。接続が切れても、後でその状態、出力、終了コードを確認する必要があります。
質問: 「このジョブは会話が終わっても実行され続けますか?」
生の SSH
$ ssh [email protected] '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 | 得られるメリット |
|---|---|---|
| ジョブが 1 つの SSH セッションに依存する | リモートジョブに永続的な ID が付与される | 安全な切断と再起動 |
| 再接続後にプロセスとファイルを探す必要がある | ステータスと終了コードが名前付きの状態で示される | 完了したかどうかを推測しなくてよい |
| 出力をもう一度読むと古いテキストが繰り返される | 出力はバイトオフセットから続けて取得できる | 長時間のジョブでトークン使用量を削減 |
ジョブの状態はこのサーバーのメモリではなく、リモートディスクに保存されます。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 を使い、
ハッシュを一括で検証します。
AI エージェント向けの破壊的コマンド保護
ガードは 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、dropdb | DROP TABLE、TRUNCATE、DELETE FROM |
docker volume rm、docker compose down -v | docker rm -f <name> |
crontab -r | 1 つのジョブの編集 |
mkfs、wipefs -a、lvremove、zfs destroy | chmod 777 |
reboot、shutdown、halt | git 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
コマンドラインを対象にするパターンは、特別なケースです。パターンを含むコマンド自身にも 一致するため、ターゲットより先にそれを実行しているシェルへシグナルが送られ、 応答が途中で途切れます。このような操作は確認されず、番号で指定するか、パターン自身には 一致しないように1文字を文字クラスで記述する形に書き換える必要があります。
pkill -f relay
# BLOCKED — two ways through:
# kill 4871
# pkill -f '[r]elay' # CONFIRMED-KILL: 4871
結果は、ターゲットが見つかった場合、展開した結果が空だった場合、照会できなかった場合の 3つに分けられます。照会できない場合には、マシン上にエンジンがない、回答が途中で 途切れた、接続に失敗したなどがあります。後者の2つも拒否されます。わからないことは 実行に進む理由にはなりません。
意図的な破壊的コマンドを確認する
恒久的に禁止されるものはありません。レビュー済みのコマンドに # CONFIRMED-DESTRUCTIVE を追加すると、実行が許可されます。バッチ内のいずれかの項目をガードが拒否すると、実行前にバッチ全体が停止するため、操作が中途半端に実行された状態でサーバーが残ることはありません。
ガードが機能するのは単一の呼び出し内です。ある呼び出しでの削除と、次の呼び出しでの読み取りを関連付けたり、認識していないツールについて判断したりすることはできません。これはシートベルトであり、ポリシーエンジンではありません。復旧可能な操作を実行するかどうかは、引き続きお客様の判断です。パスの制限と引用符のルールについては、docs/security.md に記載されています。
ツール
サーバー操作用の SSH MCP ツールが 18 個あります。すべてのパラメーターと使用例は docs/tools.md に記載されています。
| ツール | 機能 |
|---|---|
ssh_exec | 破壊的コマンドのガードとオプションのデタッチを使用して、単一のコマンドまたはバッチを実行します |
ssh_file_read | 1 つ以上のファイルを読み取ります。テキストとバイナリに対応します |
ssh_file_write | アトミックな名前変更と、オプションの SHA-256 検証を使用してファイルを書き込みます |
ssh_file_list | オプションで glob と再帰に対応し、ディレクトリを一覧表示します |
ssh_upload | 整合性チェックに対応したバイナリーセーフな方法で、SSH 経由でファイルまたはディレクトリをアップロードします。ディレクトリは対象を置き換えるか、そこにマージされます |
ssh_download | 整合性チェックに対応したバイナリーセーフな方法で、SSH 経由でファイルまたはディレクトリをダウンロードします |
ssh_job_status | バックグラウンドジョブの状態を示します:実行中、終了、または消失 |
ssh_job_output | バイトオフセットを指定して、蓄積された出力を読み取ります |
ssh_job_list | TTL を過ぎて終了したジョブを一掃しながら、ジョブを一覧表示します |
ssh_job_kill | ジョブのプロセスグループ全体にシグナルを送ります |
ssh_log_tail | 1 つ以上のログの末尾 N 行を取得します。glob に対応し、名前を指定してコンテナーを指定できます |
ssh_log_search | ログ全体を対象にパターン検索するか、コンテナーのログを検索します |
ssh_snapshot | サービス、リソース、Docker、ネットワーク、エラーを対象とした、ワンショットの健全性スナップショットを取得します |
ssh_monitor | トランスポートを制御します:統計情報の取得、再読み込み、テスト、一覧表示、クローズ |
ssh_audit_baseline | システム、ディスク、メモリ、ネットワーク、SSH、サービス、Docker、ファイアウォール、更新 |
ssh_tls_check | ドメインの証明書の有効期限、SAN、チェーン、更新フックを確認します |
ssh_disk_breakdown | ディスクの使用状況を確認します:du の上位 N 件、Docker、journald、キャッシュ |
ssh_service_status | 1 つのユニットについて、systemctl status と journalctl の末尾を取得します |
MCP ツールの安全性アノテーション
標準の MCP アノテーションにより、ツールが読み取り専用、破壊的、冪等、またはオープンワールドのどれに該当するかをクライアントに伝えます。完全な表を参照してください。
SSH コマンドを実行し、リモートファイルを管理する
コマンド、ファイルの読み取りと書き込み、ディレクトリの一覧表示など、マシン上での日常的な作業を行います。各応答はすでに解析済みです。
長時間実行される SSH ジョブを監視する
時間のかかる作業は、完了を待つのではなくデタッチして追跡します。状況を確認するたびに、どこまで進んだかがわかります。
ログを検索し、サーバーの健全性を確認する
ファイルやコンテナーのログと、マシンのワンショットの状況を取得します。出力には上限があるため、ログの末尾を取得してもコンテキストウィンドウを使い切りません。
SSH 経由でファイルをアップロード、ダウンロードする
整合性チェックに対応したバイナリーセーフな転送です。詳しくは docs/transfer.md を参照してください。
バイナリーファイルや大容量ファイルには
ssh_upload/ssh_downloadを使用してください — base64 のチャンクやヒアドキュメントはバイナリーセーフでもアトミックでもありません。
SSH 経由で Linux サーバーを監査する
読み取り専用で、1 回のラウンドトリップにまとめられています。詳しくは docs/audit.md を参照してください。
Windows SSH 互換モード
Windows では互換モードが自動的に使用されます。接続の多重化が利用できない場合、サーバーはコマンドごとに 1 つの接続を使用する方式に切り替わります。同じツールを鍵ベースの SSH 経由で引き続き利用できます。個別のセットアップや Windows 専用の実装は不要です。
破壊的コマンドのガードについては、AI エージェント向け破壊的コマンドの保護で説明しています。
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 に応答するためのパスワードです。キーでログインするプロファイルにはログインパスワードがないため、2つのパスワードが異なる場合、ログインパスワードは正しい応答になりません。sudoPassword がない場合は、password が使用されます。
シークレットファイルは、自分だけが読み取れるようにする必要があります(chmod 600)。相対パスはプロファイルファイルからの相対パスとして解決されます。シークレットは argv に含まれず、ログではマスクされます。認証情報のセキュリティ を参照してください。
Claude Code、Codex、その他の MCP クライアントを設定する
使用するクライアントを選び、同じプロファイルファイルを指定してください。
Claude Code
コマンドは1つです。-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
他のクライアントと同じく、コマンドは1つです。
qwen mcp add ssh \
-e SSH_PROFILES_FILE="$HOME/.qwen/ssh-profiles.json" \
npx -y @hypnosis/ssh-mcp-server
その他の MCP クライアント
Gemini CLI、Hermes、Cline、エディタープラグイン、自作のエージェントも同じように動作します。必要なのは、実行するコマンドと1つの環境変数だけです。
MCP クライアントを再起動する
クライアントを再起動し、ssh_monitor({ action: "list" }) を実行してプロファイルが読み込まれたことを確認してください。
SSH MCP サーバーの設定
| 変数 | 説明 | デフォルト |
|---|---|---|
SSH_PROFILES_FILE | プロファイル JSON のパス — 必須 | — |
SSH_MCP_LOG_LEVEL | debug、info、warn、error | info |
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 | プロファイルキャッシュの TTL(ミリ秒) | 60000 |
SSH_MCP_PROFILES_WATCH | 変更時にプロファイルファイルを再読み込み | true |
共有接続は意図的にこのプロセスの終了後も維持されます。終了時に閉じると、同じマシン上の別のウィンドウが使用している接続まで切断されてしまいます。
SSH MCP サーバーの制限事項
制限事項を見れば、回避方法がわかります。 できないことがあるツールは、その旨を伝え、マシン上でコマンドを直接実行する ssh_exec を案内します。未対応のログドライバー、そのマシンにないユーティリティ、このサーバーが対応していないエンジンなどが該当します。ツールの限界を事前に把握しておく必要はありません。制限に直面した時点で、拒否メッセージが教えてくれます。
シェルを案内しない拒否が意図的に3つあります。シェルを使うのが間違った解決策となる場合です。プロファイルで禁止されたパス(自分で設けたルールを迂回しても解決にはなりません)、不正な呼び出し(呼び出しを修正する必要があります)、そして ssh_exec 自体による拒否です。
- キャンセル: 呼び出しがキャンセルされると、同じ接続を使った2回目の呼び出しが送られ、サーバー上のコマンドも停止するようになりました。サーバーに
/procがない場合は、代わりにpsでコマンドを探します。FreeBSD では検証されていないため、正しく動作する保証はありません。ファイル転送とssh_snapshotはキャンセルに対応していません。 - アトミック書き込み: BSD と macOS では、ファイルシステムをまたぐ名前変更を事前に確認できません。
SSH MCP サーバーのロードマップ
-
macOS の SSH ホストに対する完全なテスト実行
-
Windows でのエンドツーエンドの互換性テスト
-
複数ホストの監査 — 1回の呼び出しで複数の SSH プロファイルの状態を比較
-
既存の
~/.ssh/configからプロファイルをインポート -
大容量ファイルや不安定な接続に対応する再開可能な転送
-
リモート操作のタイムライン — コマンド、転送、ガードの判断を1つの監査記録にまとめる
-
すぐに使える SSH トラブルシューティング用プレイブック
-
シェルに頼らずコンテナログを取得— 完了:ssh_log_tailとssh_log_searchはコンテナ名を受け取り、ログの書き込み先を docker に問い合わせて、ほかのログと同じ仕組みでそのファイルを読み取ります -
行き詰まるだけの拒否— 完了: 制限に達すると、どの拒否メッセージにも回避方法としてssh_execが表示されるようになりました。ツールの限界に達しても、推測を重ねる代わりに1文で解決できます -
モデルに届く回答— 完了: コマンド出力、該当するログ行、マシン名、スナップショットのセクションが、テキストだけでなくフィールドにも含まれるようになりました -
より小さな MCP ツールスキーマ— 完了: ツール一覧が10%軽量化され、デタッチされたジョブでは、状態を盲目的にポーリングする代わりに、最後に出力した行が表示されるようになりました -
root 権限での長時間処理— 完了: デタッチされたジョブはsudoで実行され、root として追跡されます。また、鍵のみのプロファイルでは、sudoに対して専用のsudoPasswordが使われます
SSH MCP サーバーの開発とテスト
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 を1つずつ使用します。この2つは動作がひそかに異なり、モックはそれを書いた人に合わせてしまうためです。構成については docs/architecture.md を参照してください。
SSH MCP Server を気に入ったら ⭐
このツールを気に入ったら、GitHub でスターを付けてください。プロジェクトをより多くの人に知ってもらう助けになります。
SSH MCP サーバーへの貢献
github.com/hypnosis/ssh-mcp-server で、Issue やプルリクエストを歓迎しています。
ライセンス
MIT — LICENSE を参照してください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。