Files
wiki/ssh-mcp/ARCHITECTURE.md
2026-05-09 19:09:43 -05:00

8.6 KiB

Home

Architecture

ssh-mcp is a plain JavaScript (ESM) MCP server built on the @modelcontextprotocol/sdk. It exposes 37 tools organized into 6 groups that operate on remote servers over SSH.


Source Files

The src/ directory contains 19 modules:

File Responsibility
index.js MCP server entry point. Registers all 37 tools, manages the connection pool, and wires modules together.
ssh-manager.js Thin wrapper around the ssh2 library. Handles connect, exec, file transfer, and stream operations.
config.js Constants for output limits, timeouts, and helper functions (truncateOutput, formatJSONResponse).
config-loader.js Loads server definitions from .env, TOML, and environment variables with a priority chain.
backup-manager.js Builds shell commands for MySQL, PostgreSQL, MongoDB, and file backups. Manages metadata and retention.
database-manager.js Builds shell commands for database dump, import, list, and read-only query operations. Includes query safety validation.
health-monitor.js Builds shell commands for CPU, memory, disk, network, and load average checks. Parses results and determines overall health status.
deploy-helper.js Deployment strategy builder. Detects permission issues and generates sudo scripts when needed.
session-manager.js Persistent SSH session lifecycle (create, send, list, close). Sessions maintain working directory state across commands.
tunnel-manager.js SSH tunnel creation and management (local, remote, dynamic/SOCKS). Tracks active tunnels and connection statistics.
server-aliases.js Maps short alias names to full server names. Persisted to ~/.ssh-manager/aliases.json.
command-aliases.js Maps short command strings to full commands. Supports suggestion of aliases for frequently typed commands.
server-groups.js Server group CRUD and multi-server command execution with parallel, sequential, and rolling strategies.
hooks-system.js Automation hooks that run before/after operations (e.g., pre-backup, post-bench-update).
profile-loader.js Profile management for switching between project-specific tool/configuration sets.
ssh-key-manager.js Host key verification, fingerprint comparison, and known_hosts management.
tool-registry.js Centralized registry of all 37 tools and their group assignments. Provides validation and statistics.
tool-config-manager.js Reads ~/.ssh-manager/tools-config.json to determine which tool groups are enabled.
logger.js Structured logging with levels, command history tracking, and transfer logging.

Component Diagram

Claude Code / MCP Client
        |
        | stdio (JSON-RPC)
        v
  +-----------+
  | index.js  |  <-- MCP Server (tool registration + dispatch)
  +-----------+
        |
        +---> ssh-manager.js ---> ssh2 library ---> Remote Server
        |
        +---> config-loader.js ----> .env / TOML / env vars
        |
        +---> tool-config-manager.js ---> tools-config.json
        |
        +---> session-manager.js (persistent sessions)
        +---> tunnel-manager.js  (port forwarding)
        +---> backup-manager.js  (backup commands)
        +---> database-manager.js (db commands)
        +---> health-monitor.js  (monitoring commands)
        +---> deploy-helper.js   (deployment logic)
        +---> server-groups.js   (multi-server ops)
        +---> hooks-system.js    (pre/post hooks)
        +---> server-aliases.js  (alias resolution)
        +---> command-aliases.js (command shortcuts)
        +---> profile-loader.js  (profiles)
        +---> ssh-key-manager.js (host key mgmt)
        +---> logger.js          (logging + history)

Tool Activation System

Not every user needs all 37 tools. Unused tools consume context tokens in Claude Code without providing value. The tool activation system solves this.

How It Works

  1. On startup, tool-config-manager.js loads ~/.ssh-manager/tools-config.json.
  2. The config specifies a mode (all, minimal, or custom) and per-group enabled/disabled flags.
  3. In index.js, every tool is registered through registerToolConditional(), which checks isToolEnabled(toolName) before calling server.registerTool().
  4. Disabled tools are silently skipped -- the MCP client never sees them.

Configuration Modes

Mode Tools Context Usage
all 37 ~43.5k tokens
minimal 5 (core only) ~3.5k tokens
custom Variable Variable

The CLI (ssh-manager tools configure) provides an interactive wizard for selecting groups.

Tool Groups

Group Count Tools
core 5 ssh_execute, ssh_upload, ssh_download, ssh_sync, ssh_list_servers
sessions 4 ssh_session_start, ssh_session_send, ssh_session_list, ssh_session_close
monitoring 6 ssh_health_check, ssh_service_status, ssh_process_manager, ssh_monitor, ssh_tail, ssh_alert_setup
backup 4 ssh_backup_create, ssh_backup_list, ssh_backup_restore, ssh_backup_schedule
database 4 ssh_db_dump, ssh_db_import, ssh_db_list, ssh_db_query
advanced 14 ssh_deploy, ssh_execute_sudo, ssh_alias, ssh_command_alias, ssh_hooks, ssh_profile, ssh_connection_status, ssh_tunnel_create, ssh_tunnel_list, ssh_tunnel_close, ssh_key_manage, ssh_execute_group, ssh_group_manage, ssh_history

Connection Pooling

ssh-mcp maintains a pool of SSH connections to avoid the overhead of re-authenticating on every tool call.

Pool Mechanics

Aspect Behavior
Storage Active connections are held in a Map<serverName, SSHClient>.
Reuse getConnection(serverName) returns an existing connection if it is still valid (tested via ping), or creates a new one.
Timeout Connections idle for more than 30 minutes are closed by a periodic cleanup sweep.
Keepalive Every 5 minutes, a ping is sent to each connection. If the ping fails, the connection is removed from the pool and will be re-established on the next call.
Cleanup on error If a command times out, the connection is disposed of immediately so subsequent calls get a fresh channel.

Proxy Jump Support

When PROXYJUMP is set, the server first connects to the jump host, then tunnels through it to reach the target. Jump host connections are also pooled. Dependency tracking ensures that closing a jump host also closes all connections that depend on it.

PROXYCOMMAND provides an alternative mechanism. The configured command (e.g., ncat, ssh -W) is spawned as a local child process, and its stdin/stdout are used as the transport socket.


Command Execution

Commands are executed via execCommandWithTimeout():

  1. Windows targets are detected by the platform config field. Commands are UTF-16LE base64-encoded and passed to PowerShell via -EncodedCommand, avoiding quoting issues.
  2. Linux/macOS targets wrap commands in the system timeout utility for reliable kill semantics on long-running operations.
  3. Output is truncated to configurable limits to prevent Claude Code crashes from extremely large command output.

Design Decisions

Plain JavaScript (no TypeScript)

The project uses plain JavaScript with ES modules. This keeps the build step to zero -- node src/index.js is all that is needed. JSDoc comments provide type hints where useful.

Command Building vs. Direct Execution

Manager modules (backup-manager.js, database-manager.js, health-monitor.js) build shell command strings rather than using database client libraries. This design means:

  • No database drivers need to be installed on the MCP host.
  • The remote server only needs standard tools (mysqldump, pg_dump, mongodump, tar, etc.).
  • Commands run in the same security context as the SSH user.

Read-Only Query Safety

ssh_db_query validates that the query is a SELECT statement before execution. The isSafeQuery() function in database-manager.js rejects INSERT, UPDATE, DELETE, DROP, and other mutating statements.

Hooks System

Hooks run at defined lifecycle points (e.g., pre-backup, post-bench-update). They are shell scripts stored in ~/.ssh-manager/hooks/ and are opt-in -- disabled by default until explicitly enabled via ssh_hooks.


Repo: ssh-mcp · MokoStandards

Revision Date Author Description
1.0 2026-05-09 Moko Consulting Initial version