mcpField
S
SSH MCP Server

SSH MCP Server

ローカル
yaw.shクラウド・インフラ61 日前に更新

SSH MCP Server について

SSH MCP Serverは、SSH環境の管理、障害診断、リモート操作をAIエージェントに提供するサーバーです。リポジトリやサーバーへのSSHアクセスを伴う作業向けで、リモート操作の失敗時に診断情報を添え、接続を再利用します。

主な機能

  1. 01SSHエージェントの起動と鍵の読み込み
  2. 02修正コマンド付きの接続診断
  3. 03リモートコマンドとファイル操作
  4. 04複数ホストへのコマンド実行
  5. 05SSH接続の自動再利用

活用シーン

  1. 01Git over SSHの認証失敗を調査
  2. 02リモートサーバーでコマンドを実行
  3. 03SFTPでファイルをアップロード・読み取り
  4. 04複数ホストで同じコマンドを実行

MCP 対応

MCPサーバーとして、SSH診断、リモートコマンド実行、SFTPファイル操作のツールをMCPクライアントに公開します。リモート操作が失敗すると自動で診断を行い、その結果をエラー応答に含めます。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"yawlabs-ssh": {
"command": "npx",
"args": [
"-y",
"@yawlabs/ssh-mcp"
]
}
}
}
起動コマンドnpx -y @yawlabs/ssh-mcp

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
npm@yawlabs/ssh-mcp0.18.0

ツール

21

掲載時のツール一覧 · 2 週間前に更新

パラメーター型必須説明
envobjectいいえこのコマンドに設定する環境変数です。`KEY='value' ...` のプレフィックスとして挿入されます。AcceptEnv の設定にかかわらず、どの sshd でも機能します。VALUES は POSIX の単一引用符で囲まれるため、値内のどのバイトも安全です。KEYS は引用符で囲めません(シェルの代入プレフィックスには裸の名前が必要です)。そのため、各キーは /^[A-Za-z_][A-Za-z0-9_]*$/(POSIX の名前文法)に一致する必要があります。この文法に適合しないキーは拒否され、ホストに何も送信される前に呼び出しが失敗します。コマンドポリシーはプレフィックス付きコマンドに対してチェックされるため、これを設定すると `^` で始まるホワイトリストパターンは一致しなくなります。
hoststringはいSSH ホスト名または IP アドレス
portintegerいいえSSH ポート(デフォルト:22)
commandstringはいリモートホスト上で実行するシェルコマンド(リモートのログインシェルによって解釈されます)
timeoutintegerいいえコマンドのタイムアウト時間(ミリ秒、デフォルト:30000)。コマンド開始前に接続プールの空きスロットを待つ最長時間でもあります(SSH_MCP_MAX_POOL_SIZE、デフォルト 100)。待機時間とコマンド実行時間には、それぞれ個別の上限が適用されます。
passwordstringいいえSSH パスワード。鍵ベースの認証(privateKeyPath または ssh-agent)を強く推奨します。パスワードは MCP プロトコルフレーム内を平文で通過し、トランスポートやホストプロセスによってログに記録される可能性があります。
usernamestringいいえSSH ユーザー名(デフォルト:現在のユーザー)
privateKeyPathstringいいえSSH 秘密鍵のパス

詳細

@yawlabs/ssh-mcp

Yaw MCP に追加

ワンクリックでローカルの Yaw MCP 設定に追加され、すべての Yaw Terminal セッションで利用できるようになります。または、以下の手順で手動インストールしてください。

npm バージョン ライセンス:MIT

AI ツールで SSH を使えるようにします。 SSH 環境を管理し、問題を診断して修正し、エージェントにあらゆるリモート環境へのアクセスを提供する MCP サーバーです。

Yaw Labs が開発・保守しています。

問題点

AI CLI ツールは、SSH が頻繁に壊れるサブプロセス内で実行されます。エージェントが git pull を実行すると、Permission denied (publickey) が発生します。サーバーに SSH 接続しようとすると、エージェントのソケットが古くなっています。デプロイしようとすると、インスタンスが再作成されたためホストキーが変わっています。そのたびに AI は原因がわからず、行き詰まります。

この問題は、SSH キーが必要なあらゆる場面で発生します。

  • Git — クローン、プル、プッシュ、フェッチ、サブモジュール、LFS
  • パッケージマネージャー — プライベートリポジトリからの npm install、pip install、go get、cargo、composer
  • サーバーアクセス — SSH、SCP、SFTP、rsync
  • トンネリング — データベースへのポートフォワーディング、SOCKS プロキシ
  • デプロイ — Ansible、Terraform、Capistrano、デプロイスクリプト
  • クラウド — AWS EC2、GCP、Azure、DigitalOcean、あらゆる VPS

