MCP SSH Manager
An MCP server that gives an AI agent controlled SSH access to your configured servers — run commands, move files, query databases, take backups, and monitor health.
Run commands —
ssh_execute(with aliases, cwd, timeouts),ssh_execute_sudo(password piped, masked), andssh_execute_groupacross a whole group in parallel, sequential, or rolling order.Move files —
ssh_upload/ssh_download(single files over SFTP),ssh_sync(rsync both ways with dry-run, excludes, delete), andssh_deploy(upload, permissions, backup, service restart, hooks).Persistent shells — start, send to, list, and close sessions that keep working directory, environment, and history between commands.
Databases — dump, import, list databases/tables/collections, and run strictly read-only
SELECT-only queries (MySQL, PostgreSQL, MongoDB).Backups — create, list, restore, and schedule cron-based database or file backups with compression and retention.
Health & monitoring —
ssh_health_check,ssh_service_status,ssh_process_manager(list/info/kill),ssh_alert_setupthresholds,ssh_monitorsnapshots, andssh_taillog following.Tunnelling — create local, remote, or SOCKS5 port forwards; list and close them.
Inventory & config — list servers, manage host keys in known_hosts, aliases, command aliases, groups, hooks, profiles, and connection pool status.
Observability —
ssh_historyshows the in-session command log with timings and outcomes.Safety — per-server
unrestricted/readonly/restrictedmodes and approval gates constrain what any of the above can do.
Provides Docker container management capabilities through SSH connections, including specialized command aliases and automation hooks for Docker operations
Offers ERPNext-specific SSH management features through specialized profiles, including custom command aliases and automation hooks tailored for ERPNext deployments
Provides Frappe framework-specific SSH management capabilities through dedicated profiles, including specialized command aliases and automation hooks for Frappe applications
Enables Node.js application management through SSH connections with specialized profiles, including custom command aliases and automation hooks for Node.js deployments
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MCP SSH Manager check disk usage on production server"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
The problem, in one line
An MCP SSH server is the most dangerous tool you can hand an agent: a shell on machines that matter. Today, when your agent runs something on production, you find out afterwards — if you find out at all.
This one lets you watch, and lets you say no. It is also, on the boring days, simply a very good way to let an assistant do the server work you were going to do yourself.
Related MCP server: MCP SSH Server
Choose your workspace
Desktop application (4.2.0): one installation with its own runtime,
terminal, file browser and server settings. No separate Node.js or npm
installation. Download it:
Platform | File | Signature |
macOS, Apple silicon / Intel |
| Signed and notarized by Apple — opens normally |
Windows x64 / arm64 |
| Not code-signed yet. Windows shows "Windows protected your PC": More info → Run anyway. Check the file against the SHA-256 in |
Linux x64 |
| Unsigned, as Linux packages usually are |
Already using npm / the CLI? npm update -g mcp-ssh-manager brings 4.2.0,
and the V4 engine runs without an interface, from your existing .env, TOML or
process variables — no automatic import, no forced wizard, no approval prompt
unless you enable one per server. The one behaviour that tightens is SSH
host-key verification; read that section of the
upgrade and rollback guide before upgrading. To stay on 3.8
for now, pin it: npm install -g mcp-ssh-manager@3.
From the engine to the application in one step. With a V4 engine installed,
ssh-manager app downloads the desktop application of the engine's own version,
checks it against the release (size and SHA-256 from the release manifest, and
on macOS Apple's signature and notarization), installs it and opens it — or just
opens it when it is already there. The browser interface of ssh-manager control
offers the same as one button. Your agents keep using the npm engine; the
application is the window on top of it. Nothing is downloaded unless you ask.
Without npm at all. The application carries the engine too, and your AI
tools can start that one: Options → AI tools lists Claude Code, Claude Desktop
and Codex, says which ssh-manager each one starts (npm's, the application's, or
none yet) and switches it in one click — no Node, no terminal. The same tab, in
ssh-manager control, shows npm users which version each tool really runs.
A switch rewrites that tool's one ssh-manager entry, keeps its environment
variables, and takes effect when the tool next starts; switching back is the
same click.
Updates. The application checks for a new version at launch and every six hours and says so where you will see it: a mark on the sidebar, Options → Updates (the running version, the one on offer, a Check now button, release notes) and, once per version, a notification. Downloading is a click, and installing is a second one (Restart to update) or happens when you quit. The npm engine updates with npm, on its own schedule. A change to the interface reaches npm users with the next npm release and application users with the next application release.
Up and running with npm
npm install -g mcp-ssh-manager # or: brew tap bvisible/mcp-ssh-manager https://github.com/bvisible/mcp-ssh-manager && brew install ssh-manager
ssh-manager server add # guided: host, user, key or password
claude mcp add ssh-manager mcp-ssh-managerThat's it. Restart Claude Code and ask it something.
OpenAI Codex — same install, then two files. In ~/.codex/config.toml:
[mcp_servers.ssh-manager]
command = "mcp-ssh-manager"
env = { SSH_CONFIG_PATH = "/Users/you/.codex/ssh-config.toml" }
startup_timeout_ms = 20000And your servers in ~/.codex/ssh-config.toml:
[ssh_servers.production]
host = "prod.example.com"
user = "admin"
key_path = "~/.ssh/id_rsa"
default_dir = "/var/www"
group = "production"Everything below works identically from Codex — same 37 tools, same config fields.
From source:
git clone https://github.com/bvisible/mcp-ssh-manager.git
cd mcp-ssh-manager && npm install
cd cli && ./install.sh
claude mcp add ssh-manager node /absolute/path/to/mcp-ssh-manager/src/index.jsThe MCP engine needs no build. To use the interface (ssh-manager control) from a
checkout, build it once: npm run build:ui (the published package and the desktop
app already carry it).
Scopes — --scope project writes an .mcp.json your team can commit; --scope user enables it everywhere for you.
Prerequisites — Node 18+, on Linux, macOS or Windows. rsync for ssh_sync, sshpass only if you sync with password auth (brew install hudochenkov/sshpass/sshpass, or apt-get install sshpass).
Then just ask
No syntax to learn. Your assistant already knows how to use the 37 tools; you talk to it in your own words.
You say | What happens |
"Why is production slow right now?" | CPU, memory, disk, load average and the top processes, in one round trip |
"Back up the database before I deploy." | A compressed, timestamped MySQL/PostgreSQL/MongoDB dump with a retention policy |
"Tail the nginx error log on staging." | Live output, streamed as it happens |
"Push ./dist to production:/var/www and restart nginx." | The files out, then the restart — with ownership, permissions and a rollback copy handled |
"Which of my servers is running out of disk?" | Every server in the group, checked in parallel |
"Open a tunnel to the production database." | Local port forward, so your GUI client just connects |
"Run | One command, the whole group, sequential or parallel |
Watch over its shoulder — the control plane
New in 4.0.0. The application is opt-in: with no vault and no approval enabled, the engine keeps working headlessly, exactly as 3.8.x. See the migration guide for host-key verification and recovery behaviour.
ssh-manager control # prints a local URL — or `ssh-manager app` for the desktop appOne thing in two wrappers. ssh-manager control runs the whole interface in a browser tab with nothing extra to install; the desktop builds for macOS, Windows and Linux are that same page with the engine inside them, so they need neither Node nor the npm package.
The desktop build adds the two things a browser tab cannot do:
It lives in the menu bar. The icon carries a count when something is waiting, and its menu lists what is blocked on you, which shells and commands are open right now, and your servers — so "is anything waiting on me?" costs a glance instead of a window switch. Closing the window leaves it there rather than quitting.
It posts real system notifications, with the application's own identity, so macOS and Windows show and route them properly. Destructive requests stay on screen instead of fading after four seconds. macOS asks your permission the first time it needs to.
Stop it before it runs
The agent pauses and waits. You see the machine, the user, the tool, and the command in full — wrapped, never truncated, because half a command is how you approve the wrong thing. A desktop notification fires when something is waiting, because the request that goes unseen is the one that times out and is denied.
Approval is switched on from the interface, per server — not from a file:
Servers → the server → Approval → never (default) | destructive | alwaysApproval is stored in the vault and cannot be overridden by .env or process
variables. Editing connection details preserves it. This prevents accidental
configuration overrides from disabling the gate; it is not a security boundary
against a process that already controls your local user account or vault files.
If you had SSH_SERVER_*_APPROVAL set in a file, the engine says
so on every start rather than quietly leaving you unprotected.
The destructive list is deliberately short. A prompt that cries wolf gets clicked through without being read, which is worse than no prompt at all: systemctl restart does not interrupt you, systemctl stop does.
When a request requires approval, a timeout, unreachable socket or disconnected control plane denies it. If nothing is listening, start ssh-manager control or the desktop app before retrying. Servers with approval disabled keep working headlessly.
See what it's doing, while it does it
Output goes through a terminal emulator, not a <pre> tag — what your agent ran looks exactly like what you'd have seen had you typed it yourself, colours and all. Each stream keeps a bounded scrollback, so opening the screen mid-command shows what came before instead of starting blank.
Nothing here touches the disk. A stream can carry secrets — a config being catted, a token echoed by a deploy script — so it lives in memory, capped, and disappears with the window.
Both filesystems, side by side
Drag a file across and it moves directly between your machine and the server through the control plane. The browser never holds the bytes — which is faster, and the only way a multi-gigabyte file works at all.
Health, when you ask for it
Nothing is probed in the background. Each check is an SSH handshake, and a dashboard that connects to every production box on a timer is worse than no dashboard. The button is the whole scheduling policy. Thresholds are yours to set, and crossing one is said in words rather than encoded in a colour.
A shell on any machine
Pick a machine, get a shell — the same one an agent gets, with your keyboard on it. Useful for the ordinary reasons, and for the one people ask about most: running an agent on the server rather than pointing one at it.
Including this machine. The desktop application opens a shell on your own computer too, with your login shell and everything you have configured in it, so you are not switching to a different window to run the local half of the same job. Open as many as you like, on any mix of machines: each is a tab across the top and an entry in the rail, and switching away leaves it running.
Beside it, the commands you would rather not retype, saved per server or for all of them, and picked from a list inside any shell.
And the rest of it
On the same page: an interactive shell on any server, saved commands you pick from a list instead of retyping, groups you can run one command across, the audit trail of what agents ran and what you decided, and your known host keys.
Every screenshot above is the desktop build; each has a browser twin in docs/images/. See ROADMAP-V4.md for what is built and what isn't.
Safe by default
Other SSH MCP servers hand the agent a shell and wish you luck. This one assumes you'd like some say in the matter.
Decide how far it can go — per server
Mode | What the agent can do |
| Everything. Same as any other SSH MCP server, zero overhead. |
| Mutating tools are refused outright — no deploy, no upload, no sudo, no database import. Reads still work. |
| Every command must match an allow pattern and no deny pattern. Anything else is refused before it reaches the host. |
SSH_SERVER_PROD_MODE=readonly
SSH_SERVER_STAGING_MODE=restricted
SSH_SERVER_STAGING_ALLOW_PATTERNS=^systemctl (status|restart) myapp$;^tail -n \d+ /var/log/
SSH_SERVER_PROD_AUDIT_LOG=~/.ssh-manager/audit.jsonlThis is a second layer under Claude Code's autoApprove, which is all-or-nothing per tool: once ssh_execute is approved, anything goes. Useful when you're sharing the MCP with a third-party agent, a CI bot, or working on a client's machine. Full reference in docs/SECURITY_MODES.md.
Credentials out of the clear-text .env
ssh-manager vault import # take what you already have, encrypted
ssh-manager vault list # see it, without ever printing a secret
ssh-manager vault add prod # add one interactively
ssh-manager vault backup FILE # a copy that survives a new machineAES-256-GCM, master key in your OS keychain (a 0600 file where there is none — Windows, CI, containers). Only secret values are encrypted: hosts, users and modes stay readable, so the file can still be inspected and diffed. GCM is authenticated, so a tampered vault fails loudly instead of handing a wrong password to a production server.
The key belongs to this machine. A vault copied to a new laptop is ciphertext nobody can open — vault backup writes a copy encrypted under a passphrase you choose, which is what makes the move survivable. Your .env is never modified, not by import, not by the interface, and a setup half in each is a normal state rather than a broken one. Upgrading changes nothing on its own: see docs/MIGRATION.md.
Keep the agent on the tools
Approval, modes and the audit log only see what goes through these tools. An agent that also has a terminal can run ssh prod rm -rf … there and none of it applies. For Claude Code, an opt-in hook closes that door:
ssh-manager guard install # refuse ssh, scp, sftp and remote rsync in the agent's shell
ssh-manager guard install --mode ask # …or ask you each time
ssh-manager guard print # show what it would write, change nothing
ssh-manager guard removeThe agent is told why and which tool to use instead (ssh_execute, ssh_upload, ssh_sync, ssh_tunnel_create). It is a guard against accidents, not a security boundary: it reads the command text. Keep SSH keys and agent sockets away from the account the agent runs as. Nothing is installed unless you run install; it edits only its own entry in ~/.claude/settings.json, and keeps a copy of the file as it was.
The unglamorous parts, which are the ones that bite
Your sudo password never reaches the remote command line. It travels on the SSH channel's stdin, so it isn't visible in
ps, in/proc/<pid>/cmdline, or in anauditdtrail — unlike theecho "$pass" | sudo -Spattern common in this category (#34).Every shell argument is quoted through one central helper, with a test that drives 340 builder × argument × payload combinations through a real shell to prove none of them execute.
Read-only SQL is enforced, not suggested.
ssh_db_queryrefuses anything that isn't aSELECT, runs it inside a read-only transaction, and never lets thepsqlormysqlclient read it as a script, where their own commands (\!) would reach a shell. A MySQL query is therefore one line, without backslashes.Vulnerabilities are published, not buried. SECURITY.md has the reporting process and every advisory already fixed — including the six in v3.8.6, among them a command injection on the machine running the server, triggered by any SSH server it connects to.
Reproducible installs. The lockfile is committed, CI installs with
npm ci, and a test enforces that every dependency resolves to registry.npmjs.org with an integrity hash and no unreviewed install scripts.
Questions people ask
Can I let an AI agent SSH into my servers safely?
Yes, if you decide how far it can go. Per server you can make it read-only, allow only commands that match a pattern, or hold every request (or only the destructive ones) until you approve it in the control plane; credentials can live in an encrypted vault instead of a clear-text .env; and every command and decision is logged. See Safe by default.
How do I use Claude Code or Codex with SSH?
npm install -g mcp-ssh-manager, add a server with ssh-manager server add, then claude mcp add ssh-manager mcp-ssh-manager. Codex is set up here. Then ask in plain words: "why is production slow?", "tail the nginx error log on staging".
Does it work with Cursor, Windsurf, Cline or another MCP client? It is a standard stdio MCP server, so any MCP client can run it. Claude Code, Claude Desktop and Codex are the clients documented and tested here. For another one, add what it expects, for example:
{ "mcpServers": { "ssh-manager": { "command": "npx", "args": ["-y", "mcp-ssh-manager"] } } }Do I need Node.js? With npm, yes (Node 18 or later). The desktop application carries its own runtime and engine: install it and connect your AI tools from Options → AI tools, with no Node and no npm.
How is this different from other SSH MCP servers?
Most hand the agent a shell. This one lets you choose per server how far it goes, can hold requests until you approve them, keeps an audit trail, and has a desktop app and a browser interface to see it all. It also runs without any of that, as a plain engine configured from a .env or a TOML file, if that is what you want.
Where are my passwords and keys kept?
Key files stay where they are. Saved passwords and passphrases can go into the encrypted vault (AES-256-GCM, with the key in your operating system's keychain) rather than a .env.
Is it free? Yes, it is MIT-licensed. The source is here, the package is on npm and the desktop apps are on the releases page.
Reference
Core — always enabled
Tool | What it does |
| Every configured server, with its group and mode |
| Run a command. Falls back to the server's |
| Move a single file either way |
| Bidirectional rsync, with real transfer counts |
Backup & restore (v2.1+)
Tool | What it does |
| MySQL, PostgreSQL, MongoDB or files — compressed, with metadata and retention |
| Everything available, with size, date and retention |
| Restore one, including across databases |
| Put it on cron, with automatic cleanup |
See the Backup Guide and examples/backup-workflow.md.
Health & monitoring (v2.2+)
Tool | What it does |
| CPU, memory, disk, network, uptime, load — with an overall verdict |
| nginx, mysql, docker… on systemd or sysv |
| List, inspect or kill; sort by CPU or memory |
| Thresholds, and what happens when one is crossed |
| Live resource monitoring and log following |
Databases (v2.3+)
Tool | What it does |
| MySQL, PostgreSQL, MongoDB; optional gzip, optional table subset |
| Import a dump, |
| Databases, or the tables inside one |
|
|
Deployment & sessions
Tool | What it does |
| Files out, with permissions, backup and restart handled — see the Deployment Guide |
| Sudo, with the password on stdin rather than the command line |
| Persistent shells that keep their context between commands |
| Local, remote and SOCKS forwarding |
Organisation
Tool | What it does |
| Short names for servers |
| Short names for commands you keep retyping |
| Run one thing across many machines |
| Automation hooks around SSH operations — see Aliases and Hooks |
| Swap whole sets of aliases and hooks — |
All 37 tools cost roughly 43.5k tokens of context. Most people don't need all of them.
ssh-manager tools configure # interactive wizard
ssh-manager tools list # what's on right now
ssh-manager tools enable monitoring
ssh-manager tools disable backupMode | Tools | Context | Good for |
All (default) | 37 | ~43.5k | The full set |
Minimal | 5 | ~3.5k | Just SSH, and 92% of the context back |
Custom | 5–37 | varies | Whatever you actually use |
Groups: core (5, always on) · sessions (4) · monitoring (6) · backup (4) · database (4) · advanced (14).
Fewer tools also means fewer approval prompts and a faster start. ssh-manager tools export-claude writes the matching auto-approval block.
Nobody starts here. You have a ~/.ssh/config with forty hosts, or a FileZilla
site manager, or a spreadsheet a colleague sent you.
ssh-manager import ~/.ssh/config # the format is worked out for you
ssh-manager import --from transmit # or read where a tool keeps its list
ssh-manager import --template servers.xlsx # or get a form to fill inSource | How |
Spreadsheet ( |
|
CSV | Comma or semicolon — Excel writes both, depending on where you live |
|
|
FileZilla |
|
PuTTY | An exported |
Termius | JSON export, groups included |
MobaXterm |
|
Transmit | Its favourites, read directly (macOS) |
Import is additive. A name you already have comes back as "already
configured — left alone", never a silent overwrite; --replace if that is what
you want. Two rows with the same name become web and web_2 rather than one.
No password is ever read. FileZilla keeps them base64-encoded, Transmit in the keychain, PuTTY not at all. Copying a credential out of another application's store is not a favour — it moves a secret you had forgotten into a second place you now have to think about. Hosts, users, ports and key paths come across; you are asked for a password once, when you first connect.
Going the other way:
ssh-manager export servers.xlsx # or .csv, .env, .tomlExports carry no passwords, no passphrases, no sudo passwords — an export is a file people email each other and commit by accident.
Two interchangeable formats, read by the same loader: .env is the usual choice with Claude Code, TOML with Codex (from SSH_CONFIG_PATH, or ~/.codex/ssh-config.toml). Both can coexist — the loader merges them.
SSH_SERVER_[NAME]_HOST=hostname_or_ip
SSH_SERVER_[NAME]_USER=username
SSH_SERVER_[NAME]_PASSWORD=password # or use a key
SSH_SERVER_[NAME]_KEYPATH=~/.ssh/key
SSH_SERVER_[NAME]_AGENT_PUBLIC_KEY_PATH=~/.ssh/id_ed25519.pub # alternative: select one key from SSH_AUTH_SOCK
SSH_SERVER_[NAME]_CERTPATH=~/.ssh/key-cert.pub # optional: OpenSSH certificate for KEYPATH
SSH_SERVER_[NAME]_PASSPHRASE=key_passphrase # optional
SSH_SERVER_[NAME]_PORT=22 # optional
SSH_SERVER_[NAME]_DEFAULT_DIR=/var/www # optional working directory
SSH_SERVER_[NAME]_SUDO_PASSWORD=… # optional, for automated deploys
SSH_SERVER_[NAME]_DESCRIPTION=… # optional
SSH_SERVER_[NAME]_GROUP=production # optional, free-form label
SSH_SERVER_[NAME]_PLATFORM=windows # optional: linux (default) | windows
SSH_SERVER_[NAME]_PROXYJUMP=bastion # optional: another server, as jump host
SSH_SERVER_[NAME]_PROXYCOMMAND=… # optional: ncat, ssh -W, …
SSH_SERVER_[NAME]_FORWARD_AGENT=true # optional, and a real security trade-off
SSH_SERVER_[NAME]_ANNOUNCE_AGENT=true # optional: tell this host an AI agent is driving (off by default)
SSH_SERVER_[NAME]_MODE=readonly # optional: unrestricted | readonly | restricted
# Approval is NOT a field here — it is set per server from the control plane and
# stored in the vault, so that a shell on one of your machines cannot switch off
# the gate that exists to stop it.
SSH_SERVER_[NAME]_AUDIT_LOG=~/audit.jsonl # optional JSONL trailThe same thing in TOML:
[ssh_servers.production]
host = "prod.example.com"
user = "admin"
key_path = "~/.ssh/id_rsa"
port = 22
default_dir = "/var/www"
group = "production"
description = "Production server"
[ssh_servers.winhost]
host = "192.168.1.90"
user = "svc-ssh"
key_path = "~/.ssh/winhost_key"
port = 2222
platform = "windows"
[ssh_servers.internal]
host = "10.0.0.5"
user = "admin"
proxy_jump = "bastion"Selecting an SSH agent key — use a public key file to select one identity from the existing agent at SSH_AUTH_SOCK, including compatible password-manager agents. The private key stays in the agent, which performs signing. For example:
[ssh_servers.agent_host]
host = "example.com"
user = "admin"
agent_public_key_path = "~/.ssh/id_ed25519.pub"The .env equivalent is SSH_SERVER_[NAME]_AGENT_PUBLIC_KEY_PATH; the internal configuration field is agentPublicKeyPath. This option fails if the public key file is missing or invalid, the agent is unavailable, or the agent does not offer a matching identity. It cannot be combined with key_path, password, cert_path, or enabled forward_agent; conflicting configuration is rejected. Omitting the option preserves the existing authentication behavior. ssh_sync uses system OpenSSH and does not apply this filter: configure its IdentityFile and IdentitiesOnly yes in your SSH client configuration separately.
OpenSSH user certificates — for servers that trust a certificate authority (TrustedUserCAKeys), point CERTPATH / cert_path at the certificate signed for KEYPATH (usually <key>-cert.pub). It is read on every connection, so a short-lived certificate renewed on disk needs no restart, and a missing file fails the connection instead of falling back to the bare key. It takes effect once ssh2 ships certificate support (mscdex/ssh2#1495); until then the plain key is offered, as before. ssh_sync uses the system ssh, which finds <key>-cert.pub next to the key on its own.
Loading order, highest priority first: process environment → .env → the encrypted vault → TOML. A credential you deliberately put in the vault beats one left in a .env; an operator overriding for a single run still beats both.
Profiles bundle aliases and hooks per project type. Set one with export SSH_MANAGER_PROFILE=frappe, or switch from your assistant, which remembers it in ~/.ssh-manager/profile. Ships with default, frappe, docker and nodejs; add your own in profiles/. Custom aliases and hooks also live in ~/.ssh-manager/, so an upgrade keeps them.
More examples: examples/codex-ssh-config.example.toml.
Through a bastion — the tunnel is transparent, every tool works as usual:
SSH_SERVER_BASTION_HOST=bastion.example.com
SSH_SERVER_BASTION_USER=jumpuser
SSH_SERVER_BASTION_KEYPATH=~/.ssh/bastion_key
SSH_SERVER_PRIVATE_HOST=10.0.0.5
SSH_SERVER_PRIVATE_USER=admin
SSH_SERVER_PRIVATE_PROXYJUMP=bastionChains work: if bastion itself has a PROXYJUMP, it's followed recursively. Circular references are detected and rejected.
Through a SOCKS proxy or a custom command — it runs locally and forwards to the host, with %h and %p for host and port:
SSH_SERVER_SOCKS_PROXYCOMMAND="ncat --proxy 127.0.0.1:1080 --proxy-type socks5 %h %p"
SSH_SERVER_WINPROXY_PROXYCOMMAND="C:\Windows\System32\OpenSSH\ssh.exe -W %h:%p user@jump-host"Passphrase-protected keys — load the key into ssh-agent and nothing else is needed; SSH_AUTH_SOCK is picked up automatically, the same mechanism plain ssh uses. Storing PASSPHRASE in the config works too, and is the less good option.
Agent forwarding (FORWARD_AGENT=true, off by default, per server) lets a process on the remote host authenticate to other hosts with your local keys — git clone over SSH on a server without copying a private key there.
⚠️ Anyone who can read the forwarded socket on that host — root included — can impersonate you against other hosts for the life of the connection. Only for servers you trust, exactly as
ssh_config(5)advises.
Telling your servers an agent is driving (ANNOUNCE_AGENT=true per server, or SSH_MANAGER_ANNOUNCE_AGENT=true for all of them; off by default). Every command channel then carries AI_AGENT=mcp-ssh-manager, following the AI_AGENT over SSH convention, so a host that records it can tell a session an agent drives from one a person drives — SSH cannot otherwise: same key, same user either way. Contributed by @mthamil107 in #84.
The server has to accept it. Stock OpenSSH ships
AcceptEnv LANG LC_*and drops anything else silently. AddAcceptEnv AI_AGENTtosshd_configand reload;$AI_AGENTis then set for every command and shell the agent runs, for a profile, a wrapper or an audit hook to record.Off by default, for two reasons. Upgrading must not change what reaches your servers. And the request reaches every host whatever its
AcceptEnv— sshd only declines to store it — so a host you do not control would learn an agent is on the other end, and could shape its output for one.ANNOUNCE_AGENT=falsekeeps one host silent under the global switch.Covered: commands,
ssh_tail --follow, interactive sessions, andssh_sync(viaSendEnv, supported since OpenSSH 3.9). Not covered: SFTP transfers — ssh2 would fail the whole SFTP session on a server that refuses the variable — and port forwards, which have no environment.A person is never announced. Everything you do from the control plane — a shell, a file, a health probe, a command across a group — is yours, and reaches the server unlabelled.
Groups — tag servers and they become a group, with no extra file to maintain:
SSH_SERVER_WEB1_GROUP=production
SSH_SERVER_WEB2_GROUP=productionGroups created with ssh_group_manage live in .server-groups.json and additionally carry execution settings (strategy, delay, stop-on-error). When a name exists in both places, membership is the union and the stored settings apply. Names are case-insensitive. A group that exists only through the group field is read-only for ssh_group_manage — change membership by editing the servers.
Claude Code freezes or shows "Interrupted" — almost always an output too large for the context. Output is auto-truncated and the default timeout is two minutes, but you can tune it:
MCP_SSH_MAX_OUTPUT_LENGTH=5000 # default 10000 characters
MCP_SSH_DEFAULT_TIMEOUT=180000 # default 120000 ms
MCP_SSH_COMPACT_JSON=true # fewer tokens per responseAnd prefer tail -n 100 huge.log over cat huge.log — the same advice you'd give a colleague.
Tools don't show up — claude mcp list, then restart Claude Code. /mcp inside Claude Code shows the live status.
Connection failed — ssh-manager server test <name> first; it tells you whether it's DNS, the firewall, or the credentials.
Permission denied — check chmod 600 ~/.ssh/your_key, then the username, then what that user is allowed to do on the host.
Still stuck? Open an issue — include the server's PLATFORM and what ssh-manager server test said.
Known limitations, honestly:
Command timeouts are advisory. ssh2 can't kill a remote process; on Linux and macOS hosts a
timeoutwrapper does it properly. On Windows hosts setPLATFORM=windowsto skip that wrapper, which OpenSSH for Windows doesn't understand.ssh_syncwith password auth needssshpass. Keys are better anyway. On a Windows host, pass native paths likelocal:C:\project— drive-letter and UNC paths are converted to MSYS2 form, and a path already written as/c/...is passed through untouched rather than converted twice.Connections are pooled and reused. A stale one reconnects on next use; force it with
ssh_connection_statusand thereconnectaction.
git clone https://github.com/bvisible/mcp-ssh-manager.git
cd mcp-ssh-manager && npm ci
./scripts/setup-hooks.sh # pre-commit checks, including secret detection
npm test # 43 suites
npm run typecheck # JSDoc through tsc, no build step
npm run test:all # both, plus ./scripts/validate.sh
npm run test:preview # isolated candidate fixture, restart and cleanupThe layout:
src/ index.js is the MCP server and its 37 tools; one module per domain
(ssh-manager, config-loader, session-manager, backup-manager,
health-monitor, database-manager, tunnel-manager, server-groups…)
cli/ the Bash CLI, plus the Node wrapper that makes it work on Windows
ui/ the control plane's React interface (v4)
desktop/ the Electron build (v4)
profiles/ frappe, docker, nodejs — and room for yours
scripts/ validation, and the rig that regenerates every screenshot and the video
docs/ guides, migration notes, security modesPull requests are welcome — CONTRIBUTING.md has the details. Shipping the desktop builds — signing, notarization, and why the Mac App Store is a different product rather than a further step — is in docs/DISTRIBUTION.md.
Nothing in this README's imagery is taken by hand. scripts/demo-env.mjs stands up three real ssh2 servers on the loopback with a seeded vault, a pending approval and a live stream; capture-screenshots.mjs and record-demo.mjs drive Chrome over the DevTools protocol against it; frame-screenshots.py draws the browser and application windows, and macbook-mockup.py draws the laptop. After a UI change, re-run them rather than editing an image.
The hero animation ships twice, light and dark, swapped by <picture> on prefers-color-scheme. A single transparent GIF would have been simpler, but GIF alpha is one bit and transparency defeats its inter-frame compression: the same clip came out at 11.4 MB transparent against 357 KB on a solid ground.
What's new
4.2.0 — a Chinese interface, a fuller audit log, and a guard against raw ssh. The interface and the desktop menus come in 简体中文 (Options → Appearance → Language; thanks to @xutianhao6). With AUDIT_LOG on, every tool now records what it did, not only a few. ssh_execute can start a long job in the background (background: true, an idea from @SEU-zhc), and the opt-in ssh-manager guard keeps an agent from running ssh in its own shell, where approval and audit do not apply (an idea from @fyq163). Fixed, thanks to @jaredshuai's fork: backups that reported success when the dump had failed (an empty archive, every night from ssh_backup_schedule), a corrupt archive restored in part, MongoDB backups that could not be restored, ssh-manager server remove deleting a look-alike server, editing a server turning read-only into unrestricted, remote tunnels that never worked, a database password in the audit log, and six simultaneous calls opening six connections. ssh-manager codex setup now exists. A tool that fails now sets isError in its reply. See the CHANGELOG.
4.1.0 — choose the agent key per server, and sudo in the audit log. A server can name the one key of your SSH agent it uses, so an agent that holds many keys (a password manager's, say) no longer runs into MaxAuthTries before the right one is offered (thanks to @RebornOnce, #128); with AUDIT_LOG on, ssh_execute_sudo, downloads, group commands and commands sent to a session now record what they did, never the sudo password (#134, thanks to @orshih6, reported by @revert12); llms-install.md lets an agent set the server up for you; dependencies refreshed. Nothing to change on upgrade. See the CHANGELOG.
4.0.3 — a security release; upgrade. ssh_db_query, the read-only tool, could run shell commands on the server through the psql and mysql clients' own commands (\!), including on servers in readonly or restricted mode (GHSA-qmf5-hrhv-5hjm, reported by @orshih6). Every version before it is affected. A MySQL query must now be one line without backslashes. See the CHANGELOG.
4.0.2 — commands reach a Mac, and the desktop terminal is your own. Commands sent to a macOS server no longer fail with timeout: command not found before they start (thanks to Henrik Tilly, #115); the desktop terminal starts a login shell, so what Homebrew installed is found as in Terminal or Warp, and PowerShell on Windows; keystrokes reach the shell in the order they were typed; dependencies refreshed. Nothing to change on upgrade.
4.0.1 — the application tells you about updates. Options → Updates, a mark on the sidebar and a notification once per version, so a new release no longer goes unnoticed in a menu you never open; a tidier Servers toolbar; the interface is built when the package is made rather than committed (what you install is byte for byte the same); and the npm, registry and GitHub descriptions say what V4 is. No change to the engine's behaviour.
4.0.0 — the control plane. See what an agent is running on your servers, with its output, and stop it before it runs. A desktop application for macOS, Windows and Linux, installed from the engine with ssh-manager app; an encrypted vault for credentials; human approval per server; a shell on your own machine alongside the remote ones. Everything new is opt-in — an npm install upgraded from 3.8.x keeps behaving as 3.8.x, which a test installs from the registry and proves. The Windows application is not code-signed in this release. What changes and how to go back.
v3.8.7 — a security release; upgrade. Two advisories fixed: a partial server name or an alias escaped readonly and restricted mode (GHSA-mq9f-5385-rjf4), and ssh_backup_restore's backupId ran commands on the server (GHSA-2wjh-xm9c-7qf9). See the CHANGELOG.
v3.8.6 — a security release; upgrade. Six advisories fixed. The default on-error hook let any SSH server you connect to run a command on your machine; restricted mode only checked how a line started; ssh_db_query could write; host keys were never compared. See the CHANGELOG.
v3.8.5 — a security release. Three command-injection advisories fixed, one of which defeated readonly mode. Upgrade if you use ssh_backup_*, ssh_db_dump, ssh_service_status or ssh_tail.
RCE bypassing
readonly/restricted(GHSA-m793-whw6-f537) —ssh_service_statusandssh_tailare read-only, so they stay enabled on servers you locked down, and neither quoted its arguments nor consulted the policy layer. A service name likenginx; id > /tmp/pwnedexecuted.RCE through
ssh_db_dump(GHSA-796j-h5q5-jx6p) — thestatrun after the dump interpolated the output path raw. The v3.6.7 patch had stopped one line short.RCE through every
ssh_backup_*tool (GHSA-qwwm-vrm9-4mw8) —backup-manager.jshad zero shell escaping across its nine builders whiledatabase-manager.jshad 95.
Quoting now lives in one module so "did this builder quote its inputs?" has a single answer.
Full changelog, every release back to v1.0.0 →
Documentation · Changelog · Security policy · Roadmap · Issues
Built on the Model Context Protocol, with ssh2 doing the hard part. MIT licensed — see LICENSE.
Made with ❤️ for the Claude Code community
Also from us
Relais: several Claude accounts on one Mac, one workspace. Switch Claude Desktop and Claude Code between your work and personal accounts in one click, keep every conversation, and see each account's limits. Free and open source, for macOS.
Validation before a release
The V4 testing guide describes the offline suites, real npm 3.8.5 → V4 → rollback checks, browser flows, and platform release gates. Use the final V4 test guide to review the actual npm package or desktop candidate with a disposable profile before publication.
Available Tools
37 toolsssh_alert_setupA
Configures and evaluates CPU, memory, and disk usage alert thresholds for a remote server. The action parameter selects: set writes the threshold config to /etc/ssh-manager-alerts.json on the remote host (mutating, may need write access to /etc, and is blocked on readonly servers); get reads back that config; check reads current metrics and compares them to stored thresholds. get and check are read-only. enabled defaults to true; check errors if no config exists yet.
Inputs: action (required), server (required), enabled, cpuThreshold, +2 more.
ssh_aliasA
Manages local name aliases that let you reference a configured server by a shorter or alternative name. The action parameter selects add, remove, or list. add creates an alias pointing to an existing server and requires both alias and server; remove deletes an alias and requires alias; list shows all aliases (read-only). add and remove persist the alias mapping locally. The target server must already exist for add to succeed.
Inputs: action (required), alias, server.
ssh_backup_createA
Creates a database or file backup on the remote server over SSH, writing a compressed archive plus a JSON metadata file into backupDir. Supports mysql, postgresql, mongodb, and files (full is not yet implemented and errors). Database types require database; files requires paths. After writing it prunes backups older than retention days (default 7); compress defaults to true. Runs pre-backup and post-backup hooks.
Inputs: name (required), type (required), server (required), paths, +9 more.
ssh_backup_listA
Lists existing backups found in backupDir on the remote server, returning each backup id, type, database or paths, size, compression, retention, status, and creation time parsed from stored metadata. Read-only: it inspects the filesystem and mutates nothing. Optional type filters results to mysql, postgresql, mongodb, files, or full. backupDir defaults to the configured backup directory.
Inputs: server (required), type, backupDir.
ssh_backup_restoreA
Restores a previously created backup identified by backupId, reading its metadata to pick the engine. This is destructive and overwrites the target: PostgreSQL runs pg_restore with --clean --if-exists which DROPs existing objects, MongoDB runs mongorestore --drop, and MySQL pipes the dump into the live database replacing matching objects. Supports mysql, postgresql, mongodb, and files. Runs pre-restore and post-restore hooks.
Inputs: server (required), backupId (required), dbHost, dbPort, +5 more.
ssh_backup_scheduleA
Schedules a recurring backup on the remote server by writing an executable bash script to /usr/local/bin/ssh-manager-backup-NAME.sh and installing a crontab entry for the given cron expression. Mutates the remote filesystem and crontab, and typically needs root to write that path. Supports mysql, postgresql, mongodb, and files; the generated script also deletes backups older than retention days (default 7).
Inputs: name (required), type (required), server (required), schedule (required), +3 more.
ssh_command_aliasA
Manages local shorthand aliases that map a short name to a full command string, stored in local config with no remote execution or side effects. The action selects behavior: add (requires both alias and command), remove (requires alias), list to show all aliases tagged as profile or custom, or suggest to return existing aliases matching a search term passed in the command field. Adding an existing alias overwrites it.
Inputs: action (required), alias, command.
ssh_connection_statusA
Inspects and manages the pooled SSH connections held by this server process; affects only local in-memory connections, never remote state. The action parameter selects: status lists active connections with age and keepalive (read-only); reconnect closes then reopens one connection; disconnect closes one connection; cleanup drops aged-out and dead connections. The server parameter is required for reconnect and disconnect and ignored otherwise.
Inputs: action (required), server.
ssh_db_dumpA
Dumps a database to a file on the remote server over SSH; it reads data only and does not modify the database. Supports mysql (using --single-transaction --routines --triggers), postgresql (custom format with --clean --if-exists, restorable via pg_restore), and mongodb. compress defaults to true and gzips the output. The optional tables list applies to MySQL and PostgreSQL only and is ignored for MongoDB.
Inputs: type (required), server (required), database (required), outputFile (required), +6 more.
ssh_db_importA
Imports a dump file into a target database on the remote server and is destructive to existing data. PostgreSQL uses pg_restore --clean --if-exists which DROPs existing objects before loading; MongoDB uses mongorestore with --drop controlled by the drop flag (default true); MySQL pipes the file into the live database, replacing objects defined in it. Supports mysql, postgresql, mongodb. Compressed .gz inputs are decompressed automatically.
Inputs: type (required), server (required), database (required), inputFile (required), +5 more.
ssh_db_listA
Lists database objects on the remote server for the given engine without modifying anything. When database is provided it lists the tables (SQL) or collections (MongoDB) of that database; when omitted it lists all databases with common system databases filtered out. Supports mysql, postgresql, and mongodb. Returns the items and a count. Read-only and safe to call repeatedly.
Inputs: type (required), server (required), dbHost, dbPort, +3 more.
ssh_db_queryA
Runs a read-only query against a remote database. For mysql and postgresql it is strictly limited to one SELECT statement: the query must begin with SELECT, and INTO, file functions, and any insert, update, delete, drop, create, alter, truncate, grant, revoke, or exec keyword are rejected before execution; it then runs inside a read-only transaction, so the database itself refuses any write. For mongodb it runs a find() and requires the collection parameter; the query must be a filter document (JSON or mongo shell syntax such as ObjectId("…")), and $where, $function and $accumulator are refused. Returns the raw command output as text.
Inputs: type (required), query (required), server (required), database (required), +5 more.
ssh_deployA
Deploys a list of local files to remote paths on the named server, uploading each to a temporary location first and then moving it into place. Mutates remote state. By default it backs up any existing target file before overwriting; backup can be disabled per call. Options can set owner and permissions, supply a sudo password, and name a single service to restart afterward. Detects sensible owner and permission defaults from the remote path. Runs pre and post deploy hooks. Blocked entirely on servers in readonly or restricted security mode.
Inputs: files (required), server (required), options.
ssh_downloadA
Downloads one remote file from the named server to a local destination path over SFTP, overwriting any existing local file at that path. Affects only the local filesystem and is read-only on the remote side, so it stays allowed even on servers in readonly or restricted security mode. Reads the remote file using the configured SSH user, which must have permission to read it. Handles single files only; use ssh_sync for directories.
Inputs: server (required), localPath (required), remotePath (required).
ssh_executeA
Runs a shell command over SSH on a named configured server and returns stdout, stderr, and exit code. Mutates remote state depending on the command; not read-only. Expands command aliases before running. Uses the cwd parameter or, if omitted, the server configured default directory; adapts syntax for Linux versus Windows PowerShell targets. Timeout defaults to 120000 ms and is capped at 300000 ms. Under readonly mode destructive commands like rm or dd are refused; under restricted mode the command must match allow patterns. Output is truncated when very large. For a job that outlives the timeout (a training run, a long build) set background to true instead of using nohup yourself: it starts the command detached and returns at once with its pid and the log file it writes to; read that file with ssh_tail (follow: false) and check or stop the job with ssh_process_manager.
Inputs: server (required), command (required), cwd, timeout, +1 more.
ssh_execute_groupA
Runs one command on every server belonging to the named group and returns a per-server success or failure report. Members come from the groups defined with ssh_group_manage plus every server whose configuration carries a matching group field, so a group can exist through the config alone. Mutates remote state on each member and is not idempotent. Best-effort: the security policy of each server is evaluated independently, so readonly or restricted members are reported as failed without aborting the rest unless stopOnError is set. Strategy may be parallel, sequential, or rolling (delay applies between servers). Per-server timeout is 30000 ms; cwd defaults to the default_dir of each server.
Inputs: group (required), command (required), cwd, delay, +2 more.
ssh_execute_sudoA
Runs a command with elevated privileges via sudo on the named server and returns the exit code and output. Prepends sudo when absent. If a password is given, or a sudo password is configured for the server, it is piped to sudo -S and masked in the returned output. Mutates remote state and can be destructive. Honors the cwd parameter or the server default directory and adapts to Linux or Windows. Timeout defaults to 30000 ms. Blocked entirely in readonly mode; in restricted mode the command must satisfy the allow and deny patterns.
Inputs: server (required), command (required), cwd, timeout, +1 more.
ssh_group_manageA
Creates, updates, deletes, and inspects named server groups used by ssh_execute_group, persisting changes to local configuration only with no remote side effects. The action selects the operation: create, update, delete, add-servers, remove-servers, or list. Every action except list requires name; add-servers and remove-servers also require a non-empty servers array. list is read-only and also reports the groups derived from the per-server group field of the SSH configuration, which are read-only here and change only by editing that configuration. Optional strategy, delay, and stopOnError set default group execution behavior.
Inputs: action (required), name, delay, servers, +3 more.
ssh_health_checkA
Runs a comprehensive read-only health check on the named server by executing diagnostic shell commands over SSH, then returns parsed JSON with overall status, CPU, memory, disk usage, and uptime. It only reads metrics and changes nothing on the remote host. Set detailed to true to additionally include load average and network metrics; it defaults to false. Critical CPU, memory, or disk conditions are surfaced in a critical_issues list.
Inputs: server (required), detailed.
ssh_historyA
Returns the in-memory log of SSH commands previously run through this server process during the current session, formatted with timestamps, server, duration, and success status. Purely local and read-only: it opens no SSH connection and does not persist across restarts. Optional filters narrow the results by server name, by success or failure, and by a search substring in the command text; limit defaults to 20 most recent entries. Does not expose command output, only the commands and their outcomes.
Inputs: limit, search, server, success.
ssh_hooksA
Manages automation hooks that fire around SSH operations such as pre-deploy, toggling them on or off in local configuration only with no immediate remote action. The action selects behavior: list shows each hook with its enabled state, description, and action count; enable and disable flip a hook and both require the hook name; status summarizes which hooks are currently enabled versus disabled. Toggling persists and affects later operations.
Inputs: action (required), hook.
ssh_key_manageA
Manages SSH host key fingerprints in your local known_hosts file for the named server. The action parameter selects: verify, check, and list are read-only comparisons or listings; accept adds or updates the host key in known_hosts; remove deletes it. accept and remove mutate local state and are blocked on servers configured as readonly. server is required for every action except list. autoAccept defaults to false and should be used with caution.
Inputs: action (required), server, autoAccept.
ssh_list_serversA
Lists all SSH servers defined in the loaded configuration, returning for each the name, host, user, port, authentication type (password or key), default directory, group, and description. Read-only and local: it reads configuration only and opens no SSH connections. Deliberately omits secrets, so no passwords, key paths, passphrases, or sudo passwords are returned. Takes no parameters. Useful as a first call to discover which server names other tools accept.
Inputs: no named parameters.
ssh_monitorA
Collects a read-only snapshot of system resources on the named Linux server by running inspection commands such as top, free, df, ss, and ps. The type parameter selects the view and defaults to overview; other values are cpu, memory, disk, network, and process. Does not change remote state and needs no sudo. The interval and duration parameters are accepted for continuous monitoring intent but a single snapshot is gathered. Targets Linux tooling, so output may be empty on Windows hosts.
Inputs: server (required), type, duration, interval.
ssh_process_managerA
Lists, inspects, or terminates processes on a remote server over SSH. The action parameter selects: list returns top processes (read-only), info returns details for one process (read-only), and kill sends a signal to terminate a process and mutates remote state. pid is required for kill and info. kill is blocked on servers configured as readonly. signal defaults to TERM, sortBy defaults to cpu, and limit defaults to 20; filter narrows the list by name or command.
Inputs: action (required), server (required), pid, limit, +3 more.
ssh_profileA
Manages SSH Manager profiles that bundle command aliases and hooks for different project types, affecting local configuration only with no remote side effects. The action selects behavior: list shows available profiles and the active one, current shows the active profile details, and switch activates a named profile and requires the profile argument. A successful switch reports that Claude Code must be restarted before the new profile takes effect.
Inputs: action (required), profile.
ssh_service_statusA
Checks the running state of the named system services on a remote server by querying each one over SSH, returning JSON per service plus running and stopped counts and an aggregate health rating. Read-only: it inspects status without starting, stopping, or restarting anything. The services array parameter is required and lists the service names to check, for example nginx, mysql, or docker; common names are resolved to their actual unit names automatically.
Inputs: server (required), services (required).
ssh_session_closeA
Terminates an open SSH session given its session ID, writing exit to the remote shell, ending it, and discarding its in-memory history and context; the session ID becomes unusable afterward. Destructive to session state but does not delete remote files. Passing the literal value all closes every active session at once, ignoring individual close errors. It does not drop the pooled underlying connection, only the interactive shell.
Inputs: session (required).
ssh_session_listA
Lists currently active SSH sessions with their ID, server, state, working directory, command count, age, idle time, and any defined variables. Read-only: it inspects in-memory session state and changes nothing on remote hosts or local config. The optional server argument is a case-insensitive substring filter on server name; omit it to list every active session. Closed sessions are excluded from the results.
Inputs: server.
ssh_session_sendA
Runs one command inside an already-open session identified by its session ID, reusing the persisted working directory, environment, and history of that shell. Mutates remote state like any shell command and is not idempotent; cd and export update the saved context for subsequent calls. Commands run through a bash-style shell (Unix-oriented). The security policy of the underlying server is enforced, so readonly or restricted servers may refuse. Default timeout is 30000 ms.
Inputs: command (required), session (required), timeout.
ssh_session_startA
Opens a new persistent interactive shell on the named configured server and returns a generated session ID. Stateful and side-effecting: it establishes (or reuses pooled) SSH connection and keeps an open shell that preserves working directory, environment, and command history across later ssh_session_send calls, unlike one-shot ssh_execute. The optional name is only a human label. The session stays open and consumes a remote shell until ssh_session_close is called.
Inputs: server (required), name.
ssh_syncA
Synchronizes files or directories between local and remote using rsync over SSH on the named server. Each of source and destination must carry a local: or remote: prefix and one side must be local and the other remote; with no prefix it assumes a push from local to remote. On Windows MCP hosts, provide native Windows local paths such as C:\project or .\project; the tool converts drive-letter and UNC paths to MSYS2 format before spawning rsync. Do not pre-convert a local path to /c/project because Node performs local filesystem checks using Windows path semantics. Mutates the destination. Setting delete true removes destination files absent from source, which is destructive; dryRun true previews without changing anything. Compression is on by default. Password authentication requires sshpass installed locally. Blocked on readonly or restricted servers. Timeout defaults to 30000 ms.
Inputs: server (required), source (required), destination (required), delete, +6 more.
ssh_tailA
Reads the tail of a remote log file on the named server, optionally filtered by a grep pattern. Read-only; it does not modify remote state. Behavior depends on follow, which defaults to true: in follow mode it starts a streaming tail whose output is written to the server process stderr rather than returned, and the response only reports a session note, so to capture content directly set follow to false to get the last N lines back. The lines parameter defaults to 10.
Inputs: file (required), server (required), grep, lines, +1 more.
ssh_tunnel_closeA
Tears down active SSH tunnels created earlier, freeing the bound local ports; this affects only local tunnel state, not the remote host. Exactly one of tunnelId or server must be supplied: tunnelId closes that single tunnel, while server closes every tunnel for the named server and reports how many were closed. Supplying neither raises an error. Closing is final and cannot be undone.
Inputs: server, tunnelId.
ssh_tunnel_createA
Opens a new SSH connection to the named server and starts a port-forwarding or SOCKS proxy tunnel that keeps running until closed. The type parameter selects local forward, remote forward, or dynamic SOCKS5 proxy. localPort is always required; remoteHost and remotePort are required for local and remote types but ignored for dynamic. localHost defaults to 127.0.0.1. Returns a tunnel ID used later to close it.
Inputs: type (required), server (required), localPort (required), localHost, +2 more.
ssh_tunnel_listA
Lists currently active SSH tunnels tracked by this process, showing each tunnel ID, server, type, state, local and remote endpoints, active and total connection counts, bytes transferred, error count, and timestamps. Read-only: it does not create, modify, or close anything. The optional server parameter filters results to one server; omit it to list every active tunnel across all servers.
Inputs: server.
ssh_uploadA
Uploads one local file to a remote destination path over SFTP on the named server, overwriting any existing remote file at that path. Mutates remote state and is not idempotent beyond replacing the target. Creates no backup. Requires the local file to exist. Does not use sudo, so the remote path must be writable by the configured SSH user. This tool is blocked entirely on servers set to readonly or restricted security mode. For directory trees use ssh_sync instead.
Inputs: server (required), localPath (required), remotePath (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 37 tools
Most tools target distinct resources, but several clusters overlap: ssh_execute/ssh_execute_sudo/ssh_execute_group/ssh_session_send all run commands; ssh_monitor and ssh_health_check both collect system metrics; ssh_db_dump and ssh_backup_create both dump databases; ssh_upload and ssh_deploy both push files. Detailed descriptions help, but with 37 tools the boundaries still require careful reading to avoid misselection.
All names use the ssh_ snake_case convention with predictable resource/action tokens. Minor deviations exist: ssh_list_servers puts the verb first while most others use noun_action (ssh_tunnel_list, ssh_db_list), and a few names are bare resource nouns (ssh_monitor, ssh_history).
37 tools is very heavy for an SSH manager and exceeds the typical 25-tool threshold. Even though many individual operations are distinct, related capabilities such as monitoring/health checks and database dumps/backups could be consolidated to reduce selection burden.
Core SSH lifecycle coverage is broad: server discovery, command execution, persistent sessions, tunnels, file transfer, deployment, backups, database access, and monitoring are all represented. Notable gaps remain around server configuration CRUD (only listing), remote file listing/deletion, and managing or removing scheduled backups after creation.
Maintenance
Related MCP Connectors
Work on your own computers from Claude or ChatGPT: run commands, edit and search files.
Manage servers through SSH.
Connect ChatGPT and Claude to your Mac, Windows or Linux with secure MCP remote access.
Manage remote servers, SSH terminals, files, databases and proxies with scoped access and approvals.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables SSH remote access to servers through Claude, allowing users to execute commands, transfer files via SFTP, and manage multiple remote connections using natural language.129MIT
- AlicenseNot gradedqualityNot gradedmaintenanceConnects Claude to remote servers via SSH to execute commands, manage files, and browse directories. It allows users to add, edit, and switch between multiple server configurations through natural language conversations.-
- AlicenseAqualityDmaintenanceEnables Claude to connect to servers via SSH, execute commands, transfer files, and manage connections through natural language.911 npm1MIT
- -licenseNot gradedqualityNot gradedmaintenanceEnables Claude Code to control remote servers via SSH for automated deployment, testing, and operations, including command execution and file transfer.4-