Skip to main content
Glama
safe-ssh-mcp

safe-ssh-mcp

by Areso

Safe SSH MCP Server

A secure and scoped SSH MCP server for executing read-only diagnostic commands over SSH.
In this project, "safe" refers specifically to host safety: the server is designed to prevent modifications to the remote system and reduce the risk of operational harm. It does NOT attempt to guarantee that command output cannot be misused by external agents.

Badges

Snyk Vulnerability Database report - security check
PyPI Downloads

Related MCP server: ssh-remote-mcp

Overview

The core philosophy behind this MCP server is safety first. Instead of providing an AI agent with an unrestricted bash shell, this server exposes only carefully curated, read-only commands for system diagnostics and monitoring.

Available Tools (Partial List)

A few examples from each group; <...> marks a validated parameter.

Disk and filesystems

  • get_disk_free : df -h

  • get_disk_usage : find <path> -mindepth 1 -maxdepth 1 -exec du -sh -- {} + 2>/dev/null | sort -rh | head -n 20

  • get_mounts : findmnt (or cat /proc/mounts)

CPU, memory and processes

  • get_top : top -b -n 1 -c

  • get_vmstat : vmstat 1 5

  • get_pressure : cat /proc/pressure/{cpu,io,memory}

Systemd and logs

  • get_systemd_list_failed : systemctl list-units --state=failed --no-pager

  • get_systemd_status : systemctl status <daemon>

  • get_journal_errors : journalctl -p warning -n <lines> --no-pager

  • get_dmesg : dmesg

Network

  • get_listening_sockets : ss -tulpn

  • get_ip_route : ip route show and ip -6 route show

  • get_dns_lookup : getent ahosts <name> or dig <name> <record_type>

  • check_tcp_connect : nc -vz -w 3 <target_host> <target_port>

Docker

  • get_docker_ps_all : docker ps --all

  • get_docker_logs : docker logs --tail <lines> <container>

  • get_docker_stats : docker stats --no-stream

Kubernetes

  • get_kubectl_events : kubectl get events --sort-by=.lastTimestamp

  • get_kubectl_logs : kubectl logs --tail=<lines> --namespace=<namespace> <pod>

  • get_crictl_ps : crictl ps --all

System, logins and crashes

  • get_distroname_version : cat /etc/os-release

  • get_installed_packages : dpkg-query -W (with source package), rpm -qa or /lib/apk/db/installed (with origin)

  • get_failed_logins : lastb -F -n <lines>

  • get_coredumps : coredumpctl list --no-pager

Click to open full list of available tools

Example of invocation

okay, could u check disk usage on /root/ path with help of safe-ssh-mcp server on a remote myserver.mydomain.pro using root and /Users/myUser/.ssh/id_rsa to login?

Means of connection

The server supports SSH account-password authentication and private-key authentication. Configure the private-key passphrase source in mcp_config.ini:

# ENV_VAR (SSH_KEY_PASSPHRASE) or KEYCHAIN (ssh-agent)
ssh_key_passphrase_method=ENV_VAR
  1. SSH account password: provide password to the MCP tool. Do not use this for a private-key passphrase.

  2. Unprotected SSH key: use key_path. The default ENV_VAR setting also supports unprotected keys; SSH_KEY_PASSPHRASE is not required.

  3. Passphrase-protected SSH key from an environment variable: leave ssh_key_passphrase_method=ENV_VAR, set SSH_KEY_PASSPHRASE in the environment that starts the MCP server, and provide key_path.

    export SSH_KEY_PASSPHRASE='your-key-passphrase'
    safe-ssh-mcp
  4. macOS Keychain / SSH agent: set ssh_key_passphrase_method=KEYCHAIN, then unlock the key in the same user session that starts the MCP server. The server receives SSH signatures from ssh-agent; it does not read the key passphrase or the key_path.

    ssh-add --apple-use-keychain ~/.ssh/id_ed25519
    
    # Optional: make the loaded identity expire after one hour.
    ssh-add -t 1h ~/.ssh/id_ed25519
    
    # Remove one identity, or clear every loaded identity.
    ssh-add -d ~/.ssh/id_ed25519
    ssh-add -D
  5. Cursor launched from the macOS Dock: use KEYCHAIN (recommended). You can run ssh-add from any Terminal window; it updates the SSH agent for your macOS login session, so Cursor does not need to be started from that Terminal.

    ssh-add --apple-use-keychain ~/.ssh/id_ed25519
    ssh-add -l

    After changing mcp_config.ini, restart the MCP server in Cursor (or fully quit and reopen Cursor). When invoking a tool in KEYCHAIN mode, omit key_path; the agent selects from its loaded identities.

    If you must use ENV_VAR, set it in the macOS launchd session before opening Cursor from the Dock, then fully quit and reopen Cursor:

    launchctl setenv SSH_KEY_PASSPHRASE 'your-key-passphrase'

    This value lasts only for the current login session, and putting a passphrase directly in a command may save it in shell history. Prefer KEYCHAIN.

