ssh-mcpBLOCK
MCP server for SSH operations with built-in diagnostics
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
[](https://yaw.sh/mcp/install?name=SSH&command=npx&args=-y%2C%40yawlabs%2Fssh-mcp&description=Run%20commands%20on%20remote%20hosts%2C%20transfer%20files%2C%20manage%20SSH%20tunnels%20and%20keys&source=https%3A%2F%2Fgithub.com%2FYawLabs%2Fssh-mcp)
One click adds this to your local Yaw MCP config so it's available in every Yaw Terminal session. Or install manually below.
[](https://www.npmjs.com/package/@yawlabs/ssh-mcp) [](https://opensource.org/licenses/MIT)
Make SSH work for AI tools. MCP server that manages your SSH environment, diagnoses what's broken, fixes it, and gives your agent remote access to anything.
Built and maintained by Yaw Labs.
The problem
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's wrong and spirals.
This happens across every situation that needs SSH keys:
- Git — clone, pull, push, fetch, submodules, LFS
- Package managers —
npm install,pip install,go get,cargo,composerfrom private repos - Server access — SSH, SCP, SFTP, rsync
- Tunneling — port forwarding to databases, SOCKS proxies
- Deployment — Ansible, Terraform, Capistrano, deploy scripts
- Cloud — AWS EC2, GCP, Azure, DigitalOcean, any VPS
ssh-mcp fixes this. 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.
Quick start
Add to your MCP client config:
{
"mcpServers": {
"ssh": {
"command": "npx",
"args": ["-y", "@1dafe2730408OBSERVED · 2026-10-11Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.
claude mcp add ssh-mcp -- npx -y @yawlabs/[email protected]
Exposed tools (21)
14 read · 5 write · 2 destructive. Blast radius: 2 tools can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.
| Tool | Risk | Description |
|---|---|---|
ssh_agent_ensure | read | Ensure ssh-agent is running and reachable. Starts a new agent if needed and sets environment variables so subsequent SSH operations work. Use this FIRST when SSH operations fail with agent-related errors. |
ssh_config_lookup | read | Resolve the effective SSH configuration for a host. Shows hostname, user, port, identity files, proxy settings, and all other options from ~/.ssh/config. Use this to understand how SSH will connect to a host. |
ssh_delete | destructive | Delete a file or empty directory on a remote host via SFTP. Auto-detects the path type and calls the right SFTP op (unlink for files/symlinks, rmdir for empty dirs). Recursive directory delete is intentionally NOT supported -- for that, use ssh_exec with \ |
ssh_diagnose | read | Diagnose SSH connectivity issues. Checks ssh-agent status, loaded keys, known_hosts, SSH config, and attempts a test connection. Use this BEFORE attempting SSH operations if you suspect connectivity issues, or AFTER a failed SSH operation to understand why it failed. |
ssh_download | read | Download a file from a remote host to local filesystem via SFTP.${SFTP_POOL_WAIT_NOTE} |
ssh_exec | write | Execute a command on a remote host via SSH. The command is interpreted by the remote login shell — pipes, redirects, globs, and other shell metacharacters work as expected. Returns up to three content blocks in this order: the status ( |
ssh_find | read | Search for files on a remote host. Wraps the find command with structured parameters so you don |
ssh_git_check | read | Test Git-over-SSH authentication to a hosting provider (GitHub, GitLab, Bitbucket, etc). Verifies your SSH key is registered and working. Use this when git clone/pull/push fails with SSH errors. |
ssh_key_list | read | |
ssh_key_load | read | Load an SSH private key into the running agent. Ensures the agent is running first. Use this after ssh_key_list shows a key that is not loaded. |
ssh_known_hosts_fix | destructive | Remove a stale host key from known_hosts and re-scan the host to add the current key. Use this when you see |
ssh_ls | read | List files in a directory on a remote host via SFTP.${SFTP_POOL_WAIT_NOTE} |
ssh_mkdir | write | Create a directory on a remote host via SFTP. Set \ |
ssh_multi_exec | write | Execute a command on multiple remote hosts in parallel. Runs at most SSH_MCP_MAX_POOL_SIZE hosts at once (default 100) and works through a longer list as slots free up. |
ssh_read_file | read | Read a file from a remote host via SFTP. A whole-file read is limited to 10 MB. For a large file -- or behind a proxy that caps results, such as Yaw MCP at about 100 KB -- read it in pages with \ |
ssh_service_status | read | Check the status of a systemd service on a remote host. Returns whether it |
ssh_stat | read | Get metadata for a file or directory on a remote host via SFTP. Returns size, permissions (octal), uid/gid, mtime/atime, and the path type. Symlinks are reported as \ |
ssh_tail | read | Read the last N lines of a file on a remote host, optionally filtering by a grep pattern. Use this for reading log files instead of ssh_exec with manual tail/grep commands. |
ssh_test | read | Quick connectivity test to an SSH host. Reports success/failure with timing and actionable error details. Lighter and faster than ssh_diagnose — use this for a quick check before running operations. |
ssh_upload | write | Upload a local file to a remote host via SFTP.${SFTP_POOL_WAIT_NOTE}${POLICY_EXEMPT_NOTE} |
ssh_write_file | write | Write content to a file on a remote host via SFTP. Creates or overwrites the file.${SFTP_POOL_WAIT_NOTE}${POLICY_EXEMPT_NOTE} |
Trust audit
BLOCKgrade F · trust 55/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | FAIL |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | WARN |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (6 observation(s))
- Network
- declared (5 observation(s))
- Shell
- declared (8 observation(s))
- Dependencies
- not all pinned
- Secrets in source
- found
Findings (19)
const m = text.match(/-----BEGIN OPENSSH PRIVATE KEY-----([\s\S]+?)-----END/);
const result = await exec(client, command, timeoutMs);
const result = await exec(client, command, timeoutMs);
const result = await exec(client, command, timeoutMs);
const result = await exec(client, `systemctl status -- ${shellQuote(serviceName)} 2>&1`, timeoutMs);const SSH = 'ssh -i ~/.ssh/gh_woods -o IdentitiesOnly=yes';
"authorized_keys",
return { status: "error", message: "~/.ssh directory does not exist. Run: mkdir -p ~/.ssh && chmod 700 ~/.ssh" };const keyTypes = ["id_ed25519", "id_rsa", "id_ecdsa"];
message: 'No SSH private keys found in ~/.ssh/. Generate one: ssh-keygen -t ed25519 -C "[email protected]"',
const OPENSSH_PRIV = "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAA\n-----END OPENSSH PRIVATE KEY-----";
expect(firstKey({ legacy_pem_a: "-----BEGIN RSA PRIVATE KEY-----\nMIIE\n" }).type).toBe("rsa");expect(firstKey({ legacy_pem_b: "-----BEGIN EC PRIVATE KEY-----\nMHcC\n" }).type).toBe("ecdsa");expect(firstKey({ legacy_pem_c: "-----BEGIN DSA PRIVATE KEY-----\nMIIB\n" }).type).toBe("dsa");ssh_delete, ssh_known_hosts_fix
const result = await exec(client, "echo", 30000, 1024);
const TEST_KEY = join(__dirname, "../../test/docker/test_key");
@modelcontextprotocol/sdk, ssh2, zod, @biomejs/biome, @types/node, @types/ssh2, @vitest/coverage-v8, @yawlabs/mcp-compliance
- **`@modelcontextprotocol/sdk` moves from 1.30.0 to 1.32.1, and the dependency floor is now `^1.32.1` (was `^1.30.0`)** (GHSA-6qxp-vccf-f47h, high: the SDK's OAuth client could send credentials to an
Gates applied: critical_finding, no_behavioural_pass.
1dafe2730408full audit observations/trust-audit/mcp-server/yawlabs__ssh-mcp.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-11 | 1dafe2730408 | BLOCK | F | 55 | first audit |
Questions
What is the ssh-mcp MCP server?
MCP server for SSH operations with built-in diagnostics
What tools does ssh-mcp expose?
21 in total: 14 read-only, 5 that write, and 2 that can delete or overwrite (ssh_delete, ssh_known_hosts_fix). Every one is listed on this page with its risk.
Is ssh-mcp safe to connect to an agent?
No — not without reading the findings first. The audit graded it F (55/100) and found 10 critical or high issues in the source. Each one is listed on this page with the file and line it is on. Separately from the audit: 2 of its tools can destroy data, so scope the token you give it to what you actually need.
What credentials does ssh-mcp need?
It reads GH_TOKEN, GITHUB_TOKEN and SSH_AUTH_SOCK from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.
How does ssh-mcp run?
It speaks stdio, so it runs as a local process your client starts. It is published on npm as @yawlabs/ssh-mcp at 0.17.3.
How current is this page?
The grade is for one exact copy of the source (1dafe2730408), read on 2026-10-11. The repository is watched and re-audited when it changes.