--- name: ssh-manager description: "Use when inspecting a remote server, VPS, or production host through the ssh-manager MCP plugin: health, services, logs, databases, processes, and alerts." --- # SSH Manager Skill ## When to use this skill Use this skill when the user asks to: - Inspect system health, resources, and performance on a remote server - Check status of nginx, docker, postgresql, mysql, systemd, firewall, SSL certs, cron - Analyze network connections, process activity, or memory/disk usage - View and filter remote logs (nginx error, auth, syslog, journalctl) - Manage databases (list, query, dump, import) - Run ad-hoc shell commands via SSH - Configure or check server alert thresholds (CPU, memory, disk) Do NOT use for: fully automated unattended batch operations, configuration management at scale (use Ansible/Salt for that), or any operation the user has not explicitly approved. --- ## Workflow ### Phase 0: ELIMINATED The ssh-manager MCP plugin handles SSH connections, key management, and authentication internally. No local SSH/sshpass detection is needed. --- ### Phase 1: Select server + services **Step 1 — List configured servers:** Call `ssh_list_servers` to show the user all available servers: ``` Configured servers: 1. my-vps (root@192.168.1.100:22) key auth DigitalOcean droplet 2. staging-box (deploy@staging.example.com:2222) key auth Staging environment 3. db-server (admin@10.0.0.5:22) password PostgreSQL host ``` Ask the user to pick one server by name. If no servers are configured (`ssh_list_servers` returns empty), tell the user: *"No servers found in ssh-manager config. Add a server first via your ssh-manager configuration."* **Step 2 — Select operations:** Ask the user which operations to perform (multi-select). The full list: - **System health overview** — CPU, memory, disk, uptime (ssh_health_check detailed) - **CPU** — detailed CPU metrics (ssh_monitor type=cpu) - **Memory** — detailed memory metrics (ssh_monitor type=memory) - **Disk** — detailed disk usage (ssh_monitor type=disk) - **Network connections** — listening ports, connections (ssh_monitor type=network) - **Service status** — check specific services (ssh_service_status) - **Process activity (top CPU/RAM)** — sorted process list (ssh_process_manager action=list) - **Kill process** — terminate a process by PID [DESTRUCTIVE] - **Nginx config check** — nginx -t syntax validation - **Nginx enabled sites** — list sites-enabled - **Nginx error log** — tail error.log with grep filter - **Docker containers** — running/all containers - **Docker info** — engine info summary - **Database: list databases** — per engine (mysql/postgresql/mongodb) - **Database: list tables** — tables in a specific database - **Database: read-only query** — SELECT/SELECT-like query - **Database: dump** — export database to file [DESTRUCTIVE] - **Database: import** — import dump into database [DESTRUCTIVE] - **Systemd: failed units** — list failed services - **Systemd: running units** — list active services - **Firewall rules** — ufw status or iptables -L - **SSL certificates** — expiry dates and paths - **Cron jobs** — user crontab and system cron dirs - **Auth log** — last logins, failures (tail auth.log/secure) - **Syslog / messages** — generic system log tail - **Journalctl errors** — recent error-level journal entries - **Server alerts** — configure/check thresholds (CPU, memory, disk) [DESTRUCTIVE for action=set] - **Custom command** — any shell command via ssh_execute Also ask: **Log lines to show:** [20 / 50 / 100] --- ### Phase 2: Show the plan Display a summary: ``` ── VPS Inspection Plan (ssh-manager) ──────── Server: my-vps Config: key auth Services: System health, Nginx, Docker, SSL Log lines: 50 Step 1/10 — Health: ssh_health_check(detailed=true) Step 2/10 — Nginx: ssh_execute("nginx -t 2>&1") Step 3/10 — Nginx: ls -la /etc/nginx/sites-enabled/ Step 4/10 — Nginx: ssh_tail(/var/log/nginx/error.log, lines=50, follow=false) Step 5/10 — Docker: ssh_execute("docker ps -a") Step 6/10 — Docker: ssh_execute("docker info --format '{{.Containers}} containers'") Step 7/10 — SSL: ssh_execute("...openssl...") Step 8/10 — Firewall: ssh_execute("ufw status verbose") Step 9/10 — Process: ssh_process_manager(action=list, sortBy=cpu, limit=10) Step 10/10 — Custom: ssh_execute("your command") ───────────────────────────────────────────── Proceed? [y/N] ``` --- ### Phase 3: Execute (with authorization) **Authorization rules:** - NO "Run all in this group". NO "Run all remaining". Every single command requires explicit confirmation. - For each step: show `[Step N/M — Group: {service}] Description: {tool_name}({params})` - Ask: **Run** / **Skip** / **Abort all** - For destructive operations, label as `[DESTRUCTIVE]` and ALWAYS require explicit confirmation regardless of any prior approval. **Destructive operations list (always require extra confirmation):** - `ssh_db_import` - `ssh_db_dump` (writes file on remote filesystem) - `ssh_process_manager` action=kill - `ssh_alert_setup` action=set - `ssh_execute_sudo` (elevated privileges, can modify anything) **Per-step flow:** 1. Display: `[Step N/M — Group: {service}] Description` and tool invocation 2. Label `[DESTRUCTIVE]` if applicable 3. Ask: **Run** / **Skip** / **Abort all** 4. Execute the appropriate ssh-manager tool 5. Show output (truncated to configured log lines limit, with option to view full) 6. Append output to local session log file (`vps-session-{server}-{date}.txt`) in real time, including tool name, params, and timestamp 7. Also call `ssh_history` to log the command in-memory for in-session recall 8. Move to next step If a non-destructive tool fails or returns an error, note it and move on — do not abort the sequence. If a DESTRUCTIVE operation (ssh_db_import, ssh_execute_sudo, ssh_process_manager kill) fails or returns a partial error, STOP immediately, report the failure to the user with full error details, and ask whether to continue or abort. Do NOT silently move on from a failed destructive operation — the server may be in an inconsistent state. --- ### Phase 4: Final summary and follow-up ``` ── Complete ────────────────────────────── Steps run: 8/10 Skipped: 2 Anomalies: Nginx error log has warnings ───────────────────────────────────────── ``` After the summary, offer follow-up options: - Re-run a skipped step - Run a custom command (`ssh_execute`) - Export the full session output as text report - Start a new session ## Tool mapping | Category | ssh-manager tool | Notes | |----------|-----------------|-------| | System health overview | `ssh_health_check` | `detailed=true` for full metrics | | CPU detail | `ssh_monitor` type=cpu | | | Memory detail | `ssh_monitor` type=memory | | | Disk detail | `ssh_monitor` type=disk | | | Network connections | `ssh_monitor` type=network | | | Service status | `ssh_service_status` | pass array of service names | | Process top list | `ssh_process_manager` action=list | sortBy=cpu or memory | | Process kill | `ssh_process_manager` action=kill | DESTRUCTIVE | | Nginx config test | `ssh_execute` | `nginx -t 2>&1` | | Nginx enabled sites | `ssh_execute` | `ls -la /etc/nginx/sites-enabled/` | | Nginx error log | `ssh_tail` | file=/var/log/nginx/error.log, follow=false | | Docker containers | `ssh_execute` | `docker ps -a` | | Docker info | `ssh_execute` | `docker info` | | Database list | `ssh_db_list` | type per engine | | Database tables | `ssh_db_list` with database param | | | Database query | `ssh_db_query` | SQL SELECT only (enforced); MongoDB find() | | Database dump | `ssh_db_dump` | DESTRUCTIVE | | Database import | `ssh_db_import` | DESTRUCTIVE | | Systemd failed | `ssh_execute` | `systemctl list-units --failed --no-pager` | | Systemd running | `ssh_execute` | `systemctl list-units --state=running --no-pager` | | Firewall | `ssh_execute` | `ufw status verbose` or `iptables -L -n` | | SSL certs | `ssh_execute` | `find /etc/letsencrypt/live/ ... openssl x509 ...` | | Cron jobs | `ssh_execute` | `crontab -l` and `ls -la /etc/cron.d/` | | Auth log | `ssh_tail` | file=/var/log/auth.log (or secure), follow=false | | Syslog | `ssh_tail` | file=/var/log/syslog (or messages), follow=false | | Journalctl errors | `ssh_execute` | `journalctl -p err -n {N} --no-pager` | | Server alerts | `ssh_alert_setup` | action: set (DESTRUCTIVE) / get / check | | Custom command | `ssh_execute` | any shell command | --- ## Common predefined commands (via ssh_execute) Replace `{N}` with the user's log lines selection (20, 50, or 100). Note: If a command requires root privileges and the SSH user is not root, use `ssh_execute_sudo` instead of `ssh_execute`. Common cases: `ufw status`, `journalctl`, `openssl` on `/etc/letsencrypt/`, `systemctl` on some systems. ### Commands requiring sudo (use ssh_execute_sudo) ``` ufw status verbose journalctl -p err -n {N} --no-pager openssl x509 -in /etc/letsencrypt/live/*/cert.pem -noout -dates -subject ``` ### Nginx config check ``` nginx -t 2>&1 ``` ### Nginx enabled sites ``` ls -la /etc/nginx/sites-enabled/ ``` ### Nginx error log tail Use `ssh_tail` — not raw exec. ### Docker containers ``` docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}' ``` ### Docker info ``` docker info --format '{{.Containers}} containers, {{.Images}} images' ``` ### Systemd failed units ``` systemctl list-units --failed --no-pager ``` ### Systemd running units ``` systemctl list-units --state=running --no-pager ``` ### Firewall ``` ufw status verbose 2>/dev/null || iptables -L -n --line-numbers 2>/dev/null ``` ### SSL certificates ``` command -v openssl >/dev/null 2>&1 && find /etc/letsencrypt/live/ -name 'cert.pem' -exec openssl x509 -in {} -noout -dates -subject \; 2>/dev/null || echo "openssl not found" ``` ### Cron jobs ``` crontab -l 2>/dev/null; ls -la /etc/cron.d/ 2>/dev/null ``` ### Journalctl errors ``` journalctl -p err -n {N} --no-pager 2>/dev/null ``` --- ## Important rules 1. **NEVER** assume which server to connect to. Even if `ssh_list_servers` returns only one server, ALWAYS ask the user to explicitly confirm the server name before proceeding. No auto-selection, no defaulting to the first result, no inferring from context. Every connection must be explicitly confirmed by the user. 2. **NEVER** run a tool without explicit permission from the user. Every step requires Run / Skip / Abort. 3. Destructive operations (db_import, db_dump, process kill, ssh_execute_sudo, alert_setup action=set) ALWAYS require explicit confirmation regardless of any prior approval. Label them clearly as `[DESTRUCTIVE]`. No exceptions. 4. Inspect-only operations respect the inspect-only principle — do not modify server state unless the user explicitly requested a destructive operation. 5. If a non-destructive tool fails or returns an error, note it and move on. If a DESTRUCTIVE operation fails or returns a partial error, STOP immediately, report the failure with full error details, and ask whether to continue or abort — the server may be in an inconsistent state. 6. Long output is truncated to the configured log lines limit; always offer to view the full output on request. 7. Use `ssh_history` for in-session command recall AND maintain a local session log file (`vps-session-{server}-{date}.txt`) for export. 8. Server selection is always from `ssh_list_servers` — never ask the user to type host/port/user manually. If no servers are configured, tell the user to add them to ssh-manager config. 9. For database queries: SQL must be SELECT only (`ssh_db_query` enforces this). MongoDB uses `find()` with collection parameter. 10. For logs: prefer `ssh_tail` with `follow=false` to get lines back directly. Use the `grep` param for filtering. 11. For health checks: prefer `ssh_health_check` (structured JSON) over manual `ssh_execute` commands. 12. For service status: prefer `ssh_service_status` over manual `systemctl status` commands. 13. For monitoring: prefer `ssh_monitor` with the appropriate `type` over manual `top`/`free`/`df` commands. 14. When constructing `ssh_execute` commands with user-provided input (file paths, grep patterns, command arguments), ALWAYS single-quote the interpolated values. Never concatenate raw user input into a shell command without quoting. Example: `ssh_execute("tail -50 '/var/log/nginx/error.log'")` not `ssh_execute("tail -50 " + userInput)`. If the input contains single quotes, reject it and ask the user to provide a safe value. 15. Before running any git command on the remote server, ALWAYS verify the target directory contains a `.git` folder first. Run a guard check (e.g., `ssh_execute("test -d '{path}/.git' && echo ok || echo no")`) and only proceed with the git command if the check returns `ok`. If `.git` is absent, report it to the user and abort the git operation. Never run `git pull`, `git status`, `git log`, `git checkout`, or any other git subcommand against a directory that is not a git repository. --- ## Debug guidance Errors come from tool responses, not raw SSH. The plugin handles `ConnectTimeout`, `StrictHostKeyChecking`, and most connection-layer issues internally. | Tool response | Likely cause | Action | |---|---|---| | `server "{name}" not found` | Server name does not match `ssh_list_servers` output | Show `ssh_list_servers` again; ask user to pick an exact name | | `permission denied` / auth error | Auth type mismatch in server config | Ask user to verify auth method (key vs password) in their ssh-manager config | | `connection refused` / `timeout` | Server unreachable, wrong host/port, or SSH daemon down | Report to user; suggest checking server availability independently | | `command not found: {name}` | Remote tool not installed | Note it and continue with next step | | `tool is blocked on readonly server` | Server in readonly/restricted mode | Inform user; skip destructive steps | | `required parameter missing` | Bug or incomplete tool spec | Re-check parameters against tool spec; do not fall back to raw `ssh_execute` for destructive tools as it bypasses plugin safety guards |