fast-mcp-ssh 0.4.2

Fast MCP SSH server with persistent PTY sessions, SFTP, and AI-first tool surface
# fast-mcp-ssh sample configuration
# Place at ~/.fast-mcp-ssh/hosts.toml (or override with --config)

[defaults]
# Read aliases from ~/.ssh/config in addition to the explicit hosts below.
import_ssh_config = false

# toon | json | text. TOON cuts ~40% of tokens vs JSON for tabular output.
output = "toon"

# Idle session timeout. Connection is closed (and reopened on next call) after this.
session_idle_timeout = "15m"

# Append-only NDJSON record of every tool call. See ~/.fast-mcp-ssh/audit.log.
audit_log = true
# audit_log_path = "~/.fast-mcp-ssh/audit.log"

# Rotate the audit log past this size, keeping audit.log.1 … .N.
# 0 bytes disables rotation; 0 files discards instead of archiving.
audit_max_bytes = 16777216
audit_keep_files = 5

# Sanity caps on output bytes returned per call. Anything over this is truncated with a hint.
truncate_bytes = 32768

# Hard cap on bytes captured during exec/sh/tail before further data is dropped.
# Prevents OOM if a command spews gigabytes. Should be >= truncate_bytes.
max_capture_bytes = 262144

# Per-host concurrent SSH channel limit (matches sshd MaxSessions, default 10).
max_channels_per_host = 8

# Optional: tools accept omitted `host=` and fall back to this alias.
# default_host = "box1"

connect_timeout = "15s"
keepalive = "30s"

# Server fingerprint policy.
#  tofu   — pin on first connect, reject on later mismatch (default, recommended)
#  strict — reject any host that has not been pinned yet
#  off    — accept any server key (legacy 0.1.0 behavior)
# Pinned fingerprints live at ~/.fast-mcp-ssh/known_hosts.toml.
strict_host_key_checking = "tofu"

# How long an approved confirm_patterns command stays approved. The exact same
# command on the same host runs without a second prompt inside the window;
# anything else prompts again. "0s" prompts every single time.
confirm_ttl = "15m"

# Default guard rules apply to all hosts unless a host has its own [host.<name>.guards] block.
[defaults.guards]
use_default_deny = true        # rm -rf /, dd of=/dev/sd*, mkfs, fork bombs, etc.
use_default_confirm = true     # shutdown, reboot, DROP TABLE, systemctl stop, docker rm…
read_only = false              # Set true to refuse heuristically write-ish commands.

# Add your own deny/confirm patterns (regex). They are merged with defaults.
deny = [
    # { name = "no-prod-db", pattern = "(?i)\\bDROP\\s+DATABASE\\s+prod" },
]
confirm = [
    # { name = "git-push-main", pattern = "git\\s+push.*\\bmain\\b" },
]


# === Hosts ===

[host.box1]
addr = "10.0.0.1"
user = "root"
port = 22
auth = "key"
key = "~/.fast-mcp-ssh/keys/box1"
# Optional fallback keys tried after `key`. Useful when migrating ed25519/rsa.
# keys = ["~/.ssh/id_ed25519", "~/.ssh/id_rsa"]

[host.bastion]
addr = "203.0.113.5"
user = "ops"
port = 22
auth = "agent"          # use ssh-agent (Windows OpenSSH agent or *nix SSH_AUTH_SOCK)

# Example of a host reached through a bastion. The connection to "bastion"
# is opened first, then a direct-tcpip channel to private-host:22 is used as
# the transport for the SSH handshake. Chains are followed recursively.
[host.private-host]
addr = "10.10.0.42"
user = "deploy"
auth = "key"
key = "~/.fast-mcp-ssh/keys/deploy"
proxy_jump = "bastion"

[host.legacy-nas]
addr = "10.0.0.50"
user = "nas"
port = 35
auth = "password"       # password is supplied per-call by the AI (cached in memory only)

[host.prod-db]
addr = "db.internal"
user = "deploy"
port = 22
auth = "key"
key = "~/.fast-mcp-ssh/keys/prod-db"

# Per-host guard override: prod-db is read-only by default.
[host.prod-db.guards]
use_default_deny = true
use_default_confirm = true
read_only = true
confirm = [
    { name = "psql-write", pattern = "(?i)\\bINSERT\\s+INTO|\\bUPDATE\\s+\\w+\\s+SET|\\bDELETE\\s+FROM" },
]