SSH MCP Server

公式

エージェントからSSH経由でコマンド実行、ファイル移動、ログ検索、マシン監査を行います。

SSH MCPで何ができますか?

  • 安全ガード付きでコマンドを実行 — アシスタントに ssh_exec 経由で単一またはバッチのコマンドを実行させ、破壊的な操作をサーバーに到達する前にブロックする保護機能を備えています。
  • リモートファイルの読み取り、書き込み、一覧表示ssh_file_readssh_file_writessh_file_list を使用してファイルを検査または変更し、アトミック書き込みとオプションの SHA-256 検証をサポートします。
  • ログの検索とサーバーヘルスの確認 — ファイルやコンテナを横断して ssh_log_search または ssh_log_tail をクエリするか、ssh_snapshotssh_audit_baseline で構造化されたヘルススナップショットを取得します。
  • 整合性チェック付きでファイルを転送ssh_uploadssh_download を使用してファイルやディレクトリをアップロードまたはダウンロードし、古いデバイス向けの自動レガシー scp フォールバックを備えています。
  • 長時間実行されるバックグラウンドジョブを管理ssh_exec で遅い操作を切り離し、ssh_job_statusssh_job_outputssh_job_kill で追跡し、切断後も維持されます。

ドキュメント

SSH MCP Server — AIエージェント向けリモートサーバーツール

SSH MCP Server

SSH MCPサーバー — デバッグ、開発、サーバーメンテナンスにおいて、あなたとAIエージェントの時間とトークンを節約するマルチツールです。

SSH経由でコマンドを実行し、ファイルを移動し、ログを読み、マシンを監査します — クラウドVPS、ベアメタルマシン、あるいはクローゼットに眠るBusyBoxルーターでも対応します。

お使いのマシンに既にあるOpenSSHクライアントを使用します:あなたのキー、~/.ssh/config、ジャンプホスト、エージェント転送。バンドルされたものはなく、コンパイル不要、ネイティブバインディングもありません。

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"
      }
    }
  }
}

次に、少なくとも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 を読み取るため、 そのファイルを先に作成すれば、サーバーはマシンがすでに読み込まれた状態で起動します。

要件

npm version Node.js TypeScript MCP SDK

Node.js 18+ と、PATH 上のシステム ssh クライアントが必要です。Windowsでは、キーベースのプロファイルを使用してください。 パスワードおよびパスフレーズプロファイルは現在利用できません。

固定バージョンを希望する場合、オフライン作業、または起動ごとのレジストリチェックを減らしたい場合は: npm install -g @hypnosis/ssh-mcp-server を使用し、npx の代わりにコマンドとして ssh-mcp-server を使用します。

対象読者

  • DevOpsおよびSRE — より迅速な監査、インシデントチェック、日常のサーバー作業を求める方。
  • Vibeコーダーやインディービルダー — AIアシスタントで開発し、自分たちのサーバーで実行する方。
  • システム管理者およびプラットフォームエンジニア — 無制限の生シェルではなく、構造化されたツールを求める方。
  • 専任の運用チームを持たずに独自の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 vs 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テーブル1つの結果の名前付きフィールド1回の呼び出し、名前付きフィールド、ラウンドトリップ削減
不足しているツールが空の出力に見えることがあるunavailable が測定されなかったものを明示推測の減少と悪い修正の削減
ディスク、サービス、エラーを自分で整理問題のシグナルがすでに表面化より高速なデバッグ

完全な ssh_audit_baseline 結果は、いくつかの生コマンド出力よりも長くなることがあります — 当社のラボ測定では約1,077トークン対765トークン。節約は完全な ワークフローから来ており、1つの応答を短くすることからではありません。

実際のトラブルシューティングセッションでは、目的に特化したツールにより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

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つの出力ファイルとグロブを横断する1つの検索トークンとラウンドトリップの削減
権限エラーが消えることがあるfiles_unreadable がすべての見逃したパスを明示誤った「ログはクリーン」という結論なし
出力が有用な上限なしに増えることがあるlimitedtruncated がすべての切り詰めを明示部分的な結果からのより安全な判断

since はサーバーのクロックを使用し、namesOnly: true は一致するパスのみを返し、 ssh_log_tail は複数のログから最後のN行を1回の呼び出しで読み取ります。

安全なリモート設定編集

状況: 稼働中のサーバーで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回のリネームで置き換える半分書き込まれた設定なし
終了コードのみバイト数と検証結果が明示される実際に何が着地したかがわかる
権限はシェルテキスト内にあるsudomodeverify はファイルごとのフィールド予測可能な所有権と引用ミスの削減

verified には3つの正直な結果があります:verified、サーバーにハッシュツールがない場合の unavailable、 検証が要求されなかった場合の skipped。読み取りの場合、ssh_file_read は パスのリストを受け入れます。ssh_file_list はグロブ、再帰、サイズ、モードを処理します。

sudoでバッチSSHコマンドを実行

状況: デプロイの準備はできていますが、トラフィックを移動する前にnginx構文、サービス状態、最近のエラーをすべて チェックする必要があります。1つの失敗したチェックが結合ダンプ内に消えてはいけません。

質問: 「すべての事前チェックが合格しましたか?」

