mcpField
M
MCP SSH Gateway

MCP SSH Gateway

ローカル
github.comクラウド・インフラ34 週間前に更新

MCP SSH Gateway について

MCP SSH Gatewayは、AIエージェントに状態を保つSSHシェルを提供し、コマンド終了時に終了コードを返します。リモートでのコマンド実行やファイル操作向けで、KeeneticルーターのCLIにも対応します。

主な機能

  1. 01リモートコマンドの実行と追跡
  2. 02呼び出し間でのシェル状態維持
  3. 03ページ分割された出力と履歴の取得
  4. 04入力待ちと割り込みへの対応
  5. 05SFTPまたはシェルでのファイル操作

活用シーン

  1. 01リモートホストのディスク使用量確認
  2. 02継続セッションでのビルド実行
  3. 03Keeneticルーターの管理
  4. 04リモートファイルの読み取りや編集

MCP 対応

stdio経由で、サーバー一覧、コマンド実行・読み取り、シェルセッション管理、リモートファイル操作のMCPツールを提供します。SSH設定またはサーバー設定からホストを読み込めます。

設定

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

mcp.json · 共通設定
{
"mcpServers": {
"d00mus-mcp-ssh-gateway": {
"command": "uvx",
"args": [
"mcp-ssh-gateway"
],
"env": {
"SSH_SERVERS_CONFIG": "<SSH_SERVERS_CONFIG>",
"SSH_MCP_CACHE_DIR": "<SSH_MCP_CACHE_DIR>",
"SSH_READ_ONLY": "<SSH_READ_ONLY>",
"SSH_COMMAND_BLACKLIST": "<SSH_COMMAND_BLACKLIST>"
}
}
}
}
起動コマンドuvx mcp-ssh-gateway
設定が必要な環境変数
SSH_SERVERS_CONFIGPath to the JSON file with SSH server targets, or an inline JSON string. Overridden by --servers-config.
SSH_MCP_CACHE_DIRWhere logs and known_hosts are kept. Default: the user cache directory.
SSH_READ_ONLYSet to true/1 to force the global read-only guardrail.
SSH_COMMAND_BLACKLISTComma-separated global prohibited command patterns.

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

パッケージ
pypimcp-ssh-gateway7.0.0

ツール

ツール一覧はまだ取得されていません

ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。

詳細

MCP SSH ゲートウェイ

コマンドの完了を認識できる AI エージェント向けの SSH ターミナルです。

6 つのツール、状態を維持する実際のシェル、コマンド終了時に即座に取得できる終了コード、小規模なモデルでも操作できる応答を備えています。直接の依存関係は 1 つ(paramiko)だけで、デーモンもクラウドも不要です。

CI Python 3.11+ PyPI License: MIT

英語 · ロシア語

このツールを選ぶ理由

  • コマンドの完了を認識します。 シェル自身が終了と終了コードを報告するため、echo hi は数ミリ秒で返り、失敗したコマンドは exit_code: 2 を返し、3 分かかるビルドは 10 秒後に running と応答してから read で結果を取得できます。待機時間を設けたり、無応答から推測したりする必要はありません。
  • 状態を維持するターミナルです。 セッションは 1 つの実際のシェルです。cd、変数、仮想環境は呼び出し間で維持されます。session_id を指定せずに run を呼び出すと新しいシェルが開くため、意図せず状態が共有されることはありません。
  • 小規模なモデル向けに設計されています。 ツールは 6 つで、説明の合計は約 1.6k トークンです。どの停止時にも次に行うこと(hint)が示され、ページャーはオフ、進捗バーは 1 行で表示され、引数が誤っている場合は正しい呼び出しを明記した応答が返り、長い出力はページ分割されます(has_more、tail、offset)。
  • 質問があっても処理は停止しません。 Continue? [y/N]、パスワード入力、閉じられていない引用符があると、waiting_input が返ります。エージェントは signal で応答するか Ctrl+C を押すことができ、シェルの状態は維持されます。
  • ルーターにも対応します。 同じツールで Keenetic ルーターの CLI(show interface、--More-- に対応)や、Linux シェルを操作できます。
  • 実際の sshd でテスト済みです。 bash、BusyBox ash、zsh、dash、fish、tcsh のログインに対応するコンテナーと、SFTP のないホストを、プッシュのたびに CI で実行します。

