Skip to main content
Glama
perhamm

ssh-mcp-server

by perhamm

ssh-mcp-server

CI npm

MCP server over SSH: the agent executes commands on remote machines, while keys, passwords, and sudo stay on our side.

A fork of classfang/ssh-mcp-server under ISC.

English | Русский

What it is

ssh-mcp-server is a bridge between an MCP client (Claude Code, Cursor, Cline) and SSH. The agent calls tools, the server connects to the machine and returns the output. The model never sees the private key, password, or sudo password: all of that is read from the local config and environment variables of the process.

One server serves any number of hosts. The host is selected on the fly by alias from ~/.ssh/config, so you don't need to add every machine to the MCP client config.

Related MCP server: ssh-mcp-server

What's new in this fork

Feature

Why

Hosts from ~/.ssh/config on the fly

One MCP for the whole fleet. The alias is passed in connectionName, the connection is established on first use

ProxyJump

A host behind a bastion is reachable by alias, the ProxyJump chain is parsed from the SSH config

sudo from environment variable

The agent requests sudo: true, the server supplies the password and strips it from the output

Forbidden core

A list of operations that never run: not under sudo, not in any profile, not via SFTP

Guard profiles

Ready-made guard sets safe and readonly, versioned and updatable

Tunnels

SOCKS5 (like ssh -D) and port forwarding (like ssh -L) to any local port

Host key checking

known_hosts is verified by default, an unknown key means connection refused

Modern cryptography

Ed25519 first in the list, no SHA-1, CBC, or DSA

Audit log

Every call is written to JSONL with rotation and gzip archives

No file upload

upload is not published by default: a file that guards can't read is a way to smuggle code onto the host

Tools

Tool

What it does

execute-command

Runs a command, supports sudo and arbitrary connectionName

download

Fetches a file from the server

list-servers

Shows configured connections, their status, and the active guard profile

list-ssh-hosts

Shows aliases from the SSH config available as connectionName

open-tunnel

Starts a SOCKS5 proxy or port forward through the connection

close-tunnel

Closes a tunnel

list-tunnels

Shows open tunnels and connection counters

list-ssh-hosts only appears with the --ssh-config-hosts flag, tunnel tools are removed with --disable-tunnels. The upload tool is not in the list: it is only published with --enable-upload.

Host facts without extra commands

On connection, the server collects machine state once: name, addresses, OS, kernel, uptime, disk, memory, process count. All probes are joined with markers into a single command, so it's one SSH session, not six.

The server caches the result and returns it from list-servers:

[connected] prod-1 | deploy@10.0.0.5:22 | hostname=prod-1 | os=Linux | updated=2026-08-19T18:14:23Z

Raw JSON:
[{"name":"prod-1","connected":true,"guards":"guards=safe ruleset=2026.08.19 ...",
  "status":{"reachable":true,"osVersion":"Ubuntu 24.04.1 LTS","kernelVersion":"6.8.0-51-generic",
  "uptime":"12 days","diskSpace":{"free":"9.8G","total":"229.6G"},
  "memory":{"free":"5.6G","total":"15.5G"},"processes":{"running":214}}}]

That is, you don't need to ask for uname -a, df -h, free -h, and uptime separately — the answers are already there. The agent calls list-servers once and reads the status from there.

Probes go through guards one by one. With the whitelist, only allowed fields remain in the status. An incomplete status does not mean the host is down.

A successful command with no output returns [exit code] 0, not an empty string. The model would read an empty response as an unclear result and go re-check via echo $? — that's an extra SSH session and extra tokens.

Quick start: one server for the whole fleet

MCP client config:

{
  "mcpServers": {
    "ssh": {
      "command": "npx",
      "args": [
        "-y",
        "@perhamm/ssh-mcp-server",
        "--ssh-config-hosts",
        "--guards-profile", "safe"
      ],
      "env": {
        "SSH_MCP_SUDO_PASSWORD": "..."
      }
    }
  }
}

Then the agent works like this:

  1. Calls list-ssh-hosts and finds the right alias, e.g. r-ulybka-prod-master. For large configs, the list is filtered, so the agent passes filter: a substring or a pattern like r-ulybka-*.

  2. Calls execute-command with connectionName: "r-ulybka-prod-master".

  3. The server reads the alias from ~/.ssh/config, takes HostName, User, Port, IdentityFile, and ProxyJump from there, establishes the connection, and runs the command.