For KEYCHAIN, the MCP server must inherit SSH_AUTH_SOCK from that user session. This is normally automatic for a stdio server started from a desktop application. ENV_VAR stores the passphrase in the server process environment, so restrict access to the process and avoid putting it in source control.

Changelog

Project Contents

  1. mcp_ssh.py - the SSH MCP server

  2. mcp_config.ini - the server's config

  3. check_health_sse.py - check the server's tools and either it's up (sse transport)

  4. check_health_stdio.py - check the server's tools and either it's up (stdio transport)

Compatibility

Tested manually on Python3.11 running on MacOS against remote Ubuntu server
Autotested with Python 3.11 3.12 3.13 3.14

The MCP Registry

mcp-name: io.github.Areso/safe-ssh-mcp

The License

This project is licensed under the GNU AGPLv3 License.

Why AGPL?

This server acts as core infrastructure and contains no business logic. By using the AGPL license, we ensure that any security improvements, bug fixes, or new diagnostic tools added to the server are shared back with the open-source community.

Note for Client Developers

Because MCP clients communicate with this server via standard Inter-Process Communication (IPC) or network protocols (like HTTP/SSE), the AGPL license does not "infect" or restrict the client applications connecting to it.
You can safely connect proprietary, closed-source, or permissively licensed (e.g., MIT, Apache 2.0) AI agents to this server without violating the license terms.

Available Tools

55 tools
check_tcp_connectA

Checks FROM a remote Linux host (reached via SSH) whether a TCP connection to target_host:target_port can be opened (nc -vz). USE THIS TOOL to tell whether a service (database, API, another server) is reachable from that host. target_host is a hostname or an IPv4/IPv6 address, target_port is 1-65535. Do not confuse them with host/port, which are the SSH connection to the machine the check runs on. Only the connection is opened and closed, no data is sent. One port per call. ok=True means the port accepted the connection. Otherwise nc's message tells why: 'Connection refused' - the target is reachable but nothing listens on the port; 'timed out' - no answer in 3 seconds, usually a firewall or a routing problem; a name resolution error - check the name with get_dns_lookup. Fails with exit code 127 if nc is not installed on the host.

Inputs: host (required), user (required), target_host (required), target_port (required), +5 more. Returns: object

get_coredumpsA

Inspects crashes (core dumps) recorded by systemd-coredump on a remote Linux host via SSH. command='list' lists the last lines core dumps (time, PID, signal, executable), below the column header. When there are no core dumps, coredumpctl exits with code 1 and ok is False - that is not a failure. command='info' shows details of a core dump (signal, command line, stack trace if available): of the given pid, or of the most recent one when pid is omitted. Optional since limits them to a time window: '30 min ago', '2 hours ago', 'today', 'yesterday' or 'YYYY-MM-DD HH:MM:SS'. Seeing other users' core dumps usually requires root.

Inputs: host (required), user (required), pid, port, +7 more. Returns: object

get_crictl_imagefsinfoB

Collects crictl imagefsinfo output (mountpoint, used bytes and inodes of the CRI image filesystem of a kubernetes node) from a remote Linux host via SSH. USE THIS TOOL to investigate image garbage collection and DiskPressure. Usually requires root.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_crictl_imagesB

