# mcp-ssh-manager: an MCP SSH server with per-server permission modes for Claude Code and Codex

> bvisible/mcp-ssh-manager exposes 37 SSH tools to Claude Code and OpenAI Codex, and lets you cap each server at unrestricted, readonly or restricted. The security history is the interesting part.

**bvisible/mcp-ssh-manager** — MCP SSH Server: 37 tools for remote SSH management | Claude Code & OpenAI Codex | DevOps automation, backups, database operations, health monitoring

- Repository: https://github.com/bvisible/mcp-ssh-manager
- Website: https://www.npmjs.com/package/mcp-ssh-manager
- Stars: 494 · Forks: 71
- Language: JavaScript
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/bvisible-mcp-ssh-manager

## The problem: an agent with a shell on machines that matter

Claude Code and OpenAI Codex can already run commands locally. The gap opens when the work lives on another host: deploy a build, tail a log, dump a database, check whether a service is up. Without a bridge, you copy output back into the chat by hand, and the agent never sees the machine it is reasoning about.

mcp-ssh-manager is that bridge. It is a Model Context Protocol server that holds SSH connections and exposes them as tools the assistant can call. The README describes the audience as DevOps automation, backups, database operations and health monitoring, and the tool count is 37. The project's own framing is blunt about the risk: an MCP SSH server is described as the most dangerous tool you can hand an agent, because it is a shell on machines that matter.

That framing is why the design centres on a per-server permission mode rather than a global switch. If you run one staging box and one production cluster, they should not be governed by the same policy. The README states this explicitly: you decide how far the agent can go, per server, not globally.

## How the permission modes actually work

Three modes are documented. In unrestricted, the default, the agent can do everything, which the README equates with the behaviour of any other SSH MCP server. In readonly, mutating tools are refused outright: no deploy, no upload, no sudo, no database import, while read commands still work. In restricted, every command must match an allow pattern and no deny pattern, and anything else is refused before it reaches the host.

The mode is set per server through environment variables. Patterns are semicolon-separated, as the README's example shows.

```env
SSH_SERVER_PROD_MODE=readonly
SSH_SERVER_STAGING_MODE=restricted
SSH_SERVER_STAGING_ALLOW_PATTERNS=^systemctl (status|restart) myapp$;^tail -n \d+ /var/log/
```

A second control sits below the mode: sudo passwords are sent over the SSH channel's stdin rather than interpolated into a remote command line. The README contrasts this with the echo "$pass" | sudo -S pattern it says is common in the category, noting that the stdin approach keeps the password out of ps, /proc/<pid>/cmdline and an auditd trail. Read-only SQL is enforced rather than suggested, with ssh_db_query refusing anything that is not a SELECT. Database arguments pass through one centralised shell-quoting helper, and v3.8.5 states the helper now lives in a single module, src/shell-quote.js, so the question of whether a given builder quotes its inputs has one answer.

## Installing mcp-ssh-manager and making a first connection

The package is published on npm as mcp-ssh-manager and ships two binaries: mcp-ssh-manager, which starts the MCP server, and ssh-manager, an interactive CLI. The README points at an interactive CLI menu screenshot and the repository carries INSTALLATION.md and QUICKSTART.md at the top level, but the README itself does not spell out an install command, so treat the package page as the source for that step. What the repository does show is the shape of the configuration.

Server entries are environment variables following SSH_SERVER_[NAME]_[PROPERTY], where NAME is your uppercase identifier. The .env.example file documents HOST, USER, PASSWORD for password auth, KEYPATH for key auth, PORT, DEFAULT_DIR and DESCRIPTION. Copy it and fill in real values.

```bash
cp .env.example .env
```

A minimal entry for a staging box using key authentication looks like this, taken from the example file's naming convention.

