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 ประหยัดเวลาและโทเค็นในการดีบัก พัฒนา และบำรุงรักษาเซิร์ฟเวอร์ รันคำสั่ง ย้ายไฟล์ อ่านล็อก และตรวจสอบเครื่องจักรผ่าน SSH — ไม่ว่าจะเป็น VPS บนคลาวด์ เครื่องจริง หรือเราเตอร์ BusyBox ที่วางอยู่ในตู้เสื้อผ้าของคุณ |
มันใช้ OpenSSH client ที่มีอยู่แล้วบนเครื่องของคุณ: คีย์ของคุณ ~/.ssh/config โฮสต์กระโดดของคุณ การส่งต่อเอเจนต์ของคุณ ไม่มีอะไรถูกผูกไว้ ไม่มีอะไรให้คอมไพล์ ไม่มีการเชื่อมโยงเนทีฟ
ทำงานร่วมกับ Claude Code, Codex CLI, Cline, opencode, Gemini CLI, Qwen Code, Hermes และ MCP clients อื่นๆ
ติดตั้ง · เครื่องมือ · การตั้งค่า · ความปลอดภัย · โรดแมป · เอกสาร · บันทึกการเปลี่ยนแปลง
ติดตั้งใน 30 วินาที
ไม่ต้องติดตั้งทั้งระบบ npx ดาวน์โหลดแพ็กเกจเมื่อใช้งานครั้งแรก:
npx -y @hypnosis/ssh-mcp-server
เพิ่มลงใน MCP client ของคุณ — Claude Code ตัวอย่างเช่น — สำหรับทุกโปรเจกต์:
claude mcp add ssh -s user \
-e SSH_PROFILES_FILE="$HOME/.claude/ssh-profiles.json" \
-- npx -y @hypnosis/ssh-mcp-server
หรือเขียนด้วยมือ — เซิร์ฟเวอร์เดียวกันในรูปแบบคอนฟิกที่ clients ส่วนใหญ่ใช้ร่วมกัน:
{
"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 และ clients อื่นๆ ครอบคลุมใน ตั้งค่า SSH MCP server
ติดตั้งเป็นปลั๊กอิน
บาง clients — 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 client บน PATH บน Windows ให้ใช้โปรไฟล์แบบคีย์
โปรไฟล์รหัสผ่านและพาสเฟรสยังไม่พร้อมใช้งานในขณะนี้
ต้องการเวอร์ชันที่ปักหมุด ทำงานออฟไลน์ หรือลดการตรวจสอบรีจิสทรีหนึ่งครั้งต่อการเปิดใช้:
npm install -g @hypnosis/ssh-mcp-server จากนั้นใช้ ssh-mcp-server เป็นคำสั่งแทน
npx
ใครควรใช้สิ่งนี้
- DevOps และ SREs ที่ต้องการตรวจสอบที่เร็วขึ้น การเช็คเหตุการณ์ และงานเซิร์ฟเวอร์ประจำ
- Vibe coders และอินดี้บิลเดอร์ ที่ส่งงานด้วยผู้ช่วย AI และรันสิ่งที่สร้างบนเซิร์ฟเวอร์ของตัวเอง
- Sysadmins และวิศวกรแพลตฟอร์ม ที่ต้องการเครื่องมือที่มีโครงสร้างแทนเชลล์ดิบที่ไม่จำกัด
- นักพัฒนาและทีมเล็กๆ ที่รัน VPS ของตัวเอง โดยไม่มีทีมปฏิบัติการโดยเฉพาะ
- เจ้าของ Homelab, NAS และเราเตอร์ ที่ฮาร์ดแวร์ที่มีประโยชน์มีอายุเกินโปรโตคอลสมัยใหม่
ทำไมต้อง SSH MCP server แทนเชลล์ดิบ
โทเค็นน้อยลง ต้นทุน AI ต่ำลง
เชลล์ดิบให้เอเจนต์ AI ได้ข้อมูลท่วมท้น: คำสั่งซ้ำๆ ตาราง ASCII และดัมพ์ล็อก มันเผาโทเค็นเพื่อแปลงเสียงรบกวนนั้นเป็นภาพของเซิร์ฟเวอร์ — เงินของคุณ
ดีบักเซิร์ฟเวอร์เร็วขึ้น
เครื่องมือที่ออกแบบมาโดยเฉพาะจัดกลุ่มการตรวจสอบประจำ จำกัดเอาต์พุตที่ดัง และคืนส่วนที่สำคัญ เอเจนต์ใช้เวลาน้อยลงในการแปลเอาต์พุตเทอร์มินัลและไปถึงจุดแก้ไขได้เร็วขึ้น
เดาน้อยลง ผิดพลาดน้อยลง
คำตอบที่มีโครงสร้างบอกว่าพบอะไร สิ่งใดวัดไม่ได้ และสิ่งใดถูกตัดทอน นั่นทำให้เอเจนต์มีช่องว่างน้อยลงในการเติมด้วยภาพหลอน — และทำให้คุณมีการแก้ไขที่แย่น้อยลง การดีพลอยที่สงบขึ้น และโค้ดที่เชื่อถือได้มากขึ้น
ความเข้ากันได้ของ SSH: เซิร์ฟเวอร์สมัยใหม่ อุปกรณ์รุ่นเก่า และ Windows
ใช้การตั้งค่า OpenSSH ที่มีอยู่ของคุณ
ไม่มีการรวม SSH ที่ผูกไว้ ไม่มีการเชื่อมโยงเนทีฟ ไม่ต้องคอมไพล์ใหม่ต่อแพลตฟอร์ม คำสั่งใช้
ระบบ ssh client ดังนั้นคีย์ของคุณ ~/.ssh/config โฮสต์กระโดดของคุณ และการส่งต่อเอเจนต์ของคุณ
ทั้งหมดยังทำงานเหมือนในเทอร์มินัลทุกประการ เมื่อรองรับ การเชื่อมต่อแบบมัลติเพล็กซ์ที่ใช้ร่วมกันหนึ่งครั้งต่อปลายทาง
หมายความว่าคุณยืนยันตัวตนครั้งเดียว ไม่ใช่ทุกคำสั่ง
รองรับ SSH สำหรับเซิร์ฟเวอร์รุ่นเก่า เราเตอร์ และ NAS
ส่งไฟล์ไปยังเราเตอร์ด้วย scp สมัยใหม่ แล้วคุณจะได้สิ่งนี้:
scp app.conf router:/etc/
# scp: subsystem request failed on channel 0
ไม่มีอะไรเสียหาย — scp ปัจจุบันพูดโปรโตคอลใหม่ และเราเตอร์ไม่รู้จัก
ในเทอร์มินัล คุณต้องไปอ่านกระทู้ฟอรัมแล้วกลับมาพร้อมแฟล็กพิเศษ ที่นี่คุณไม่ต้องทำอะไร:
การถ่ายโอนถูกพยายาม การปฏิเสธถูกรับรู้ โปรโตคอลเก่าถูกใช้แทน
และเครื่องนั้นถูกจดจำ เพื่อให้ไฟล์ถัดไปไปถึงที่นั่นโดยตรง
การสำรองสำหรับ SSH clients รุ่นเก่าและเครื่องมือที่ขาดหายไป
อุปกรณ์เก่าได้รับการสำรอง ไม่ใช่ทางตัน เมื่อฟีเจอร์สมัยใหม่ขาดหายไป เซิร์ฟเวอร์ ใช้เส้นทางเก่าที่ทำได้:
| เครื่องของคุณ | สิ่งที่คุณได้รับ |
|---|---|
| เราเตอร์หรือ NAS เล็กเกินไปสำหรับการถ่ายโอนไฟล์สมัยใหม่ | ไฟล์ยังไปถึง — โปรโตคอลเก่าถูกใช้โดยอัตโนมัติ |
| เซิร์ฟเวอร์จากสิบปีที่แล้ว | เวิร์กโฟลว์ยังทำงาน; เพียงเปิดการเชื่อมต่อใหม่ต่อคำสั่งแทนการ复用 |
| อิมเมจที่ถูกตัดทอนโดยไม่มีวิธีแฮชไฟล์ | การอัปโหลดบอก "ไม่สามารถยืนยันได้" แทนการอ้างว่าตรงกันโดยไม่มีใครตรวจสอบ |
| เครื่องที่ไม่มีเครื่องมือติดตั้งอยู่ | คำตอบบอก "ไม่ได้วัด" — ไม่เคยเป็นศูนย์ที่อ่านว่า "ไม่มีอะไรอยู่" |
สร้างขึ้นสำหรับ Model Context Protocol
สร้างบน MCP SDK อย่างเป็นทางการ TypeScript ตลอดทั้งชิ้น การทดสอบหน่วย 2500+ รายการบวกชุดทดสอบสดที่ รันกับคอนเทนเนอร์จริงแทนม็อก
Raw SSH เทียบกับ SSH MCP server: งานเดียวกัน ทั้งสองวิธี
การตรวจสอบสุขภาพเซิร์ฟเวอร์ SSH
สถานการณ์: การดีพลอยเพิ่งออกไป เซิร์ฟเวอร์รู้สึกช้า และคุณไม่รู้ว่า ดิสก์ หน่วยความจำ บริการ คอนเทนเนอร์ หรือข้อผิดพลาดเป็นต้นเหตุ
คำถาม: "เครื่องนี้สุขภาพดีหรือไม่"
Raw 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": []
}
สิ่งที่เอเจนต์ได้รับ
| Raw SSH | Structured MCP | สิ่งที่คุณได้ |
|---|---|---|
| หลายคำสั่งและตาราง ASCII | ฟิลด์ที่มีชื่อในผลลัพธ์เดียว | หนึ่งคอล ฟิลด์ที่มีชื่อ และรอบการเดินทางน้อยลง |
| เครื่องมือที่ขาดหายไปอาจดูเหมือนเอาต์พุตว่าง | unavailable ระบุสิ่งที่ไม่ได้วัด | เดาน้อยลงและการแก้ไขที่แย่น้อยลง |
| คุณต้องจัดเรียงดิสก์ บริการ และข้อผิดพลาด | สัญญาณปัญหาถูกผิวเผินแล้ว | ดีบักเร็วขึ้น |
ผลลัพธ์ ssh_audit_baseline เต็มรูปแบบอาจยาวกว่าคำสั่งดิบสองสามคำสั่ง —
ประมาณ 1,077 โทเค็นเทียบกับ 765 ในการวัดในห้องแล็บของเรา การประหยัดมาจากเวิร์กโฟลว์ที่สมบูรณ์
ไม่ใช่จากการทำให้การตอบสนองหนึ่งครั้งสั้นลง
ในเซสชันการแก้ปัญหาจริง เครื่องมือที่ออกแบบมาโดยเฉพาะลดการเรียกคำสั่ง 49 ครั้งที่แยกจากกันเป็น การเรียก MCP 4 ครั้ง ทุกการเรียกเพิ่มเติมเริ่มต้นเทิร์นโมเดลใหม่พร้อมบทสนทนาที่สะสม Prompt caching สามารถลดต้นทุนของอินพุตซ้ำ แต่คำสั่งใหม่และเอาต์พุตยังคงใช้บริบท รอบการเดินทางน้อยลงหมายถึงโทเค็นน้อยลงตลอดเซสชัน การวิเคราะห์ซ้ำน้อยลง และเส้นทางที่เร็วกว่าสู่คำตอบ
ต้องการภาพรวมทั้งหมดแทนชีพจรหรือไม่? ssh_audit_baseline จัดกลุ่มระบบ ดิสก์
หน่วยความจำ พอร์ต sshd ยูนิตที่ล้มเหลว Docker ไฟร์วอลล์ และอัปเดต ผลการค้นพบมาถึงเป็น
CRITICAL / WARNING / OK; ส่วนที่ไม่ได้วัดถูกระบุชื่อแทนการอ่านเป็นศูนย์อย่างเงียบๆ
การค้นหาล็อกเซิร์ฟเวอร์ Linux
สถานการณ์: API หมดเวลา แต่ข้อความเดียวกันอาจอยู่ใน nginx, syslog, journald หรือล็อกแอปพลิเคชันที่คุณไม่สามารถอ่านด้วยผู้ใช้ปกติของคุณ
คำถาม: "ข้อผิดพลาดนั้นมาจากไหน"
Raw 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
}
สิ่งที่เอเจนต์ได้รับ
| Raw SSH | Structured MCP | สิ่งที่คุณได้ |
|---|---|---|
| สี่การค้นหาและสี่เอาต์พุต | หนึ่งการค้นหาข้ามไฟล์และ globs | โทเค็นและรอบการเดินทางน้อยลง |
| ข้อผิดพลาดสิทธิ์อาจหายไป | files_unreadable ระบุทุกเส้นทางที่พลาด | ไม่มีข้อสรุปเท็จว่า "ล็อกสะอาด" |
| เอาต์พุตสามารถเติบโตโดยไม่มีเพดานที่มีประโยชน์ | limited และ truncated เปิดเผยทุกจุดตัด | การตัดสินใจที่ปลอดภัยขึ้นจากผลลัพธ์บางส่วน |
since ใช้นาฬิกาของเซิร์ฟเวอร์ namesOnly: true คืนเฉพาะเส้นทางที่ตรงกัน และ
ssh_log_tail อ่าน N บรรทัดสุดท้ายจากหลายล็อกในการเรียกเดียว
การแก้ไขคอนฟิกระยะไกลอย่างปลอดภัย
สถานการณ์: คุณต้องแทนที่คอนฟิก nginx บนเซิร์ฟเวอร์สด การเชื่อมต่อที่หลุด โหมดผิด หรือการคัดลอกที่ไม่ตรวจสอบอาจทำให้บริการมีไฟล์ที่เสียหาย
คำถาม: "ฉันสามารถแทนที่คอนฟิกนี้โดยไม่ทิ้งไฟล์บางส่วนได้หรือไม่"
Raw 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
รหัสออกศูนย์บอกว่าเชลล์เสร็จสิ้น มันไม่ได้พิสูจน์ว่าไบต์ใดลงจอด และ >
ตัดทอนไฟล์เก่าก่อนไบต์แรกของไฟล์ใหม่มาถึง หากการเชื่อมต่อหลุดกลางการเขียน
บริการจะเหลือคอนฟิกบางส่วน
ผลลัพธ์ 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 }]
}
สิ่งที่เอเจนต์ได้รับ
| Raw SSH | Structured MCP | สิ่งที่คุณได้ |
|---|---|---|
| เป้าหมายถูกตัดทอนก่อนการคัดลอกเสร็จสิ้น | ไฟล์ชั่วคราวที่สมบูรณ์แทนที่ด้วยการเปลี่ยนชื่อครั้งเดียว | ไม่มีคอนฟิกที่เขียนครึ่งเดียว |
| รหัสออกเท่านั้น | ไบต์และผลการยืนยันถูกระบุชื่อ | คุณรู้ว่าอะไรลงจอดจริง |
| สิทธิ์อยู่ในข้อความเชลล์ | sudo, mode และ verify เป็นฟิลด์ต่อไฟล์ | ความเป็นเจ้าของที่คาดเดาได้และข้อผิดพลาดการอ้างน้อยลง |
verified มีสามผลลัพธ์ที่ซื่อสัตย์: verified, unavailable เมื่อเซิร์ฟเวอร์ไม่มีเครื่องมือแฮช
และ skipped เมื่อไม่ได้ขอการยืนยัน สำหรับการอ่าน ssh_file_read ยอมรับ
รายการเส้นทาง; ssh_file_list จัดการ globs การเรียกซ้ำ ขนาด และโหมด
รันคำสั่ง SSH แบบแบตช์ด้วย sudo
สถานการณ์: การดีพลอยพร้อมแล้ว แต่ไวยากรณ์ nginx สถานะบริการ และข้อผิดพลาดล่าสุดต้อง ถูกตรวจสอบทั้งหมดก่อนที่ทราฟฟิกจะย้าย การตรวจสอบที่ล้มเหลวหนึ่งครั้งไม่ควรหายไปในดัมพ์รวม
คำถาม: "การตรวจสอบก่อนดีพลอยทุกครั้งผ่านหรือไม่"
Raw 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
สามการเชื่อมต่อคืนสามเอาต์พุตที่ไม่เกี่ยวข้อง หากคำสั่งถูกรวมด้วย ; เชลล์
รายงานเฉพาะรหัสออกสุดท้าย; หากรวมด้วย && การตรวจสอบหลังๆ หายไป
หลังความล้มเหลวครั้งแรก
ผลลัพธ์ 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
}
สิ่งที่เอเจนต์ได้รับ
| Raw SSH | Structured MCP | สิ่งที่คุณได้ |
|---|---|---|
| สามการเรียกและเอาต์พุตที่ไม่เกี่ยวข้อง | รายการคำสั่งที่เรียงลำดับหนึ่งรายการ | รอบการเดินทางน้อยลง |
| เชลล์รวมสามารถซ่อนสถานะกลาง | ทุกคำสั่งรักษา 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 ถาวร | การตัดการเชื่อมต่อและการรีสตาร์ทอย่างปลอดภัย |
| การเชื่อมต่อใหม่หมายถึงการค้นหากระบวนการและไฟล์ | สถานะและรหัสออกมีสถานะที่มีชื่อ | ไม่ต้องเดาว่าเสร็จหรือไม่ |
| การอ่านผลลัพธ์อีกครั้งจะทำซ้ำข้อความเก่า | ผลลัพธ์ดำเนินต่อไปจากออฟเซ็ตไบต์ | ใช้โทเค็นน้อยลงสำหรับงานยาว |
สถานะของงานอยู่บนดิสก์ระยะไกล ไม่ใช่ในหน่วยความจำของเซิร์ฟเวอร์นี้ ssh_job_status แยกแยะ
running, finished และ lost; ssh_job_output ดำเนินการต่อจากออฟเซ็ตไบต์สุดท้าย; และ
ssh_job_kill ส่งสัญญาณไปยังกลุ่มกระบวนการทั้งหมดแทนที่จะเป็นเพียงเชลล์ของมัน
ถ่ายโอนไฟล์ไปยังเราเตอร์และอุปกรณ์ 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
เชลล์จะลบไดเรกทอรีแล้วค้นพบว่าแหล่งสำรองหายไปเท่านั้น การ์ดเห็นว่าขั้นตอนหลังอ่าน
เป้าหมายที่ถูกทำลายโดยขั้นตอนก่อนหน้าแล้ว ดังนั้นการเรียกทั้งหมดจึงอยู่บนเครื่องของคุณ
การตรวจสอบเดียวกันจับ 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 ก็ถูกปฏิเสธเช่นกัน รวมถึงเมื่อ symlink นำไปที่นั่น เป้าหมายที่ไม่ได้รับการแก้ไข
เช่น 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
รูปแบบเหนือบรรทัดคำสั่งเป็นกรณีพิเศษ มันจับคู่คำสั่งที่พกพามันเอง ดังนั้นเชลล์ที่รันมันจะถูกส่งสัญญาณก่อนเป้าหมายและคำตอบจะขาดกลางคัน การโจมตีเช่นนี้ไม่ได้รับการยืนยันแต่ถูกเขียนใหม่ — ด้วยตัวเลข หรือด้วยอักขระหนึ่งตัว ที่เขียนเป็นคลาสเพื่อให้รูปแบบหยุดจับคู่ตัวเอง:
pkill -f relay
# BLOCKED — two ways through:
# kill 4871
# pkill -f '[r]elay' # CONFIRMED-KILL: 4871
ผลลัพธ์สามอย่างแยกจากกัน: พบเป้าหมาย การขยายไม่ถึงอะไรเลย และไม่มีอะไรให้ถาม ด้วย — ไม่มีเอนจินบนเครื่อง คำตอบที่ถูกตัดทอน การเชื่อมต่อที่ล้มเหลว สองอย่างหลัง เป็นการปฏิเสธเช่นกัน: การไม่รู้ไม่ใช่เหตุผลที่จะดำเนินการ
ยืนยันคำสั่งทำลายล้างโดยเจตนา
ไม่มีอะไรถูกห้ามถาวร เพิ่ม # CONFIRMED-DESTRUCTIVE ลงในคำสั่งที่ตรวจสอบแล้วและมัน
จะถูกอนุญาตให้ผ่าน เมื่อการ์ดปฏิเสธหนึ่งรายการในชุด ชุดทั้งหมดจะหยุด
ก่อนการดำเนินการ ดังนั้นเซิร์ฟเวอร์จะไม่ถูกทิ้งไว้หลังการดำเนินการครึ่งทาง
การ์ดทำงานภายในหนึ่งการเรียก มันไม่สามารถเชื่อมต่อการลบในการเรียกหนึ่งกับ การอ่านในการเรียกถัดไป หรือให้เหตุผลเกี่ยวกับเครื่องมือที่ไม่รู้จัก มันคือเข็มขัดนิรภัย ไม่ใช่เอนจินนโยบาย: การดำเนินการที่กู้คืนได้ยังคงเป็นการตัดสินใจของคุณ ข้อจำกัดเส้นทางและกฎการอ้างอิงถูก บันทึกไว้ใน docs/security.md
เครื่องมือ
18 เครื่องมือ MCP SSH สำหรับการดำเนินการเซิร์ฟเวอร์ พารามิเตอร์เต็มและตัวอย่างอยู่ใน docs/tools.md
| เครื่องมือ | สิ่งที่มันทำ |
|---|---|
ssh_exec | รันหนึ่งคำสั่งหรือชุดคำสั่ง พร้อมการ์ดป้องกันคำสั่งทำลายล้างและ detach ทางเลือก |
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 top-N, Docker, journald, แคช |
ssh_service_status | systemctl status บวก journalctl tail สำหรับหนึ่งยูนิต |
คำอธิบายความปลอดภัยของเครื่องมือ MCP
คำอธิบาย MCP มาตรฐานบอกไคลเอนต์ว่าเครื่องมือใดอ่านอย่างเดียว ทำลายล้าง idempotent หรือ open-world ดู ตารางเต็ม
รันคำสั่ง SSH และจัดการไฟล์ระยะไกล
คำสั่ง การอ่านและเขียนไฟล์ การแสดงรายการไดเรกทอรี — งานทั่วไปบนเครื่อง แต่ละคำตอบถูกแยกวิเคราะห์แล้ว
ติดตามงาน SSH ที่มีอายุยาวนาน
งานช้าถูกแยกและติดตามแทนที่จะรอ: ทุกการมองบอกว่ามันไปไกลแค่ไหน
ค้นหาล็อกและตรวจสอบสุขภาพเซิร์ฟเวอร์
ล็อกของไฟล์และคอนเทนเนอร์ และภาพรวมครั้งเดียวของเครื่อง โดยมีขีดจำกัดผลลัพธ์เพื่อให้ tail ไม่กินบริบทวินโดว์
อัปโหลดและดาวน์โหลดไฟล์ผ่าน SSH
การถ่ายโอนปลอดภัยไบนารีพร้อมการตรวจสอบความสมบูรณ์ รายละเอียดใน docs/transfer.md
สำหรับไบนารีและไฟล์ขนาดใหญ่ใช้
ssh_upload/ssh_download— ชิ้นส่วน base64 และ heredocs ไม่ปลอดภัยไบนารีหรืออะตอมมิก
ตรวจสอบเซิร์ฟเวอร์ Linux ผ่าน SSH
อ่านอย่างเดียวและรวมเป็นหนึ่งรอบ รายละเอียดใน 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 และ passphrase ออกจากโปรไฟล์
ให้ความสำคัญกับคีย์ หากต้องใช้รหัสผ่านหรือ passphrase ของคีย์ที่เข้ารหัสอย่างเลี่ยงไม่ได้ ให้เก็บไว้ในไฟล์ 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 clients อื่น ๆ
เลือก client ที่คุณใช้และชี้ไปที่ไฟล์โปรไฟล์เดียวกัน
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 clients อื่น ๆ
Gemini CLI, Hermes, Cline, ปลั๊กอินของตัวแก้ไข หรือ agent ของคุณเองทำงานในลักษณะเดียวกัน ทั้งหมดที่ต้องการคือคำสั่งให้รันและตัวแปรสภาพแวดล้อมหนึ่งตัว
รีสตาร์ท MCP client ของคุณ
รีสตาร์ท client จากนั้นรัน 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 | ตำแหน่งที่เก็บ control sockets | ~/.ssh/ssh-mcp |
SSH_MCP_PROFILES_CACHE_TTL | อายุแคชของโปรไฟล์, มิลลิวินาที | 60000 |
SSH_MCP_PROFILES_WATCH | โหลดไฟล์โปรไฟล์ใหม่เมื่อมีการเปลี่ยนแปลง | true |
การเชื่อมต่อที่ใช้ร่วมกันมีอายุยืนกว่า process นี้โดยตั้งใจ: การปิดเมื่อออกจากระบบจะตัดช่องทางที่หน้าต่างอื่นบนเครื่องเดียวกันกำลังใช้อยู่
ข้อจำกัดของ SSH MCP server
ทุกข้อจำกัดบอกวิธีแก้ไขให้คุณ เครื่องมือที่ไม่สามารถทำบางสิ่งได้จะแจ้งและระบุ ssh_exec ซึ่งรันคำสั่งบนเครื่องโดยตรง — ไดรเวอร์บันทึกที่ไม่รองรับ ยูทิลิตี้ที่เครื่องไม่มี เอ็นจินที่เซิร์ฟเวอร์นี้ไม่รองรับ คุณไม่จำเป็นต้องรู้ล่วงหน้าว่าเครื่องมือสิ้นสุดที่ใด: การปฏิเสธจะบอก ณ ช่วงเวลาที่สำคัญ
การปฏิเสธสามกรณีจงใจไม่พูดถึงเชลล์ เพราะที่นั่นเป็นคำตอบที่ผิด: เส้นทางที่โปรไฟล์ของคุณห้าม (การเลี่ยงกฎของคุณเองไม่ใช่การแก้ไข), การเรียกที่ผิดรูปแบบ (การแก้ไขอยู่ที่การเรียก), และการปฏิเสธจาก ssh_exec เอง
- การยกเลิก: การเรียกที่ถูกยกเลิกตอนนี้จะหยุดคำสั่งบนเซิร์ฟเวอร์ด้วย โดยส่งเป็นการเรียกครั้งที่สองผ่านการเชื่อมต่อเดียวกัน ในกรณีที่เซิร์ฟเวอร์ไม่มี
/procคำสั่งจะถูกค้นหาผ่านpsแทน FreeBSD ไม่ได้รับการตรวจสอบ: พฤติกรรมที่ถูกต้องที่นั่นไม่รับประกัน การถ่ายโอนไฟล์และssh_snapshotไม่รองรับการยกเลิกเลย - การเขียนแบบอะตอม: BSD และ macOS ไม่สามารถตรวจสอบล่วงหน้าเรื่องการเปลี่ยนชื่อข้ามระบบไฟล์ได้
แผนงานของ SSH MCP server
-
ทดสอบเต็มรูปแบบกับโฮสต์ SSH บน macOS
-
ทดสอบความเข้ากันได้แบบครบวงจรบน Windows
-
การตรวจสอบหลายโฮสต์ — เปรียบเทียบสุขภาพในหลายโปรไฟล์ SSH ในการเรียกครั้งเดียว
-
นำเข้าโปรไฟล์จาก
~/.ssh/configที่มีอยู่ -
การถ่ายโอนที่สามารถดำเนินการต่อได้สำหรับไฟล์ขนาดใหญ่และการเชื่อมต่อที่ไม่เสถียร
-
ไทม์ไลน์การทำงานระยะไกล — คำสั่ง การถ่ายโอน และการตัดสินใจด้านการป้องกันในเส้นทางการตรวจสอบเดียว
-
เพลย์บุ๊กการแก้ไขปัญหา SSH ที่พร้อมใช้
-
บันทึกคอนเทนเนอร์โดยไม่ต้องลงไปที่เชลล์— เสร็จสิ้น:ssh_log_tailและssh_log_searchรับชื่อคอนเทนเนอร์ ถาม docker ว่ามันเขียนที่ไหน และอ่านไฟล์นั้นด้วยกลไกเดียวกับบันทึกอื่น ๆ -
การปฏิเสธที่ทำให้คุณติดอยู่— เสร็จสิ้น: ทุกข้อจำกัดตอนนี้ระบุssh_execเป็นทางผ่าน ดังนั้นการถึงขอบของเครื่องมือใช้เพียงหนึ่งประโยคแทนการเดา -
คำตอบที่ไปถึงโมเดล— เสร็จสิ้น: ผลลัพธ์คำสั่ง บรรทัดบันทึกที่ตรงกัน ชื่อเครื่อง และส่วนสแนปช็อตเดินทางในฟิลด์ ไม่ใช่เฉพาะในข้อความ -
สคีมาเครื่องมือ MCP ที่เล็กลง— เสร็จสิ้น: รายการเครื่องมือเบาลง 10% และงานที่แยกออกไปตอนนี้แสดงบรรทัดสุดท้ายที่เขียนแทนการถูกสอบถามแบบไม่เห็นอะไร -
งานยาวภายใต้ root— เสร็จสิ้น: งานที่แยกออกไปรันด้วยsudoและถูกติดตามเป็น root และโปรไฟล์ที่ใช้คีย์เท่านั้นตอบsudoด้วยsudoPasswordของตัวเอง
พัฒนาและทดสอบ 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
ยินดีรับ Issues และ Pull Requests ที่ github.com/hypnosis/ssh-mcp-server
สัญญาอนุญาต
MIT — ดู LICENSE