Collects crictl images output (image, tag, image ID, size of images present in the CRI runtime of a kubernetes node) from a remote Linux host via SSH. Usually requires root.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_crictl_inspectA

Inspects a SPECIFIC CRI container (crictl inspect, JSON) on a kubernetes node from a remote Linux host via SSH: state, exit code and reason, timestamps, image, mounts, log path, pid and the runtime spec. Use get_crictl_ps first to find the container ID. Usually requires root. Values of environment variables are replaced by ''; their names are kept.

Inputs: host (required), user (required), container_id (required), port, +4 more. Returns: object

get_crictl_inspect_imageA

Inspects a SPECIFIC image (crictl inspecti, JSON) in the CRI runtime of a kubernetes node from a remote Linux host via SSH: repo tags and digests, size, user, entrypoint/cmd, layers. image is an image name ('registry.k8s.io/pause:3.9') or an image ID from get_crictl_images. Usually requires root. Values of environment variables are replaced by ''; their names are kept.

Inputs: host (required), user (required), image (required), port, +4 more. Returns: object

get_crictl_logsA

Gets the last lines log lines (stdout and stderr merged) of a SPECIFIC CRI container on a kubernetes node from a remote Linux host via SSH. Works when the API server is down. Use get_crictl_ps first to find the container ID. Usually requires root. Optional since limits them to a time window: '30 min ago', '2 hours ago', '30m', '1h30m' or an absolute UTC time 'YYYY-MM-DDTHH:MM:SSZ'.

Inputs: host (required), user (required), container_id (required), port, +6 more. Returns: object

get_crictl_psB

Collects crictl ps --all output (all CRI containers on a kubernetes node, including exited ones: container ID, image, state, name, attempt, pod) from a remote Linux host via SSH. Usually requires root.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_crontab_tasksC

Collects crontab -l (crontab list) output from a remote Linux host via SSH

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_current_datetimeB

Collects current datetime from a remote Linux host via SSH

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_disk_freeC

Collects disk usage (df -h) from a remote Linux host via SSH.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_disk_usageA

Collects disk usage for a specific abs path from a remote Linux host via SSH. Returns the top 20 largest files/folders in the specified directory.

Inputs: host (required), user (required), port, timeout, +4 more. Returns: object

get_distroname_versionC

Collects distr details from os-release from a remote Linux host via SSH

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_dmesgA

Collects dmesg log from a remote Linux host via SSH. Reading the kernel log usually requires root: when the login user is not root, the command is run through passwordless sudo or doas (sudo -n / doas -n). Optional since limits it to recent messages: '30 min ago', '2 hours ago', 'today', 'yesterday' or 'YYYY-MM-DD HH:MM:SS'. It requires util-linux 2.37+ on the remote host; on older hosts dmesg fails with an unrecognized option error - then call without since.

Inputs: host (required), user (required), port, since, +4 more. Returns: object

get_dns_lookupA

Resolves a hostname (or reverse-resolves an IP address) ON a remote Linux host via SSH, and prints its /etc/resolv.conf. USE THIS TOOL to check what a name resolves to from that host. By default uses getent, which resolves exactly like applications on the host do (/etc/hosts, nsswitch.conf, then DNS). A name that does not resolve gives exit code 2. use_dig=True queries the DNS servers directly with dig (bypassing /etc/hosts) and returns the full answer: status (NOERROR, NXDOMAIN, SERVFAIL), CNAME chain, TTLs, the server that answered and query time. It fails with exit code 127 if dig is not installed - then use use_dig=False. The next two parameters require use_dig=True: record_type is one of A (default), AAAA, CNAME, MX, NS, TXT, SOA, PTR, SRV, CAA. server (IP address or hostname, e.g. '8.8.8.8') asks that DNS server instead of the ones in /etc/resolv.conf - compare both answers to tell a broken resolver from a broken record.

Inputs: host (required), name (required), user (required), port, +7 more. Returns: object

