@yawlabs/ssh-mcp manages your SSH environment, diagnoses what is broken, fixes it, and gives your agent remote access to anything. It manages the SSH agent, loads keys, diagnoses failures with actionable fix commands, and provides remote operations - all as MCP tools your AI agent can call. Built and maintained by Yaw Labs, MIT licensed, published on npm.

npx -y @yawlabs/ssh-mcp@latest

GitHub · npm · Add to Yaw MCP

Who it is for

Anyone whose agent keeps tripping over SSH. AI CLI tools run in subprocesses where SSH is constantly broken: the agent tries to git pull and gets Permission denied (publickey), it tries to SSH into a server and the agent socket is stale, it tries to deploy and the host key changed because the instance was recreated. Every time, the AI has no idea what is wrong and spirals.

That happens across every situation needing SSH keys - Git (clone, pull, push, fetch, submodules, LFS), package managers pulling from private repos, server access over SSH, SCP, SFTP and rsync, port forwarding and SOCKS proxies, deployment tooling like Ansible and Terraform, and any cloud VPS. The README's worked examples are the shape of it:

Install

1. Add the server to your client. No API key and no required env var - ssh-mcp uses the SSH setup you already have. On macOS, Linux and WSL:

{ "mcpServers": { "ssh": { "command": "npx", "args": ["-y", "@yawlabs/ssh-mcp@latest"] } } }

On Windows, wrap the call in cmd /c, since Node 20+ cannot spawn .cmd files directly and that is what npx is there:

{ "mcpServers": { "ssh": { "command": "cmd", "args": ["/c", "npx", "-y", "@yawlabs/ssh-mcp@latest"] } } }

The @latest tag makes npx re-resolve against the registry on every spawn, so each MCP session uses the newest published version. If you would rather pin and skip auto-updates, install globally with npm install -g @yawlabs/ssh-mcp and set "command": "ssh-mcp" in the client config instead.

2. Restart and approve. Restart Claude Code (or your MCP client) and approve the ssh server when prompted. Then ask it something: "Diagnose my SSH setup and tell me why git pull is failing."

Running Yaw MCP? One click adds it to your local config, and it is then available in every Yaw Terminal session.

What it covers

SSH environment management - the tools that fix your local setup so everything else stops breaking:

ToolWhat it does
ssh_agent_ensureEnsure ssh-agent is running. Starts one if needed and sets env vars for the session.
ssh_key_listList all SSH keys in ~/.ssh/ with type, fingerprint and agent status.
ssh_key_loadLoad a key into the running agent, ensuring the agent is started first.
ssh_config_lookupResolve the effective SSH config for a host: hostname, user, port, proxy, identity files.
ssh_known_hosts_fixRemove a stale host key and re-scan. Fixes host key verification failures - and it is the one tool here that writes known_hosts.
ssh_git_checkTest Git-over-SSH auth to GitHub, GitLab, Bitbucket and friends.
ssh_testQuick connectivity test with timing and actionable error details.
ssh_diagnoseFull SSH environment diagnostic: agent, keys, config, known_hosts and connectivity, with exact fix commands for every failure.

Remote operations - execution over SSH, file work over SFTP:

ToolWhat it does
ssh_execExecute a command on a remote host, returning stdout, stderr and exit code. An optional env param sets per-call environment variables with a POSIX-safe prefix, which works regardless of sshd's AcceptEnv.
ssh_read_file / ssh_write_fileRead or write a file on a remote host via SFTP.
ssh_upload / ssh_downloadMove a file to or from the remote host via SFTP.
ssh_ls / ssh_statList a directory, or get metadata (size, octal mode, uid/gid, mtime/atime, file/dir/symlink) instead of parsing ls -la.
ssh_mkdirCreate a directory via SFTP, with recursive: true for mkdir -p behavior. Unlike the other SFTP tools the path may be relative, resolving against the SFTP working directory; ~ is not expanded, because SFTP has no shell.
ssh_deleteDelete a file or empty directory via SFTP, dispatching unlink or rmdir from the path's own type via lstat, so a symlink is always unlinked and never followed. Recursive delete is intentionally not supported.

Higher-level operations - wrappers for the patterns agents otherwise rebuild out of ssh_exec:

ToolWhat it does
ssh_multi_execRun a command on multiple hosts in parallel, returning results per host. The same env prefix applies, built once and sent to every host.
ssh_findSearch for files remotely with structured parameters: name, type, size, depth, and newer for files modified more recently than a reference path.
ssh_tailRead the last N lines of a file, optionally filtered by a grep pattern.
ssh_service_statusCheck systemd service status: active, PID, uptime, description. Flags an error only when the unit could not be found or queried, not when an existing unit is intentionally stopped.