ssh-mcp がこの問題を解決します。SSH エージェントを管理し、キーを読み込み、実行可能な修正コマンドとともに障害を診断し、リモート操作を提供します。すべて、AI エージェントが呼び出せる MCP ツールとして利用できます。

クイックスタート

MCP クライアントの設定に追加します。

{
  "mcpServers": {
    "ssh": {
      "command": "npx",
      "args": ["-y", "@yawlabs/ssh-mcp@latest"]
    }
  }
}

Windows では、Node 20 以降は .cmd ファイルを直接起動できないため、cmd /c で囲みます。

{
  "mcpServers": {
    "ssh": {
      "command": "cmd",
      "args": ["/c", "npx", "-y", "@yawlabs/ssh-mcp@latest"]
    }
  }
}

@latest タグを指定すると、npx は起動のたびにレジストリから最新バージョンを再取得するため、各 MCP セッションで公開済みの最新バージョンが使用されます。バージョンを固定したい場合は、グローバルにインストールすることもできます(自動更新はありません)。

npm install -g @yawlabs/ssh-mcp
# then in client config: "command": "ssh-mcp"

ツール

SSH 環境管理

ローカルの SSH 設定を修正し、Git、デプロイ、トンネルなど、ほかのすべての動作を正常に保つツールです。

ツール説明
ssh_agent_ensuressh-agent が実行中であることを確認します。必要に応じて起動し、セッション用の環境変数を設定します。
ssh_key_list~/.ssh/ にあるすべての SSH キーを、種類、フィンガープリント、エージェントの状態とともに一覧表示します。
ssh_key_load実行中のエージェントにキーを読み込みます。先にエージェントが起動していることを確認します。
ssh_config_lookupホストの有効な SSH 設定(ホスト名、ユーザー、ポート、プロキシ、ID ファイル)を解決します。
ssh_known_hosts_fix古くなったホストキーを削除して再スキャンします。「host key verification failed」エラーを修正します。
ssh_git_checkGitHub、GitLab、Bitbucket などへの Git-over-SSH 認証をテストします。
ssh_test接続時間と、対処に役立つエラーの詳細を表示する簡易接続テストです。
診断
ツール説明
ssh_diagnoseSSH 環境全体を診断します。エージェント、キー、設定、known_hosts、接続を確認します。各障害に対して、正確な修正コマンドを返します。
リモート操作
ツール説明
ssh_execリモートホストでコマンドを実行します。最大3つのコンテンツブロックを返します。順に、ステータス:[exit code: N](チャネルがシグナルのみで閉じた場合は code: -1 とともに [signal: NAME]、ストリームが 10 MB の上限に達した場合はフラグも表示)、[stderr]、[stdout] です。ステータスを先頭に配置しているため、結果の長さを制限するプロキシ(Yaw MCP のデフォルトでは約 100 KB)が長い結果を切り詰めても、終了コードが失われません。オプションの env パラメーターを指定すると、呼び出しごとの環境変数を設定できます(POSIX 対応のプレフィックスを使うため、sshd の AcceptEnv の設定にかかわらず動作します)。設定されている場合は、コマンドポリシーの対象となります。
ssh_read_fileSFTP 経由でリモートホスト上のファイルを読み込みます(ファイル全体、最大 10 MB)。オプションの offset と length(バイト数)を指定すると、1ページ分を読み込み、テキストの前に [bytes A-B of SIZE; next offset: N] ステータスブロックを返します。大きなファイルや、結果の長さを制限するプロキシ経由の場合に使用してください。
ssh_write_fileSFTP 経由でリモートホスト上のファイルにコンテンツを書き込みます。
ssh_uploadSFTP 経由でローカルファイルをリモートホストにアップロードします。
ssh_downloadリモートホスト上のファイルをローカルファイルシステムにダウンロードします。
ssh_lsリモートホスト上のディレクトリ内のファイルを一覧表示します。
ssh_statファイルまたはディレクトリのメタデータ(サイズ、8進数表記のモード、uid/gid、mtime/atime、isFile/isDirectory/isSymbolicLink)を取得します。ls -la の解析の代わりに使用してください。
ssh_mkdirSFTP 経由でディレクトリを作成します。mkdir -p と同じ動作にするには、recursive: true を設定します。他の SFTP ツールとは異なり、パスには相対パスを指定できます。SFTP の作業ディレクトリ(通常はリモートユーザーのホームディレクトリ)を基準に解決されます。~ は展開されません。SFTP にはシェルがありません。
ssh_deleteSFTP 経由でファイルまたは空のディレクトリを削除します。パス自体の種類(lstat)に基づいて unlink と rmdir を自動的に使い分けるため、シンボリックリンクは必ずリンク自体が削除され、リンク先をたどることはありません。これは、リンク先が存在しない場合やディレクトリを指している場合も同様です。ディレクトリの再帰的な削除には意図的に対応していません。必要な場合は ssh_exec rm -rf を使用してください。
高レベル操作