get_docker_imagesA

Collects docker images --digests output (repository, tag, digest, image ID, age, size) from a remote Linux host via SSH When the login user cannot reach the docker daemon (not root, not in the docker group), the command is run through passwordless sudo or doas (sudo -n / doas -n).

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_docker_inspectA

Inspects a SPECIFIC docker container, given by name or ID, on a remote Linux host via SSH. Returns a curated subset of docker inspect: image, entrypoint/cmd, state (status, exit code, OOMKilled, start/finish time, health check log), restart count and policy, memory/CPU limits, networks, ports, mounts and log driver. Environment variables are NOT returned. When the login user cannot reach the docker daemon (not root, not in the docker group), the command is run through passwordless sudo or doas (sudo -n / doas -n).

Inputs: host (required), user (required), container (required), port, +4 more. Returns: object

get_docker_logsA

Gets the last lines log lines (stdout and stderr merged) of a SPECIFIC docker container, given by name or ID, from a remote Linux host via SSH. Works for stopped containers too. Use get_docker_ps_all first to find container names. Optional since limits them to a time window: '30 min ago', '2 hours ago', '30m', '1h30m' or 'YYYY-MM-DD HH:MM:SS' ('today' and 'yesterday' are not supported here). When the login user cannot reach the docker daemon (not root, not in the docker group), the command is run through passwordless sudo or doas (sudo -n / doas -n).

Inputs: host (required), user (required), container (required), port, +6 more. Returns: object

get_docker_ps_allB

Collects docker ps --all output from a remote Linux host via SSH When the login user cannot reach the docker daemon (not root, not in the docker group), the command is run through passwordless sudo or doas (sudo -n / doas -n).

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_docker_statsA

Collects a one-shot docker stats snapshot from a remote Linux host via SSH: CPU %, memory usage/limit, network and block I/O, PIDs for every RUNNING container. When the login user cannot reach the docker daemon (not root, not in the docker group), the command is run through passwordless sudo or doas (sudo -n / doas -n).

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_failed_loginsA

Lists recent FAILED login attempts (lastb, from btmp) on a remote Linux host via SSH, newest first: attempted user, terminal, source address, time. USE THIS TOOL to check for brute-force attempts. Requires root, since btmp is not world-readable: when the login user is not root, the command is run through passwordless sudo or doas (sudo -n / doas -n). Optional since limits them to a time window: '30 min ago', '2 hours ago', 'today', 'yesterday' or 'YYYY-MM-DD HH:MM:SS'.

Inputs: host (required), user (required), port, lines, +5 more. Returns: object

get_free_memoryC

Returns free memory, swap (total/free), page cache, HugePages and so on

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_hostnameB

Collects the hostname of a remote Linux host via SSH, as the host itself reports it. Output: "hostname: " (the kernel hostname, uname -n), then "fqdn: " (hostname -f) when the host can resolve its fully qualified name; that line is absent otherwise.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_inode_usageC

Collects inode usage (df -i) from a remote Linux host via SSH.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_installed_packagesB

Lists installed packages with versions on a remote Linux host via SSH, using the first available package manager. The FIRST output line starts with '#' and names the package manager and the columns; the rest is one package per line: dpkg (Debian/Ubuntu), tab-separated: package, version, architecture, status ('ii' installed, 'hi' installed and held), source_package, source_version. Security advisories for Debian/Ubuntu are keyed by SOURCE package (openssh, not openssh-server). Removed packages whose config files are left behind (state 'rc') are not listed. rpm (RHEL/Fedora/SUSE): name-version-release.arch, as printed by rpm -qa. apk (Alpine), tab-separated: package, version, architecture, origin. Security advisories for Alpine are keyed by ORIGIN (openssl, not libcrypto3); it has the same version as the package.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_ip_addrC

Collects ip addr show output (network interfaces, their state, MAC, IPv4 and IPv6 addresses) from a remote Linux host via SSH

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_ip_routeA

Collects the IPv4 and IPv6 routing tables (ip route show, ip -6 route show: default gateway, per-interface routes) from a remote Linux host via SSH

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_is_installedC