生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

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つの エントリが拒否された場合、他のすべてのエントリは未実行としてマークされ、サーバーには何も送信されません。 各コマンドには独自のstdoutstderrが付与されます。実行されて何も出力しなかったコマンドは空文字列を持ち、実行されなかったコマンドにはそのようなフィールドが一切存在しないため、両者を混同することはありません。コマンドごとに128 KBを超える出力は両端を保持します——表用の先頭とログ用の末尾——間に継ぎ目があり、その量を示し、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得られる利点
ジョブは1つのSSHセッションに結び付けられるリモートジョブには永続的なIDがある安全な切断と再起動
再接続はプロセスとファイルの検索を意味するステータスと終了コードには名前付きの状態がある完了したかどうかの推測が不要
出力の再読み取りは古いテキストを繰り返す出力はバイトオフセットから継続する長時間ジョブでのトークン使用量が減る

ジョブの状態はこのサーバーのメモリではなくリモートディスクに保存されます。ssh_job_statusrunningfinishedlostを区別し、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検証には名前付きの結果がある破損が成功と誤認されない
直接置換は部分的な対象を残すことがある一時ファイルが転送後に所定の位置へ移動される作業ファイルは中断を生き延びる

デバイスにsha256sumopensslもない場合、結果は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 DATABASEdropdbDROP TABLETRUNCATEDELETE FROM
docker volume rmdocker compose down -vdocker rm -f <name>
crontab -r1つのジョブの編集
mkfswipefs -alvremovezfs destroychmod 777
rebootshutdownhaltgit 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-1kill 4871systemctl 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を追加すると許可されます。ガードがバッチ内の1つのエントリを拒否すると、バッチ全体が実行前に停止するため、サーバーが半分実行された操作の後に残されることはありません。

ガードは単一の呼び出し内で機能します。ある呼び出しの削除を次の呼び出しの読み取りと結び付けたり、認識しないツールについて推論したりすることはできません。これはシートベルトであり、ポリシーエンジンではありません: 回復可能な操作はあなたの判断に委ねられます。パス制限と引用規則は**docs/security.md**に文書化されています。

ツール

サーバー操作用の18のSSH MCPツール。完全なパラメータと例は**docs/tools.md**にあります。

ツール機能
ssh_exec破壊的コマンドガードとオプションのデタッチ付きで、1つのコマンドまたはバッチを実行
ssh_file_read1つまたは複数のファイルをテキストまたはバイナリで読み取り
ssh_file_writeアトミックな名前変更とオプションのSHA-256検証付きでファイルを書き込み
ssh_file_listオプションのグロブと再帰付きでディレクトリを一覧表示
ssh_uploadSSH経由でファイルまたはディレクトリをアップロード、完全性チェック付きのバイナリセーフ; ディレクトリは対象を置換またはマージ
ssh_downloadSSH経由でファイルまたはディレクトリをダウンロード、完全性チェック付きのバイナリセーフ
ssh_job_statusバックグラウンドジョブの状態: 実行中、完了、または消失
ssh_job_outputバイトオフセットから蓄積された出力を読み取り
ssh_job_listジョブを一覧表示し、TTLを超えた完了ジョブを掃引
ssh_job_killジョブのプロセスグループ全体にシグナルを送信
ssh_log_tail1つまたは複数のログの最後のN行、グロブ対応; 名前によるコンテナ
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_statussystemctl statusに1ユニット分のjournalctlテールを加えたもの

MCPツールの安全性アノテーション

標準のMCPアノテーションは、どのツールが読み取り専用、破壊的、冪等、またはオープンワールドかをクライアントに伝えます。完全な表を参照してください。

SSHコマンドの実行とリモートファイルの管理

コマンド、ファイルの読み書き、ディレクトリ一覧——マシン上の通常の作業で、各回答は既に解析されています。

長時間実行されるSSHジョブの監視

遅い作業はデタッチされ、待つのではなく追跡されます: 各確認でどこまで進んだかがわかります。

ログの検索とサーバーヘルスの確認

ファイルとコンテナのログ、およびマシンのワンショット画像。出力は上限付きで、テールがコンテキストウィンドウを消費しないようにします。

SSH経由でのファイルのアップロードとダウンロード

完全性チェック付きのバイナリセーフ転送。詳細はdocs/transfer.mdにあります。

バイナリと大きなファイルにはssh_upload / ssh_downloadを使用してください — base64チャンクと heredocはバイナリセーフでもアトミックでもありません。

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 が返す応答です。キーでログインするプロファイルにはログインパスワードを提供するものはなく、両者が異なる場合、ログイン用のパスワードは誤った答えです。それがなければ、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_LEVELdebuginfowarnerrorinfo
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_tailssh_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 1つ、coreutils 1つ)に対して実行されます。この2つは静かに意見が異なり、モックはそれを書いた人に同意するためです。レイアウトについては docs/architecture.md を参照してください。

SSH MCPサーバーが気に入りましたか? ⭐

このツールが気に入ったら、GitHubでスターを付けてください — より多くの人がプロジェクトを発見するのに役立ちます。

SSH MCPサーバーへの貢献

Issueとプルリクエストは github.com/hypnosis/ssh-mcp-server で歓迎します。

ライセンス

MIT — LICENSE を参照してください。