@yawlabs/ssh-mcp
π npm version
π License: MIT
AI λꡬλ₯Ό μν SSH νκ²½μ ꡬμΆνμΈμ. SSH νκ²½μ κ΄λ¦¬νκ³ , λ¬Έμ λ₯Ό μ§λ¨ λ° ν΄κ²°νλ©°, μμ΄μ νΈμκ² λͺ¨λ κ²μ λν μ격 μ‘μΈμ€ κΆνμ λΆμ¬νλ MCP μλ²μ λλ€.
Yaw Labsμμ κ΅¬μΆ λ° μ μ§ κ΄λ¦¬ν©λλ€.
λ¬Έμ μ
AI CLI λꡬλ SSHκ° μ§μμ μΌλ‘ λκΈ°λ νμ νλ‘μΈμ€μμ μ€νλ©λλ€. μμ΄μ νΈκ° git pullμ μλνλ©΄ Permission denied (publickey) μ€λ₯κ° λ°μν©λλ€. μλ²μ SSHλ‘ μ μνλ €κ³ νλ©΄ μμ΄μ νΈ μμΌμ΄ λ§λ£λμ΄ μμ΅λλ€. λ°°ν¬λ₯Ό μλνλ©΄ μΈμ€ν΄μ€κ° μ¬μμ±λμ΄ νΈμ€νΈ ν€κ° λ³κ²½λ©λλ€. λ§€λ² AIλ 무μμ΄ μλͺ»λμλμ§ μμ§ λͺ»νκ³ ν€λ§€κ² λ©λλ€.
μ΄λ¬ν λ¬Έμ λ SSH ν€κ° νμν λͺ¨λ μν©μμ λ°μν©λλ€:
Git β clone, pull, push, fetch, submodules, LFS
ν¨ν€μ§ κ΄λ¦¬μ β λΉκ³΅κ° μ μ₯μμμμ
npm install,pip install,go get,cargo,composerμλ² μ‘μΈμ€ β SSH, SCP, SFTP, rsync
ν°λλ§ β λ°μ΄ν°λ² μ΄μ€λ‘μ ν¬νΈ ν¬μλ©, SOCKS νλ‘μ
λ°°ν¬ β Ansible, Terraform, Capistrano, λ°°ν¬ μ€ν¬λ¦½νΈ
ν΄λΌμ°λ β AWS EC2, GCP, Azure, DigitalOcean, λͺ¨λ VPS
ssh-mcpκ° μ΄ λ¬Έμ λ₯Ό ν΄κ²°ν©λλ€. SSH μμ΄μ νΈλ₯Ό κ΄λ¦¬νκ³ , ν€λ₯Ό λ‘λνλ©°, μ€λ₯ λ°μ μ μ€ν κ°λ₯ν μμ λͺ λ Ήμ΄λ₯Ό μ§λ¨νκ³ , μ격 μμ μ μ 곡ν©λλ€. μ΄ λͺ¨λ κ²μ΄ AI μμ΄μ νΈκ° νΈμΆν μ μλ MCP λκ΅¬λ‘ μ 곡λ©λλ€.
λΉ λ₯Έ μμ
npm install -g @yawlabs/ssh-mcpMCP ν΄λΌμ΄μΈνΈ μ€μ μ μΆκ°νμΈμ:
{
"mcpServers": {
"ssh": {
"command": "ssh-mcp"
}
}
}λꡬ
SSH νκ²½ κ΄λ¦¬
git, λ°°ν¬, ν°λλ§ λ± λͺ¨λ μμ μ΄ μ€λ¨λμ§ μλλ‘ λ‘컬 SSH μ€μ μ μμ νλ λꡬμ λλ€.
λꡬ | μ€λͺ |
| ssh-agentκ° μ€ν μ€μΈμ§ νμΈν©λλ€. νμμ μμνκ³ μΈμ μ λν νκ²½ λ³μλ₯Ό μ€μ ν©λλ€. |
| ~/.ssh/μ μλ λͺ¨λ SSH ν€λ₯Ό μ ν, μ§λ¬Έ, μμ΄μ νΈ μνμ ν¨κ» λμ΄ν©λλ€. |
| μ€ν μ€μΈ μμ΄μ νΈμ ν€λ₯Ό λ‘λν©λλ€. λ¨Όμ μμ΄μ νΈκ° μμλμλμ§ νμΈν©λλ€. |
| νΈμ€νΈμ λν μ ν¨ν SSH μ€μ (νΈμ€νΈλͺ , μ¬μ©μ, ν¬νΈ, νλ‘μ, ID νμΌ)μ νμΈν©λλ€. |
| μ€λλ νΈμ€νΈ ν€λ₯Ό μ κ±°νκ³ λ€μ μ€μΊν©λλ€. "host key verification failed" μ€λ₯λ₯Ό μμ ν©λλ€. |
| GitHub, GitLab, Bitbucket λ±μ λν Git-over-SSH μΈμ¦μ ν μ€νΈν©λλ€. |
| νμ΄λ° λ° μ€ν κ°λ₯ν μ€λ₯ μΈλΆ μ 보μ ν¨κ» λΉ λ₯Έ μ°κ²° ν μ€νΈλ₯Ό μνν©λλ€. |
μ§λ¨
λꡬ | μ€λͺ |
| μ 체 SSH νκ²½ μ§λ¨. μμ΄μ νΈ, ν€, μ€μ , known_hosts λ° μ°κ²° μνλ₯Ό νμΈν©λλ€. λͺ¨λ μ€λ₯μ λν΄ μ νν μμ λͺ λ Ήμ΄λ₯Ό λ°νν©λλ€. |
μ격 μμ
λꡬ | μ€λͺ |
| μ격 νΈμ€νΈμμ λͺ λ Ήμ μ€νν©λλ€. stdout, stderr λ° μ’ λ£ μ½λλ₯Ό λ°νν©λλ€. |
| SFTPλ₯Ό ν΅ν΄ μ격 νΈμ€νΈμμ νμΌμ μ½μ΅λλ€. |
| SFTPλ₯Ό ν΅ν΄ μ격 νΈμ€νΈμ νμΌμ λ΄μ©μ μλλ€. |
| SFTPλ₯Ό ν΅ν΄ λ‘컬 νμΌμ μ격 νΈμ€νΈμ μ λ‘λν©λλ€. |
| μ격 νΈμ€νΈμμ λ‘컬 νμΌ μμ€ν μΌλ‘ νμΌμ λ€μ΄λ‘λν©λλ€. |
| μ격 νΈμ€νΈμ λλ ν 리μ μλ νμΌ λͺ©λ‘μ νμΈν©λλ€. |
μμ μμ€ μμ
ssh_execλ₯Ό μ¬μ©νμ¬ μμ΄μ νΈκ° ꡬμΆνλ μΌλ°μ μΈ ν¨ν΄μ λννμ¬ λ λΉ λ₯΄κ³ μ€λ₯κ° μ μ λꡬμ λλ€.
λꡬ | μ€λͺ |
| μ¬λ¬ νΈμ€νΈμμ λμμ λͺ λ Ήμ μ€νν©λλ€. νΈμ€νΈλ³ κ²°κ³Όλ₯Ό λ°νν©λλ€. |
| ꡬ쑰νλ λ§€κ°λ³μ(μ΄λ¦, μ ν, ν¬κΈ°, κΉμ΄)λ₯Ό μ¬μ©νμ¬ μ격μΌλ‘ νμΌμ κ²μν©λλ€. |
| νμΌμ λ§μ§λ§ Nμ€μ μ½μΌλ©°, μ νμ μΌλ‘ grep ν¨ν΄μΌλ‘ νν°λ§ν©λλ€. |
| systemd μλΉμ€ μν(νμ±, PID, κ°λ μκ°, μ€λͺ )λ₯Ό νμΈν©λλ€. |
μλ μ§λ¨
μ격 μμ
μ΄ μ€ν¨νλ©΄ ssh-mcpκ° μλμΌλ‘ μ§λ¨μ μ€ννκ³ μ€λ₯ μλ΅μ κ²°κ³Όλ₯Ό ν¬ν¨ν©λλ€. μμ΄μ νΈκ° λ³λλ‘ ssh_diagnoseλ₯Ό νΈμΆν νμκ° μμΌλ©°, μ€λ₯ λ©μμ§μμ λ°λ‘ 무μμ΄ μλͺ»λμκ³ μ΄λ»κ² μμ ν΄μΌ νλμ§ μ μ μμ΅λλ€.
μ°κ²° νλ§
μ격 μμ μ SSH μ°κ²°μ μλμΌλ‘ μ¬μ¬μ©ν©λλ€. μμ΄μ νΈκ° λμΌν νΈμ€νΈμ μ¬λ¬ λ² νΈμΆνλ©΄ 첫 λ²μ§Έ νΈμΆμ΄ μ°κ²°μ μ΄κ³ νμ νΈμΆμ μ΄λ₯Ό μ¬μ¬μ©ν©λλ€. μ°κ²°μ λ§μ§λ§ μ¬μ© ν 60μ΄ λμ μ μ§λ λ€μ μλμΌλ‘ λ«νλλ€.
SSH μ€μ μ§μ
λͺ¨λ μ°κ²°μ ~/.ssh/configλ₯Ό μ€μν©λλ€. νΈμ€νΈ λ³μΉ, μ¬μ©μ μ§μ ν¬νΈ, μ¬μ©μ μ΄λ¦, ID νμΌ λ° ProxyJump μ€μ μ΄ μλμΌλ‘ μ¬μ©λ©λλ€. SSH μ€μ μ Host myserverκ° κ΅¬μ±λμ΄ μλ€λ©΄ host: "myserver"λ§ μ λ¬νλ©΄ ssh-mcpκ° λͺ¨λ κ²μ ν΄κ²°ν©λλ€.
ProxyJump / λ°°μ€μ² νΈμ€νΈλ μλμΌλ‘ μ§μλ©λλ€. SSH μ€μ μ νΈμ€νΈμ λν ProxyJump bastionμ΄ μλ κ²½μ°, ssh-mcpλ λ°°μ€μ²μ ν΅ν΄ ν¬λͺ
νκ² μ°κ²°ν©λλ€. μ²΄μΈ νλ‘μλ μλν©λλ€.
νΈμ€νΈ ν€ νμΈ
λͺ¨λ μ격 μμ
μ μλ²μ νΈμ€νΈ ν€λ₯Ό ~/.ssh/known_hostsμ λΉκ΅νμ¬ νμΈν©λλ€:
μλ €μ§ νΈμ€νΈ, ν€ μΌμΉ β μλ½.
μλ €μ§ νΈμ€νΈ, ν€ λ³κ²½λ¨ β κ±°λΆ (MITM 보νΈ).
μλ €μ§μ§ μμ νΈμ€νΈ β 첫 μ°κ²° μ μλ½ (TOFU). ν₯ν λΆμΌμΉ κ°μ§λ₯Ό μν΄ ν€λ₯Ό κ³ μ νλ €λ©΄
ssh_known_hosts_fixλ₯Ό μ¬μ©νμΈμ.
λ μ격ν νκ²½μ κ²½μ° SSH_MCP_STRICT_HOST_KEY=1μ μ€μ νμ¬ μλ €μ§μ§ μμ νΈμ€νΈλ₯Ό κ±°λΆνμΈμ. λ¨Όμ ssh_known_hosts_fixλ₯Ό μ¬μ©νμ¬ λͺ
μμ μΌλ‘ μΆκ°νμμμ€.
μ§λ¨ λꡬ(ssh_test, ssh_diagnose)λ νλ‘λΈ λͺ
λ Ήμ StrictHostKeyChecking=noλ₯Ό μ¬μ©ν©λλ€. μ΄λ¬ν νλ‘λΈλ echo SSH_OKλ§ μ€ννλ©° μ격 μ¦λͺ
μ΄λ λ°μ΄ν°κ° μ λ¬λμ§ μμΌλ―λ‘ μ°κ²° ν
μ€νΈμ μμ ν©λλ€. μ€μ μμ
μ νμ hostVerifierλ₯Ό κ±°μΉ©λλ€.
Windows μ§μ
Windowsμμ ssh-mcpλ OpenSSH μΈμ¦ μμ΄μ νΈ μλΉμ€λ₯Ό μλμΌλ‘ κ°μ§ν©λλ€(\.pipeopenssh-ssh-agent λͺ
λͺ
λ νμ΄νλ₯Ό ν΅ν΄). SSH_AUTH_SOCKμ΄ νμνμ§ μμΌλ©°, OpenSSH μμ΄μ νΈ μλΉμ€κ° μ€ν μ€μΈμ§ νμΈνκΈ°λ§ νλ©΄ λ©λλ€.
μΈμ¦
λͺ¨λ μ격 μμ μ μ°κ²° λ§€κ°λ³μλ₯Ό νμ©ν©λλ€:
λ§€κ°λ³μ | μ€λͺ | κΈ°λ³Έκ° |
| SSH νΈμ€νΈλͺ λλ IP (νμ) | β |
| SSH ν¬νΈ | SSH μ€μ λλ |
| SSH μ¬μ©μ μ΄λ¦ | SSH μ€μ λλ νμ¬ μ¬μ©μ |
| SSH κ°μΈ ν€ κ²½λ‘ | μλ κ°μ§ |
| SSH λΉλ°λ²νΈ (ν€ κΆμ₯) | β |
μΈμ¦ ν΄κ²° μμ: ssh-mcpλ μ΄ λͺ©λ‘μμ 첫 λ²μ§Έ μΌμΉ νλͺ©μ μ ννλ©° μ΄ν νλͺ©μΌλ‘ λμ΄κ°μ§ μμΌλ―λ‘ μΈμ¦ λ°©λ²μ΄ κ²°μ λ‘ μ μ΄κ³ μμΈ‘ κ°λ₯ν©λλ€.
λͺ μμ
privateKeyPathλͺ μμ
passwordssh-agent (Unixμ
SSH_AUTH_SOCK, Windowsμ\.pipeopenssh-ssh-agent)νΈμ€νΈμ λν
~/.ssh/configμ ID νμΌκΈ°λ³Έ ν€ κ²½λ‘ (
~/.ssh/id_ed25519,id_rsa,id_ecdsa)
μμ μν¬νλ‘μ°
μμ΄μ νΈκ° git pullμ ν μ μλ κ²½μ°
Agent calls ssh_git_check β "Permission denied. Your SSH key is not registered with github.com."
Agent calls ssh_key_list β finds id_ed25519 exists but is not loaded
Agent calls ssh_key_load("~/.ssh/id_ed25519") β "Key loaded"
Agent calls ssh_git_check β "Git SSH authentication to github.com succeeded as username"
Agent runs git pull β worksμΈμ€ν΄μ€ μ¬μμ± ν νΈμ€νΈ ν€κ° λ³κ²½λ κ²½μ°
Agent calls ssh_exec on server β error: "Host key verification failed"
(auto-diagnostics included in error: "Fix with ssh_known_hosts_fix")
Agent calls ssh_known_hosts_fix("my-server") β "Host key refreshed"
Agent calls ssh_exec β worksμ μλ²μ λν 첫 μ°κ²°
Agent calls ssh_test("new-server") β "Connection refused at new-server:22"
Agent calls ssh_diagnose("new-server") β full report showing agent running, keys loaded, but host unreachable
Agent reports: "SSH server isn't running on new-server or port 22 is blocked"νλ‘κ·Έλλ° λ°©μ μ¬μ©
import { connect, exec, diagnose, ensureAgent, listSshKeys, checkGitSsh, ConnectionPool } from '@yawlabs/ssh-mcp';
// Fix SSH environment
const agent = ensureAgent();
console.log(agent.message);
// Check git access
const git = checkGitSsh('github.com');
console.log(git.message);
// List available keys
const keys = listSshKeys();
for (const key of keys) {
console.log(`${key.name} (${key.type}) - ${key.loadedInAgent ? 'loaded' : 'not loaded'}`);
}
// Run a remote command (one-off)
const client = await connect({ host: 'my-server', username: 'deploy' });
const result = await exec(client, 'uptime');
console.log(result.stdout);
client.end();
// Run multiple commands with connection pooling
const pool = new ConnectionPool();
await pool.withConnection({ host: 'my-server' }, async (client) => {
const r1 = await exec(client, 'uptime');
console.log(r1.stdout);
});
// Connection stays open for 60s β next call reuses it
await pool.withConnection({ host: 'my-server' }, async (client) => {
const r2 = await exec(client, 'df -h');
console.log(r2.stdout);
});
pool.drain(); // close all connections when done
// Diagnose issues
const report = diagnose('my-server');
console.log(report.overall); // "ok" | "warning" | "error"
for (const check of report.checks) {
console.log(`[${check.status}] ${check.name}: ${check.message}`);
}μꡬ μ¬ν
Node.js 18+
SSH ν΄λΌμ΄μΈνΈ μ€μΉλ¨ (μ§λ¨ λ° νκ²½ κ΄λ¦¬μ©)
λΌμ΄μ μ€
MIT
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/YawLabs/ssh-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