エージェントが ssh_exec で作成する一般的なパターンをラップするツールです。より高速で、エラーも起きにくくなります。

ツール説明
ssh_multi_exec複数のホストでコマンドを並列実行します。ホストごとに結果を返します。オプションの env パラメーターを指定すると、呼び出しごとの環境変数を設定できます(ssh_exec と同じ POSIX 対応のプレフィックスを使用し、一度だけ適用してすべてのホストに送信します)。設定されている場合は、コマンドポリシーの対象となります(ホストへの振り分け前に、環境変数のプレフィックスが付いたコマンドに対してポリシーを一度だけ確認します)。
ssh_find構造化パラメーター(name、type、size、depth、newer。参照パスよりも最近変更されたファイルに一致)を使って、リモートでファイルを検索します。
ssh_tailファイルの末尾から N 行を読み込みます。grep パターンによる絞り込みもできます。
ssh_service_statussystemd サービスのステータス(active、PID、稼働時間、説明)を確認します。isError フラグが設定されるのは、ユニットが見つからない、または照会できない場合のみです。既存のユニットが意図的に停止されている場合には設定されません。
自動診断

リモート操作が失敗すると、ssh-mcp は診断を自動的に実行し、その結果をエラーレスポンスに含めます。エージェントが別途 ssh_diagnose を呼び出す必要はありません。問題の内容と解決方法がエラーメッセージに直接表示されます。

接続プール

リモート操作では SSH 接続が自動的に再利用されます。エージェントが同じホストに複数回呼び出しを行う場合、最初の呼び出しで接続が開かれ、以降の呼び出しではその接続が再利用されます。接続は最後に使用してから 60 秒間維持され、その後自動的に閉じられます。

プールの上限はデフォルトで 100 接続で、接続確立中の接続も上限に含まれます。上限に達すると、プールは空いている接続を閉じて空きを確保します。すべての枠が使用中または接続確立中の場合、ツールの呼び出しは失敗する代わりに空きを待ちます。

  • 単一ホストのリモートツール(ssh_exec、SFTP ツール、ssh_find、ssh_tail、ssh_service_status)は、timeout まで待機します(timeout パラメーターのない SFTP ツールは 30 秒)。最初に空いた枠を使えるよう次の順番で待機するため、広範囲にわたる ssh_multi_exec の実行中に到着した ssh_exec は、実行全体を拒否されるのではなく、ホスト間で実行されます。その時間内に枠を確保できなかった場合にのみ、Connection pool is full を報告します。
  • ssh_multi_exec は、一度に最大 SSH_MCP_MAX_POOL_SIZE 個のホスト(デフォルトは 100)を実行し、枠が空くたびに残りのホストを処理します。そのため、上限を引き上げると並列数も増えます。timeout はホストごとに適用され、呼び出し全体の制限時間ではありません。プールが満杯の場合は空きを待ち、自身のホストがどれも枠を確保しておらず、どのホストも開始または完了しないまま timeout 全体が経過した場合にのみ諦めます。その時点で待機中のホスト(並列枠 1 つにつき最大 1 台)と、その後ろにキューイングされているすべてのホストは Connection pool is full を報告します。キュー内のホストは実行されません。

Connection pool is full が発生するのは、別の呼び出し(長時間実行中の ssh_exec、同時実行中の ssh_multi_exec)によって、すべての枠が timeout 全体にわたって占有された場合に限られます。プールを独占して実行する上限を超えたファンアウトでは、実行時間の長さにかかわらず発生しません。このエラーが発生した場合は、それらの呼び出し(ssh_multi_exec の場合は、エラーを報告したホスト)の終了後に再実行するか、サーバーの環境変数で SSH_MCP_MAX_POOL_SIZE=<n> を設定して上限を引き上げてください。エラーには変数名が表示されます。