クイックスタート

uv(または Python 3.11 以降と pip)および、自分が管理するホストへの SSH アクセスが必要です。

すでに ~/.ssh/config をお使いですか? これを MCP クライアントに追加すると、そのファイルにあるすべてのホストがサーバーとして使えるようになります(鍵またはエージェントによるログイン)。

{
  "mcpServers": {
    "ssh": {
      "command": "uvx",
      "args": ["mcp-ssh-gateway", "--import-ssh-config"]
    }
  }
}

これは Cursor(~/.cursor/mcp.json)と Claude Desktop(claude_desktop_config.json)の形式です。その他のクライアントについては以下をご覧ください。クライアントを再起動して、「SSH サーバーを一覧表示して、web のディスク使用量を表示して」と尋ねてみてください。

パスワードログイン、ルーター、または読み取り専用ホストを使いたいですか? servers.json を作成します。

{
  "servers": {
    "web": {"host": "192.168.1.10", "user": "deploy", "key_path": "~/.ssh/id_ed25519"},
    "router": {"host": "192.168.1.1", "user": "admin", "password": "${ROUTER_PASSWORD}"}
  }
}

次に、"args": ["mcp-ssh-gateway", "--servers-config", "/absolute/path/to/servers.json"] を使ってゲートウェイにそのファイルを指定します。絶対パスを使用してください(クライアントは作業フォルダーから起動するとは限りません。Windows では JSON 内のバックスラッシュをエスケープしてください)。また、参照する変数をクライアントの "env" ブロックでゲートウェイに渡してください。servers.json.example には、読み取り専用の本番ホストの例もあります。uv を使わない場合は、pip install mcp-ssh-gateway を実行し、"command": "mcp-ssh-gateway" を指定してください。

その他のクライアント

Claude Code

claude mcp add ssh --scope user -- uvx mcp-ssh-gateway --import-ssh-config

VS Code(.vscode/mcp.json:最上位のキーは servers です)

{
  "servers": {
    "ssh": {"type": "stdio", "command": "uvx", "args": ["mcp-ssh-gateway", "--import-ssh-config"]}
  }
}

Codex(~/.codex/config.toml、または codex mcp add ssh -- uvx mcp-ssh-gateway --import-ssh-config)

[mcp_servers.ssh]
command = "uvx"
args = ["mcp-ssh-gateway", "--import-ssh-config"]
startup_timeout_sec = 30   # the first uvx run downloads the package

このゲートウェイは公式 MCP レジストリにも io.github.d00mus/mcp-ssh-gateway として登録されています。レジストリを参照するクライアントやディレクトリで利用できます。

エージェントに表示される内容

クライアントが受け取る回答です。テストスイートの Debian コンテナーから取得しています(ホストアドレスとディスク容量の数値はサンプル値に置き換えています)。

server_list()
→ {"servers":[{"server":"web","host":"192.168.1.10:22","user":"deploy"}]}

run(server="web", command="df -h /")                  # no session_id: a new shell is opened
→ {"session_id":"web/1","status":"completed","exit_code":0,
   "output":"$ df -h /\nFilesystem      Size  Used Avail Use% Mounted on\n/dev/vda1        40G   31G  7.2G  82% /\n"}

run(session_id="web/1", command="ls /nonexistent")    # the same shell; a non-zero code is a result
→ {"session_id":"web/1","status":"completed","exit_code":2,
   "output":"$ ls /nonexistent\nls: cannot access '/nonexistent': No such file or directory\n"}

run(session_id="web/1", command="read -p 'Continue? [y/N] ' a; echo got:$a")
→ {"session_id":"web/1","status":"waiting_input",
   "output":"$ read -p 'Continue? [y/N] ' a; echo got:$a\nContinue? [y/N] ",
   "hint":"The program waits for input. Answer with signal action=stdin text=..., or stop it with signal ctrl_c."}