The key never leaves the machine: the server reads the file itself, only the path from the SSH config appears in the dialog. If IdentityFile is not set, the ssh-agent from SSH_AUTH_SOCK is used. An alias without HostName connects by its own name, just like ssh does.

Only an alias declared as a separate Host block in the config is reachable. A Host * block provides defaults but does not turn an arbitrary name into a reachable host.

The alias list can be narrowed down:

"args": [
  "-y", "@perhamm/ssh-mcp-server",
  "--ssh-config-hosts",
  "--allowed-hosts", "r-ulybka-*,*-stage-*",
  "--ssh-config-file", "/home/user/.ssh/config_work"
]

Patterns support * and ?. If an alias doesn't match any pattern, the connection is not established and the agent gets an SSH_HOST_NOT_ALLOWED error.

Safe mode and guards

Guards are a versioned set of rules that checks every command before it is sent to the server. The rules live in guards/default-guards.json and are updated together with the repository.

Profiles

Profile

Behavior

off

Profile rules are disabled, only the forbidden core works. Default value

safe

Plus a ban on destructive commands, everything else is allowed

readonly

Only reading and diagnostics allowed, inherits all safe bans

"args": ["-y", "@perhamm/ssh-mcp-server", "--ssh-config-hosts", "--guards-profile", "safe"]

What safe catches beyond the core: shutdown and reboot, firewall reset, stopping sshd and kubelet, kubectl delete, helm uninstall, docker system prune, package removal, DROP DATABASE, curl | sh, git push --force, log cleanup, kernel module unloading, interactive editors. The full list with reasons is in the JSON.

readonly additionally requires every part of the command to be in the whitelist: ls, cat, grep, find, ps, ss, df, journalctl, systemctl status, kubectl get/describe/logs, docker ps/logs, and similar. sudo is completely forbidden in this profile, along with su, doas, and pkexec.

Forbidden core

Some operations never run: not in the off profile, not under sudo, not through a custom guards file, not through SFTP bypassing commands. The list lives in the forbidden block.

Category

What is closed

Accounts

useradd, usermod, userdel, groupadd, passwd, chpasswd, chage, vipw, and writing to /etc/passwd, /etc/shadow, /etc/group

sudo

Writing to /etc/sudoers and /etc/sudoers.d, visudo

Schedules

crontab except crontab -l, writing to /etc/cron*, /var/spool/cron, /etc/anacrontab, at and batch commands

systemd

Writing units and timers to /etc/systemd, /lib/systemd, /usr/lib/systemd, systemctl edit, systemd-run

SSH

