ssh-mcp
ssh-mcp
A centralized MCP gateway that gives AI agents controlled access to SSH infrastructure over Streamable HTTP.
ssh-mcp runs as a single HTTP service. Multiple AI clients — agents, CI pipelines, dashboards — connect to one gateway. SSH credentials stay on the gateway. Authorization policies, audit logging, and rate limiting are applied centrally before any SSH command executes.
目次
Related MCP server: MCP SSH Orchestrator
アーキテクチャ
ローカル stdio MCP(一般的なパターン)
AI client
│
▼
local MCP process ──► SSH target各エージェントは独自のプロセスで実行されます。SSH 認証情報はすべてのマシンに置かれ、集中管理はありません。
ssh-mcp(集中型 HTTP ゲートウェイ)
AI clients ───────┐
CI agents ────────┼──► ssh-mcp ──► SSH targets
Dashboards ───────┘ │
├─ API-key authentication
├─ per-client authorization
├─ rate limiting
├─ audit logging
└─ connection pooling単一のデプロイがすべてのクライアントにサービスを提供します。認証情報、ポリシー、ログは一か所に集約されます。
ssh-mcp を選ぶ理由
集中型 HTTP ゲートウェイ — 単一のデプロイがすべての AI エージェント、CI パイプライン、ダッシュボードに Streamable HTTP でサービスを提供します
クライアント単位の認可 — API キーごとに異なるコマンドセットを異なるサーバーに対して許可します
多層コマンドポリシー — ブロックパターン、危険シェル検出、ターゲットごとの許可リストが連携して機能します
集中型 SSH アクセス — SSH 認証情報はゲートウェイ上に置かれ、各エージェントのマシンには置かれません
監査証跡 — すべてのコマンド、すべてのクライアント、すべての結果をリクエストトレーシング付きの構造化 JSONL ログとして記録します
運用上の回復力 — コネクションプーリング、サーキットブレーカー、指数バックオフ付きリトライ
可観測性 — 監視用の Prometheus メトリクスとヘルスエンドポイント
マルチエージェントのアクセス制御
エージェントごとに必要な権限は異なります。ssh-mcp はこれをゲートウェイで強制します。
monitoring agent → API key A → read-only commands → all servers
deployment agent → API key B → deploy commands → web servers only
database agent → API key C → db commands → database server only ┌─ monitoring agent (read-only, all servers)
├─ deployment agent (deploy commands, web only)
MCP clients ──────┼─ database agent (db commands, db server only)
└─ ...
│
▼
ssh-mcp
│
centralized policies
│
┌──────────┼──────────┐
▼ ▼ ▼
web db monitoring
servers servers serversこの構成を示す最小限の設定例:
{
"version": 1,
"ssh_targets": {
"web-1": { "host": "10.0.1.10", "username": "deploy" },
"db-1": { "host": "10.0.1.20", "username": "dbadmin" }
},
"allowed_commands": {
"default": {
"web-1": { "allow": ["uptime", "df -h", "free -m"] }
},
"api_keys": {
"deploy-key": {
"web-1": { "allow": ["systemctl restart app", "deploy *"] }
},
"db-key": {
"db-1": { "allow": ["systemctl restart postgres", "pg_dump *"] }
}
}
}
}課題
ほとんどの MCP SSH サーバーはローカルの stdio プロセスとして実行されます。クライアントごとにプロセスが分かれ、共有状態、集中認可、監査証跡がありません。複数の AI エージェント、CI パイプライン、ダッシュボードが SSH アクセスを必要とする場合、それぞれが独自の SSH キーを管理し、独自の MCP プロセスを実行します。これにより次の問題が発生します:
集中アクセス制御がない — 各クライアントが実行内容を自分で決めてしまう
監査証跡がない — コマンドは運用チームから見えない
SSH キーの乱立 — キーがエージェントを実行するすべてのマシンに散在する
レート制限がない — 暴走したエージェントがターゲットを圧倒する可能性がある
コネクションプーリングがない — 各クライアントが独立して SSH セッションを開閉する
ssh-mcp は、単一の MCP サーバーを HTTP ゲートウェイとしてデプロイすることでこの問題を解決します。すべてのクライアントがゲートウェイに接続し、ゲートウェイが SSH ターゲットに接続します。認可、認証、レート制限、コネクションプーリング、監査ログは一か所で行われます。
ユースケース
マルチエージェントサーバー管理
アクセスレベルが異なる AI エージェントのチームを運用します。デプロイエージェントは Web サーバー上で systemctl restart nginx を実行でき、監視エージェントはどこでも journalctl を実行でき、データベースエージェントは DB サーバー上で psql のみ実行できます。各エージェントは独自の API キーで認証され、各キーには独自の権限セットがあります。
CI/CD パイプライン統合
すべてのランナーで SSH キーを管理する代わりに、CI パイプラインを ssh-mcp に向けます。パイプラインごとに単一の API キー、CI サブネット用のネットワークベースのルール、コマンド許可リストにより、デプロイスクリプトは意図したコマンドだけを実行します。それ以上は実行されません。
ログと設定の一元的な取得
ssh_download_file を使用すると、MCP クライアントから離れることなく、リモートサーバーからログ、設定ファイル、データベースダンプを取得できます。8 層のパス検証とサンドボックスルート設定により、ファイル転送は安全な境界内に留まります。
サーバーヘルスダッシュボード
uptime、free、df、ps をフリート全体で照会する MCP ベースのダッシュボードを構築します。コネクションプールが SSH セッションを再利用し、サーキットブレーカーが障害のあるターゲットを隔離し、/metrics の Prometheus メトリクスが既存の監視スタックに供給されます。
コンプライアンスと監査
すべてのコマンドは構造化 JSONL で記録されます。誰が、どのサーバーで、どの IP から、何を実行したか、許可されたかどうか、所要時間。matched_via フィールドは、どの認可レイヤーが判断を下したかを正確に追跡します。設定変更は変更前後の状態とともに別途記録されます。
セキュリティモデル
ssh-mcp はすべてのレイヤーで多層防御を適用します。完全なセキュリティモデルは docs/SECURITY.md に記載されています。
セキュリティ境界: ssh-mcp は SSH の前に認可、認証、監査レイヤーを追加します。基盤となる SSH アカウントの権限を置き換えるものではありません。コマンドが許可された場合、SSH ユーザーはそのアカウントが持つ権限でコマンドを実行します。ゲートウェイ自体は TLS とネットワークアクセス制御で保護する必要があります。ログにはコマンド出力が含まれる可能性があるため、それに応じて取り扱ってください。
コマンド認可チェーン
コマンドは順序付けられた多層チェーンで評価されます。いずれかのレイヤーが拒否した場合、リクエストはそこで停止します。
レイヤー | チェック内容 | ||
1. ターゲット検証 | サーバー名が既知か? | ||
2. | コマンドがブロック済みの正規表現に一致するか? | ||
3. 危険パターン |
| ||
4. リダイレクトガード | シェルリダイレクトが | ||
5. セグメント化 | リダイレクトを除去し |
| |
6. | 全クライアントの許可/拒否ルール | ||
7. | キーごとの許可/拒否ルール | ||
8. | CIDR ごとの許可/拒否ルール | ||
9. 拒否 | 暗黙のフォールバック |
認証
API キーは X-API-Key または Authorization: Bearer ヘッダーで送信されます。キーは PBKDF2-HMAC-SHA256(100,000 回の反復、ランダムな 16 バイトのソルト)でハッシュ化され、定数時間比較で検証されます。生のキーが保存されることはありません。
入力サニタイズ
コマンド、ターゲット名、ログ文字列は処理前にサニタイズされます。ヌルバイトの除去、制御文字の削除、NFKC 正規化、そして block_patterns に対する ReDoS 保護 が適用されます。
パストラバーサル防止
SFTP 転送は、ヌルバイトチェック、制御文字の除去、ドットセグメントの正規化、シンボリックリンクの解決、サンドボックスルートの強制を含む 8 層のパス検証を通過します。
レート制限
クライアント IP ごとのスライディングウィンドウレートリミッター(60 リクエスト / 60 秒、/health は免除)。違反は Retry-After 付きの HTTP 429 を返します。
レート制限は settings.rate_limit で設定できます:
"settings": {
"rate_limit": {
"enabled": true, // set false to disable entirely
"max_requests_per_minute": 60, // max requests per client IP in the window
"window_seconds": 60.0, // sliding-window duration
"cleanup_interval_seconds": 300.0 // expired-entry GC interval
}
}注: レートリミッターはコンテナ起動時に初期設定から一度だけ構築され、設定のホットリロードでは再構築されません。レート制限を無効にするには、起動時に存在する設定(例: マウントされたボリューム内の
config/ssh-mcp-config.json)でsettings.rate_limit.enabledをfalseに設定する必要があります。これは、単一の IP から多数のリクエストを発行する高トラフィッククライアントやテストスイートに便利です。
クイックスタート
前提条件
Docker と Docker Compose
接続先サーバー用の SSH キーペア(またはターゲットごとのパスワード)
1. ディレクトリを準備する
mkdir -p config logs
ssh-keygen -t ed25519 -f ssh_key -N ""
cp default-config.json config/ssh-mcp-config.json2. SSH ターゲットを追加する
config/ssh-mcp-config.json を開き、ターゲットを 1 つ追加します:
{
"version": 1,
"ssh_targets": {
"web-server": {
"host": "192.168.1.10",
"port": 22,
"username": "deploy",
"private_key": "/app/ssh_key"
}
},
"block_patterns": [ "\\brm\\s+-rf\\b", "\\bdd\\s+if=" ],
"allowed_commands": {
"default": [
{ "targets": ["*"], "commands": ["hostname", "uptime", "free", "df", "ps", "ls", "cat"] }
]
},
"settings": {}
}3. サーバーを起動する
docker compose up -d --build4. 起動を確認する
curl http://localhost:9080/health
# {"status": "ok", "connection_pool": {...}}5. MCP クライアントを接続する
Streamable HTTP をサポートする MCP クライアントは接続できます。API キーヘッダーを付けて http://localhost:9080/mcp を指定します。詳細は MCP クライアント構成 を参照してください。
6. サーバーを一覧表示してコマンドを実行する
curl -X POST http://localhost:9080/mcp \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ssh_list_servers",
"arguments": {}
}
}'
curl -X POST http://localhost:9080/mcp \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "ssh_execute_command",
"arguments": {"server_name": "web-server", "command": "uptime"}
}
}'MCP クライアント構成
Streamable HTTP トランスポートをサポートする MCP クライアントは接続できます。設定形式はクライアントによって異なります。以下の URL とヘッダーを使用してください。
設定 | 値 |
トランスポート | Streamable HTTP |
URL |
|
認証 |
|
汎用 Streamable HTTP 構成
{
"mcpServers": {
"ssh": {
"url": "http://localhost:9080/mcp",
"headers": {
"Authorization": "Bearer <your-api-key>"
}
}
}
}Python クライアント
import requests
MCP_URL = "https://ssh-mcp.example.com/mcp"
API_KEY = "your-api-key"
def call_tool(name: str, arguments: dict) -> dict:
response = requests.post(
MCP_URL,
headers={
"Content-Type": "application/json",
"X-API-Key": API_KEY,
},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {"name": name, "arguments": arguments},
},
)
response.raise_for_status()
return response.json()
print(call_tool("ssh_list_servers", {}))
print(call_tool("ssh_execute_command", {
"server_name": "web-server",
"command": "uptime",
}))生の JSON-RPC
ツール呼び出しを JSON-RPC の tools/call リクエストとして /mcp に送信します:
curl -X POST http://localhost:9080/mcp \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ssh_execute_command",
"arguments": {"server_name": "web-server", "command": "uptime"}
}
}'ツール
すべてのツール呼び出しは JSON-RPC の tools/call リクエストとして /mcp に送信されます。すべてのツールは文字列(JSON またはプレーンテキスト)を返します。
Tool | Parameters | Description |
| (なし) | 設定済みのSSHターゲット(ホスト、ポート、ユーザー名 — シークレットは含まない)を一覧表示します |
|
| 現在のクライアントがターゲット上で実行できるコマンドを一覧表示します(default + api_key + networkルールの和集合) |
|
| SSH経由でコマンドを実行します。stdoutを返します(stderrは |
|
| SFTP経由でファイルをダウンロードします。認可は |
|
| SFTP経由でファイルをアップロードします。認可は |
|
| ターゲットの |
例
# List available servers
call_tool("ssh_list_servers", {})
# {"web-server": {"host": "192.168.1.10", "port": 22, "username": "deploy"}}
# List what this client can run on web-server
call_tool("ssh_list_allowed_commands", {"server_name": "web-server"})
# ["cat", "df", "du", "free", "grep", "head", "hostname", ...]
# Execute a command
call_tool("ssh_execute_command", {
"server_name": "web-server",
"command": "uptime",
})
# " 07:12:33 up 10 days, 2:15, 1 user, load average: 0.08, 0.03, 0.01"
# Download a file
call_tool("ssh_download_file", {
"server_name": "web-server",
"remote_path": "/etc/hostname",
})
# "web-server\n"
# Upload a file
call_tool("ssh_upload_file", {
"server_name": "web-server",
"remote_path": "/tmp/backup.sql",
"content": "CREATE TABLE ...;\n",
"permissions": "0640",
})
# "OK: Uploaded 19 bytes to /tmp/backup.sql"
# Check SSH connectivity
call_tool("ssh_check_connection", {"server_name": "web-server"})
# {"success": true, "output": "ping", "error": null, "exit_code": 0, "checkcommand": "echo ping"}
# Check with custom timeout
call_tool("ssh_check_connection", {"server_name": "web-server", "timeout": 5})sudoに関する注意:
sudo_passwordパラメータはありません。sudoがパスワードを要求する場合、設定内のターゲットのpasswordフィールドから取得されます。sudoフラグはsudo -S -p ''(設定からのパスワード)またはsudo -n(パスワードなし)でラップします。
エラーレスポンス
失敗時、ツールは以下を返します:
{
"error": true,
"error_type": "AuthorizationError",
"message": "Command rejected: target 'foo' not found",
"retryable": false,
"request_id": "abc-123"
}一般的なerror_typeの値: AuthorizationError、PathValidationError、FileTransferError、SSHAuthenticationError、SSHTimeoutError、MCPSSHError。retryableフラグはSSHTimeoutErrorの場合にtrueになります。レート制限違反は代わりにHTTP 429を返します。
設定
設定ファイルの場所
サーバーは<config_dir>/ssh-mcp-config.jsonを読み取ります。config_dirは--configCLIフラグまたはMCP_SSH_CONFIG_PATH環境変数で設定します(デフォルト: /config)。ファイルが存在しない場合、サーバーはバンドルされたdefault-config.jsonを書き込みます。
トップレベルの構造
{
"version": 1,
"ssh_targets": { ... },
"block_patterns": [ ... ],
"allowed_commands": {
"default": [ ... ],
"api_keys": [ ... ],
"networks": [ ... ]
},
"settings": { ... }
}設定はロード時にconfig.schema.json(JSON Schema Draft 2020-12)に対して検証されます。不明なキーはハードエラーを引き起こします。
ssh_targets
サーバー識別子をキーとするオブジェクト。各ターゲットにはhost、port、usernameと、private_keyまたはpasswordの少なくとも一方が必要です。
"ssh_targets": {
"web-server": {
"host": "192.168.1.10",
"port": 22,
"username": "deploy",
"private_key": "/app/ssh_key",
"checkcommand": "echo ping"
}
}フィールド | 必須 | デフォルト | 説明 |
| はい | — | ホスト名またはIPアドレス |
| いいえ |
| SSHポート |
| はい | — | SSHユーザー名 |
| * | — | サーバーのファイルシステム上のSSH秘密鍵ファイルへのパス |
| * | — | SSHパスワード( |
| いいえ |
|
|
* private_keyまたはpasswordの少なくとも一方が必要です。
private_keyはサーバーのファイルシステム上のパスです(Dockerではコンテナにマウントされます)。インラインキーではありません。
block_patterns
正規表現パターンのリスト。パターンに一致するコマンドは、他の許可リスト層にかかわらず拒否されます。パターンはロード時に破滅的バックトラッキング構造がないか検査され(ReDoS保護)、実行時にはタイムアウトガード付きでコンパイルされます。
allowed_commands
3つのサブオブジェクトが、各クライアントが実行できるコマンドを制御します:
default— すべてのクライアント向けのルール(より具体的なレイヤーが先に決定しない限り)api_keys— キーごとのルール。key_hashで照合されますnetworks— CIDRごとのルール。クライアントの送信元IPで照合されます
各ルールにはtargetsリスト(サーバーIDまたは全対象の"*")とcommandsリスト(基本コマンド名または任意のコマンドの"*")があります。
"allowed_commands": {
"default": [
{ "targets": ["*"], "commands": ["hostname", "uptime", "free", "df", "ps"] }
],
"api_keys": [
{
"name": "ci-bot",
"key_hash": "pbkdf2:sha256:100000$<salt>$<hash>",
"rules": [
{ "targets": ["web-server"], "commands": ["systemctl", "journalctl"] }
]
}
],
"networks": [
{
"name": "home-lan",
"range": "192.168.1.0/24",
"rules": [
{ "targets": ["*"], "commands": ["*"] }
]
}
]
}settings
設定 | デフォルト | 説明 |
|
| クライアントに返されるコマンド出力の最大バイト数(整数またはサイズ文字列) |
|
| コマンドタイムアウトの上限(秒) |
|
| 一時的なSSH障害時の再試行回数 |
|
| 指数バックオフの基準値(秒) |
|
| ターゲットごとにサーキットブレーカーが開くまでの失敗回数 |
|
| 開いたサーキットの回復タイムアウト(秒) |
|
| ログレベル: DEBUG、INFO、WARNING、ERROR |
|
| ログエントリに保存される出力の最大文字数 |
|
| ローテーションされたログファイルをGzip圧縮する |
|
| ターゲットごとのプールされるSSH接続の最大数 |
|
| アイドル接続のタイムアウト(秒) |
|
| プールのクリーンアップ間隔(秒) |
|
| 全ターゲットにわたるグローバル上限。超過時はHTTP 503を返します |
|
| 設定リロード間の最小間隔。 |
|
| 信頼するリバースプロキシのIP(IPv4/IPv6) |
SFTP設定(settings.sftp)
設定 | デフォルト | 説明 |
|
| SFTPパス検証のルートディレクトリ |
|
| SFTPパスの最大許容長(バイト)。 |
シークレット
SSHターゲットのパスワードとAPIキーのハッシュは、メイン設定から分離して<config_dir>/secrets.jsonまたはMCP_SSH_SECRET_*環境変数に格納できます。優先順位:
environment variables > secrets.json > ssh-mcp-config.jsonシークレットソース | 効果 |
| ターゲットごとの |
|
|
|
|
<TARGET_ID>と<KEY_NAME>は大文字化され、-は_に変換されます。APIキーの値はハッシュ文字列である必要があり、生のキーではありません。
環境変数とCLIフラグ
環境変数 | CLIフラグ | デフォルト | レガシーフォールバック |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| — |
| — |
| — |
| — | (API有効時に必須) | — |
— |
|
| — |
— |
| — | — |
CLIフラグは環境変数より優先されます。settingsの任意のキーは、実行時にMCP_SSH_SETTING_<KEY>(大文字化、- → _)で上書きできます。
ホットリロード
サーバーは設定ファイルの変更をポーリングします(15秒間隔、2秒デバウンス)。変更が検出されると、再読み込み、検証を行い、新しい設定をアトミックに入れ替えます。設定変更コールバック(認可ルールの再構築、接続プールの更新)は、入れ替えが成功した後に実行されます。利用可能な場合はウォッチドッグベースのファイル監視が使用されます。
可観測性
ヘルスチェック
GET /healthは{"status": "ok"}と接続プールの統計情報を返します。コンテナのHEALTHCHECKはこのエンドポイントを使用します。
Prometheusメトリクス
GET /metricsは専用レジストリにメトリクスを公開します。すべてmcpssh_プレフィックスが付きます:
メトリクス | タイプ | ラベル |
| Counter |
|
| Counter |
|
| Histogram |
|
| Counter |
|
| Histogram |
|
| Gauge |
|
| Gauge |
|
| Counter |
|
構造化ロギング
mcp-sshサーバーは、設定ファイルのsettings.logging.log_targetsで構成されるプラグイン可能なログターゲットをサポートしています。各ターゲットは、すべてのログエントリを受け取る独立したドライバーです。
デフォルトの動作
デフォルトでは、ログエントリは人間が読めるテキスト形式でstdoutに書き込まれます。これは、コンテナログがランタイムによって取得されるDocker環境に適しています。
ログターゲットの種類
Target | Config value | Format | Description |
Stdout |
| Text | 標準出力に書き込みます。デフォルトのターゲット。 |
JSON File |
| JSONL | 1行に1つのJSONオブジェクトをファイルに書き込みます。 |
Text File |
| Text | 人間が読みやすいテキストをファイルに書き込みます。 |
設定
{
"settings": {
"log_level": "INFO",
"logging": {
"log_targets": [
{ "target": "stdout" },
{ "target": "jsonfile", "filepath": "logs/ssh-mcp.log" }
],
"max_log_output": 4096,
"compress_rotated": true
}
}
}ログレベル
設定ファイル:
settings.log_levelを設定してデフォルトレベルを制御します。環境変数:
MCP_SSH_LOG_LEVELを設定すると、設定ファイルのデフォルトを上書きします(例:MCP_SSH_LOG_LEVEL=DEBUG)。ターゲットごと: 各ログターゲットは独自の
log_levelを持ち、デフォルトを上書きできます。
レガシー設定
settings.logging が存在しない場合、サーバーはログディレクトリ(デフォルトでは /logs)内の単一の JSONL ファイルターゲットにフォールバックします。これにより、既存の設定との後方互換性が維持されます。
テキストフォーマット
標準出力およびテキストファイルターゲットは次の形式を使用します:
2025-01-15 10:30:00 INFO ssh_execute_command: Command executed on server1JSONフォーマット
JSONファイルターゲットは1行に1つのJSONオブジェクトを書き込みます:
{"timestamp": "2025-01-15T10:30:00+00:00", "event": "ssh_execute_command", "level": "INFO", "message": "Command executed on server1", "request_id": "abc-123", "log_level": "INFO", "log_format_version": 1}ファイルローテーション
ファイルベースのターゲットは max_file_size_mb(デフォルト: 10 MiB)を超えるとローテーションされ、backup_count 個のバックアップ(デフォルト: 5)が保持されます。ローテーションされたファイルは、compress_rotated が true の場合に gzip 圧縮されます。
設定変更イベント
Event | Meaning |
| 起動時に初期設定が読み込まれた |
| 設定がディスクから再読み込みされた( |
| スキーママイグレーションが適用された( |
| 同梱のデフォルト設定がコピーされた |
| メモリ内デフォルトにフォールバックした |
| 設定変更コールバックが例外を発生させた |
設定APIとWebダッシュボード
統合コンテナには、オプションの 設定APIとWebダッシュボード が含まれています。これは、SSHポリシー、ターゲット、コマンドルール、バックアップのための完全な管理プレーンです。設定ファイルの編集は不要です。この機能はデフォルトで無効になっています。
機能概要
Webダッシュボード — 5つのページ(SSH Targets、Block Patterns、Command Rules、Settings、Backups)を備えたレスポンシブなシングルページアプリケーションです。APIトークンでログインし、ブラウザからすべてを管理できます。
REST API — すべての設定セクションに対する完全なCRUDに加え、設定検証、APIキーのハッシュ化、バックアップ管理、インラインSSH接続テストを提供します。
APIキーハッシュ化ユーティリティ — プレーンテキストのAPIキーを、設定にそのまま使えるPBKDF2文字列にハッシュ化します。ハッシュ形式を推測する必要はもうありません。
バックアップとリストア — 書き込みのたびに設定を自動バックアップします。ダッシュボードまたはAPIからバックアップの一覧表示、リストア、削除が可能です。
アトミックでスレッドセーフな書き込み — すべての設定書き込みは検証され、スレッドロックで直列化され、ディスクにアトミックに書き込まれます。
Swagger UI & ReDoc —
/api/docsと/api/redocに自動生成された対話型APIドキュメントを提供します。
設定APIを有効にする
compose.yaml または .env ファイルで以下の環境変数を設定します:
Variable | Default | Description |
|
| Configuration API を有効にするには |
| (有効時は必須) | APIリクエストを認証するためのBearerトークン |
services:
mcp-ssh:
environment:
CONFIG_API_ENABLED: "true"
CONFIG_API_TOKEN: "your-secret-token-here"APIエンドポイント
すべてのエンドポイントは、MCPサーバーと同じStarlette ASGIアプリケーションの /api にマウントされます。
ヘルスとユーティリティ
Method | Path | Description |
|
| 設定APIのヘルスチェック(認証不要) |
|
| プレーンテキストのAPIキーをPBKDF2-HMAC-SHA256文字列にハッシュ化します |
|
| 設定JSONスキーマを返します(認証不要) |
|
| ディスクに書き込まずに設定dictを検証します |
設定
Method | Path | Description |
|
| 完全な設定を取得します(シークレットは秘匿されます) |
|
| 完全な設定を置き換えます |
|
| 単一の設定セクションを取得します( |
|
| 単一の設定セクションを置き換えます |
SSHターゲット
Method | Path | Description |
|
| 特定のSSHターゲットを取得します(シークレットは除去されます) |
|
| SSHターゲットを作成または置き換えます |
|
| SSHターゲットを削除します |
|
| ターゲットの |
コマンドルール
Method | Path | Description |
|
| 許可コマンドルールを一覧表示します( |
|
| 許可コマンドルールを置き換えます( |
ブロックパターン
Method | Path | Description |
|
| ブロックパターンを一覧表示します( |
|
| すべてのブロックパターンを置き換えます |
|
| ブロックパターンを追加します |
|
| インデックスを指定して単一のブロックパターンを置き換えます |
|
| インデックスを指定して単一のブロックパターンを削除します |
バックアップ
Method | Path | Description |
|
| 設定バックアップを一覧表示します(新しい順) |
|
| バックアップから設定をリストアします |
|
| バックアップファイルを削除します |
認証
/api/health と /api/config/schema を除くすべてのAPIリクエストでは、Authorization ヘッダーに Bearer トークンが必要です:
curl -H "Authorization: Bearer your-secret-token-here" http://localhost:9080/api/configWebダッシュボード
有効にすると、http://localhost:9080/ui/ で レスポンシブなシングルページアプリケーション を利用できます。これは Tailwind CSS で構築された完全な管理UIです。ページリロードなし、すべての操作に対するトースト通知、編集用のモーダルダイアログを備えています。
Page | Capabilities |
SSH Targets | ターゲットの表示、追加、編集、削除; |
Block Patterns | 個々のパターンの追加、編集(インデックス指定)、削除; パターン一覧の表示 |
Command Rules | デフォルト、APIキー、ネットワークルールの編集; ターゲットとコマンド一覧を備えた完全なルールエディタ |
Settings | すべてのサーバー設定を編集: SFTPサンドボックス、レート制限、ロギング、接続プーリング、サーキットブレーカーなど |
Backups | 設定バックアップの一覧表示、リストア、削除; 各バックアップのタイムスタンプとサイズ |
追加機能:
トークンベースのログイン — セッション管理付き(
sessionStorageに保存)設定検証 — 変更は書き込み前に検証されます
APIキーのハッシュ化 — ダッシュボードから直接プレーンテキストのキーをハッシュ化します
レスポンシブデザイン — デスクトップとモバイルで動作します
トースト通知 — すべての操作に対する成功/エラーのフィードバック
Swagger / ReDoc
対話型APIドキュメントは FastAPI によって自動生成されます:
Swagger UI:
http://localhost:9080/api/docsReDoc:
http://localhost:9080/api/redoc
デプロイ
Docker Compose
compose.yaml は、MCPサーバーと、オプションとして設定API & Webダッシュボードの両方をホストする単一の mcp-ssh サービスを定義します。設定APIは CONFIG_API_ENABLED 環境変数(デフォルト: false)で有効になります。
mcp-ssh — MCP SSH ゲートウェイ + 設定API
Host path | Container path | Mode |
|
| rw |
|
| rw |
|
| ro |
|
| ro |
ホストのポート 9080 で公開されます(コンテナのポート 8080 にマッピング)。ランタイムイメージは python:3.13-alpine で、ダイジェストがハッシュ固定されています。非rootユーザー mcpssh がプロセスを実行します。CycloneDX SBOM はビルド時に sbom ステージで生成されます。
設定API & Webダッシュボード(オプション)
.env ファイルまたは環境で CONFIG_API_ENABLED=true と設定すると、設定APIが有効になります:
# Generate an auth token
openssl rand -hex 32CONFIG_API_ENABLED=true
CONFIG_API_TOKEN=<your-token>有効にすると、設定APIはMCPゲートウェイと同じHTTPサーバーの /api にマウントされます。以下を提供します:
REST API —
http://localhost:9080/api/...に、SSHターゲット、ブロックパターン、コマンドルール、バックアップ、設定の完全なCRUDを提供しますWebダッシュボード(GUI) —
http://localhost:9080/ui/に、ビジュアルなポリシー管理のためのシングルページアプリケーションを提供します(SSHターゲット、ブロックパターン、コマンドルール、設定、バックアップ)APIドキュメント —
http://localhost:9080/api/docs(Swagger UI)とhttp://localhost:9080/api/redoc(ReDoc)
Makefile
コマンド | 説明 |
| Dockerイメージをビルドする( |
|
|
|
|
| 単体テストを実行する |
| config-apiの単体テストを実行する |
| テストイメージをビルドし、統合テストを実行する |
| テスト成果物とコンテナを削除する |
GHCRからのプル
Dockerイメージは自動的にビルドされ、GitHub Container Registryに公開されます:
docker pull ghcr.io/gelse/ssh-mcp:latest制限事項と脅威モデル
ssh-mcpができないこと
シェルではない。 対話型ターミナルセッションは利用できません。すべての実行はワンショットのコマンド呼び出しです。
ファイルマネージャーではない。 SFTPは、パス検証とサンドボックス強制を備えた単一ファイルのアップロード/ダウンロードに限定されます。ディレクトリ一覧表示や再帰的操作はできません。
ネットワークファイアウォールではない。 レート制限はIP単位で、デフォルト値は固定されています。暴走するクライアントからは保護しますが、執拗な攻撃者からは保護しません。
脅威モデル
脅威 | 対策 |
チェインによるコマンドインジェクション( | コマンド分割 — 各セグメントが完全な認可チェーンを実行します |
シェルリダイレクションによる機密パスへの書き込み( | リダイレクト先ガードが |
SFTPでのパストラバーサル | 8層のパス検証:nullバイトチェック、制御文字の除去、ドットセグメントの正規化、シンボリックリンク解決、サンドボックスルートの強制 |
| 読み込み時の静的スクリーニング+実行時タイムアウトガード |
APIキーのブルートフォース攻撃 | 定数時間検証を備えたPBKDF2-HMAC-SHA256、IP単位のレート制限 |
ログインジェクション | ログ記録前に、ユーザー制御のすべてのフィールドに対して改行サニタイズを実施 |
設定内のシークレット |
|
対象外
TLS終端(リバースプロキシで処理)
APIキーを超えたユーザー認証(アプリケーション層でのOAuth、mTLSはなし)
SSHセッションマルチプレクシング(tmux/screenパススルーなし)
監査ログの改ざん防止(ログはローカルファイルです。不変性の確保には独自のログ転送を使用してください)
開発
プロジェクト構成
server.py— FastMCPアプリのファクトリ+CLIエントリポイントlib/— 単一責任のモジュール30個(認証、設定、SSHクライアント、ファイル転送、ロギングなど)config-api/— 設定API+Webダッシュボード(FastAPI、CONFIG_API_ENABLED=trueのときに/apiにマウント)tests/— 単体テストファイル36個+実際のDockerコンテナを使用した統合テスト
技術スタック
Python 3.13, FastMCP 3.4.x, paramiko 5.0, Starlette 1.4, FastAPI 0.115+, Pydantic 2.10+, httpx 0.28+, uvicorn 0.34+
テストの実行
# Unit tests (fast inner loop)
source .venv/bin/activate
python -m pytest tests/test_<module>.py -x
# Full unit test suite
make test
# Integration tests (requires Docker)
make integrationtest新しいツールの追加
AGENTS.mdの実例では、新しい @mcp.tool() ハンドラをエンドツーエンドで追加する手順を説明しています:定数、型、再エクスポート、ハンドラ、テスト、コミット。
リンター/型チェックツールなし
このプロジェクトには ruff、mypy、pyright、flake8 の設定はありません。フォーマットは .editorconfig のデフォルト(Pythonはスペース4つ、88文字行)に従います。
ロードマップ
ビジュアルなポリシー管理のための設定GUI
ライセンス
MIT License — 詳細は LICENSE を参照してください。
This server cannot be installed
Maintenance
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables secure remote access operations through SSH, SFTP, rsync, VPN, and tunneling with enterprise-grade policy enforcement and audit logging. Provides AI assistants with secure, policy-driven access to remote systems while maintaining comprehensive audit trails and zero-trust security.1Apache 2.0
- AlicenseBqualityAmaintenanceProvides policy-driven, auditable SSH access to server fleets for AI assistants with zero-trust security controls, command whitelisting, and comprehensive audit logging to safely manage infrastructure.1327Apache 2.0
- AlicenseAqualityCmaintenanceEnables AI assistants to securely execute remote SSH commands, perform file transfers, and monitor system status through a standardized interface. It features robust security controls including command whitelisting, blacklisting, and credential isolation to prevent unauthorized operations.1022MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to securely execute SSH commands on remote servers with connection pooling, session isolation, and a web audit panel.3MIT
Related MCP Connectors
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Let AI operate servers without SSH. Choose actions, approve risky changes, and audit every step.
Agent payments, API key vaulting, and governed mandates. Agents spend within user-defined limits.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/gelse/ssh-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server