ライブラリの acquire() / withConnection() は、デフォルトでは待機しません。{ waitForCapacityMs } を渡さない限り、ただちに PoolFullError(code: "ERR_SSH_MCP_POOL_FULL"、isPoolFullError())で拒否されます。詳しくはプログラムからの使用を参照してください。

SSH config のサポート

すべての接続で ~/.ssh/config の設定が尊重されます。ホストエイリアス、カスタムポート、ユーザー名、ID ファイル、ProxyJump 設定が自動的に使用されます。SSH config で Host myserver を設定している場合は、host: "myserver" を渡すだけで、ssh-mcp がすべてを解決します。

ProxyJump/踏み台ホストは自動的にサポートされます。ホストの SSH config に ProxyJump bastion が設定されている場合、ssh-mcp は踏み台を経由して透過的に接続します。プロキシの連鎖にも対応しています。

ホストキーの検証

すべてのリモート操作では、サーバーのホストキーを ~/.ssh/known_hosts と照合して検証します。

  • 登録済みホストで、キーが一致する場合 — 許可します。
  • 登録済みホストで、キーが変更されている場合 — 拒否します(MITM 対策)。拒否メッセージでは、実際にキーが一致しない場合と、「サーバーが提示したキータイプが known_hosts のエントリーでカバーされていない場合」を区別するため、ed25519 の行がないだけで攻撃と誤解されることはありません。
  • 未登録のホスト — 厳格モード(SSH_MCP_STRICT_HOSTKEYS=1)が有効でない限り、許可します。

最後のケースは、TOFU ではなく常に信頼する動作です。 本来の初回利用時信頼(trust-on-first-use)は、最初に確認したキーを記録し、その後の変更を拒否します。接続処理では known_hosts に書き込まないため、接続時に副作用としてエントリーを追加するツールはありません。つまり、接続してもキーは記録されず、known_hosts にないホストへの接続は 毎回「初回」利用として許可されます。最後の呼び出し以降に攻撃者がキーを差し替えた場合も同様です。不一致の保護が適用されるのは、あなた自身、ssh-keyscan、またはファイルに書き込む唯一のツールである ssh_known_hosts_fix を使って、別途 known_hosts に登録したホストだけです。今後の変更を検出できるよう、ssh_known_hosts_fix を明示的に呼び出してエントリーを追加してください。

より厳格な環境では、SSH_MCP_STRICT_HOSTKEYS=1(大文字小文字を区別せず true も可)を設定して、未登録のホストを拒否します。まず ssh_known_hosts_fix で明示的に登録してください。従来の名前である SSH_MCP_STRICT_HOST_KEY も引き続き使用でき、同じ値を受け付けますが、新しい名前を推奨します。Yaw MCP の doctor などの認証情報スキャナーは、名前に _KEY の部分を含む変数を秘密情報として扱い、従来の名前を平文の認証情報として警告するためです。

診断ツール(ssh_test、ssh_diagnose)では、プローブコマンドに StrictHostKeyChecking=no を使用します。これらのプローブで実行するのは echo SSH_OK だけで、認証情報やデータが送信されることはないため、接続テストではこの緩和設定を安全に使用できます。実際の操作は常に hostVerifier を経由します。

コマンドポリシー

ssh_exec と ssh_multi_exec は、エージェントから自由形式のシェルコマンドを受け付けます。セキュリティを重視するデプロイでは、カンマ区切りの正規表現パターンを受け付ける次の 2 つの環境変数を使って、実行可能なコマンドを制限できます。

  • SSH_MCP_COMMAND_WHITELIST — 設定されている場合、コマンドは少なくとも 1 つのパターンに一致する必要があります。一致しない場合はブロックされます。
  • SSH_MCP_COMMAND_BLACKLIST — 設定されている場合、コマンドはいずれのパターンにも一致してはなりません。一致した場合はブロックされます。

両方が設定されている場合、コマンドは両方のチェックに合格する必要があります(最初にホワイトリスト、次にブラックリストを確認します)。どちらも設定されていない場合(デフォルト)は、すべてのコマンドが許可されます。

パターンは JavaScript の正規表現です。完全一致にするには ^ と $ を使用してください。指定しない場合、パターンは部分文字列として照合されます。区切り文字はカンマのため、パターン内のカンマを文字として扱うには \x2c または文字クラスを使用してください。

# Read-only allowlist: only ls / df / cat / find / tail
SSH_MCP_COMMAND_WHITELIST="^ls( .*)?,^df( .*)?,^cat ,^find ,^tail "

