How SSH MCP servers work
They connect to hosts over SSH and run commands the agent sends. To limit the agent, they add host allow-lists and command allow or deny patterns: for example regex and CIDR rules (n0madic/ssh-mcp), per-host command and path allow-lists in a config file (python-ssh-mcp), or a central gateway with per-client rules, audit and rate limits (gelse/ssh-mcp). On the host, the agent's commands run in an SSH session under a Unix account.
A good fit when one person, a few hosts, a lab, and you are watching the agent.
What changes at fleet scale
- The limit on the host is the Unix account. A pattern allow-list sits above a shell. Anything that passes the pattern runs with that account's rights.
- Keys to manage. The agent, or its server, holds SSH credentials for the hosts it reaches.
- One shape for every agent. Per-agent differences live in config you maintain by hand.
How bastn does it
- Named actions, not command strings. The agent calls
restart_servicewithunit: nginx. It does not send a command line. - Typed arguments. Every argument has a type, an enum or a pattern.
eth0; rebootas an interface name is refused before it reaches the host. - A connector on the host. It checks its own allowlist, maximum effect and blocked flags, and runs the program with plain arguments and no shell by default. Commands run as a dedicated non-root user (run_as). Running commands as root is refused. The connector process must run as root to switch to that user. The published image runs as uid 10001 and does not switch users. Raw commands are off unless you enable them, and every raw call waits for approval.
- No SSH keys and no open ports for the agent. The connector dials out to your gateway.
- Per-agent menus, approvals and a verifiable audit trail are part of the same path.
Side by side
| SSH MCP server (typical) | bastn | |
|---|---|---|
| What the agent sends | A command string | An action name and typed arguments |
| Limit on the host | Unix account of the SSH user | Connector allowlist, effect ceiling, run_as user (root refused) |
| Credentials | SSH keys | None for the agent; the connector uses mutual TLS |
| Inbound access to hosts | SSH port | None |
| Per-agent views | Config per client, where supported (gelse/ssh-mcp) | Per action and argument value |
| Approvals | Varies | Destructive actions held by default |
| Audit | Varies | Keyed hash chain, offline verify |
Sources last checked 23 October 2026.