Skip to main content
Glama

ssh-mcp

ssh-mcp is a local Model Context Protocol server that gives AI agents safe, structured access to remote machines over SSH. Instead of hand-assembling ssh snow "docker ps" commands, the agent calls typed tools like ssh_exec({host: "snow", command: "docker ps"}) and gets back stdout, stderr, exit code, timeout and truncation information.

AI Agent
   │  MCP / stdio
   ▼
ssh-mcp
   │  SSH (connection pool, your OpenSSH config/credentials)
   ▼
Remote machines

The MCP server has essentially the same remote privileges as the SSH identity in use. Only connect it to agents you trust, on machines you are comfortable granting shell access to.

Requirements

  • Node.js >= 20

  • An SSH configuration you already use (~/.ssh/config) with working credentials

Related MCP server: SSH MCP Server

Installation

From npm:

npm install -g @callumbicknell/ssh-mcp

From source:

git clone https://github.com/CallumBicknell/ssh-mcp.git
cd ssh-mcp
pnpm install
pnpm run build

SSH configuration

ssh-mcp discovers hosts from your existing OpenSSH client config — ~/.ssh/config by default, or SSH_CONFIG_PATH if set. Nothing is duplicated: hostnames, users, ports and key paths all come from the file the ssh CLI already uses.

Per host, it resolves: HostName, Port, User, IdentityFile (tried in order), and IdentityAgent/SSH_AUTH_SOCK. Agent auth is preferred when available, then identity files.

MCP client configuration

Local/source usage:

{
  "mcpServers": {
    "ssh": {
      "command": "node",
      "args": ["/path/to/ssh-mcp/dist/index.js"]
    }
  }
}

npm usage:

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

The server speaks MCP on stdio; all logs go to stderr.

Tools

Tool

Purpose

ssh_hosts

List the concrete host aliases available in your SSH config (no secrets)

ssh_exec

Run a remote command; returns stdout/stderr/exit code/timeout/truncation

ssh_read_file

Read a remote text file (size-capped)

ssh_write_file

Atomically write a remote file (temp file + rename; never creates parent dirs)

ssh_list_directory

Compact directory listing with type, size, mode, mtime

ssh_stat

File/dir metadata: type, size, permissions, mtime, uid/gid

ssh_tunnel / ssh_tunnels / ssh_tunnel_stop

Persistent local port-forwards over the pooled SSH connection

ssh_exec result shape

exit code: 0

stdout:
...

stderr:
...
[stdout truncated at 65536 bytes]   ← only when truncated

Non-zero exit codes and stderr are always surfaced as MCP errors/text; timeouts return partial output plus a clear timeout message. On timeout the remote process is sent KILL and the channel is closed.

Configuration

Variable

Default

Meaning

SSH_MCP_MAX_OUTPUT

65536

Max bytes kept per output stream before truncation

SSH_MCP_COMMAND_TIMEOUT

30000

Default ssh_exec timeout (ms)

SSH_MCP_CONNECTION_TIMEOUT

10000

SSH connection timeout (ms)

SSH_MCP_OPERATION_TIMEOUT

30000

Per-operation SFTP timeout (ms)

SSH_MCP_IDLE_TIMEOUT

120000

How long an idle pooled connection is kept (ms)

SSH_CONFIG_PATH

~/.ssh/config

Path to the OpenSSH client config

Timeout semantics: connection timeout bounds establishing the TCP/SSH session; command timeout bounds a single ssh_exec; operation timeout bounds one SFTP read/write/stat/readdir; idle timeout only evicts a connection with no active operations.

SSH compatibility limitations

ssh-mcp uses the ssh2 library plus a parser for ~/.ssh/config. It is not a full OpenSSH client:

Supported

Not supported (or approximate)

HostName, Port, User

ProxyCommand (not honored)

IdentityFile (multiple)

ProxyJump (not honored)

IdentityAgent / SSH_AUTH_SOCK

Match blocks (not evaluated)

Per-host aliases

LocalForward/RemoteForward, ControlMaster, PermitLocalCommand, CertificateFile agent quirks

Agent and publickey auth

Password prompts (no interactive prompting — use keys/agent)

For hosts that require jump hosts or proxy commands, either add direct HostName/Port entries you can reach, or front them with a reachable bastion address in your config. Switching to shelling out to the system ssh binary was evaluated and rejected: it would lose connection pooling, structured results, and make timeout/output control and atomic-write SFTP impossible to guarantee portably.

Security model

  • No command allowlist by design: the server is an administration tool, and a blanket allowlist would make it useless for that purpose. Command-policy/confirmation middleware is a clear extension point.

  • Private keys/passwords are read by ssh2 during auth only; they are never logged or returned.

  • Host identifiers are validated and never interpolated into a local shell.

  • All output is byte-capped; all operations are timeout-bounded.

  • File writes are atomic and do not create parent directories.

Examples

ssh_hosts()
→ snow\nserver01\nfileserver

