SSH MCP Server
官方透過SSH從您的代理程式執行指令、移動檔案、搜尋日誌及稽核機器。
你可以用 SSH MCP 做什麼?
- 以安全防護執行指令 — 要求您的助理透過
ssh_exec執行單一或批次指令,並具備破壞性指令防護,可在不可逆操作送達伺服器前予以阻擋。 - 讀取、寫入及列出遠端檔案 — 使用
ssh_file_read、ssh_file_write和ssh_file_list檢查或修改檔案,支援原子寫入及選用的 SHA-256 驗證。 - 搜尋日誌並檢查伺服器健康狀態 — 跨檔案與容器查詢
ssh_log_search或ssh_log_tail,或透過ssh_snapshot與ssh_audit_baseline取得結構化的健康狀態快照。 - 傳輸檔案並進行完整性檢查 — 透過
ssh_upload和ssh_download上傳或下載檔案與目錄,並針對較舊裝置自動使用傳統scp作為備援方案。 - 管理長時間執行的背景工作 — 使用
ssh_exec將緩慢操作分離至背景,並透過ssh_job_status、ssh_job_output和ssh_job_kill追蹤,即使斷線也能持續運作。
文件
SSH MCP Server — 供 AI 代理使用的遠端伺服器工具
|
|
一個 SSH MCP 伺服器——一個多功能工具,能為你和你的 AI 代理在除錯、開發和伺服器維護上節省時間與 token。 透過 SSH 執行指令、移動檔案、讀取日誌和稽核機器——無論是雲端 VPS、裸機伺服器,還是你家角落那台 BusyBox 路由器。 |
它使用你機器上已有的 OpenSSH 用戶端:你的金鑰、你的 ~/.ssh/config、你的跳板主機、你的代理轉發。無需捆綁任何東西,無需編譯,沒有原生綁定。
適用於 Claude Code、Codex CLI、Cline、opencode、Gemini CLI、Qwen Code、Hermes 及其他 MCP 用戶端。
安裝 · 工具 · 設定 · 安全性 · 路線圖 · 文件 · 變更日誌
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"
}
}
}
}
然後建立 ~/.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
此外掛會讀取 ~/.claude/ssh-profiles.json,除非 SSH_PROFILES_FILE 另有指定,所以
請先建立該檔案,伺服器啟動時就會載入你的機器。
系統需求
Node.js 18+ 以及系統上的 ssh 用戶端,適用於 PATH。在 Windows 上,請使用金鑰型設定檔;
目前不提供密碼和通行密碼片語設定檔。
偏好固定版本、離線工作,或每次啟動少一次 registry 檢查:
npm install -g @hypnosis/ssh-mcp-server,然後使用 ssh-mcp-server 作為指令,而不是
npx。
適用對象
- DevOps 和 SRE——想要更快的稽核、事件檢查和例行伺服器工作。
- Vibe 程式設計師和獨立開發者——使用 AI 助理發布產品,並在自己伺服器上運行所建構的內容。
- 系統管理員和平台工程師——想要結構化工具,而不是不受限制的原始 shell。
- 自行運行 VPS 的開發者和小型團隊——沒有專職維運團隊。
- Homelab、NAS 和路由器擁有者——其實用硬體已超出其現代協定的支援範圍。
為什麼選擇 SSH MCP 伺服器而不是原始 shell
更少的 token,更低的 AI 成本
原始 shell 給 AI 代理的是大量資訊流:重複的指令、ASCII 表格和日誌傾印。 它會消耗 token 將這些雜訊轉換成伺服器狀態的圖像——也就是你的錢。
更快的伺服器除錯
專用工具會批次處理例行檢查、限制過多輸出,並回傳重要的部分。 代理花較少時間翻譯終端機輸出,能更快找到修復方法。
更少的猜測,更少的 AI 錯誤
結構化答案會說明找到了什麼、哪些無法測量、哪些被截斷。 這讓代理沒有太多空間用幻覺填補空白——也讓你減少錯誤 修復、更安心的部署和更可靠的程式碼。
SSH 相容性:現代伺服器、舊設備和 Windows
使用你現有的 OpenSSH 設定
沒有捆綁的 SSH 實作、沒有原生綁定、不需要為每個平台重新建置。指令透過
系統 ssh 用戶端執行,所以你的金鑰、你的 ~/.ssh/config、你的跳板主機和你的代理
轉發都能像在終端機中一樣正常運作。在支援的情況下,每個目的地共用一個
多工連線,表示你只需驗證一次,而不是每個指令都驗證一次。
對舊版伺服器、路由器和 NAS 裝置的 SSH 支援
傳送檔案到一台使用現代 scp 的路由器,你會得到這個結果:
scp app.conf router:/etc/
# scp: subsystem request failed on channel 0
沒有什麼壞掉——目前的 scp 會說新協定,而路由器不知道。在終端機中,你現在得去
讀論壇文章,然後帶著額外的旗標回來。在這裡你什麼都不用做:傳輸會先嘗試、拒絕會被辨識、
改用舊協定,而且該機器會被記住,所以下次檔案會直接送達。
舊版 SSH 用戶端和缺少工具的後備方案
舊設備會得到後備方案,而不是死路。當現代功能缺失時,伺服器會在可行的情況下 走較舊的路徑:
| 你的機器 | 你會得到什麼 |
|---|---|
| 一台太小、無法進行現代檔案傳輸的路由器或 NAS | 檔案仍然會送達——自動使用舊協定 |
| 一台十年前的老伺服器 | 工作流程仍然有效;只是每個指令會開啟新連線,而不是重複使用 |
| 一個精簡映像,沒有辦法計算檔案雜湊 | 上傳會顯示「無法驗證」,而不是宣稱沒人檢查過的相符 |
| 一台根本沒安裝某工具的機器 | 答案會說「未測量」——絕不會是讀起來像「什麼都沒有」的零 |
專為 Model Context Protocol 打造
基於官方 MCP SDK,全程使用 TypeScript,超過 2500 個單元測試,加上針對真實容器 而非模擬物件運行的即時測試套件。
原始 SSH 與 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 表格 | 單一結果中的具名字段 | 一次呼叫、具名字段和更少的往返 |
| 缺少工具可能看起來像空輸出 | unavailable 會說明哪些未測量 | 更少的猜測和更少的錯誤修復 |
| 你自行整理磁碟、服務和錯誤 | 問題訊號已經浮現 | 更快的除錯 |
完整的 ssh_audit_baseline 結果可能比少數原始指令輸出更長——
在我們的實驗室測量中約為 1,077 個 token,而原始方式為 765 個。節省來自於完整的
工作流程,而不是讓單一回應更短。
在真實的故障排除過程中,專用工具將 49 次單獨的指令呼叫減少為 4 次 MCP 呼叫。每次額外的呼叫都會以累積的對話內容開始另一個模型回合。 提示快取可以降低重複輸入的成本,但新指令及其輸出仍會消耗上下文。更少的往返 意味著整個工作階段中更少的 token、更少的重複分析,以及更快的答案路徑。
需要全貌而不是脈搏? 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
第三個指令看起來很乾淨,但 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 | 你的收穫 |
|---|---|---|
| 四次搜尋和四個輸出 | 一次跨檔案和 glob 的搜尋 | 更少的 token 和往返 |
| 權限錯誤可能消失 | files_unreadable 會說明每個錯過的路徑 | 不會有錯誤的「日誌乾淨」結論 |
| 輸出可能無限增長而沒有實用上限 | limited 和 truncated 會揭露每個截斷點 | 從部分結果做出更安全的決策 |
since 使用伺服器的時鐘,namesOnly: true 只回傳相符的路徑,而
ssh_log_tail 在一次呼叫中讀取多個日誌的最後 N 行。
安全的遠端設定編輯
情境: 你需要在線上伺服器上替換 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
退出碼為零表示 shell 完成了。它不能證明哪些位元組實際寫入,而且 >
在新檔案的第一個位元組到達之前就截斷了舊檔案。如果連線在寫入中途
中斷,服務就會留下部分設定。
結構化 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 | 你的收穫 |
|---|---|---|
| 目標在複製完成前就被截斷 | 完整的暫存檔透過一次重新命名取代它 | 不會有寫入一半的設定 |
| 只有退出碼 | 位元組數和驗證結果都有名稱 | 你知道實際寫入了什麼 |
| 權限存在於 shell 文字中 | sudo、mode 和 verify 是每個檔案的字段 | 可預測的擁有權和更少的引號錯誤 |
verified 有三種誠實的結果:verified、伺服器沒有雜湊工具時的 unavailable,
以及未要求驗證時的 skipped。對於讀取,ssh_file_read 接受
路徑清單;ssh_file_list 處理 glob、遞迴、大小和權限模式。
使用 sudo 執行批次 SSH 指令
情境: 部署已準備就緒,但在流量切換前必須檢查 nginx 語法、服務狀態和近期錯誤。 一個失敗的檢查不應該消失在合併的傾印中。
問題:「每個預檢檢查都通過了嗎?」
原始 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
三個連線回傳三個不相關的輸出。如果指令用 ; 連接,shell 只會回報最後一個退出碼;
如果用 && 連接,後續的檢查會在第一次失敗後消失。
結構化 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 | 你的收穫 |
|---|---|---|
| 三次呼叫和不相關的輸出 | 一個有序的指令清單 | 更少的往返 |
| 合併的 shell 可能隱藏中間狀態 | 每個指令保留自己的 exit_code | 不會漏掉失敗的檢查 |
sudo 和引號在指令文字中重複出現 | sudo 套用於整個批次 | 更少的引號錯誤 |
破壞性指令防護會在執行第一個指令前檢查完整清單。如果某個條目
被拒絕,其他所有條目都會標記為未執行,且不會傳送任何內容到伺服器。
每個指令都帶有各自的 stdout 與 stderr。已執行但未輸出任何內容的指令,其值為空字串;從未執行的指令則完全沒有該欄位,因此兩者不會混淆。每個指令超過 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 | 你的優勢 |
|---|---|---|
| 任務綁定於單一 SSH 工作階段 | 遠端任務具有持久性 ID | 安全斷線與重新啟動 |
| 重新連線意味著搜尋程序與檔案 | 狀態與結束碼具有具名狀態 | 無需猜測是否完成 |
| 重新讀取輸出會重複舊文字 | 輸出從位元組偏移量繼續 | 長時間任務的 Token 使用量更低 |
任務狀態儲存在遠端磁碟上,而非此伺服器的記憶體中。ssh_job_status 區分 running、finished 與 lost;ssh_job_output 從最後的位元組偏移量繼續;而 ssh_job_kill 會對整個程序群組發出訊號,而不僅是其 shell。
傳輸檔案至舊款路由器與 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 驗證具有具名結果 | 損毀不會被誤認為成功 |
| 直接取代可能留下不完整的目標 | 暫存檔在傳輸後才移至定位 | 運作中的檔案能承受中斷 |
如果裝置既沒有 sha256sum 也沒有 openssl,結果會顯示 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
Shell 會先刪除目錄,然後才發現備份來源已消失。防護機制會看到後續步驟讀取已被先前步驟摧毀的目標,因此整個呼叫會留在你的機器上。相同的檢查也會攔截 dropdb app && pg_dump app > backup.sql。
拒絕不可逆的損失,警告可復原的變更
| 拒絕——容器本身 | 僅警告——其內容 |
|---|---|
DROP DATABASE、dropdb | DROP TABLE、TRUNCATE、DELETE FROM |
docker volume rm、docker compose down -v | docker rm -f <name> |
crontab -r | 編輯單一任務 |
mkfs、wipefs -a、lvremove、zfs destroy | chmod 777 |
reboot、shutdown、halt | git 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-1、kill 4871、systemctl stop app。
若要繼續,請指名要停止的對象。名稱會與指令實際觸及的內容進行比對,因此已漂移到其他對象的遮罩會被拒絕而非確認:
docker kill $(docker ps -q --filter ancestor=web) # CONFIRMED-KILL: edge
針對指令列的樣式是另一種情況。它會比對到攜帶該樣式的指令本身,因此執行它的 shell 會在目標之前被發出訊號,而回覆會在中途斷開。此類打擊不會被確認,而是被改寫——以編號方式,或將一個字元寫成字元類別,使樣式不再比對到自身:
pkill -f relay
# BLOCKED — two ways through:
# kill 4871
# pkill -f '[r]elay' # CONFIRMED-KILL: 4871
三種結果彼此區分:找到目標、展開未觸及任何內容,以及沒有可詢問的對象——機器上沒有引擎、答案被截斷、連線失敗。後兩者同樣屬於拒絕:不知道並非繼續進行的理由。
確認有意的破壞性指令
沒有什麼是永久禁止的。在已審查的指令中加入 # CONFIRMED-DESTRUCTIVE 即可允許通過。當防護機制拒絕批次中的一個項目時,整個批次會在執行前停止,因此伺服器絕不會處於半執行操作的狀態。
防護機制僅在單一呼叫內運作。它無法將一次呼叫中的刪除與下一次呼叫中的讀取關聯起來,也無法推斷它不認識的工具。它是安全帶,而非政策引擎:可復原的操作仍由你決定。路徑限制與引號規則記載於 docs/security.md。
工具
18 個用於伺服器操作的 SSH MCP 工具。完整參數與範例位於 docs/tools.md。
| 工具 | 功能 |
|---|---|
ssh_exec | 執行單一指令或批次,具破壞性指令防護與可選的分離模式 |
ssh_file_read | 讀取一個或多個檔案,文字或二進位 |
ssh_file_write | 以原子性重新命名寫入檔案,可選 SHA-256 驗證 |
ssh_file_list | 列出目錄,可選 glob 與遞迴 |
ssh_upload | 透過 SSH 上傳檔案或目錄,二進位安全且具完整性檢查;目錄可取代目標或合併至其中 |
ssh_download | 透過 SSH 下載檔案或目錄,二進位安全且具完整性檢查 |
ssh_job_status | 背景任務的狀態:執行中、已完成或已遺失 |
ssh_job_output | 從位元組偏移量讀取累積輸出 |
ssh_job_list | 列出任務,清除超過 TTL 的已完成任務 |
ssh_job_kill | 對任務的整個程序群組發出訊號 |
ssh_log_tail | 一個或多個日誌的最後 N 行,支援 glob;可依名稱指定容器 |
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_status | systemctl status 加上單一單元的 journalctl 尾部 |
MCP 工具安全註記
標準 MCP 註記會告知用戶端哪些工具是唯讀、破壞性、冪等或開放世界的。請參閱完整表格。
執行 SSH 指令並管理遠端檔案
指令、檔案讀寫、目錄列表——機器上的日常工作,每個答案都已經過解析。
監控長時間運作的 SSH 任務
緩慢的工作會被分離並追蹤,而非等待:每次查看都會顯示進度。
搜尋日誌並檢查伺服器健康狀態
檔案與容器的日誌,以及機器的單次快照,輸出有上限,因此尾部不會耗盡上下文視窗。
透過 SSH 上傳與下載檔案
具完整性檢查的二進位安全傳輸。詳情請見 docs/transfer.md。
對於二進位與大型檔案,請使用
ssh_upload/ssh_download——base64 區塊與 heredoc 既不二進位安全也不具原子性。
透過 SSH 稽核 Linux 伺服器
唯讀且批次化為單次往返。詳情請見 docs/audit.md。
Windows SSH 相容模式
Windows 會自動使用相容模式。當連線多工不可用時,伺服器會切換為每個指令一條連線。相同的工具仍可透過金鑰型 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 密碼與通行密碼短語排除在設定檔之外
優先使用金鑰。如果密碼或加密金鑰的通行詞短語無法避免,請將其保存在單獨的 secrets 檔案中,切勿放在設定檔本身:
{
"secretsFile": "~/.config/ssh-mcp/secrets.json",
"profiles": {
"production": {
"host": "server.example.com",
"username": "admin"
}
}
}
secrets 檔案以設定檔名稱為鍵——請參閱 secrets.json.example:
{
"production": { "password": "..." },
"buildbox": { "sudoPassword": "..." }
}
sudoPassword 是該機器上對 sudo 的回應。使用金鑰登入的設定檔沒有登入密碼可提供,
而當兩者不同時,登入密碼是錯誤的答案;若無此設定,則使用 password。
secrets 檔案必須僅供您本人讀取(chmod 600)。相對路徑從設定檔解析;
secrets 不會進入 argv,並會在日誌中被遮罩。請參閱
憑證安全。
設定 Claude Code、Codex 及其他 MCP 用戶端
選擇您使用的用戶端,並將其指向同一個設定檔。
Claude Code
一個指令;-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
一個指令,與其他相同:
qwen mcp add ssh \
-e SSH_PROFILES_FILE="$HOME/.qwen/ssh-profiles.json" \
npx -y @hypnosis/ssh-mcp-server
其他 MCP 用戶端
Gemini CLI、Hermes、Cline、編輯器外掛或您自己的代理程式都以相同方式運作。它們 只需要一個要執行的指令和一個環境變數。
重新啟動您的 MCP 用戶端
重新啟動用戶端,然後執行 ssh_monitor({ action: "list" }) 以確認設定檔已載入。
SSH MCP server 設定
| 變數 | 功能 | 預設值 |
|---|---|---|
SSH_PROFILES_FILE | 設定檔 JSON 的路徑——必填 | — |
SSH_MCP_LOG_LEVEL | debug、info、warn、error | info |
LOG_LEVEL | 後備方案,僅在 SSH_MCP_LOG_LEVEL 未設定時使用 | info |
SSH_MCP_LOG_TIMESTAMP | 日誌行中的時間戳記 | true |
SSH_MCP_CONTROL_PERSIST | 共享連線在最後一個指令後保持存活的秒數;0 立即關閉它 | 600 |
SSH_MCP_CONTROL_DIR | 控制 socket 的存放位置 | ~/.ssh/ssh-mcp |
SSH_MCP_PROFILES_CACHE_TTL | 設定檔快取 TTL,毫秒 | 60000 |
SSH_MCP_PROFILES_WATCH | 當設定檔變更時重新載入 | true |
共享連線刻意比此程序存活更久:在退出時關閉它會切斷同一機器上另一個視窗正在使用的通道。
SSH MCP server 限制
每個限制都告訴您繞過的方法。 無法執行某項操作的工具會明確說明,並指出
ssh_exec——它直接在機器上執行指令——例如不支援的日誌驅動程式、
機器上沒有的工具、此伺服器不支援的引擎。您不需要事先知道工具的邊界在哪裡:
拒絕訊息會在關鍵時刻說出來。
三種拒絕會刻意對 shell 保持沉默,因為在那裡它是錯誤的答案:您的設定檔禁止的路徑
(繞過自己的規則不是解決方案)、格式錯誤的呼叫(修正點在呼叫本身),以及
ssh_exec 本身的拒絕。
- 取消: 已取消的呼叫現在也會停止伺服器上的指令,透過同一連線以第二個呼叫發送。在伺服器沒有
/proc的地方,指令會改透過ps找到。FreeBSD 未經驗證:不保證其正確行為。檔案傳輸和ssh_snapshot完全不接受取消。 - 原子寫入: BSD 和 macOS 無法預先檢查跨檔案系統的重新命名。
SSH MCP server 路線圖
-
針對 macOS SSH 主機的完整測試執行
-
在 Windows 上進行端對端相容性執行
-
多主機稽核——在一次呼叫中比較多個 SSH 設定檔的健康狀態
-
從現有的
~/.ssh/config匯入設定檔 -
大型檔案和不穩定連線的可續傳傳輸
-
遠端操作時間軸——指令、傳輸和守衛決策在單一稽核軌跡中
-
現成的 SSH 疑難排解手冊
-
容器日誌無需進入 shell— 已完成:ssh_log_tail和ssh_log_search接受容器名稱,詢問 docker 日誌寫入位置,並使用與其他日誌相同的機制讀取該檔案 -
讓您卡住的拒絕— 已完成: 每個限制現在都指出ssh_exec作為突破方式,因此碰到工具邊界只需一句話,而不是猜謎遊戲 -
能送達模型的答案— 已完成: 指令輸出、相符的日誌行、機器名稱和快照區段會放在欄位中,而不僅在文字中 -
更小的 MCP 工具 schema— 已完成: 工具清單輕量了 10%,且分離的工作現在會顯示其寫入的最後幾行,而不是盲目輪詢 -
在 root 下的長時間工作— 已完成: 分離的工作以sudo執行並以 root 身分追蹤,且僅金鑰設定檔以自身的sudoPassword回應sudo
開發和測試 SSH MCP server
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、一個 coreutils——因為兩者會靜默地意見分歧,而 mock 會同意撰寫它的人。請參閱 docs/architecture.md 了解佈局。
喜歡 SSH MCP Server?⭐
如果您喜歡這個工具,在 GitHub 上給它一顆星——這有助於更多人發現這個專案。
貢獻 SSH MCP server
歡迎在 github.com/hypnosis/ssh-mcp-server 提交 issue 和 pull request。
授權
MIT——請參閱 LICENSE。