Checks whether a binary/command is installed and callable on a remote Linux host via SSH.

Inputs: host (required), user (required), port, binary, +4 more. Returns: object

get_journal_errorsA

Gets recent journald entries of priority warning and above (warning, err, crit, alert, emerg) from ALL units and the kernel. USE THIS TOOL FIRST when you don't know which service is at fault. With current_boot_only=True only entries since the last boot are returned. Optional since limits them to a time window: '30 min ago', '2 hours ago', 'today', 'yesterday' or 'YYYY-MM-DD HH:MM:SS' (the last lines entries within it are returned).

Inputs: host (required), user (required), port, lines, +6 more. Returns: object

get_kernel_versionB

Collects kernel name, release, build version and architecture (uname -srvm) from a remote Linux host via SSH

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_kubectl_describeA

Describes a SPECIFIC kubernetes resource (kubectl describe) by running kubectl on a remote Linux host via SSH: status, conditions, containers and its recent events. kubectl must be configured for the SSH user there. resource_type is one of: pod, node, namespace, deployment, statefulset, daemonset, replicaset, job, cronjob, service, endpoints, ingress, networkpolicy, persistentvolumeclaim, persistentvolume, storageclass, horizontalpodautoscaler, poddisruptionbudget (plural and short names like 'po', 'deploy', 'svc' work too). Secrets and configmaps are NOT available. namespace is ignored for cluster-scoped resources (node, namespace, persistentvolume, storageclass).

Inputs: host (required), name (required), user (required), resource_type (required), +6 more. Returns: object

get_kubectl_eventsA

Gets the most recent lines kubernetes events (kubectl get events), oldest first, below the column header, by running kubectl on a remote Linux host via SSH. kubectl must be configured for the SSH user there. USE THIS TOOL FIRST when you don't know what is wrong in a cluster. Events of ALL namespaces are returned unless namespace is given. warnings_only=True keeps only events of type Warning (FailedScheduling, BackOff, Unhealthy...).

Inputs: host (required), user (required), port, lines, +6 more. Returns: object

get_kubectl_logsA

Gets the last lines log lines of a SPECIFIC kubernetes pod (kubectl logs) by running kubectl on a remote Linux host via SSH. kubectl must be configured for the SSH user there. container selects a container in a multi-container pod. previous=True returns logs of the previous, crashed instance - use it for CrashLoopBackOff. Optional since limits them to a time window: '30 min ago', '2 hours ago', '30m', '1h30m' or an absolute UTC time 'YYYY-MM-DDTHH:MM:SSZ'.

Inputs: pod (required), host (required), user (required), port, +9 more. Returns: object

get_last_loginsA

Lists recent SUCCESSFUL logins (last, from wtmp) to a remote Linux host via SSH, newest first: user, terminal, source address, login/logout time. Also shows reboots. For failed login attempts use get_failed_logins. On BusyBox hosts (Alpine), where last takes no options, the output is plain last cut to lines rows: its first line starts with '# BusyBox', times have no year, and since is not supported (the tool fails with a message saying so). Optional since limits them to a time window: '30 min ago', '2 hours ago', 'today', 'yesterday' or 'YYYY-MM-DD HH:MM:SS'.

Inputs: host (required), user (required), port, lines, +5 more. Returns: object

get_libc_versionB

Collects C library implementation and version (glibc or musl) from a remote Linux host via SSH. Uses ldd --version; musl is detected by the presence of /lib/ld-musl-* loader. The last line, "package: ", is the distro package version (e.g. 2.39-0ubuntu8.9, 2.34-289.el9, 1.2.6-r3) that security data is keyed by; it is taken from dpkg, rpm or apk and is absent on other distros.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_listening_socketsA

Collects ss -tulpn output (open ports and sockets) from a remote Linux host via SSH. If ss is not installed (e.g. Alpine), falls back to netstat -tulnp; the output then starts with a "# ss is not installed: netstat -tulnp" line and has netstat's columns. If the login user is not root, runs through sudo -n / doas -n to get the process names of all sockets; if that is not allowed, runs as the login user and says so in a "# not run as root" line (process names are then shown only for its own sockets).

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_list_of_filesB

