Skip to main content
Glama
nagual2

ssh-hosts-mcp

by nagual2

ssh-hosts-mcp

English | Русский | Deutsch

Unified SSH MCP server with a host registry — one host_id per call instead of IPs, users and key paths.

Overview

ssh-hosts-mcp is a Model Context Protocol server that gives LLM clients access to SSH hosts through a named registry. The agent calls ssh_exec with host_id — addresses, users, ports and private keys stay in a local config file and never enter the model context.

Three transports are supported:

Transport

Implementation

Tools

direct

ssh2 (Node.js)

exec, read, SFTP write (base64-exec fallback)

ssh3

spawns the ssh3 Go client (QUIC over UDP)

exec, read, base64-exec write (≤48 KB)

wsl

WSL bash -lc 'ssh …' (proxied setups)

exec only

Related MCP server: MCP SSH Server

Features

  • Host registry — host_id abstraction; the agent never guesses IP/user/key

  • Three transports — direct ssh2, ssh3 (QUIC over UDP), WSL-proxied SSH

  • File transfer — SFTP with automatic base64-exec fallback for hosts without sftp-server (OpenWrt/dropbear), written bytes verified

  • RouterOS-aware — MikroTik hosts flagged with "shell": "routeros"

  • Server instructions — docs/INSTRUCTIONS.md is served to the MCP client automatically

  • Agent Skills — a ready SKILL.md for skill-aware clients (copy to the client's skills/ directory)

  • Safety rails — rejects literal undefined/null file paths, propagates remote exit codes (incl. win32 unsigned 32-bit fixup)

  • Local-first config — real addresses live in a gitignored hosts.local.json; the repository ships an anonymized template

Tools

Tool

Purpose

ssh_list_hosts

List configured hosts and metadata

ssh_exec

Run a remote command

ssh_read_file

Read a remote file (cat)

ssh_write_file

Write a remote file (SFTP on direct hosts, base64-exec on ssh3; ≤48 KB)

Installation

git clone https://github.com/nagual2/ssh-hosts-mcp.git
cd ssh-hosts-mcp
npm install
npm test

MCP configuration

Generic mcpServers entry (Claude Desktop, Cursor, ZCode, …):

{
  "mcpServers": {
    "ssh": {
      "command": "node",
      "args": ["/path/to/ssh-hosts-mcp/index.mjs"],
      "env": {
        "SSH_HOSTS_CONFIG": "/path/to/hosts.local.json"
      }
    }
  }
}

Config precedence: SSH_HOSTS_CONFIG env override → hosts.local.json (real addresses, gitignored) → hosts.json (anonymized template, safe to publish).

Host configuration

{
  "version": 1,
  "hosts": {
    "my-host": {
      "label": "Linux box",
      "transport": "direct",
      "host": "192.0.2.10",
      "user": "root",
      "port": 22,
      "privateKeyPath": "~/.ssh/id_ed25519"
    },
    "my-ssh3-host": {
      "label": "Same box over ssh3",
      "transport": "ssh3",
      "host": "server.example.lan",
      "port": 443,
      "urlPath": "/ssh3-term",
      "user": "user",
      "privateKeyPath": "~/.ssh/id_ed25519",
      "clientPath": "ssh3-client"
    },
    "my-proxy-host": {
      "label": "Reachable only through a proxy",
      "transport": "wsl",
      "wslHost": "alias-from-ssh-config",
      "user": "coder"
    }
  }
}

Transports in detail

  • direct — Node ssh2 from the MCP host machine; ~ in privateKeyPath is expanded to the home directory.

  • ssh3 — spawns the ssh3 client (clientPath, default ssh3-client on PATH); all flags go before the positional user@host:port/urlPath target. The server certificate must be pinned in ~/.ssh3/known_hosts (TOFU) or "insecure": true set (dev only). Requires ssh3 server ≥ 0.1.8 — use the nagual2/ssh3 fork, which adds the -privkey flag.

  • wsl — runs wsl bash -lc 'ssh -o BatchMode=yes <wslHost> <command>'; only ssh_exec is supported (use cat / tar|ssh for files).

Full field reference: hosts.json and docs/INSTRUCTIONS.md.

Design notes

  • 48 KB base64-exec limit — remote writes are embedded as echo <base64> | base64 -d > file; an ARG_MAX-safe ceiling, with the written size verified via wc -c.

  • Exit codes — Go clients surface unsigned 32-bit codes on win32; normalizeExitCode maps them back to signed values.

  • filePath, not path — the write schema uses filePath; a literal "undefined"/"null" path is rejected before touching the remote host.

Testing

npm test   # node --test test/lib.test.mjs — pure helpers, no network

License

MIT © 2026 nagual2

Available Tools

4 tools
ssh_execB

Execute a command on a configured host by host_id

ParametersJSON Schema
NameRequiredDescriptionDefault
commandYesRemote command (bash on Linux; RouterOS on MikroTik hosts)
host_idYesHost id from ssh_list_hosts (e.g. prod-openwrt, dslab-ml)
timeoutMsNoTimeout in milliseconds (default 120000)

TDQS

B3.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full disclosure burden. It only states that a command is executed; it does not warn that command execution can have side effects, or clarify output behavior, exit codes, stderr handling, or privileges on the remote host.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence with no filler. It states the action and the key routing parameter immediately.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema and no annotations, yet the description does not explain what the tool returns, whether it streams output, how errors/timeouts surface, or that it is capable of mutating remote state. An agent invoking ssh_exec would lack critical execution-context details.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with useful parameter descriptions: command specifies bash on Linux vs RouterOS on MikroTik, host_id references ssh_list_hosts, and timeoutMs has a default. The tool description adds little beyond mentioning host_id, but the schema already handles parameter semantics adequately.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Execute') and a specific resource ('a command on a configured host by host_id'). This clearly distinguishes it from siblings like ssh_list_hosts, ssh_read_file, and ssh_write_file, which handle listing and file operations rather than command execution.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the obvious use case: run a command on a host instead of reading/writing files or listing hosts. However, it gives no explicit guidance on when to prefer this tool over alternatives, nor any exclusions or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ssh_list_hostsA

List configured SSH hosts (use host_id with ssh_exec)

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the operation is a list (read-only), which is clear, but it does not mention any side effects, permissions, or output format. For a simple listing operation this is likely safe, but the lack of detail about what is returned (e.g., host names, IDs, connection details) leaves some ambiguity. The description is not misleading, but it is minimal.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that states the purpose and provides a usage hint. There is no fluff or redundant information. It is front-loaded with the main action and follows with the relevant cross-tool reference. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple list tool with no parameters, no output schema, and no annotations, the description is fairly complete. It tells the agent what the tool does and how the result connects to a sibling tool. However, it does not specify the exact format of the returned hosts (e.g., whether it returns host names, IDs, or full connection strings), which could be a minor gap. Given the simplicity and the cross-reference to ssh_exec, it is adequate for a basic tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, and schema description coverage is 100% (trivially, since there are none). Per the rules, the baseline for 0 params is 4. The description does not need to add parameter details because there are none. It adds a hint about the output being used as host_id, which indirectly explains the semantic meaning of what is returned, but no parameter explanation is needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists configured SSH hosts, using a specific verb ('List') and a specific resource ('configured SSH hosts'). It also hints at a follow-up action ('use host_id with ssh_exec'), which distinguishes it from the sibling tools (ssh_exec, ssh_read_file, ssh_write_file) that operate on files or execute commands. The purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly mentions 'use host_id with ssh_exec', which tells the agent that the output of this tool is intended as input for ssh_exec. This implies when to use this tool (to discover available hosts before executing commands). It does not explicitly state when not to use it or alternatives, but for a simple listing tool this is adequate guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ssh_read_fileB

Read a remote file (direct hosts only; uses cat)

ParametersJSON Schema
NameRequiredDescriptionDefault
host_idYes
filePathYes
timeoutMsNo

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden for behavioral disclosure. It only reveals that the tool 'uses cat', which hints at output behavior but does not state whether SSH credentials are required, what happens on missing files, error behavior, or that the operation is read-only.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very short, front-loaded with the core action, and contains no filler. Every element ('Read a remote file', 'direct hosts only', 'uses cat') adds value and fits in a single compact sentence.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is a simple 3-parameter read operation with no output schema and no annotations. The description is minimally viable: 'cat' implies the file content is returned, and host/file parameters are reasonably self-explanatory. However, it does not explain return format, failure behavior, timeout handling, or permissions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for parameter meaning. It marginally helps by indicating host_id refers to a direct host and filePath is the remote file, but timeoutMs is completely undocumented, and no parameter-specific details such as formats or constraints are provided.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Read a remote file'. It adds a useful qualifier ('direct hosts only') and an implementation hint ('uses cat'), making the tool's function clear. However, it does not explicitly differentiate itself from siblings like ssh_exec, which could also read files by executing commands.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'direct hosts only' provides an implicit scope limitation and some usage context, suggesting it is not for indirect/proxied hosts. Yet the description offers no explicit when-to-use versus alternatives guidance, such as 'use ssh_write_file to write' or 'use ssh_exec for arbitrary commands'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

ssh_write_fileA

Write remote file via SFTP (base64-exec fallback). Param is filePath (not path). direct hosts only.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNorewrite
contentYesFile content (UTF-8)
host_idYes
filePathYesAbsolute remote path (required name: filePath, not path)
timeoutMsNo

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description bears full responsibility. It discloses a fallback mechanism (base64-exec) and a host restriction, which adds value beyond the obvious write operation. However, it does not explicitly state overwrite behavior, permissions, or failure handling – significant gaps for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single, tightly packed sentence conveys the method, fallback, parameter trap, and a constraint. Every element earns its place; the most important info is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with five parameters, no annotations, and no output schema, this description is far too thin. It lacks usage guidance, behavioral safety disclosures, and parameter semantics beyond a single naming caveat. An agent would need to rely heavily on the incomplete schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 40%, yet the description only clarifies the filePath naming. It provides no elaboration on mode, timeoutMs, or host_id, leaving those parameters ambiguous. The description does not compensate for the low schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (write remote file), the method (SFTP), and a naming gotcha (filePath). This distinguishes it from siblings like ssh_read_file and ssh_exec, and even pre-empts a common parameter mistake.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It establishes the purpose (writing files) implicitly, which indicates when to use it, but does not explicitly contrast with alternatives. The 'direct hosts only' constraint adds context but does not mention exclusions or alternative tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 4 tool updatesv1.1.0
    • First observedssh_exec
    • First observedssh_list_hosts
    • First observedssh_read_file
    • First observedssh_write_file

TDQS

A3.8/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clear, unique purpose: listing hosts, executing commands, reading files, and writing files. There is no overlap or ambiguity between the tools, and the descriptions clarify any caveats like 'direct hosts only'.

Naming Consistency5/5

All tools follow a consistent 'ssh_' prefix followed by a verb_noun pattern: list_hosts, exec, read_file, write_file. The naming is predictable and uniform, making it easy for an agent to infer functionality.

Tool Count5/5

Four tools is a well-scoped number for an SSH host management server. It covers the essential operations without unnecessary bloat, and each tool clearly earns its place.

Completeness4/5

The tool set covers the core lifecycle of SSH host interaction: listing, executing, reading, and writing. While operations like deleting files or managing host configurations are missing, the common workflows are fully supported, leaving only minor gaps.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables LLMs to interact with remote servers via SSH, supporting command execution, file upload/download, and directory listing.
    7
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI assistants to securely execute commands, transfer files, and manage port forwarding on remote servers via SSH.
    132 npm
    37
    Apache 2.0