Three behaviors run underneath all of it. Auto-diagnostics: when a remote operation fails, ssh-mcp runs diagnostics automatically and includes the results in the error response, so your agent is told what is wrong and how to fix it without calling ssh_diagnose separately. Connection pooling: the first call to a host opens a connection and later calls reuse it, kept alive for a period after the last use, then closed; the pool caps at a default size you can raise with SSH_MCP_MAX_POOL_SIZE for fan-out workloads, evicting an idle entry when full and rejecting with Connection pool is full when every entry is in use. SSH config support: host aliases, custom ports, usernames, identity files and ProxyJump all come from your ~/.ssh/config automatically, so host: "myserver" is enough, and chained bastion proxies work too.

Safety and control

# Read-only allowlist: only ls / df / cat / find / tail SSH_MCP_COMMAND_WHITELIST="^ls( .*)?,^df( .*)?,^cat ,^find ,^tail " # Block destructive ops even if your agent goes off-script SSH_MCP_COMMAND_BLACKLIST="^rm ,^shutdown,^reboot,^mkfs,^dd if=" # Env-prefix-tolerant anchor: allow zero or more KEY='value' prefixes SSH_MCP_COMMAND_WHITELIST="^([A-Za-z_][A-Za-z0-9_]*='[^']*' )*ls( |$)"

Use it as a library, without an agent

The same package exposes its internals as a TypeScript API, so the environment-repair and remote-execution logic is reusable from a plain script with no model involved:

import { connect, exec, diagnose, ensureAgent, ConnectionPool } from '@yawlabs/ssh-mcp'; ensureAgent(); const report = diagnose('my-server'); console.log(report.overall); // "ok" | "warning" | "error" const pool = new ConnectionPool(); await pool.withConnection({ host: 'my-server' }, async (client) => { const r = await exec(client, 'uptime'); console.log(r.stdout); }); pool.drain();

listSshKeys and checkGitSsh are exported the same way, and diagnose returns a structured report whose checks each carry a status, a name and a message, alongside an overall verdict of ok, warning or error.

Frequently asked questions

What does the SSH MCP server do?

It manages your SSH environment, diagnoses what is broken, fixes it, and gives your agent remote access. That splits into environment tools (ensure the agent is running, list and load keys, resolve effective SSH config, repair a stale known_hosts entry, test Git-over-SSH auth), a full diagnostic that returns exact fix commands for every failure, remote operations over SSH and SFTP, and higher-level wrappers for multi-host execution, remote search, log tailing and systemd service status.

Do I need to configure credentials?

Usually not. All connections respect your ~/.ssh/config, so host aliases, ports, usernames, identity files and ProxyJump settings are used automatically. If you pass an explicit privateKeyPath or password it is used alone and nothing else is offered. With neither given, ssh-mcp offers the ssh-agent and one on-disk key together and lets the server pick. Only one on-disk key is ever offered, so if the first readable candidate is the wrong one, authentication rests on the agent's keys; pass privateKeyPath to force a specific key.

Does it verify host keys?

For known hosts, yes: a matching key is accepted and a changed key is rejected as MITM protection, with a message that distinguishes a genuine mismatch from a key type your known_hosts entry does not cover. An unknown host is accepted unless SSH_MCP_STRICT_HOST_KEY=1. Read that last branch carefully, because it is trust-always, not trust-on-first-use: the connection path never writes to known_hosts, so connecting pins nothing and every connection to an absent host is a first use. Only hosts added out of band, by you, by ssh-keyscan, or by ssh_known_hosts_fix, get mismatch protection.

Can I restrict which commands the agent runs?

Partly. SSH_MCP_COMMAND_WHITELIST and SSH_MCP_COMMAND_BLACKLIST take comma-separated regex patterns and are enforced before the SSH connection opens, so no remote process starts for a blocked command. The scope is the caveat: the policy covers ssh_exec and ssh_multi_exec only. The mutating SFTP tools ssh_write_file, ssh_upload, ssh_mkdir and ssh_delete never build a shell command string, so a blacklist of ^rm does not stop ssh_delete. To prevent remote mutation, drop those four tools from your MCP client's tool allowlist; these env vars cannot do it.

Does it work on Windows?

Yes. On Windows ssh-mcp uses the OpenSSH Authentication Agent's named pipe automatically when SSH_AUTH_SOCK is not set, so no SSH_AUTH_SOCK is needed as long as the OpenSSH agent service is running. One wrinkle: ssh_agent_ensure and ssh_diagnose probe that pipe and tell you if the service is down, but remote operations do not, so a stopped agent service shows up as an auth failure rather than an agent-not-running error until you run the diagnostic tools.

Related MCP servers

Further reading

Published by Yaw Labs.