# Block destructive ops even if your agent goes off-script
SSH_MCP_COMMAND_BLACKLIST="^rm ,^shutdown,^reboot,^mkfs,^dd if=,>\s*/dev/"

ブロックされたコマンドでは、どのパターン(または環境変数)によって呼び出しが拒否されたかを示す明確なエラーが返されるため、エージェントは推測するのではなく対応できます。ポリシーは SSH 接続が確立される前に適用されるため、ブロックされたコマンドではリモートプロセスは起動されません。

対象範囲:ポリシーの適用対象は ssh_exec と ssh_multi_exec のみです

その他のツールはすべてチェックされずに実行されます。これには、まったく異なる2つのケースがあります。

構造化された読み取りツールはすべて対象外ですが、その理由は2通りあります。リモートにアクセスする方法がそれぞれ異なるためです。

ssh_find、ssh_tail、ssh_service_status はシェルコマンド(find、tail、systemctl)を組み立てますが、型指定されたパラメーターから作成し、値を挿入する際はすべてシェルで引用符付けします。エージェントの自由形式の入力から作成することはありません。これらを対象外にしているのは、使いやすさのためです。厳密な ^ls ホワイトリストを使うと、これらのツールを動作させるだけでも ^find 、^tail 、^systemctl を許可する必要があり、厳密なホワイトリストの意味がなくなってしまいます。

ssh_ls、ssh_stat、ssh_read_file、ssh_download はコマンドを一切作成しません。これらは純粋な SFTP(readdir、stat、readFile、fastGet)を使うため、以下の状態を変更する SFTP ツールとまったく同様に、正規表現で照合するコマンド文字列がありません。つまり、たとえ制限したくても、これらの環境変数では制御できません。リモートの状態を変更しないため、問題点としてではなく読み取りツールと一緒に分類しています。ただし、1つ注意点があります。ssh_download はリモートでは状態を変更しませんが、指定されたローカルパスには書き込みます。この動作も、これらの環境変数では制限できません。

リモートの状態を変更する SFTP ツールもチェックされませんが、これは利便性のための判断ではなく、実際の抜け穴です。 ssh_write_file、ssh_upload、ssh_mkdir、ssh_delete はシェルコマンド文字列を作成しないため、コマンド形式の正規表現で照合する対象がありません。具体的には、SSH_MCP_COMMAND_BLACKLIST="^rm " を指定しても ssh_delete は停止されず、SSH_MCP_COMMAND_WHITELIST="^ls " を指定しても ssh_write_file は停止されません。この問題を解決するには、別のパス・ポリシーの仕組みが必要ですが、このサーバーには意図的に用意されていません。リモートの状態変更を防ぐ必要がある場合は、MCP クライアントのツール許可リストからこの4つのツールを除外する(または、これらのツールを制限するクライアントを実行する)必要があります。この2つの環境変数では制限できません。

env パラメーターとのポリシーの相互作用(ssh_exec、ssh_multi_exec)

ssh_exec または ssh_multi_exec を env: { KEY: "value" } とともに呼び出すと、値はコマンドの前に KEY='value' ... というシェルプレフィックスとして挿入されます(ツールの説明を参照)。ポリシーは command 引数だけでなく、プレフィックスを含む完全なコマンドに対してチェックされます。ssh_multi_exec の場合、このチェックは複数の接続先に展開する前に1回行われます。プロトコル層では、これがより安全な順序です。ただし、そのためホワイトリストのパターンではプレフィックスを考慮し、部分文字列の一致ではなくアンカー付きにする必要があります。

# WRONG -- blocks any ssh_exec call that uses `env`, because the final command
# starts with `KEY='value' ` and never matches `^ls`.
SSH_MCP_COMMAND_WHITELIST="^ls "

# RIGHT -- allow zero or more `KEY='value' ` prefixes before the real command.
SSH_MCP_COMMAND_WHITELIST="^([A-Za-z_][A-Za-z0-9_]*='[^']*' )*ls( |$)"

拒否されたときに理由を自分で調べる必要はありません。env を使った呼び出しがホワイトリストによってブロックされると、エラーにはプレフィックスによって ^ アンカーが一致しなくなったことを説明する注記が追加され、上記の柔軟なパターンが提案されます。

