← Back to MCP servers

hex-ssh-mcp

Token-efficient SSH MCP server with hash-verified remote file editing and SFTP transfer

$ npm i -g @levnikolaevich/hex-ssh-mcp Copy

Your agent can read and edit local files — but what about the server running in production? hex-ssh-mcp brings the same hash-verified editing to remote machines over SSH, plus binary-safe SFTP upload and download. Every remote read returns FNV-1a hashes, every edit verifies them, and file transfers stage to temp paths before rename.

When it matters

🔍 “Check a config on the remote server”

Your agent needs to read the nginx config on production — 200 lines, but only the upstream block matters. With raw ssh cat /etc/nginx/nginx.conf, the entire file floods your context. With ssh-read-lines, the agent requests lines 40–80 with hash annotations. Each line carries an FNV-1a tag, ready for immediate editing — no second read needed.

Targeted partial read instead of full dump. Edit-ready hashes included.
“Fix a typo in production config”

The agent read the file 5 minutes ago and wants to fix a typo on line 65. But someone deployed a hotfix since then — lines shifted. With a raw SSH command, the edit silently overwrites the wrong line. hex-ssh’s ssh-edit-block checks the range checksum before applying. Stale? Edit rejected, current checksum returned. The agent re-reads just the changed range and retries.

Zero silent overwrites across SSH boundaries.
📋 “Search remote logs for an error pattern”

The agent greps 10,000 lines of application logs looking for “connection timeout.” Raw output: thousands of duplicate stack traces with different timestamps, UUIDs, and request IDs. ssh-search-code normalizes all dynamic values — UUIDs become <UUID>, timestamps become <TS>, IPs become <IP>. Identical normalized lines collapse with (xN) counts. 10K lines become 40 actionable ones.

Actionable output instead of token-flooding noise.
🔒 “Write to a restricted path”

The agent generates a new config and writes it to the staging server. But a typo in the path targets /etc/ instead of /home/deploy/etc/. With raw SSH, the file lands in the system directory — potentially breaking the server. hex-ssh checks against ALLOWED_DIRS before every write. Path outside the permitted list? Rejected immediately.

Path validation prevents writes outside permitted directories.

8 tools

ToolWhat it does
remote-ssh Execute shell commands. Disabled by default; set REMOTE_SSH_MODE=safe or open to enable
ssh-read-lines Read remote files with hash-annotated lines. Partial reads via startLine/endLine
ssh-edit-block Hash-verified anchor edits. Checksum verification + compact diff
ssh-search-code Search remote files with grep. Deduplicated results with (xN) counts
ssh-write-chunk Write or append to remote files. Rewrite is atomic; append is direct
ssh-upload Upload a local file over SFTP. Binary-safe, overwrite-safe by default, and staged via the strongest available remote finalize path
ssh-download Download a remote file over SFTP. Binary-safe, overwrite-safe by default, and staged to a verified local finalize path
ssh-verify Check if held checksums are still valid. Single-line response

Security

Security is not optional when agents talk to production servers. hex-ssh verifies host fingerprints, restricts host/path reachability, enforces platform-aware absolute canonical paths, times out long commands, and keeps raw remote shell access disabled by default.

Host Key Verification

Connections fail closed unless the host fingerprint matches ALLOWED_HOST_FINGERPRINTS or a matching known_hosts record. Plain, hashed, and marker-prefixed entries are parsed for fingerprint matching.

SHA256:<base64> or KNOWN_HOSTS_PATH

remote-ssh Disabled by Default

Raw shell execution starts in REMOTE_SSH_MODE=disabled. Opt into safe or open explicitly.

REMOTE_SSH_MODE=safe

ALLOWED_HOSTS

Comma-separated permitted hostnames/IPs. When set, connections to unlisted hosts are rejected.

ALLOWED_HOSTS=prod-web,prod-db,10.0.0.5

ALLOWED_DIRS

Comma-separated permitted remote directory prefixes. File operations outside these paths are rejected.

ALLOWED_DIRS=/home/deploy,/var/www

ALLOWED_LOCAL_DIRS

Optional local allowlist for ssh-upload and ssh-download. When set, transfers outside these directories are rejected.

ALLOWED_LOCAL_DIRS=/Users/alice/projects,/tmp/hex-ssh

Absolute Canonical Paths

Remote file operations require absolute paths for their platform. POSIX shell tools stay POSIX-oriented; SFTP transfers also support Windows remote paths via remotePlatform. . and .. segments are resolved before allowlist checks.

/home/deploy/app/config.js or C:\repo\config.js

Write Semantics

rewrite uses temp file + rename. append writes directly and is intentionally non-atomic.

rewrite = atomic; append = direct >>

Exec Timeout

Long-running SSH commands are terminated after 120 seconds instead of hanging the agent indefinitely.

EXEC_TIMEOUT after 120s

Transfer Size Guard

ssh-upload and ssh-download fail fast when the file exceeds MAX_TRANSFER_BYTES (default 128 MiB).

MAX_TRANSFER_BYTES=134217728

Transfer Timeout

SFTP uploads and downloads abort with TRANSFER_TIMEOUT when transfer activity stalls longer than TRANSFER_TIMEOUT_MS.

TRANSFER_TIMEOUT_MS=120000

SSH Key Authentication

Key-only (no passwords) — RSA, ED25519, ECDSA. Resolution order:

privateKeyPath → SSH_PRIVATE_KEY env → ~/.ssh/id_*

Output Normalization

The package currently publishes a normalization diagnostic, not a comparative workflow benchmark. Command output from remote servers is often noisy — timestamps, UUIDs, IPs change on every run. The normalization pipeline reduces this noise automatically:

PatternReplacementExample
UUIDs <UUID> 550e8400-e29b-41d4-... → <UUID>
Timestamps <TS> 2026-03-19 14:30:00 → <TS>
IP addresses <IP> 192.168.1.100:8080 → <IP>
Hex IDs in paths /<ID> /a1b2c3d4e5 → /<ID>
Large numbers <N> 1234567 → <N>

Diagnostic command

npm run benchmark:diagnostic

IDScenarioInputOutputSavings
1Hash annotation overhead3,934 ch4,526 ch-15%
2Normalize: npm install11,219 ch2,953 ch74%
3Normalize: server logs19,715 ch4,470 ch77%
4Dedup: grep results5,799 ch3,199 ch45%
5Smart truncate: large output22,281 ch2,717 ch88%
Normalization diagnostic summary: 72% average reduction (62,948 → 17,865 chars)

Processing pipeline

  1. Normalize — replace dynamic values with placeholders
  2. Deduplicate — collapse identical lines with (xN) counts
  3. Truncate — keep first 40 + last 20 lines, omit middle

This measures normalization, deduplication, and truncation efficiency only. It is not yet a benchmark against built-in remote workflows.