Back to Discover

fast-mcp-ssh

connector

klNuno

SSH, SFTP, PTY shells, host-to-host copies and remote screenshots for AI agents. One Rust binary.

View on GitHub
0 starsSynced Aug 13, 2026

Install to Claude Code

/plugin marketplace add klNuno/fast-mcp-ssh

README

fast-mcp-ssh

SSH, SFTP and persistent shells for AI agents. One Rust binary, no runtime.

crates.io CI License Rust 1.89+ Platform

An MCP server that gives a model real SSH access: one connection per host kept alive across calls, a PTY shell that remembers cd and export, SFTP instead of cat > file, host-to-host copies that never touch your disk, a screenshot of the remote desktop, regex guards before anything leaves your machine, and an append-only audit log. Answers come back as TOON, roughly 40 percent fewer tokens than JSON on tabular data.

Install

cargo install fast-mcp-ssh

Or take a prebuilt binary from the latest release and check it against SHA256SUMS.txt. Linux and macOS ship x86_64 and aarch64, Windows ships x86_64.

Copy hosts.example.toml to ~/.fast-mcp-ssh/hosts.toml and fill in your hosts. Keys go in ~/.fast-mcp-ssh/keys/<name>; auth is key, agent or password.

Wire it up

.mcp.json, or claude_desktop_config.json for Claude Desktop:

{
  "mcpServers": {
    "ssh": {
      "type": "stdio",
      "command": "fast-mcp-ssh"
    }
  }
}

The same block works in Claude Code, Claude Desktop, Cursor, Windsurf, Zed, VS Code Copilot and anything else that speaks MCP over stdio.

In the MCP registry it is mcp-name: io.github.klNuno/fast-mcp-ssh.

Tools

host is optional on every tool once [defaults] default_host is set.

GroupTools
Runexec exec_batch sh interruptOne-shot, parallel fan-out, persistent PTY, Ctrl-C
Filesls stat dn up cp wr mkdir rm tailSFTP, plus tail -n / tail -F in a bounded window
VisualshotScreenshots the remote desktop, downscaled before it reaches the model
Opsfacts sys svcCached host profile, parsed ps/df/mem/net, systemd units
Sessionhosts ping disconnect disconnect_all reload shellsDiscovery and lifecycle; reload swaps config without a restart, shells closes named PTYs
Networkforward unforward forwardsLocal TCP forwards over the same connection

Every tool carries MCP annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) so a client can gate destructive calls.

Host-to-host copy

cp moves a file straight from one configured host to another. The bytes never land on your disk and never reach the model, and the server compares a sha256 on both ends before reporting success. Guards apply to the destination as well, so a read-only target still refuses the write.

Remote screenshots

shot captures the remote desktop and hands the model an image instead of a wall of text. It probes the host for grim, gnome-screenshot, spectacle, ImageMagick import or scrot and uses whichever is installed, covering X11 and wlroots Wayland. The capture is downscaled and re-encoded locally, so a 4K screen does not arrive as a multi-megabyte payload.

Protocol

Speaks every revision from 2024-11-05 to 2026-07-28 and adapts per peer.

On 2026-07-28 a server may no longer open a request of its own, so a confirmation comes back as an input_required result the client answers and retries (SEP-2322). Older clients keep getting a plain elicitation/create. Persistent sessions are unaffected: a PTY has always been addressed by the host and session arguments of the call, which is exactly the explicit handle the stateless core asks for.

Long operations use the Tasks extension (SEP-2663) when the client declares it: exec past the default 60s timeout and tail with follow=true return a task handle to poll instead of holding the call open. Every other client gets the blocking call it always got.

Security

  • Guards run before any SSH packet. deny_patterns refuse outright, confirm_patterns ask the user, and a client that cannot answer is denied. read_only = true blocks anything that looks like a write.
  • Paths are checked on both sides. Remote reads of keys, shadow files and cloud credentials are refused, and so are local writes that would land in your ~/.bashrc or an autostart folder. Every path-taking tool runs both checks, tail included. Paths are re-checked after the server resolves them, so a symlink cannot launder a blocked target, and a resolution that fails outright refuses the call rather than skipping the check.
  • Host keys are pinned (TOFU by default, strict and per-host fingerprints available). Every call is appended to ~/.fast-mcp-ssh/audit.log as NDJSON, with credentials scrubbed.

Guards are a speed bump against accidents, not a boundary against an adversary who controls the model. Scope the remote account accordingly: full threat model in SECURITY.md. What changed between versions: CHANGELOG.md.

Benchmark

50 iterations per scenario against the same Linux host over the same LAN, same SSH key, bench client on Windows 11. Medians, lower is better. Measured on 0.5.0; reproduce with benchmark/, raw runs in benchmark/results/.

fast-mcp-sshmcp-ssh-managerssh-mcp-server
Cold start48 ms280 ms260 ms
exec echo ok2.2 ms89.7 ms46.7 ms
exec uname -a; whoami; pwd3.6 ms90.9 ms50.6 ms
exec seq 1 5000 (~29 KB)19.6 ms90.4 ms 149.2 ms
Write a 1 KB file1.1 ms89.9 ms47.9 ms
Read a 1 KB file1.7 ms90.3 ms48.9 ms
Tool surface, sent every session26 tools, 21.1 KB37 tools, 39.9 KB4 tools, 1.7 KB

Both alternatives are Node processes, so ~250 ms of their cold start is the runtime booting. The steady-state gap is the connection: fast-mcp-ssh keeps one SSH session per host and spawns a channel per call, while the other two reconnect. Writes go over SFTP here and through a cat > file heredoc there.

Development

cargo install --path .        # build and install from a clone
cargo test                    # unit tests
cargo clippy --all-targets    # no warnings allowed in CI
./scripts/test-sh.ps1         # end-to-end against a real host (Windows)

Never write to stdout outside the MCP transport: a stray println! corrupts the JSON-RPC stream and the client disconnects without an error. tracing macros go to stderr and are safe.

License

MIT.

Footnotes

  1. mcp-ssh-manager truncates that response to 12 KB, so it is not returning the same output. ssh-mcp-server returns raw stdout with no exit code, which is why its replies are the shortest and why a failed command looks like a successful one.

Rendered live from klNuno/fast-mcp-ssh's GitHub README — not stored, always reflects the source repo.

1 Install Method

NameDescriptionCategorySource
cargo packageInstall via cargo (stdio transport)mcp-serverfast-mcp-ssh

0 Comments

Login required
Log in to post a comment or update on this repo.

No comments yet — be the first to share an update.