敵対的なエージェントを懸念している場合は、ls のような部分文字列一致のパターンは避けてください。エージェントが env: { ATTACK: " ls " } を渡すと、最終的なコマンドは ATTACK=' ls ' rm -rf / となり、部分文字列 ls に一致してホワイトリストを回避できる可能性があります。上記の形式のアンカー付きパターンには、この弱点がありません。引用符で囲まれた環境変数の値の中ではなく、環境変数のプレフィックスの後に実際のコマンド名が続くことを要求するためです。

ブラックリストにも同じ注意が必要です。^rm は rm 単体の呼び出しをブロックしますが、FOO='bar' rm はブロックしません。同じ環境変数プレフィックスに対応したアンカーを使ってください。

SSH_MCP_COMMAND_BLACKLIST="^([A-Za-z_][A-Za-z0-9_]*='[^']*' )*rm( |$)"

エージェントの env の値をまったく信用できない場合は、クライアント設定で env を使わず、すべてを command 文字列に直接渡すのが、最も簡単な緩和策です。

Windows のサポート

Windows では、SSH_AUTH_SOCK が設定されていない場合、ssh-mcp は OpenSSH Authentication Agent の名前付きパイプ \\.\pipe\openssh-ssh-agent を自動的に使用します。SSH_AUTH_SOCK は不要です。OpenSSH agent サービスが実行中であることだけ確認してください。

oam ランタイムでエージェント認証を使用するには、oam 0.18.0 以降が必要です。これは、net が名前付きパイプまたは Unix ソケットに接続できるようになった最初の oam バージョンであり、ssh2 がエージェントに接続する方法です。また、ランチャーはそれより古いバージョンでは起動しません。エージェントを利用するリモート操作は、これまで Node でのみエンドツーエンドで検証されています。oam ではハンドシェイクと ssh_config_lookup は検証済みですが、エージェントのみを使った ssh_exec は検証されていません。oam でエージェント認証に失敗する場合は、SSH_MCP_RUNTIME=node を設定してランタイムが原因かどうかを切り分けてください。

ssh_agent_ensure と ssh_diagnose はこのパイプを確認し、サービスが停止しているかどうかを知らせます。リモート操作では確認しません。パイプがあることを前提とし、エージェントが起動していない場合は接続自体が失敗します。そのため、エージェントサービスが停止していると、診断ツールを実行するまでは「エージェントが起動していません」というエラーではなく、認証エラーとして表示されます。

認証

すべてのリモート操作で、次の接続パラメーターを指定できます。

パラメーター説明デフォルト
hostSSH ホスト名または IP アドレス(必須)—
portSSH ポートSSH 設定または 22
usernameSSH ユーザー名SSH 設定または現在のユーザー
privateKeyPathSSH 秘密鍵のパス自動検出
passwordSSH パスワード(鍵の使用を推奨)—

認証情報の解決方法。 明示的に指定された認証情報があれば、それが優先され、ほかの認証情報は提示されません。どちらも指定されていない場合、ssh-mcp は ssh-agent とディスク上の鍵を 1 つ同時に提示します。これは OpenSSH クライアントと同じ動作で、認証のやり取りの中でサーバーに選択させます。ステップ 2 以降は、厳密な先着順の連鎖ではありません。

  1. 明示的な privateKeyPath — これだけが使用されます。エージェントは提示されず、ほかの鍵も読み込まれません。
  2. 明示的な password — 上記と同様に、これだけが使用されます。
  3. どちらも指定されていない場合 — 次の両方が同じ接続で設定されます。
    • ssh-agent — SSH_AUTH_SOCK、または Windows では \\.\pipe\openssh-ssh-agent 名前付きパイプです。Windows のパイプは無条件であるものとして扱われます。ssh-mcp は OpenSSH Authentication Agent サービスが実際に実行中かどうかを確認しません。
    • ディスク上の鍵 1 つ — ssh -G <host> の identityfile リスト内で、最初に読み取り可能なパスです。OpenSSH は、IdentityFile 行がないホストを含め、すべてのホストに対してこのリストを出力します(デフォルトは ~/.ssh/id_rsa、id_ecdsa、id_ecdsa_sk、id_ed25519、id_ed25519_sk です)。したがって、これは通常の動作であり、アイデンティティを設定したホスト専用の動作ではありません。ssh-mcp 独自の組み込みリスト(~/.ssh/id_ed25519、id_rsa、id_ecdsa)は、SSH クライアントがインストールされていないなどの理由で ssh -G を実行できない場合にのみ使われるフォールバックです。