Collects list of files for a specific abs path from a remote Linux host via SSH. Returns with -lah option

Inputs: host (required), user (required), port, timeout, +4 more. Returns: object

get_list_of_files_with_filterA

Collects list of files for a specific abs path with filter (grep) applied from a remote Linux host via SSH. -lah option is applied.

Inputs: host (required), user (required), port, filter, +5 more. Returns: object

get_lockfilesA

Finds dependency lockfiles under the given absolute app paths on a remote Linux host via SSH: composer.lock (PHP), package-lock.json, yarn.lock, pnpm-lock.yaml (Node.js), requirements*.txt, poetry.lock, uv.lock, Pipfile.lock (Python), Gemfile.lock (Ruby), go.sum (Go) and Cargo.lock (Rust). Skips node_modules, vendor, .git, venv and .venv directories. Lists the files found with size and mtime (ls -l). With include_contents=True prints the files instead (the first 20 found), each after a '===== ( bytes) =====' line. A file is always printed WHOLE or not at all, never cut: a file over 5 MB, or one that would take the output over 10 MB in total, gets a '===== (skipped: ...) =====' line instead. The output can be several MB - call without include_contents first to see the sizes, then narrow abs_paths to the application you need.

Inputs: host (required), user (required), abs_paths (required), port, +6 more. Returns: object

get_lsblkC

Collects lsblk output from a remote Linux host via SSH

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_memory_pressureA

DEPRECATED: use get_pressure, which returns CPU, I/O and memory pressure in one call. Returns memory pressure if supported

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_mountsA

Lists mounted filesystems with their mount point, source device, filesystem type and mount options (findmnt, as a tree; /proc/mounts if findmnt is not installed). USE THIS TOOL to find a filesystem that was remounted read-only after an error ('ro' in the options), to check options like noexec/nosuid, and to see network (nfs, cifs), overlay and tmpfs mounts. For free space use get_disk_free instead.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_php_infoA

Collects PHP details from a remote Linux host via SSH: CLI version and SAPI, installed CGI (php-cgi) and FPM (php-fpm) binaries with their versions/SAPIs, whether php-fpm or php-cgi processes are currently running, and, if Apache is installed, whether mod_php is enabled in its config and loaded.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_pressureA

Returns Linux pressure stall information (PSI) for CPU, I/O and memory in one call. USE THIS TOOL to tell which resource the host is short of: unlike load average, it shows how much time tasks actually spent waiting for each resource. 'some' = share of time at least one task was stalled, 'full' = all non-idle tasks were stalled at once; avg10/avg60/avg300 are percentages over the last 10, 60 and 300 seconds, total is the cumulative stall time in microseconds. Prints 'PSI not supported' for a resource if the kernel is older than 4.20 or PSI is disabled.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_ps_aux_top_cpu_consumersB

Lists top 10 cpu consumers, CPU desc (ps aux --sort=-%cpu). On BusyBox hosts (Alpine), where ps cannot sort, the output comes from top -b -n 1 instead: its first line starts with '# BusyBox', the columns differ, and %CPU is the usage at this moment, not the average over the process lifetime that ps reports.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_ps_aux_top_mem_consumersB

Lists top 10 mem consumers, MEM desc (ps aux --sort=-%mem). On BusyBox hosts (Alpine), where ps cannot sort and has no %MEM, the output is ps -o pid,user,rss,vsz,args sorted by RSS instead: its first line starts with '# BusyBox' and sizes are in KB unless they carry an m or g suffix.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_service_logs_from_journalctlC

Gets recent journald logs for a specific systemd service. Optional since limits them to a time window: '30 min ago', '2 hours ago', 'today', 'yesterday' or 'YYYY-MM-DD HH:MM:SS' (the last lines entries within it are returned).

Inputs: host (required), user (required), service (required), port, +6 more. Returns: object

get_sshd_effective_configA

