Atlas / MCP servers / yawlabs / ssh-mcp

ssh-mcpBLOCK

mcp/yawlabs/ssh-mcp

MCP server for SSH operations with built-in diagnostics

Verdict
BLOCK
Grade
F
Trust score
55 /100
Exposed tools
21 14r · 5w · 2d
Transport
stdio
License
MIT
Stars
6
01

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, composer from 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", "@
Read from source at commit 1dafe2730408OBSERVED · 2026-10-11
Advertisement
02

Connect

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-code (npm)
claude mcp add ssh-mcp -- npx -y @yawlabs/[email protected]
03

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.

ToolRiskDescription
ssh_agent_ensurereadEnsure 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_lookupreadResolve 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_deletedestructiveDelete 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_diagnosereadDiagnose 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_downloadreadDownload a file from a remote host to local filesystem via SFTP.${SFTP_POOL_WAIT_NOTE}
ssh_execwriteExecute 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_findreadSearch for files on a remote host. Wraps the find command with structured parameters so you don
ssh_git_checkreadTest 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_listread
ssh_key_loadreadLoad 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_fixdestructiveRemove a stale host key from known_hosts and re-scan the host to add the current key. Use this when you see
ssh_lsreadList files in a directory on a remote host via SFTP.${SFTP_POOL_WAIT_NOTE}
ssh_mkdirwriteCreate a directory on a remote host via SFTP. Set \
ssh_multi_execwriteExecute 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_filereadRead 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_statusreadCheck the status of a systemd service on a remote host. Returns whether it
ssh_statreadGet 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_tailreadRead 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_testreadQuick 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_uploadwriteUpload a local file to a remote host via SFTP.${SFTP_POOL_WAIT_NOTE}${POLICY_EXEMPT_NOTE}
ssh_write_filewriteWrite content to a file on a remote host via SFTP. Creates or overwrites the file.${SFTP_POOL_WAIT_NOTE}${POLICY_EXEMPT_NOTE}
04

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.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfaceWARN
L4Behavioural (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)

CRITICALHard-coded secrets · secret.private_key · CWE-798, CWE-321
src/ssh.ts:477
const m = text.match(/-----BEGIN OPENSSH PRIVATE KEY-----([\s\S]+?)-----END/);
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/ops.ts:119
const result = await exec(client, command, timeoutMs);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/ops.ts:291
const result = await exec(client, command, timeoutMs);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/ops.ts:323
const result = await exec(client, command, timeoutMs);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/ops.ts:354
const result = await exec(client, `systemctl status -- ${shellQuote(serviceName)} 2>&1`, timeoutMs);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
scripts/update-manifests.mjs:214
const SSH = 'ssh -i ~/.ssh/gh_woods -o IdentitiesOnly=yes';
Why it matters. touches a credential store
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
src/diagnose.ts:70
"authorized_keys",
Why it matters. touches a credential store
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
src/diagnose.ts:169
return { status: "error", message: "~/.ssh directory does not exist. Run: mkdir -p ~/.ssh && chmod 700 ~/.ssh" };
Why it matters. touches a credential store
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
src/diagnose.ts:172
const keyTypes = ["id_ed25519", "id_rsa", "id_ecdsa"];
Why it matters. touches a credential store
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
src/diagnose.ts:204
message: 'No SSH private keys found in ~/.ssh/. Generate one: ssh-keygen -t ed25519 -C "[email protected]"',
Why it matters. touches a credential store
MEDIUMHard-coded secrets · secret.private_key · CWE-798, CWE-321
src/tests/env-diagnose-coverage.test.ts:415
const OPENSSH_PRIV = "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAA\n-----END OPENSSH PRIVATE KEY-----";
MEDIUMHard-coded secrets · secret.private_key · CWE-798, CWE-321
src/tests/env-diagnose-coverage.test.ts:696
expect(firstKey({ legacy_pem_a: "-----BEGIN RSA PRIVATE KEY-----\nMIIE\n" }).type).toBe("rsa");
MEDIUMHard-coded secrets · secret.private_key · CWE-798, CWE-321
src/tests/env-diagnose-coverage.test.ts:700
expect(firstKey({ legacy_pem_b: "-----BEGIN EC PRIVATE KEY-----\nMHcC\n" }).type).toBe("ecdsa");
MEDIUMHard-coded secrets · secret.private_key · CWE-798, CWE-321
src/tests/env-diagnose-coverage.test.ts:704
expect(firstKey({ legacy_pem_c: "-----BEGIN DSA PRIVATE KEY-----\nMIIB\n" }).type).toBe("dsa");
MEDIUMFilesystem / path · mcp.destructive_tools · CWE-22, CWE-59
ssh_delete, ssh_known_hosts_fix
Why it matters. 2 tool(s) can delete or overwrite
Fix. prefer a read-only mode or scoped tokens; the page states the blast radius
LOWCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/tests/exec.test.ts:33
const result = await exec(client, "echo", 30000, 1024);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/tests/integration.test.ts:16
const TEST_KEY = join(__dirname, "../../test/docker/test_key");
LOWSupply chain · supply.unpinned · CWE-829, CWE-1357
package.json
@modelcontextprotocol/sdk, ssh2, zod, @biomejs/biome, @types/node, @types/ssh2, @vitest/coverage-v8, @yawlabs/mcp-compliance
Why it matters. 13 dependency range(s) float
Fix. pin exact versions or ship a lockfile
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
CHANGELOG.md:46
- **`@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
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine

Gates applied: critical_finding, no_behavioural_pass.

Audited 2026-10-11 · audit v0.4.1 · source sha 1dafe2730408full audit observations/trust-audit/mcp-server/yawlabs__ssh-mcp.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-111dafe2730408BLOCKF55first audit
06

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.

Advertisement