ssh_exec({host: "snow", command: "docker ps --format '{{.Names}} {{.Status}}'"})
ssh_read_file({host: "snow", path: "/etc/caddy/Caddyfile"})
ssh_write_file({host: "snow", path: "/tmp/deploy.yaml", content: "..."})
ssh_list_directory({host: "snow", path: "~/stacks"})
ssh_stat({host: "snow", path: "/etc/nginx/nginx.conf"})

Quick install (one-liner for an AI agent)

Copy this into any agent that can read URLs and follow instructions:

Read https://raw.githubusercontent.com/CallumBicknell/ssh-mcp/main/AGENT_INSTALL.md and follow it exactly.

Development

pnpm install        # install
pnpm run dev        # rebuild on change (tsc --watch)
pnpm run build      # emit to dist/
pnpm run typecheck  # strict type check
pnpm test           # vitest — no real SSH server required
pnpm start          # run on stdio

Tests mock the SSH/SFTP layer; they validate host parsing, validation, truncation (including UTF-8 edge cases), timeout logic, atomic write failure handling, connection-pool behaviour under concurrency, error formatting, and tool registration. CI runs install/typecheck/test/build on Node 22, 24 and 26.

Publishing

Package name on npm: @callumbicknell/ssh-mcp (the unscoped ssh-mcp name is owned by another project).

npm login
npm publish --access public

prepublishOnly runs typecheck + tests + build automatically. npm pack --dry-run shows that only dist/, README.md and LICENSE ship.

Available Tools

9 tools
ssh_execA

Run a shell command on a remote machine over SSH. host is an alias from your local ~/.ssh/config (see ssh_hosts). The command runs via a non-interactive remote shell; stdout and stderr are captured, truncated at a configured size limit, and reported together with the exit code. Non-zero exit codes are surfaced, not hidden. Default timeout: 30s (override per call with timeout). For long-running jobs, run them in the background remotely and poll.

Inputs: host (required), command (required), timeout.

ssh_hostsA

List the SSH host aliases available in the local SSH client configuration (~/.ssh/config). Use these aliases as the host argument to the other ssh tools. Does not expose keys, usernames' credentials, or secrets.

Inputs: no named parameters.

ssh_list_directoryA

List the contents of a directory on a remote SSH host (defaults to the remote user's home directory). Each entry includes type, size, permission bits and modification time.

Inputs: host (required), path.

ssh_read_fileA

Read the contents of a text file on a remote SSH host. Large files are truncated at a configured size limit; for very large files prefer ssh_exec with head/tail/grep.

Inputs: host (required), path (required).

ssh_statA

Get metadata about a file or directory on a remote SSH host: type, size, permissions, modification time, owner/group ids.

Inputs: host (required), path (required).

ssh_tunnelA

Start a persistent local port-forward through the pooled SSH connection for host, e.g. forward local port 8080 to snow:80. The tunnel stays open until ssh_tunnel_stop is called. Returns the tunnel id and the local URL to use.

Inputs: host (required), localPort (required), remotePort (required), localHost, +1 more.

ssh_tunnelsB

List currently open SSH port-forward tunnels.

Inputs: no named parameters.

ssh_tunnel_stopA

Stop a port-forward tunnel by id (from ssh_tunnel / ssh_tunnels).

Inputs: id (required).

ssh_write_fileB

Write text to a file on a remote SSH host. The write is atomic (temporary file, then rename), so a failed or interrupted write never leaves a partially-written destination. Parent directories are NOT created automatically.

Inputs: host (required), path (required), content (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 9 tools

    Disambiguation5/5

    Each tool targets a distinct operation: file read/write, command execution, directory listing, metadata stat, host enumeration, and tunnel start/list/stop. The tunnel trio (ssh_tunnel, ssh_tunnels, ssh_tunnel_stop) is clearly differentiated by verb, and file/metadata tools do not overlap. No realistic misselection risk.

    Naming Consistency4/5

    All tools use a consistent `ssh_` snake_case prefix, which is strong. Minor deviations exist: some are verb_noun (ssh_read_file, ssh_list_directory), some are bare resource plurals (ssh_hosts, ssh_tunnels), and ssh_tunnel_stop puts the verb last instead of following the verb_noun pattern.

    Tool Count5/5

    Nine tools is well-scoped for an SSH remote-operations server. Each tool covers a distinct capability (file I/O, exec, directory listing, stat, hosts, tunnel lifecycle) without redundancy or bloat.

    Completeness4/5

    The surface covers core SSH workflows: read, write, execute, list, stat, host discovery, and tunnel lifecycle. Gaps exist for dedicated delete, mkdir, move, and binary/SCP-style transfer, but most of these are workable via ssh_exec with shell commands.

    Maintenance

    ActivitySlowing
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI coding agents to securely execute shell commands on remote SSH servers with granular per-host permission controls. Automatically discovers hosts from ~/.ssh/config and exposes dedicated tools for each allowed host to ensure proper authorization before remote execution.
      17,673 npm
      Apache 2.0
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to securely execute commands, transfer files, and manage port forwarding on remote servers via SSH.
      158 npm
      38
      Apache 2.0