```env
SSH_SERVER_STAGING_HOST=staging.example.com
SSH_SERVER_STAGING_USER=deploy
SSH_SERVER_STAGING_KEYPATH=~/.ssh/staging_key
SSH_SERVER_STAGING_PORT=22
SSH_SERVER_STAGING_DEFAULT_DIR=/home/deploy/app
```

Once the server is defined, the agent side is a normal MCP client configuration. The repository ships examples/claude-code-config.example.json and examples/codex-ssh-config.example.toml as the two reference files for wiring the server into Claude Code and OpenAI Codex respectively. The package.json also lists a setup script that installs npm dependencies and then pip install -r tools/requirements.txt, and a configure script that runs python tools/server-manager.py, suggesting a Python-side helper for managing server entries. The README does not document what that helper writes, so read tools/server-manager.py before running it against a config you care about. For a first real use, set the new server to readonly and ask the agent to report uptime or list a directory. If that works and the mode holds, widen it deliberately.

## The security record is the thing to read before adopting

The release history reads like a sequence of fixes to the same class of bug, and that is the most useful signal in the repository. v3.8.5, released on 2026-08-28, addresses three command-injection advisories. One, GHSA-m793-whw6-f537, defeated readonly and restricted mode: ssh_service_status and ssh_tail are read-only, so they remained enabled on locked-down servers, and neither quoted its arguments nor consulted the policy layer. A service name such as nginx; id > /tmp/pwned executed. That is the exact control those modes exist to provide, bypassed through the tools the modes leave switched on.

A second advisory, GHSA-796j-h5q5-jx6p, ran through ssh_db_dump, where the stat command after the dump interpolated the output path raw; the changelog notes a v3.6.7 patch had stopped one line short. A third, GHSA-qwwm-vrm9-4mw8, covered every ssh_backup_* tool: backup-manager.js had zero shell escaping across its nine builders, while database-manager.js had 95, and the earlier fix was never extended to the backup side.

The response is concrete. Quoting moved into one module, and a test drives 340 builder, argument and payload combinations through a real shell to confirm none execute. The README also states a 648-combination injection test guards database arguments. Earlier releases in the same window fixed a sudo password that reached the remote command line, and a logger that wrote secrets in clear text to ~/.ssh-manager.log and stderr. The honest reading: this project takes the problem seriously now, and it was not taking it seriously for a long stretch before August 2026.

## Where mcp-ssh-manager is the wrong tool

If you want to run commands on remote hosts yourself, this is not the tool. It has no value without an MCP client driving it; the interactive ssh-manager CLI exists, but the project's identity is the agent bridge. Reach for plain ssh, or a configuration manager like Ansible, when a human is the one deciding what runs.

The permission modes also have a shape worth understanding before you trust them. readonly refuses mutating tools, but read-only tools still execute on the host, and v3.8.5 exists precisely because two of them did not quote their arguments. restricted is stricter, but it depends on your allow and deny patterns being right; a pattern that is too broad reopens the door, and the README gives one example rather than a hardened default set. Neither mode is an OS-level sandbox. The agent's SSH user still holds whatever permissions that account holds, so the account you configure is the real ceiling.

Finally, the project is JavaScript on Node with a Python tooling side, and the setup script installs Python requirements. If your environment is Node-only and you will not run pip, check whether the parts you need work without that half of the repository. The README does not answer that question.

## Alternatives and how they differ

The related searches around this project name several other SSH MCP servers: Aiondadotcom/mcp-ssh, Classfang's ssh mcp server, Idletoaster's ssh-MCP-server, and Shaike1's mcp server ssh. Those are the direct comparisons, and the searches suggest people treat them as a set.

The difference that matters is policy. The README's own comparison is that unrestricted mode behaves like any other SSH MCP server, which implies the others do not offer per-server readonly and restricted modes with allow and deny patterns evaluated before a command reaches the host. If you only need an agent to run a command over SSH, that distinction is invisible and any of them will do. If you need to hand an agent access to a production host while keeping deploy and sudo off the table, the mode layer is the reason to pick this one, and the v3.8.5 bypass is the reason to verify it rather than assume it.