ステップ 3 について、知っておくべき点が 2 つあります。

  • ディスク上の鍵は 1 つだけ提示されます — 最初に見つかった候補だけです。ssh-mcp は ssh のようにアイデンティティのリスト全体を順に確認しないため、最初に読み取り可能な鍵が間違っている場合は、エージェントの鍵に頼ることになります。特定の鍵を強制的に使うには、privateKeyPath を指定してください。
  • エージェントが設定されている場合、暗号化されたディスク上の鍵はスキップされ、次の候補に進みます。基盤となる ssh2 ライブラリは privateKey をすぐに解析し、暗号化された鍵に対して「パスフレーズが指定されていません」というエラーを返します。そのため、暗号化された鍵をディスク上に置き、復号済みのコピーをエージェントに読み込ませる一般的な構成が機能しなくなります。エージェントが設定されていない場合は、暗号化の有無にかかわらず、最初に見つかった鍵が読み込まれ、ssh2 自体がパスフレーズのエラーを返します。上記の Windows のパイプは無条件であるものとして扱われるため、Windows では常にスキップが適用されます。

ワークフローの例

エージェントで git pull できない
Agent calls ssh_git_check → "Permission denied. Your SSH key is not registered with github.com."
Agent calls ssh_key_list → finds id_ed25519 exists but is not loaded
Agent calls ssh_key_load("~/.ssh/id_ed25519") → "Key loaded"
Agent calls ssh_git_check → "Git SSH authentication to github.com succeeded as username"
Agent runs git pull → works
インスタンスの再作成後にホスト鍵が変更された
Agent calls ssh_exec on server → error: "Host key verification failed"
  (auto-diagnostics included in error: "Fix with ssh_known_hosts_fix")
Agent calls ssh_known_hosts_fix("my-server") → "Host key refreshed"
Agent calls ssh_exec → works
新しいサーバーへの初回接続
Agent calls ssh_test("new-server") → "Connection refused at new-server:22"
Agent calls ssh_diagnose("new-server") → full report showing agent running, keys loaded, but host unreachable
Agent reports: "SSH server isn't running on new-server or port 22 is blocked"

プログラムからの使用

import { connect, exec, diagnose, ensureAgent, listSshKeys, checkGitSsh, ConnectionPool, isPoolFullError } from '@yawlabs/ssh-mcp';

// Fix SSH environment
const agent = ensureAgent();
console.log(agent.message);

// Check git access
const git = checkGitSsh('github.com');
console.log(git.message);

// List available keys
const keys = listSshKeys();
for (const key of keys) {
  console.log(`${key.name} (${key.type}) - ${key.loadedInAgent ? 'loaded' : 'not loaded'}`);
}

// Run a remote command (one-off)
const client = await connect({ host: 'my-server', username: 'deploy' });
const result = await exec(client, 'uptime');
console.log(result.stdout);
client.end();

// Run multiple commands with connection pooling
const pool = new ConnectionPool();
await pool.withConnection({ host: 'my-server' }, async (client) => {
  const r1 = await exec(client, 'uptime');
  console.log(r1.stdout);
});
// Connection stays open for 60s — next call reuses it
await pool.withConnection({ host: 'my-server' }, async (client) => {
  const r2 = await exec(client, 'df -h');
  console.log(r2.stdout);
});
// A full pool rejects at once with a PoolFullError; pass a budget to wait for a slot
// instead, the way the tools do, and match the rejection on its code, not its text
try {
  await pool.withConnection(
    { host: 'other-server' },
    async (client) => {
      const r3 = await exec(client, 'uptime');
      console.log(r3.stdout);
    },
    { waitForCapacityMs: 30_000 },
  );
} catch (err) {
  if (!isPoolFullError(err)) throw err;
  console.log('no slot within 30s:', err.message);
}
pool.drain(); // close all connections when done

// Diagnose issues
const report = diagnose('my-server');
console.log(report.overall); // "ok" | "warning" | "error"
for (const check of report.checks) {
  console.log(`[${check.status}] ${check.name}: ${check.message}`);
}

ランタイムの選択

ssh-mcp コマンド(npx @yawlabs/ssh-mcp が実行するもの)は、小さなランチャーです。oam ランタイムを優先し、利用できない場合は Node でサーバーを実行するため、oam は必須ではありません。最低要件は**最新の oam リリース(現在は 0.18.0)**であり、それより古い oam ではサーバーを実行しません。