signal(session_id="web/1", action="stdin", text="y")
→ {"session_id":"web/1","status":"completed","output":"got:y\n","exit_code":0}

run(session_id="web/1", command="seq 1 500", lines=5)  # long output comes in pages
→ {"session_id":"web/1","status":"completed","output":"$ seq 1 500\n1\n2\n3\n4\n","exit_code":0,"has_more":496}

read(session_id="web/1", tail=3)                       # just the end
→ {"session_id":"web/1","status":"completed","output":"498\n499\n500\n","exit_code":0,"skipped_lines":493}

read(session_id="web/1", offset=-20, lines=3)          # scroll back; the unread position stays put
→ {"session_id":"web/1","status":"completed","output":"481\n482\n483\n","exit_code":0}

run(session_id="web/1", command="sleep 30", wait=1)
→ {"session_id":"web/1","status":"running","output":"$ sleep 30\n",
   "hint":"Still running. Call read(session_id='web/1') again, or stop it with signal ctrl_c."}

signal(session_id="web/1", action="ctrl_c")
→ {"session_id":"web/1","status":"interrupted","output":"\n","process_stopped":true}

間違いがあっても、正しい呼び出しを示す回答が返されます。run に cwd 引数があると誤って想定したモデルが受け取る内容は次のとおりです。

{"error": "run has no argument 'cwd'. It takes: command, server, session_id, shell, wait, timeout, lines. To work in a folder, start the command with 'cd /path && '."}
フィールド意味
statuscompleted、running、waiting_input、interrupted、timed_out、failed、または idle です。
exit_codecompleted の場合のみ設定されます。ゼロ以外の値は結果であり、ツールエラーではありません。
has_moreセッション内に未読の行が残っていることを示します。read で取得できます。
hintエージェントが次に何をすべきか推測しなくて済むよう、次の操作を示します。
skipped_lines新しいコマンドまたは read(tail=…) が読み飛ばした、古い未読行です。データは失われていません。read(offset=0) で戻って確認できます。
dropped_data完全に失われた未読の出力です(セッションバッファーの上限を超過しました。直近の 400 万文字は保持されます)。

ツール

ツール説明
server_listサーバーと、そのサーバーで開いているセッションを一覧表示します。
runコマンドを実行します。コマンドが終了するか、wait 秒(デフォルトは 10 秒)が経過すると、running を返します。session_id を指定しない場合は、新しいシェルを開きます。
readセッションの次の未読行を返します(実行中のコマンドの場合は、最大 wait 秒待機します)。末尾の行を取得するには tail を、過去の行に戻るには offset を指定します。
signalstdin は質問への回答、ctrl_c はコマンドの停止、ctrl_d は入力の終了に使います。
session_closeシェルを閉じます。サーバーごとに開けるシェルは限られています(max_sessions のデフォルトは 8)。
fileSFTP、またはホストに SFTP がない場合はシェル経由で、リモートファイルの list、read、write、edit を実行します。読み取りにはフィルター(contains、tail_lines)を指定できます。編集では、expected_sha256 を使って条件を指定できる、テキストの完全一致置換を行います。
server_add--allow-add-server を指定した場合に限り、servers.json にサーバーを追加します。

セッション名は server/N です。シェルは状態(フォルダー、変数)を保持するため、デフォルトのシェルはありません。session_id を指定せずに run を実行すると、常に新しいシェルを開いて ID を返します。その ID を使うと、同じシェルを引き続き利用できます。使い終わったシェルは閉じてください。

対応環境

