SKILL.md 14 KB


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."

compatibility: "Pi Coding Agent with the ssh-manager MCP server configured."

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