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
- On startup,
tool-config-manager.jsloads~/.ssh-manager/tools-config.json. - The config specifies a mode (
all,minimal, orcustom) and per-group enabled/disabled flags. - In
index.js, every tool is registered throughregisterToolConditional(), which checksisToolEnabled(toolName)before callingserver.registerTool(). - 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():
- Windows targets are detected by the
platformconfig field. Commands are UTF-16LE base64-encoded and passed to PowerShell via-EncodedCommand, avoiding quoting issues. - Linux/macOS targets wrap commands in the system
timeoututility for reliable kill semantics on long-running operations. - 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 |