ホスト状態
ログインシェルが bash、dash、BusyBox ash、または zsh の Linux対応しています。実際の sshd コンテナー(Debian、Alpine、zsh と dash のログイン環境)でテスト済みです。
fish または tcsh のログインシェルservers.json で "shell": "bash" を指定すれば対応します(ゲートウェイはログイン後に exec bash を実行します)。指定しない場合、ゲートウェイはただちに拒否し、その理由を示します。
Keenetic ルーター(NDM CLI およびその背後の Linux シェル)対応しています。テストではスクリプト化されたフェイクで確認しており、実際のルーターでも日常的に使用しています。
その他のルーター CLI(Cisco IOS、Junos、…)未対応です:#1。一般的なベンダー CLI でも動作する可能性はありますが、テストされていません。
踏み台サーバーの背後にあるホスト(ProxyJump)未対応です:#2。--import-ssh-config は ProxyJump を無視します。
SSH シェルが cmd または PowerShell の Windows ホスト対応していません。ゲートウェイには POSIX シェルが必要です。

ゲートウェイ自体は、Python 3.11 以降が動作する環境(Linux、macOS、Windows)で実行できます。

セキュリティ

エージェントには、指定した SSH アカウントと同等のアクセス権があります。以下は誤操作による被害を抑えるものであり、制限付きアカウントの代わりにはなりません。

  • ガードレールは誤操作を防ぐためのものです。 read_only と command_blacklist(大文字と小文字を区別しないリテラル文字列)は、コマンドテキストを確認します。シェルのトリックや base64、python -c で回避できます。実際の制限には、読み取り専用シェル、ForceCommand、信頼レベルごとの個別の認証情報など、制限付き SSH アカウントを使用してください。
  • ローカルファイルにはアクセスできません。 local_path(アップロード、ダウンロード)は、--project-root <folder> を指定してゲートウェイを起動した場合にのみ使用でき、そのフォルダー内に限られます。
  • ホスト鍵: 初回接続時に新しいホストを信頼し、その鍵をキャッシュディレクトリ内の known_hosts に保存します。鍵が変更されている場合は拒否されます。verify_host: false にすると確認を無効にできます。
  • シークレット: 値を直接記述する代わりに、servers.json に ${NAME} の参照を記述してください。ログではパスワードに該当する値がマスクされます(--log-output off を指定するとログは書き込まれません)。
  • 待ち受けポートはありません: ゲートウェイは stdio のみで MCP と通信します。server_add は --allow-add-server を指定して起動した場合にのみ使用でき、追記のみを行います。

ポリシーの全文と問題の報告方法については、SECURITY.md をご覧ください。

設定

servers.json のフィールド:host、user、port(22)、key_path(~ と ${NAME} が使用可能)、password と key_passphrase(${NAME} 参照のみが置き換えられ、それ以外は記述どおりに使用)、verify_host(true)、description、extra_path(PATH に追加)、read_only、command_blacklist、max_sessions(8)、shell(ログイン後に exec する POSIX シェル。fish または tcsh アカウント向け)。ゲートウェイの実行中もファイルは再読み込みされます。変更されていないサーバーのセッションは維持されます。

コマンドライン(サーバーの指定元以外はすべて省略可能):

オプション説明
--servers-configservers.json のパス、または JSON 自体。$SSH_SERVERS_CONFIG も使用できます。指定がない場合は ./servers.json にフォールバックします。
--import-ssh-config~/.ssh/config にあるホスト(鍵またはエージェントによるログイン)も候補に含めます。
--read-only、--command-blacklist a,bすべてのサーバーに適用するガードレール(または $SSH_READ_ONLY、$SSH_COMMAND_BLACKLIST)。
--log-output meta|full|offログファイルに書き込む内容(既定値は meta:コマンドとライフサイクル)。
--cache-dirログと known_hosts の保存先。既定値はユーザーのキャッシュディレクトリ($SSH_MCP_CACHE_DIR)。
--project-rootfile ツールが読み書きできるローカルフォルダー(local_path)。指定しない場合、ローカルファイルにはアクセスできません。ツールは引き続きサーバー上で使用できます。
--allow-add-server、--allow-system-temp、--allow-gateway-dir明示的に有効化する追加機能。

ログ:キャッシュディレクトリに、セッションごとに 1 つ、コマンドごとに 1 つの JSON Lines ファイルを作成します。古いファイルは経過時間とサイズに基づいて削除されます。

仕組み