選択を制御する環境変数は 2 つあります。MCP クライアントの env ブロックで設定してください。

  • SSH_MCP_RUNTIME — 使用するランタイムです。大文字と小文字は区別されません。oam または node 以外の値はすべて auto として扱われます。
    • auto(デフォルト)— 見つかった中で最も新しい、0.18.0 以降の oam を使用します。見つからない場合は Node で実行します。
    • oam — oam を必須とします。利用可能な oam が見つからない場合、ランチャーは見つかったものと、それぞれを使用しなかった理由を stderr に出力して、サーバーを起動せずにステータス 1 で終了します。
    • node — 常に Node を使用します。ランチャーは oam を探さず、OAM_BIN も無視します。
  • OAM_BIN — 検出より優先して使用する oam バイナリへのパスです。0.18.0 以降の場合に使用されます。存在しない、古い、実行できない、または oam 独自の oam x.y.z バナーを含む --version の応答がない場合(Node の v22.x.y など、別のプログラムのバージョンは該当しません)、ランチャーはその旨を stderr に出力して検出を続行します。

検出では、まず $OAM_INSTALL_DIR(設定されている場合。oam インストーラーのインストール先)、次に %LOCALAPPDATA%\oam\bin(Windows のみ)、次に ~/.oam/bin、その後 PATH 上のすべてのディレクトリを調べます。見つかったすべての oam バイナリにバージョンを問い合わせ、oam 独自の oam x.y.z バナーを含む応答だけを対象として、0.18.0 以降で最も新しいものを使用します。同じバージョンの場合は先に見つかったものを優先するため、インストール済みのコピーは PATH 上のものより優先されます。Windows では oam.exe のみが対象です。PATH 上の oam.cmd/oam.bat シムは実行されず、使用可能な oam が見つからない場合は stderr にその名前が出力されます。

デフォルトでは、使用できない oam はエラーになりません。ランチャーが Node によって起動された場合(npx またはグローバルインストールを通じて、MCP クライアントが直接実行)、auto モードは Node にフォールバックします。見つかるものがなかった場合は何も出力せず、見つかった oam を使用しなかった場合は、それぞれの oam(および OAM_BIN)の名前と理由(0.18.0 より古い、実行できない、またはシム)を stderr に出力します。選択した oam の起動に失敗した場合も、その名前を stderr に出力します。古い oam がランチャーを起動した場合は、Node への引き継ぎを必ず stderr に出力します(以下を参照)。SSH_MCP_RUNTIME=oam の場合、これらのケースはいずれもステータス 1 で終了します。

ランチャー自体が oam によって起動された場合(oam run <path>/bin/ssh-mcp.mjs。最近の oam がインストールされている環境で、Yaw MCP が npx @yawlabs/ssh-mcp エントリを起動する方法です):

  • oam 0.18.0 以降では、その oam 上で同じプロセス内でサーバーを実行します。検出や子プロセスの起動は行われず、OAM_BIN は読み込まれません。また、SSH_MCP_RUNTIME=oam の要件も満たします。
  • 古い oam では、その oam 上でサーバーを実行しません。見つかった中で最も新しい使用可能な oam にサーバーを引き継ぎます。それがない場合は PATH 上の Node に引き継ぎ、どちらもない場合はステータス 1 で終了します(SSH_MCP_RUNTIME=oam の場合は Node に引き継がず、ステータス 1 で終了します)。
  • SSH_MCP_RUNTIME=node の場合は、oam のバージョンにかかわらず、PATH 上の Node にサーバーを引き継ぎます。

oam ホストが子プロセスを起動する場合(上記の引き継ぎ)、ランチャーは子プロセスが継承する NODE_OPTIONS から --permission および --allow-* フラグを削除します。oam 0.18.0 は各子プロセスの NODE_OPTIONS に独自の権限フラグを追加しますが、Node はその一部を受け付けません(--allow-net is not allowed in NODE_OPTIONS、終了コード 9)。このサーバーはサンドボックス化されていないため、引き継ぐフラグはありません。--permission 付きで起動した oam は spawn() 内でフラグを再追加します。ランチャーではそれを取り消せないため、その方法で起動しないでください。

たとえば、常に Node で実行するには、次のようにします。

{
  "mcpServers": {
    "ssh": {
      "command": "npx",
      "args": ["-y", "@yawlabs/ssh-mcp@latest"],
      "env": { "SSH_MCP_RUNTIME": "node" }
    }
  }
}

Windows では、クイックスタートにある cmd /c 形式に同じ env ブロックを追加してください。

要件

  • Node.js 18 以降
  • SSH クライアントがインストールされていること(診断および環境管理用)
  • 任意:oam 0.18.0 以降 — ランタイムの選択を参照してください

License

MIT

Follow @YawLabs on X

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。