SSH Host Storage & Remote Exec について
SSHホストの保存とリモートコマンド実行を行う、stdio型MCPサーバーとCLIです。認証情報をローカルで暗号化し、SSHセッションを再利用して接続プールを監視できます。
主な機能
- 01ローカルホスト情報の暗号化保存
- 02SSHホスト管理ツール
- 03コマンド・対話型・プレイブック実行
- 04SSH接続のプール管理
- 05情報を伏せたプール監視
- 06対話型・バッチCLI
活用シーン
- 01MCP経由の運用対象ホスト管理
- 02MCPクライアントからのリモート実行
- 03複数段階のSSH手順の自動化
- 04ホストの一括登録と接続状態の確認
MCP 対応
SSHホスト管理、リモートコマンド実行、プレイブック、プール状態をstdio型MCPツールとして公開します。MCPクライアントがサーバーをローカルで起動します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
npx -y @xlbbb-cn/mcp_ssh_exec_cmdMCP_SSH_CONFIG_DIRDirectory holding hosts.json and master.key (default: ~/.mcp-ssh-server).MCP_SSH_MASTER_KEYシークレット64-hex-character AES-256-GCM master key; auto-generated into <config dir>/master.key when unset.MCP_SSH_POOL_IDLE_MSIdle SSH connections are closed after this many milliseconds (default: 60000, 0 disables pooling).MCP_SSH_POOL_MAX_HOSTSMaximum number of pooled SSH connections (default: 32).ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
9掲載時のツール一覧 · 6 日前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| topic | string | いいえ | ヘルプを絞り込む任意のトピック:'install'、'list'、'get'、'create'、'update'、'delete'、'import'、'monitor'、'tui'。トラブルシューティングには 'windows' を指定します。省略するとヘルプ全文を取得します。 |
詳細
mcp-ssh-server
SSH ホスト管理とリモートコマンド実行を MCP ツールとして提供する、スタンドアロンのstdio MCP サーバーです。 ローカルマシン上の SSH ホストを管理するための、対話型/バッチ型 CLI(
remote-ssh-cli)も同梱しています。简体中文文档见 README.zh-CN.md。
FastAPI プロジェクト(app/services/llm_tools.py + app/utils/ssh_helper.py + app/models/host.py)の SSH ホスト部分を Node.js で再実装したものです。バックエンドもデータベースもネットワークサービスも必要ありません。任意の MCP
クライアント(Claude Desktop、Continue、カスタムエージェントなど)は、npx
で起動し、JSON-RPC 経由で通信できます。
主な特長
- 完全にスタンドアロン — 独自の暗号化 JSON ストア(
~/.mcp-ssh-server/hosts.json)を使用します。FastAPI バックエンドやデータベースは不要です。 - ネイティブ依存関係なし — AES-256-GCM には Node 組み込みの
crypto、SSH には純粋な JavaScript 製のssh2を使用します。Windows/macOS/Linux で動作します。 - 標準準拠の stdio MCP — Model Context Protocol の stdio トランスポートに対応し、
npxで起動できます。 - 認証情報がサーバーの外部に出ることはありません — ホストツールのレスポンスにパスワードや秘密鍵が含まれることはありません。SSH ツールは内部でそれらを復号して接続を確立し、レスポンスに含めることはありません。
- 接続プーリング — ホストごとに 1 つの SSH セッションを維持し、コマンドやプレイブックの各ステップで再利用します。
- プールの監視 —
pool_statusは、稼働中のプールを機密情報をマスキングした表として表示します。uid、名前、接続状態、実行済み/失敗したコマンド、最後のコマンドが含まれます(シークレットと IP アドレスはマスキングされます)。 - CLI コンパニオン —
remote-ssh-cliで同じストアを管理できます(対話型 TUI、バッチコマンド、CSV 一括インポート)。接続プールをリアルタイムで監視することもできます(1 秒ごとに更新。qで終了)。
MCP ツール
9 つのツールを公開しています。
| ツール | 説明 |
|---|---|
cli_help | remote-ssh-cli のインストール/使用方法/トラブルシューティングに関する説明です。topic で対象を絞り込めます |
host_list | 登録済みホストを一覧表示します — sys_name と uid のみ(アドレス、ユーザー名、認証情報は含まれません)。uid は、ほかのすべてのツールが必要とする host_id です |
host_create | ホストを追加します(パスワード/秘密鍵/パスフレーズは保存時に暗号化されます) |
host_update | ホストのフィールドを更新します(省略した認証情報フィールドは現在の値を維持します) |
host_delete | ホストを削除します |
ssh_run_command | ホストで対話なしの単一コマンドを実行します |
ssh_run_interactive | expect/answer のペアを使ってプロンプトに応答しながらコマンドを実行します(sudo など) |
ssh_run_playbook | 複数ステップのプレイブックを実行します(構造化された # @… ディレクティブ、または従来形式の expect:/answer: 行) |
pool_status | 稼働中の接続プールを監視します — ホストごとに、機密情報をマスキングした行を 1 つ表示します(uid、名前、状態、実行済み/失敗したコマンド、最後のコマンド) |
ホスト識別子の仕様
host_listが最初の入口です。各ホストはsys_name(名前)とuid(一意の識別子)の、ちょうど 2 つのフィールドで返されます。- ほかのすべてのツールでは、同じ値を
host_idという名前で指定します。そのまま渡してください。組み立てたり推測したりしないでください。 - ID が見つからない場合、ツールは対処方法を示すエラーを返します。
Host '<id>' not found. host_id must be the exact 'uid' value returned by host_list — call host_list to obtain the valid identifiers.host_listを再度呼び出し、返された値を使って再試行してください。 host_getは、意図的に MCP クライアントには公開されていません。モデルに見えるのはsys_nameとuidのみで、ホストのアドレス、ユーザー名、認証情報が見えることはありません。CLI には、人間のオペレーター向けのgetサブコマンドが用意されています。
引数の概要
| ツール | 引数 |
|---|---|
cli_help | topic? — install、list、get、create、update、delete、import、monitor、tui、windows のいずれか |
host_list | enabled_only?(bool) |
host_create | ip_address(必須)、system_name?、ssh_port?(1~65535、デフォルトは 22)、ssh_username?(デフォルトは root)、ssh_auth_method?(password | pubkey)、ssh_password?、ssh_private_key?、ssh_private_key_passphrase?、is_enable?(デフォルトは true) |
host_update | host_id(必須)と、上記のフィールドのいずれか — 省略したフィールドは現在の値が保持されます |
host_delete | host_id |
ssh_run_command | host_id、command、timeout_seconds(1~600、必須) |
ssh_run_interactive | host_id、command、expects?([{ expect, answer }])、timeout_seconds |
ssh_run_playbook | host_id、content(複数行のプレイブック本文)、timeout_seconds |
pool_status | host_id?(ホストを1台のみ指定)、format?(table | json)、include_history?(bool)、include_idle_hosts?(bool) |
ip_address はストア内で一意です。同じ値で作成すると失敗します。
コマンドの結果はプレーンテキストのブロックとして返されます。
[stdout]
hello
[stderr]
[exit_code=0 success=true duration_ms=42]
クイックスタート
git clone https://github.com/xlbbb-cn/mcp_ssh_exec_cmd.git
cd mcp_ssh_exec_cmd
npm install
npm run build
ビルドの出力は dist/ に配置され、2つのバイナリが提供されます。
| バイナリ | エントリーポイント | 用途 |
|---|---|---|
remote-ssh-server | dist/index.js | stdio MCP サーバー(MCP クライアントによって起動されます) |
remote-ssh-cli | dist/cli.js | 対話型/バッチ型のホスト管理 CLI |
インストール
パッケージは、同じ名前で2つのレジストリに公開されています。
@xlbbb-cn/mcp_ssh_exec_cmd:
| レジストリ | インストールコマンド | 認証 |
|---|---|---|
| npmjs.org(推奨) | npm install -g @xlbbb-cn/mcp_ssh_exec_cmd | 不要 — パブリックレジストリではトークンは必要ありません |
| GitHub Packages | npm install -g @xlbbb-cn/mcp_ssh_exec_cmd --registry=https://npm.pkg.github.com | read:packages 権限を持つクラシック形式の個人用アクセストークン |
GitHub Packages では、公開パッケージでもインストール時に認証が必要です。 そのため、最初に npm にトークンを渡します。
npm login --scope=@xlbbb-cn --auth-type=legacy --registry=https://npm.pkg.github.com
# Username: <your GitHub username>
# Password: <your PAT>
同じ内容を ~/.npmrc に記述することもできます。
//npm.pkg.github.com/:_authToken=<PAT>
@xlbbb-cn:registry=https://npm.pkg.github.com
グローバルインストール(CLI)
ローカルのチェックアウトからインストールする場合:
npm run build
npm install -g --allow-scripts=ssh2,cpu-features .
または、レジストリから直接インストールします。
npm install -g @xlbbb-cn/mcp_ssh_exec_cmd # npmjs.org
npm install -g @xlbbb-cn/mcp_ssh_exec_cmd --registry=https://npm.pkg.github.com # GitHub Packages
インストール後、remote-ssh-cli を PATH から実行できます。
remote-ssh-cli # interactive TUI
remote-ssh-cli list # JSON list of every host
remote-ssh-cli get <host_id>
remote-ssh-cli create <json-file>
remote-ssh-cli update <host_id> <json-file>
remote-ssh-cli delete <host_id>
remote-ssh-cli import <csv-file> [--dry-run]
remote-ssh-cli monitor # live connection-pool monitor (q quits)
create/update では、次の例のように JSON ファイルを指定します。
{
"ip_address": "192.168.1.100",
"system_name": "web-01",
"ssh_port": 22,
"ssh_username": "root",
"ssh_auth_method": "password",
"ssh_password": "your-password"
}
CSV の一括インポート
remote-ssh-cli import hosts.csv は、ファイルの各行を 1 台のホストに変換します。
1 行目はヘッダー行で、ip_address 列を少なくとも含める必要があります。
ip_address,system_name,ssh_port,ssh_username,ssh_auth_method,ssh_password
192.168.1.100,web-01,22,root,password,secret
192.168.1.101,db-01,2222,deploy,password,secret
| 列 | 説明 | デフォルト |
|---|---|---|
ip_address | 必須。ホストアドレス。テーブル内で一意 | — |
system_name | 表示名 | 空 |
ssh_port | SSH ポート | 22 |
ssh_username | SSH ユーザー名 | root |
ssh_auth_method | password / pubkey | password |
ssh_password | 平文のパスワード(ディスクに書き込まれる前に暗号化されます) | — |
ssh_private_key | 平文の秘密鍵(ディスクに書き込まれる前に暗号化されます) | — |
ssh_private_key_path | 鍵ファイルへのパス(ssh_private_key の代替) | — |
ssh_private_key_passphrase | 鍵を保護するパスフレーズ(ディスクに書き込まれる前に暗号化されます) | — |
is_enable | true/false/1/0/yes/no | true |
- 列名では大文字と小文字が区別されず、別名(
ip、host、name、port、user、passwordなど)も使用できます。 - 引用符で囲まれたカンマ、セル内改行、
""によるエスケープは RFC 4180 に準拠して解析されるため、秘密鍵の内容が壊れることはありません。 - 不正な行があってもバッチ処理は中断されません。すべての行について、作成済み/スキップ(IP アドレスの重複)/失敗(データが無効)のいずれかが報告されます。コマンドはサマリーを出力し、1 行以上失敗した場合は終了コード
1で終了します。 --dry-runは検証と報告のみを行い、何も書き込みません。- CSV テンプレート(ヘッダー+サンプル行)は
remote-ssh-cliのバナーに表示されます。また、ファイル引数なしでremote-ssh-cli importを実行すると、同じ内容が表示されます。
Windows に関する注意:
JScript runtime error (800A03EA)が発生した場合は、npm が 10.8.0 以上であることを確認するか、PowerShell ではなく cmd.exe からコマンドを実行してください。古いバージョンの npm が生成する.ps1ラッパーでは、nodeが明示的に呼び出されないことがあります。どちらのエントリーポイントにも#!/usr/bin/env nodeのシバンが含まれています。
接続プールを監視する
remote-ssh-cli monitor は SSH 接続プールのライブビューを開きます。画面は
1 秒ごとに再描画され、q キーで終了します(Ctrl+C でも終了できます)。
SSH connection pool monitor · 15:05:31 · tick 42 · refresh 1000ms · last tick 41ms
pool 2/32 connections · idle timeout 1m00s · probe timeout 5s · press q to quit
HOST ADDRESS USER STATUS LATENCY LAST OK POOL
------------ ------------ ----- -------- ------- ------- ----
web-01 192.168.1.100 root up 12ms 15:05:30 1m05s
db-01 10.0.0.5:2222 deploy down 8ms - -
problems (1):
db-01 (10.0.0.5:2222): connect ECONNREFUSED 10.0.0.5:2222
| 列 | 意味 |
|---|---|
STATUS | up(プローブ成功)、down(接続失敗)、または disabled(ホストはオフになっており、一覧には表示されますが、プローブは実行されません) |
LATENCY | 最後のプローブにかかった往復時間 |
LAST OK | 最後にプローブが成功した時刻 |
POOL | プール内接続の経過時間。セッションが再確立されずに再利用されている時間です |
- モニターはプールをウォーム状態に保ちます。各ホストは SSH ツールが使用するものとまったく同じ
ConnectionPoolを通じてプローブされるため、表示される接続は実際に再利用可能なセッションです。 - CLI は独自のプールを実行します。MCP サーバーは別プロセスのため、その接続をターミナルから調べることはできません。
- デフォルトのプローブコマンドは
echoです(POSIX シェルとcmd.exeのどちらにもあります)。各プローブは間隔ごとに 1 回実行されるため、ホスト数が多い場合は--intervalを適切な値に設定してください。 - ターミナル以外(パイプ、CI ジョブなど)では、モニターはループせずに 1 フレームだけ出力します。停止するためのキーがないためです。
| オプション | 説明 | デフォルト |
|---|---|---|
--interval <ms> | 再描画間隔 | 1000 |
--timeout <s> | ホストごとのプローブタイムアウト(1~600 秒) | 5 |
--probe-cmd <cmd> | ホストのプローブに使用するコマンド | echo |
--host <host_id> | このホストのみを監視します(複数回指定可能) | すべてのホスト |
--all | 無効なホストも一覧に表示します(プローブは実行されません) | オフ |
--once | 1 フレームだけ出力して終了します | オフ |
同じビューは対話型 TUI(7) 接続プールを監視)からも利用できます。
MCP クライアントに登録する
~/Library/Application Support/Claude/claude_desktop_config.json(macOS)または
%APPDATA%\Claude\claude_desktop_config.json(Windows):
{
"mcpServers": {
"ssh": {
"command": "npx",
"args": ["-y", "-p", "@xlbbb-cn/mcp_ssh_exec_cmd", "remote-ssh-server"]
}
}
}
GitHub Packages を使用する場合は、同じスニペットにレジストリを追加します。
"args": [
"--registry=https://npm.pkg.github.com",
"-y",
"-p",
"@xlbbb-cn/mcp_ssh_exec_cmd",
"remote-ssh-server"
]
file: 形式を使うと、npx がローカルのチェックアウトを直接参照します。開発中に便利で、レジストリも必要ありません。
"args": ["-y", "-p", "file:./mcp_ssh_exec_cmd", "remote-ssh-server"]
設定
すべて環境変数で設定します。
| 変数 | 説明 | デフォルト |
|---|---|---|
MCP_SSH_MASTER_KEY | 16 進文字列(16 進数 64 文字)で指定する 32 バイトのキーです。必ずバックアップしてください。この値を変更するか、別のマシンに移行すると、hosts.json 内のすべての認証情報を復号できなくなります | 初回起動時に生成され、<config_dir>/master.key に保存されます |
MCP_SSH_CONFIG_DIR | 設定ディレクトリを上書きします | ~/.mcp-ssh-server |
MCP_SSH_POOL_IDLE_MS | アイドル状態のプール接続を維持する時間(ミリ秒)です。0 にするとプールが完全に無効になり、コマンドごとに新しい接続を開きます | 60000 |
MCP_SSH_POOL_MAX_HOSTS | プールで接続を維持するホスト数です(この数を超えると、最後に使用されたアイドル接続から削除されます) | 32 |
SSH 接続プール
- 各ホストにつき SSH 接続を 1 つ維持します。連続するコマンドやプレイブックの各ステップでは、再接続せずにその接続を再利用します。
- プールのキーは、ホスト ID と認証情報のフィンガープリントです。アドレス、ポート、ユーザー名、認証方式、パスワード、またはキーを変更すると、キャッシュされた接続は直ちに無効になります。
host_updateとhost_deleteは接続を明示的に破棄します。 - アイドル状態の接続は
MCP_SSH_POOL_IDLE_MSの経過後に閉じられます。その間にピアが切断した接続は、次回の使用時に透過的に再確立されます(再試行は 1 回です)。 - プロセスの終了時(つまり、MCP クライアントの切断時)に、プール内のすべての接続が閉じられます。
remote-ssh-cli monitorでプールの状態をリアルタイムに確認できます(接続プールの監視を参照)。同じ 2 つの環境変数が適用されます。
MCP からのプールの監視
pool_status は MCP サーバー自体のプールの状態をテーブルで表示します。プール接続またはコマンドの実行履歴があるホストごとに 1 行表示されます。
SSH connection pool · 1/32 connections · idle timeout 1m00s · 2 commands executed · 1 failed · updated 15:24:16
Commands and error messages are desensitized (secrets masked, IP addresses replaced).
UID SYS_NAME STATE EXECUTED FAILED LAST CMD LAST RESULT LAST ERROR LAST RUN POOL AGE
------------------------------------ -------- ------ -------- ------ ----------------------- ----------- ----------------------------- -------- --------
9bcb7682-537b-4837-8b9e-6e37b4c61cdc web-01 pooled 3 1 systemctl restart nginx exit=1 connect to *** port 22 failed 12s ago 1m05s
| 列 | 意味 |
|---|---|
UID | ホスト ID。host_list が uid として返す値と同じです |
SYS_NAME | ホスト名 |
STATE | pooled(接続中でアイドル状態)、busy(コマンド実行中)、down(接続が切断された)、idle(コマンドは実行されたが、接続は開いていない)、new(未使用) |
EXECUTED / FAILED | ホストで実行されたコマンド数と、そのうち失敗したコマンド数 |
LAST CMD / LAST RESULT | 最後のコマンドとその結果(ok、exit=1、timeout、error) |
LAST ERROR | 最後の失敗時の、機密情報を伏せたエラーメッセージ |
LAST RUN | 最後にコマンドを実行してからの経過時間 |
POOL AGE | プール接続が再利用されている時間 |
- テーブルには 機密情報を含めません。認証情報はマスクされ(
mysql -pS3cret→mysql -p***)、IP アドレスは置き換えられ(10.0.0.5→***)、ホストのアドレス、ポート、ユーザー名、認証情報が表示されることはありません。 format: "json"を指定すると同じ情報を JSON ドキュメントで返します。include_history: trueを指定するとホストごとの直近のコマンドが追加され、include_idle_hosts: trueを指定するとコマンドの実行履歴がないホストも一覧に含まれます。host_idを指定すると、表示対象を 1 つのホストに絞り込めます。- カウンターはサーバープロセス内で管理されます。この MCP サーバーが実行したコマンドを記録し、再起動するとリセットされます。CLI モニターは別プロセスであり、独自のプールを表示します。
タイムアウトの動作
timeout_seconds は 厳密な上限時間です。時間切れになるとコマンドは終了され、結果は success=false、exit_code=null となり、stderr に注記が含まれます。ssh_run_playbook では、この値はプレイブック 全体に対する時間枠です。各ステップには残り時間だけが割り当てられます。
プレイブックの形式
ssh_run_playbook は複数行の本文を受け付けます。2つの形式に対応しています。
構造化形式 — ディレクティブは # @ で始まります。
# @section "Restart nginx"
# @desc "Reload the reverse proxy after the config change"
systemctl stop nginx
# @on "systemctl start nginx"
expect: [sudo] password for deploy:
answer: secret
# @note "the site is back up"
# @danger "this drops active connections"
| ディレクティブ | 意味 |
|---|---|
# @section "Title" | 以降の手順をセクションにまとめます |
# @desc "…" | 現在のセクションの説明 |
# @note "…" | 現在の手順に付けるメモ |
# @danger "…" | 現在の手順に付ける危険警告 |
# @on "command" | このコマンドを対話形式で実行します(後続の expect: / answer: ペアに応答します) |
レガシー形式 — 1行につき1つのコマンドを記述し、必要に応じて expect: / answer: ペアを続けます。
sudo systemctl restart nginx
expect: [sudo] password for deploy:
answer: secret
通常の # コメントは無視されます。最初に終了コードが 0 以外になった時点でプレイブックは停止し、実行されたすべての手順の記録を返します。
ストレージとセキュリティ
hosts.json— すべてのホストレコードをプレーンな JSON 形式で保存します。メタデータフィールドは平文で保存されます。ssh_password、ssh_private_key、ssh_private_key_passphraseには Base64 形式の暗号文が格納されます。master.key— 32 バイトのマスターキー(POSIX ではモード0o600)。- 書き込みは
.tmpファイルを経由してからリネームするため、クラッシュしても書き込み途中のファイルが残ることはありません。
シークレットは、マスターキーから PBKDF2-SHA-256(200k 回の反復処理、16 バイトのランダムソルト)で導出したキーを使い、AES-256-GCM で暗号化されます。形式は [16-byte salt][12-byte IV][16-byte tag][ciphertext] です。ソルトと IV は暗号化のたびに新しく生成されるため、同じ平文から同じ暗号文が生成されることはありません。
ディレクトリ全体をバージョン管理の対象から除外してください。
# mcp-ssh-server state
.mcp-ssh-server/
プロジェクト構成
src/
index.ts MCP entry point — stdio transport, tool registration, dispatch
cli.ts remote-ssh-cli — interactive TUI + batch subcommands
monitor.ts live connection-pool monitor (one frame per second, q quits)
pool.ts ConnectionPool — one live SSH connection per host + the command counters
ssh.ts ssh2 execution primitives + playbook parser/runner
storage.ts HostStore — hosts.json load/save with an atomic full-file rewrite
csv.ts RFC 4180 CSV reader + host-row mapping
importer.ts bulk-import orchestration
crypto.ts AES-256-GCM encryption of secrets at rest
master_key.ts master-key resolution (env var or key file)
paths.ts configuration-directory paths
redact.ts masks secrets and IP addresses in commands and error messages
tools/
_types.ts shared MCP tool shape + the host-identifier wording
hosts.ts host_list / host_create / host_update / host_delete
ssh_exec.ts ssh_run_command / ssh_run_interactive / ssh_run_playbook
pool_status.ts pool_status — the desensitized pool table
help.ts cli_help
test/
smoke.mjs encryption round-trip, HostStore CRUD, playbook parsing
features_smoke.mjs CSV parsing/import, connection pool behaviour
monitor_smoke.mjs monitor frames, the refresh loop, the 'q' shortcut
mcp_smoke.mjs end-to-end JSON-RPC handshake against a spawned server
開発
npm run dev # tsc --watch
npm run build # tsc (the only typecheck)
npm test # all four smoke tests
検証には npm run build を使用します。個別の lint や format の設定はありません。テストは dist/ を実行するため、テストの前にビルドしてください。
スモークテスト
npm test は4つの通常の Node スクリプトを実行します。どのスクリプトも実際の SSH サーバーを必要としません。
test/smoke.mjs— 暗号化と復号の往復、HostStore の CRUD 全体(書き込み時の暗号化、読み戻し、一覧表示、更新、削除)、および両形式のプレイブック解析。test/features_smoke.mjs— CSV の解析とホスト行のマッピング、一括インポート(--dry-runと重複 IP のスキップを含む)、接続プールの再利用/認証情報の変更時の無効化/アイドル接続の削除、プレイブック解析のエッジケース、コマンドとエラーのマスキング、およびpool_statusテーブル。test/monitor_smoke.mjs— 接続プールのモニター:フレームの描画、インターバルごとに1フレームを描画するループ、qショートカット、SSH プローブ(偽のクライアントに対して実行)、およびプールのスナップショット。test/mcp_smoke.mjs— MCP サーバーを子プロセスとして起動し、JSON-RPC ハンドシェイクを実行して、9個すべてのツールが登録されていること、host_listがsys_nameとuidのみを公開すること、host_getが公開されていないこと、pool_statusが機微情報を除去したテーブルを返すこと、および応答にパスワードが一切含まれないことを検証します。
個別のファイルを実行するには、node test/<file>.mjs を使用します。各テストは一時的な MCP_SSH_CONFIG_DIR 内に状態を隔離するため、実際の hosts.json が変更されることはありません。
リリース
リリースは GitHub Actions のワークフロー(.github/workflows/publish.yml)によって、npmjs.org、GitHub Packages、および公式 MCP Registry(registry.modelcontextprotocol.io)に公開されます。2 つのパッケージジョブでビルドとスモークテストを実行し、tarball に npm provenance を付与して公開します。その後、3 つ目のジョブで server.json を MCP Registry に公開します。レジストリをミラーリングするディレクトリ(Glama、PulseMCP など)では、この情報をもとにサーバーが掲載されます。
MCP Registry のジョブは、npmjs.org のジョブの後に実行されます。レジストリは、公開済みパッケージから mcpName マーカーを読み取って npm の所有権を検証するためです。package.json の mcpName は常に server.json の name と一致している必要があり(test/features_smoke.mjs で検証されます)、CI は公開前に package.json から server.json のバージョンを再同期します。
# 1. bump the version in package.json, commit
git commit -am "chore: release v0.1.1" && git push github master
# 2. tag it — the tag triggers the publish workflow
git tag v0.1.1
git push github v0.1.1
| レジストリ | 認証情報 | 入手先 |
|---|---|---|
| GitHub Packages | GITHUB_TOKEN | ワークフローに組み込まれているため、設定は不要です |
| npmjs.org | 信頼された公開(OIDC) | npmjs.org で一度設定します:Packages → @xlbbb-cn/mcp_ssh_exec_cmd → Settings → Trusted publisher → ユーザー xlbbb-cn、リポジトリ mcp_ssh_exec_cmd、ワークフローファイル名 publish.yml。シークレットは不要です — ワークフローが有効期間の短い GitHub OIDC トークンを公開用トークンと交換します。 |
| npmjs.org(フォールバック) | NPM_TOKEN | リポジトリのシークレット(Settings → Secrets and variables → Actions)。@xlbbb-cn スコープで Read and write が付与され、Bypass 2FA が有効になっているきめ細かな npm トークンが必要です — 通常のユーザートークンでは npm が EOTP(ワンタイムパスワードが必要)を返します。信頼された公開の設定が完了するまでのみ必要です。 |
| MCP Registry | GitHub OIDC | ワークフローに組み込まれているため、シークレットは不要です。publish-mcp-registry ジョブは npmjs.org のジョブの後に mcp-publisher login github-oidc と mcp-publisher publish を実行します。mcp-publisher バイナリのバージョンは publish.yml で固定されています — 更新は意図的に行ってください。 |
NPM_TOKEN シークレットも信頼された公開の設定もない場合、npmjs.org のジョブは対処方法を示すエラーメッセージとともに失敗します。
フォールバックトークンは CLI から作成できます(2FA コードを一度入力する必要があります)。
npm token create --scopes=xlbbb-cn --packages-and-scopes-permission=read-write \
--bypass-2fa --otp=<your-2fa-code>
--bypass-2fa を指定すると、公開のたびに npm からワンタイムパスワードを求められなくなります。指定しない場合、レジストリは EOTP を返します。
信頼された公開が機能するようになったら、NPM_TOKEN シークレットを削除し、パッケージの Publishing access を 「Require two-factor authentication and disallow tokens」 に設定できます。
ワークフローは Actions タブから手動で開始することもできます(workflow_dispatch)。公開せずに tarball を検証するには、dry_run にチェックを入れてください。
公開されたパッケージは https://github.com/xlbbb-cn/mcp_ssh_exec_cmd/packages および https://www.npmjs.com/package/@xlbbb-cn/mcp_ssh_exec_cmd に表示されます。
npmjs.org への公開には、
@xlbbb-cnスコープを所有する npm アカウント(xlbbb-cnという名前の npm ユーザーまたは組織)が必要です。
FastAPI バックエンドとの関係
| 項目 | Python バックエンド | この MCP サーバー |
|---|---|---|
| ストレージ | PostgreSQL / SQLite(app.models.host) | ローカル JSON ファイル(~/.mcp-ssh-server/hosts.json) |
| 暗号化 | Fernet(AES-128-CBC + HMAC) | AES-256-GCM + PBKDF2 |
| SSH クライアント | paramiko | ssh2 |
| プロトコル | HTTP REST(/api/v1/hosts など) | MCP(stdio 上の JSON-RPC) |
| 認証/認可 | RBAC(host:* 権限コード) | ファイルシステム権限で保護された単一ユーザーのローカルプロセス |
ストレージバックエンドを共有することは、意図的に避けています。データベースや API が関与した時点で、このサーバーは単独で実行できなくなるためです。また、MCP サーバーとバックエンドの両方から同じ JSON ファイルを参照すると、一貫性の問題が発生します。
FastAPI バックエンドからホストを移行するには、使い捨てのスクリプトを作成してください。
/api/v1/hosts(host:secure:readが必要)を一覧表示し、同じフィールド構造のデータをこのサーバーのhost_createに渡します。
既知の制限事項
- ホストのデータは単一の JSON ファイルに保存され、変更のたびにファイル全体が書き換えられます。数百台のホストなら問題ありませんが、数千台には適していません。
- 同時書き込みの保護機能はありません。同じ
MCP_SSH_CONFIG_DIRに対して、2 つのインスタンスを同時に実行しないでください。 - 監査ログや操作履歴はありません。必要な場合は、呼び出し元のエージェントのトランスクリプトに SSH の出力を記録してください。
- MCP のインターフェースでは、ホストごとのメタデータを意図的に公開していません。そのため、モデルはホストのアドレスやユーザー名を参照できず、
host_list/host_createを経由する必要があります。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。