ゲートウェイは PTY を使って対話型シェルを開き、セットアップ用の 1 行を実行して、エコーを無効にし、終了コードを含むマーカーを出力するプロンプトを設定することで、静かで機械可読なモードに切り替えます。マーカーが現れるとコマンドの実行が終了し、エージェントは終了コードを取得します。構文エラー、バックグラウンドジョブ、Ctrl+C も同様に処理されます。閉じられていない引用符や heredoc は、2 つ目のマーカーで検出されます。

ログイン後にベンダー CLI(Keenetic NDM)が起動し、通常のシェルがないルーターでは、この方法は使えません。その場合、ゲートウェイはログインバナーからプロンプトを学習し、入力したコマンドのエコーを削除し、--More-- が表示されたときはスペースキーを押して、プロンプトが安定した時点でコマンドが終了したものと判断します。run(shell=false) は CLI を使用し、run(shell=true) はその背後にある Linux シェルに入ります。1 つのセッションでは、どちらか一方のモードを維持します。

出力はセッションごとに 1 つの行単位バッファに格納され、未読位置は 1 つだけです。run と read は未読位置を進め、tail は末尾に移動し、offset は位置を変えずに参照します。ページ表示を予測しやすくするため、1024 文字を超える行は分割されます。

制限事項

  • シェルでは一度に1つのコマンドを実行します。同時に2つ実行するには、シェルを2つ開くか、クライアントから run を並列で呼び出してください。
  • セッション内で起動したシェル(sudo -s、su、bash)ではコマンドの終了位置が隠れるため、ゲートウェイはエージェントにそのシェルを終了するよう指示します。root 権限が必要な場合は sudo <command> を使ってください。
  • ジャンプホストはまだサポートしていません(#2)。他のルーター CLI もまだサポートしていません(#1)。
  • ゲートウェイには承認、ポリシーエンジン、監査証跡がありません。必要な場合は、以下をご覧ください。

MCP 向けのその他の SSH サーバー

それぞれ異なるトレードオフがあります(2026年9月時点。選択する前に README をお読みください)。

  • tufantunc/ssh-mcp:セキュリティを最優先します。コマンドの分類、ロールおよびホストのポリシー、人的承認、監査ログ、ProxyJump、SSH CA、ストリーミング SFTP に対応し、ツールは14個です。承認と監査証跡が必要な本番環境のフリートに適しています。
  • classfang/ssh-mcp-server:Node(npx)上で4つのツールを提供し、コマンドのホワイトリスト、SOCKS および HTTP プロキシ、バスチョンモード、MFA に対応します。プロキシまたは MFA が必要で、最小構成の Node 環境を求める場合に適しています。
  • bvisible/mcp-ssh-manager:37個のツールを備えた、バックアップ、データベース、監視向けの DevOps ツールボックスです。こうしたワークフローをすぐに使いたい場合に適しています。

このゲートウェイは逆の方向を選びます。ツールの数を絞り、状態を保持するシェルを用意し、エージェントが行き詰まるあらゆる状況に対応します。

Docker

docker build -t mcp-ssh-gateway .
docker run -i --rm \
  -v /absolute/path/to/servers.json:/app/servers.json:ro \
  -v /absolute/path/to/.ssh:/root/.ssh:ro \
  -v /absolute/path/to/cache:/app/.ssh-cache \
  mcp-ssh-gateway --servers-config /app/servers.json --cache-dir /app/.ssh-cache

キャッシュボリュームにより、実行間で known_hosts とログが保持されます。

開発

pip install -r requirements.txt ruff mypy
python -m unittest discover -s tests -t .     # unit tests; integration tests need Docker
ruff check . && mypy

統合テストでは、実際の sshd コンテナを起動し、クライアントが使用するのと同じ JSON-RPC エントリーポイントを通じてゲートウェイを操作します。Docker がない場合は自動的にスキップされます(意図的にスキップするには MCP_SSH_IT=0 を指定します)。CONTRIBUTING.md と変更履歴をご覧ください。MIT ライセンスです。

コメント

MCP は初めてですか?

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

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