Editing /etc/ssh/*, ~/.ssh/*, authorized_keys, sshd_config, as well as ssh-keygen, ssh-copy-id, ssh-add

Interpreters

python, perl, ruby, node, php, lua, Rscript, and running a script by file: bash /tmp/x.sh, sh -s, source. Guards don't read foreign code, so such execution is closed entirely

Mass deletion

rm -r of a top-level directory or system subdirectory, rm -r by pattern, find -delete, deletion via xargs rm, the --no-preserve-root flag

Disks and secrets

mkfs, wipefs, dd of=/dev/, writing to /dev/sd*, fork bomb, reading /etc/shadow and private keys

Normal work still works: crontab -l, cat /etc/ssh/sshd_config, systemctl restart nginx, rm -rf /var/lib/myapp/cache/tmp all pass. A parseable bash -c "..." also works: its contents are checked by the same rules.

The core also closes file tools. download won't fetch /etc/shadow or the contents of ~/.ssh, and allowedRemotePaths doesn't re-enable anything here. The local side is also protected: download won't place a file into our own ~/.ssh.

File upload is completely disabled. The upload tool is not published until --enable-upload is passed, and the readonly profile rejects uploads even with that flag.

If the server is needed specifically for creating users or editing cron, the core will have to be modified in the fork deliberately: there is no flag that disables it.

Why a semicolon doesn't bypass this

The command is split into parts by ;, |, &&, ||, &, newline, and $(...) substitutions, and each part is checked separately. Quotes are taken into account during parsing. So ls; rm -rf / doesn't pass in any profile, even though the whole string starts with the allowed ls.

Wrappers are stripped before checking: sudo, env, timeout 5, nohup, and assignments like LC_ALL=C don't hide the command from the rules. A script inside bash -c "..." is parsed separately and checked by the same rules. Command length is limited to 5000 characters.

Guards close agent mistakes, not deliberate bypass. An interpreter with arbitrary code inside, like python -c, cannot be parsed by the rules. Where bypass is unacceptable, we restrict the SSH user's own permissions.

Updating rules

Three ways to keep rules fresh:

  1. Merge upstream into your fork. The rules file is versioned with the version field; the version is visible in list-servers and in the denial message.

  2. Keep your own file and point to it with --guards-file /etc/ssh-mcp/guards.json. Rules from it are added to the built-in ones, the version becomes 2026.08.19+local-1.

  3. Update the file on a schedule:

node scripts/update-guards.js https://example.com/guards.json /etc/ssh-mcp/guards.json

The script validates the JSON and compiles every regular expression before replacing the file. A broken download doesn't break the working rule set.

Format of your own file:

{
  "version": "local-1",
  "profiles": {
    "safe": {
      "deny": [
        { "id": "no-ansible", "pattern": "^ansible-playbook\\b", "reason": "выкат идёт из CI" }
      ]
    }
  }
}

The scope: "command" field makes the rule check the entire command rather than individual parts. This is how the curl | sh and SQL rules work. You can add your own bans to the forbidden block of your file, but you can't remove the built-in ones: the lists are merged.

The old --whitelist and --blacklist are still there and are checked before the guards.

sudo without a password in the dialog

The sudo password is stored in the server process's environment variable. The agent passes sudo: true, but never sees the password itself, neither in the call arguments nor in the output.

{
  "mcpServers": {
    "ssh": {
      "command": "npx",
      "args": ["-y", "@perhamm/ssh-mcp-server", "--ssh-config-hosts", "--guards-profile", "safe"],
      "env": {
        "SSH_MCP_SUDO_PASSWORD": "..."
      }
    }
  }
}

Tool invocation:

{
  "tool": "execute-command",
  "params": {
    "cmdString": "systemctl restart nginx",
    "connectionName": "r-ulybka-prod-master",
    "sudo": true
  }
}

The command goes to the server as sudo -S -k -p '' -u root -- /bin/sh -c '<command>', and the password is written to the channel's stdin. It's not in the command line, so it doesn't end up in ps or in the history. The pseudo-terminal is disabled for such commands, otherwise the tty would echo the input back into the output. Just in case, the password is also stripped from the output and from error text.

The variable name and target user are changed with the --sudo-password-env and --sudo-user flags. If the variable is empty, the call fails with SUDO_PASSWORD_MISSING before even connecting. The readonly profile forbids sudo entirely.

In shell mode, the password is appended as a separate line right after the command, since sudo reads the same stdin as the shell itself. The -k flag guarantees the password prompt will always appear and the line won't be executed as a command. For sudo, exec mode is preferable.

Tunnels

open-tunnel starts a local listener and routes traffic through the SSH connection. Useful when there's no direct access to cluster services, but there is access to the node.

SOCKS5 on port 8777:

{
  "tool": "open-tunnel",
  "params": {
    "type": "socks5",
    "localPort": 8777,
    "connectionName": "r-ulybka-prod-master"
  }
}

From then on, any client goes through the proxy, and names are resolved on the remote side:

curl --socks5-hostname 127.0.0.1:8777 http://prometheus.monitoring.svc:9090/api/v1/query?query=up
kubectl --request-timeout=30s ... # через HTTPS_PROXY=socks5h://127.0.0.1:8777

Forwarding a single port, the equivalent of ssh -L:

{
  "tool": "open-tunnel",
  "params": {
    "type": "local",
    "localPort": 15432,
    "remoteHost": "pg-master.internal",
    "remotePort": 5432
  }
}

If localPort is not specified, the system picks the port and returns it in the response. Tunnels live until close-tunnel, until the SSH connection drops, or until the server stops.

Limits are set with flags:

Flag

Default value

What it does

--tunnel-bind-address

127.0.0.1

Address on which tunnels listen

--allowed-tunnel-ports

no limits

List of ports allowed to be occupied

--max-tunnels

8

How many tunnels we keep at once

--disable-tunnels

off

Removes tunnel tools from the list

The listener is brought up on loopback by default. An address wider than loopback opens the proxy to your network, so change it deliberately.

Host key verification

The server key is checked against known_hosts on every connection, including intermediate hosts in a ProxyJump chain. The default mode is strict: a host missing from known_hosts means refusal.

Mode

Behavior

strict

Default. Only connects to hosts in known_hosts

accept-new

An unknown host is recorded on first connection; a key mismatch is still a refusal

off

No verification, upstream behavior

Checked are ~/.ssh/known_hosts, ~/.ssh/known_hosts2, and /etc/ssh/ssh_known_hosts, and for an alias with UserKnownHostsFile, the file specified in the SSH config. A custom list is set with the --known-hosts-file flag. Hashed entries, patterns, the [host]:port form, and the @revoked marker are all understood.

A refusal comes with code SSH_HOST_KEY_REJECTED and text containing the fingerprint:

Host key of prod.example.com is not in known_hosts (~/.ssh/known_hosts): ssh-ed25519 SHA256:xxxx.
Verify that fingerprint, add the host to known_hosts, or start the server with --host-key-checking accept-new.

A key mismatch is never accepted, in any mode: the server refuses to connect and reports that the host was either recreated or someone is in the middle.

For a first acquaintance with a fleet, it's convenient to run once with --host-key-checking accept-new, then switch back to strict.

Audit log

Every call is written as a JSON line: command, connection, sudo flag, guard verdict, duration, output size. The output content does not go into the log, and the sudo password is stripped.

{"time":"2026-08-19T08:12:44.101Z","pid":8123,"event":"command","result":"blocked","connection":"r-ulybka-prod-master","command":"useradd deploy","sudo":true,"code":"COMMAND_VALIDATION_FAILED","reason":"Blocked by the forbidden core ..."}
{"time":"2026-08-19T08:12:51.880Z","pid":8123,"event":"command","result":"ok","connection":"r-ulybka-prod-master","command":"systemctl status nginx","sudo":false,"durationMs":412,"bytes":1840}

The events connect, command, download, upload, tunnel-open, tunnel-close, and host-key are written.

By default, the file lives at $XDG_STATE_HOME/ssh-mcp-server/audit.jsonl, i.e. usually ~/.local/state/ssh-mcp-server/audit.jsonl, with permissions 0600.

Flag

Default

What it does

--audit-log <path>

XDG state directory

Path to the log; the value off disables writing

--audit-max-size <bytes>

10485760

Size after which the file is rotated. 0 disables built-in rotation

--audit-keep <count>

10

How many gzip archives we keep

Rotation is built-in: once the limit is reached, the current file moves to audit.jsonl.1.gz, older archives shift, and everything beyond --audit-keep is deleted. Ten archives of 10 MiB each is on the order of a hundred megabytes uncompressed and noticeably less after gzip.

If logs are already managed by logrotate, set --audit-max-size 0 and configure rotation with the copytruncate mode.

A write error does not bring down the command: the server logs it once to stderr and keeps working.

Connection methods

Below are scenarios from simple to complex. In args, each flag and its value are two separate array elements: "--host", "192.168.1.1", not "--host 192.168.1.1".

Login and password

{
  "mcpServers": {
    "ssh-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@perhamm/ssh-mcp-server",
        "--host", "192.168.1.1",
        "--port", "22",
        "--username", "root",
        "--password", "pwd123456"
      ]
    }
  }
}

Private key

"args": [
  "-y", "@perhamm/ssh-mcp-server",
  "--host", "192.168.1.1",
  "--username", "root",
  "--privateKey", "~/.ssh/id_rsa",
  "--passphrase", "pwd123456"
]

The key passphrase doesn't have to be written in the config; it can be placed in the SSH_MCP_PASSPHRASE variable.

A single alias from ~/.ssh/config

"args": ["-y", "@perhamm/ssh-mcp-server", "--host", "myserver"]

The server reads HostName, Port, User, IdentityFile, and ProxyJump from the Host myserver block, including Include directives and patterns. Command-line flags take precedence: --port 2222 overrides the port from the config.

Bastion and ProxyJump

If an alias has ProxyJump, the chain is brought up on its own:

Host r-ulybka-prod-master
    HostName 10.20.30.40
    User ops
    ProxyJump bastion
    IdentityFile ~/.ssh/prod_key

Each next hop connects through the channel of the previous one, just like ssh -J does. The chain can also be set manually: --proxy-jump "bastion,gateway:2222". The chain depth is limited to five hops.

Proxy

"args": [
  "-y", "@perhamm/ssh-mcp-server",
  "--host", "192.168.1.1",
  "--username", "root",
  "--password", "pwd123456",
  "--proxy", "socks5://user:pwd@proxy-host:1080"
]

Supported are socks://, socks5://, http://, and https://. HTTP and HTTPS use the CONNECT method with Basic authentication, with default ports 80 and 443. For SOCKS5, the port is required. The old --socksProxy flag still works but only accepts SOCKS. --proxy and --proxy-jump are not used together.

Jump host with an interactive shell

transportMode defaults to exec. Switch to shell if commands don't execute after a successful login or the device only provides an interactive session:

"args": [
  "-y", "@perhamm/ssh-mcp-server",
  "--host", "bastion.example.com",
  "--username", "ops",
  "--password", "pwd123456",
  "--transport-mode", "shell",
  "--shell-ready-timeout", "15000"
]

In shell mode, commands go one at a time through a single persistent session, and upload and download don't work: SFTP is disabled there.

Two-factor authentication

The --try-keyboard flag enables keyboard-interactive. The password and key are supplied automatically, and the second-factor code is read from the SSH_MCP_2FA_CODE variable.

Multiple connections in one server

Besides aliases from the SSH config, the old method remains: a file describing connections.

[
  {
    "name": "dev",
    "host": "1.2.3.4",
    "port": 22,
    "username": "alice",
    "privateKey": "~/.ssh/dev_key",
    "guardProfile": "safe",
    "commandTimeoutMs": 120000
  },
  {
    "name": "prod",
    "host": "5.6.7.8",
    "port": 22,
    "username": "bob",
    "privateKey": "~/.ssh/prod_key",
    "guardProfile": "readonly",
    "allowedRemotePaths": ["/var/log", "/tmp"]
  }
]
"args": ["-y", "@perhamm/ssh-mcp-server", "--config-file", "/abs/path/ssh-config.json"]

The object format where the key is the connection name is also supported. The connection is selected with the connectionName parameter; without it, the first one is used.

Command and path restrictions

Whitelist and blacklist

"args": [
  "-y", "@perhamm/ssh-mcp-server",
  "--host", "192.168.1.1",
  "--username", "root",
  "--privateKey", "~/.ssh/id_rsa",
  "--whitelist", "^ls( .*)?,^cat .*,^df.*",
  "--blacklist", "^rm .*,^shutdown.*"
]

Patterns are comma-separated regular expressions. A command is first checked against the whitelist, then the blacklist, then the guard profile, and must pass all three checks.

Command template

--command-template wraps every command. <quotedCommand> substitutes the command as an escaped argument, <command> inserts it as is. The template is applied after the working directory substitution.

su root -c <quotedCommand>
docker exec -i mycontainer sh -c <quotedCommand>

Paths for file operations

--allowed-local-paths extends the list of local directories available for upload and download (by default, only the current directory). --allowed-remote-paths restricts remote paths; absolute POSIX paths are written there, comma-separated. Without this flag, SFTP sees the entire host filesystem, which the server warns about at startup.

Timeouts and output limit

Parameter

Default

What it limits

timeout in the tool call

none

A single command, overrides connection settings

commandTimeoutMs

30000

A command in exec mode

shellCommandTimeoutMs

30000

A command in shell mode

connectionTimeoutMs

30000

Connection setup and handshake

sftpTimeoutMs

300000

SFTP operations

maxOutputBytes

10485760

Captured output of a single command

keepaliveIntervalMs

10000

Keepalive interval

When the output limit is exceeded, the command is terminated and the tool returns OUTPUT_LIMIT_EXCEEDED along with the already collected chunk. Errors come as a structure of code, message, and retriable.

Command-line flags

  --config-file <path>             Файл с описанием соединений
  --ssh-config-file <path>         Путь к SSH-конфигу (по умолчанию ~/.ssh/config)
  --ssh <config>                   Соединение как JSON или пары key=value
  -h, --host <host>                Хост или алиас из SSH-конфига
  -p, --port <port>                Порт
  -u, --username <name>            Пользователь
  -w, --password <password>        Пароль
  -k, --privateKey <path>          Путь к приватному ключу
  -P, --passphrase <passphrase>    Пароль от ключа
  -a, --agent <path>               Сокет ssh-agent
  -W, --whitelist <patterns>       Белый список команд, через запятую
  -B, --blacklist <patterns>       Чёрный список команд, через запятую
  --proxy <url>                    Прокси SOCKS5, HTTP или HTTPS
  -s, --socksProxy <url>           Старый флаг только для SOCKS5
  --allowed-local-paths <paths>    Локальные каталоги для upload и download
  --allowed-remote-paths <paths>   Удалённые каталоги для SFTP
  --transport-mode <mode>          exec или shell (по умолчанию exec)
  --shell-ready-timeout <ms>       Таймаут готовности shell (по умолчанию 10000)
  --command-template <template>    Шаблон с <command> или <quotedCommand>
  --pty                            Псевдотерминал для exec (по умолчанию включён)
  --try-keyboard                   Keyboard-interactive для 2FA
  --pre-connect                    Подключиться ко всем хостам при старте
  --ssh-config-hosts               Разрешить хосты из SSH-конфига на лету
  --allowed-hosts <patterns>       Шаблоны разрешённых алиасов, через запятую
  --proxy-jump <chain>             Цепочка ProxyJump, через запятую
  --guards-profile <name>          off, safe или readonly (по умолчанию off)
  --guards-file <path>             Свой набор правил поверх встроенного
  --sudo-password-env <var>        Переменная с паролем sudo
  --sudo-user <user>               Пользователь для sudo (по умолчанию root)
  --host-key-checking <mode>       strict, accept-new или off (по умолчанию strict)
  --known-hosts-file <paths>       Свои файлы known_hosts, через запятую
  --host-key-algorithms <list>     Алгоритмы хост-ключа, через запятую
  --enable-upload                  Опубликовать инструмент upload (по умолчанию выключен)
  --audit-log <path|off>           Путь к аудит-логу (по умолчанию каталог состояния XDG)
  --audit-max-size <bytes>         Порог ротации, 0 отключает (по умолчанию 10485760)
  --audit-keep <count>             Сколько архивов держим (по умолчанию 10)
  --disable-tunnels                Убрать туннельные инструменты
  --tunnel-bind-address <addr>     Адрес для туннелей (по умолчанию 127.0.0.1)
  --allowed-tunnel-ports <ports>   Разрешённые порты туннелей, через запятую
  --max-tunnels <count>            Лимит одновременных туннелей (по умолчанию 8)
  --version, -v                    Версия пакета
  --help                           Справка

Security

  • For production, enable --guards-profile safe; for on-call incident triage, readonly is suitable. With off, only the forbidden core remains: everything else will execute, which the server logs as a warning.

  • The key, its passphrase, and the sudo password are read from files and environment variables. In the MCP client config, store the path to the key, not the key itself.

  • Tunnels listen on loopback. SOCKS5 has no authentication, so a proxy on 0.0.0.0 opens the internal network to anyone who can reach the port, and the server logs a warning about this at startup.

  • Without --allowed-remote-paths, any path on the host can be read and written via SFTP, including ~/.ssh/authorized_keys.

  • The host key is verified against known_hosts in strict mode. Disabling verification via --host-key-checking off is only worth doing in a lab.

  • There are no call rate limits.

Development

npm install
npm run build
npm test

Tests run with the built-in Node.js runner and live in test/.

Upstream and license

The project grew out of classfang/ssh-mcp-server (author junki.cn), ISC license. The upstream copyright is preserved in LICENSE, which also contains a link to the source repository.

The guard set is partially assembled from ideas in tufantunc/ssh-mcp (MIT).

The package on NPM: @perhamm/ssh-mcp-server.

F
license - not found
Not graded
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
0dRelease cycle
6Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    B
    quality
    F
    maintenance
    An MCP server that allows AI models to execute system commands on local machines or remote hosts via SSH, supporting persistent sessions and environment variables.
    1
    32
    28
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that enables remote SSH command execution and bidirectional file transfers through a standardized interface. It allows AI assistants to securely manage remote servers while keeping credentials isolated and applying command-level security controls.
    ISC
  • A
    license
    A
    quality
    B
    maintenance
    MCP server for managing multiple SSH servers via AI assistants, offering tools for remote command execution, file operations, and system monitoring.
    11
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that gives AI agents SSH capabilities to execute commands, transfer files, and inspect remote systems through a preconfigured host list.
    43
    MIT

View all related MCP servers

Related MCP Connectors

  • MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.

  • MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.

  • MCP server connecting AI agents to non-custodial staking data across 130+ networks.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/perhamm/ssh-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server