Outside the MCP category, Ansible and similar tools solve remote execution with a declarative playbook and no language model in the loop. They are not alternatives for the same job, but they are the right answer when the task is repeatable and the agent adds nothing but variability.

## Maintenance, licence and what upgrading costs

The repository is not archived and the last push was on 2026-09-10, which is recent. Releases cluster tightly: v3.8.3, v3.8.4 and v3.8.5 all landed on 2026-08-28, and the changelog describes v3.8.3 as the first release published from CI, with SLSA provenance and an attested CycloneDX SBOM. The project is listed in the official MCP Registry as io.github.bvisible/mcp-ssh-manager.

The upgrade cost is real if you are on anything older than 3.8.5 and you use ssh_backup_*, ssh_db_dump, ssh_service_status or ssh_tail, or if you rely on readonly or restricted mode. The README says to upgrade in exactly those cases. Since the injection fixes touch the quoting path, behaviour for unusual arguments may change, and the project's own test suite is the way to see that: npm test runs a long chain of individual checks including test:dbquoting, test:dbinjection, test:backupinjection, test:sudostdin, test:redaction and test:policy. Running it after an upgrade is a concrete verification step, not a formality.

The licence is MIT, stated in the repository and shown on the badge. That permits commercial use and modification with the copyright notice retained. It also means no warranty, which for a tool that holds SSH credentials and a sudo password is worth stating plainly rather than treating as boilerplate. This is not legal advice; read the LICENSE file.

## Conclusion

Adopt mcp-ssh-manager if you already drive Claude Code or Codex and want SSH work inside that loop, and start with SSH_SERVER_<NAME>_MODE=readonly on anything that matters. Do not adopt it if you want a general-purpose SSH client or a tool whose security surface has been stable for years: v3.8.5 fixed three command-injection advisories, one of which bypassed readonly mode, so the permission layer is younger than it looks. Before pointing it at production, read SECURITY.md, confirm which mode each server entry carries in .env, and check that your installed version is 3.8.5 or later.

## FAQ

### What is mcp-ssh-manager and what does it let Claude Code do?

It is a Model Context Protocol server that exposes SSH connections as tools, so Claude Code or OpenAI Codex can execute commands, transfer files, run database operations, create backups and check health on remote servers. The README lists 37 tools and describes per-server permission modes that cap what the agent may do on each host.

### How do I install mcp-ssh-manager?

It is published on npm as mcp-ssh-manager and ships an mcp-ssh-manager binary that starts the server plus an ssh-manager CLI. The README does not spell out the install command, so the npm package page is the place to confirm it; the repository's setup script runs npm install followed by pip install -r tools/requirements.txt.

### How are servers configured in mcp-ssh-manager?

Through environment variables named SSH_SERVER_[NAME]_[PROPERTY], where NAME is your uppercase server identifier. The .env.example file documents HOST, USER, PASSWORD, KEYPATH, PORT, DEFAULT_DIR and DESCRIPTION, and the README adds MODE plus ALLOW_PATTERNS and DENY_PATTERNS for the restricted mode.

### What is the difference between readonly and restricted mode in mcp-ssh-manager?

In readonly, mutating tools are refused outright while read commands still work. In restricted, every command must match an allow pattern and no deny pattern, and anything that fails is refused before it reaches the host.

## Sources

- [bvisible/mcp-ssh-manager on GitHub](https://github.com/bvisible/mcp-ssh-manager)
- [License: MIT](https://github.com/bvisible/mcp-ssh-manager/blob/main/LICENSE)
- [Project website](https://www.npmjs.com/package/mcp-ssh-manager)
- [README](https://github.com/bvisible/mcp-ssh-manager/blob/main/README.md)
- [Releases](https://github.com/bvisible/mcp-ssh-manager/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/bvisible-mcp-ssh-manager