Collects the effective sshd configuration (sshd -T) from a remote Linux host via SSH. Shows the final values after defaults, includes and Match blocks are applied. Requires root, since sshd -T reads the host keys: when the login user is not root, the command is run through passwordless sudo or doas (sudo -n / doas -n).

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_systemd_list_allC

Lists all systemd units. USE THIS TOOL to check for running, stopped, or FAILED daemon/service/unit.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_systemd_list_failedA

Lists failed systemd units. USE THIS TOOL to check for FAILED daemons/services/units.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_systemd_list_timersC

Lists all systemd timers. USE THIS TOOL to check for timers.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_systemd_statusC

Gets the detailed status, logs, and error messages for a SPECIFIC systemd daemon/service/unit.

Inputs: host (required), user (required), daemon (required), port, +4 more. Returns: object

get_topB

Collects top's snapshot from a remote Linux host via SSH (top -b -n 1 -c). On BusyBox hosts (Alpine), where top has no -c, the output is plain top -b -n 1: its first line starts with '# BusyBox' and the columns differ.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_uptimeA

Collects uptime from a remote Linux host via SSH

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

get_vmstatA

Samples system activity for 4 seconds with vmstat (5 reports, 1 second apart): run queue and blocked processes (r, b), memory, swap-in/out (si, so), block I/O (bi, bo), interrupts and context switches (in, cs) and the CPU split (us, sy, id, wa, st). USE THIS TOOL to see what the host is doing right now: swapping, I/O wait, CPU saturation. The FIRST report line is the average since boot - judge the current state by the other four.

Inputs: host (required), user (required), port, timeout, +3 more. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.2/5.0

    Scored across 55 tools

    Disambiguation3/5

    Most tools target a clearly distinct command or resource (docker vs crictl vs kubectl logs, df -h vs df -i vs du), and several descriptions explicitly say when to prefer one over another. However, there is a dense cluster of overlapping host-state samplers (get_top, get_vmstat, get_pressure, get_ps_aux_top_cpu_consumers, get_ps_aux_top_mem_consumers, get_free_memory) and near-duplicates like get_list_of_files vs get_list_of_files_with_filter, plus a DEPRECATED get_memory_pressure that shadows get_pressure and should be removed.

    Naming Consistency4/5

    Overwhelmingly consistent snake_case with a get_ prefix and noun-qualified names (get_docker_ps_all, get_crictl_inspect_image, get_systemd_list_failed). Minor deviations: check_tcp_connect uses a different verb form, and get_list_of_files_with_filter is unusually verbose compared to its sibling.

    Tool Count2/5

    At 55 tools for one SSH diagnostic server, the surface is far past the comfortable range and forces the agent to choose among many narrowly splintered variants. The tools are individually meaningful, but several could be merged (deprecated memory_pressure, list_files vs filtered list, the redundant top/vmstat/pressure trio), and the sheer count increases selection cost.

    Completeness4/5

    Coverage of Linux host triage is genuinely broad: disk/inode/mounts, memory and pressure, network/routing/DNS/reachability, systemd units and journald, logins and brute-force attempts, packages/libc/kernel, plus docker, crictl and kubectl layers. Gaps are modest: no general file/config reader beyond lockfiles and sshd -T, no full process listing or per-service resource view, and no non-disk-usage inspection of arbitrary application logs.

    Maintenance

    ActivityMaintained
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      C
      maintenance
      A server that enables secure interaction with remote SSH hosts through standardized MCP interface, providing functions like listing hosts, executing commands, and transferring files using native SSH tools.
      7
      400 npm
      105
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      A secure SSH-based MCP server for diagnosing remote servers. It allows AI agents to execute read-only commands and read files automatically, while requiring user confirmation for write operations.
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      A read-only MCP server for Linux and macOS system administration, diagnostics, and troubleshooting, supporting remote SSH execution and multi-host management.
      Apache 2.0
    • A
      license
      A
      quality
      D
      maintenance
      An MCP server for SSH/SCP operations with passwordless authentication, enabling remote command execution, file transfer, and session management.
      19
      17 npm
      1
      MIT