Unified MCP server for persistent SSH work across Linux hosts, network devices, and offensive pentest environments.
ssh_session: Persistent SSH connections (connect/disconnect/list), jump hosts (bastions), zlib compression, proactive keepalivessh_server: Credential-safe server inventory management (save/remove/list) via Windows Credential Managerssh_exec: Unified shell execution (standard sync, sudo with automated prompt detection & secret masking, background job dispatching)ssh_job: Background job supervision (status, realtime log streaming tail, kill)ssh_sftp: File & recursive directory transfers (upload, download, upload_dir, download_dir, list) with configurable timeoutsssh_network: Network automation for Cisco, MikroTik, FortiGate, Juniper (interactive exec, config push, backup, safe dry-run restore, diff)ssh_tunnel: TCP port forwarding and dynamic SOCKS5 proxy server
ssh_server(
action='save',
alias='vps',
host='192.168.10.1',
username='admin',
password='your-password',
auto_connect=True # Auto-connect on startup
)ssh_session(action='connect', alias='vps') # Loads credentials automaticallyssh_exec(alias='vps', command='df -h')
ssh_exec(alias='vps', command='systemctl restart nginx', sudo_password='...')
ssh_exec(alias='vps', command='nmap -sS -p- 10.0.0.0/24', background=True, label='full-scan')# Recursive download without manual zip:
ssh_sftp(action='download_dir', alias='vps', remote_path='/home/user/app', local_path='D:\\home\\app')
# Dynamic SOCKS5 proxy on port 1080:
ssh_tunnel(action='start_socks', alias='vps', label='socks-vps', local_port=1080)ssh_connectnow accepts just an alias to load credentials automatically- No need to provide host, username, password every time
- Fixes Claude Code integration issues
- Timeout handling: Now raises
TimeoutErrorinstead of silently returning incomplete buffers - Rate limiting: 100ms minimum between network commands to prevent device saturation
- Magic numbers eliminated: Named constants for shell dimensions
- New
CLAUDE_CODE_USAGE.mdwith complete usage examples - New
CLAUDE_CODE_SETUP.mdfor configuration - New
CHANGELOG.mdwith full version history
servers.json only keeps non-sensitive metadata. Secrets live in Windows Credential Manager under targets like:
ssh-mcp:<alias>:passwordssh-mcp:<alias>:key_passphrase
strict: default, rejects unknown host keys (production)auto_add: accepts unknown host keys (lab only)
Supported device types:
cisco- Cisco IOS/IOS-XEcisco_xr- Cisco IOS-XRmikrotik- MikroTik RouterOSfortigate- FortiGate FortiOSjuniper- Juniper JunOSgeneric- Linux/Unix (default)
# Save once
ssh_save_server(
alias='cisco-core',
host='10.0.0.1',
username='admin',
password='...',
device_type='cisco',
auto_connect=True
)
# Connect
ssh_connect(alias='cisco-core')
# Backup config
ssh_backup_config(alias='cisco-core', device_type='cisco')
# Saved to: backups/cisco-core/cisco-core_2026-05-31_14-30-00.txt
# Execute commands
ssh_exec_network(
alias='cisco-core',
commands=['show version', 'show ip interface brief'],
device_type='cisco'
)
# Push config
ssh_push_config(
alias='cisco-core',
config='''
interface GigabitEthernet0/1
description Uplink to Core
ip address 10.0.1.1 255.255.255.0
no shutdown
''',
device_type='cisco',
save_config=True
)See CLAUDE_CODE_USAGE.md for detailed examples.
TL;DR:
1. Save: ssh_server(action='save', alias='vps', host='192.168.10.1', username='admin', password='...')
2. Connect: ssh_session(action='connect', alias='vps')
3. Execute: ssh_exec(alias='vps', command='df -h')connect- Establish or reuse persistent SSH connection (smart mode: just provide alias)disconnect- Close connection, tunnels, SOCKS proxies, and jobslist- List all active connections with uptime, tunnels, proxies, and background jobs
save- Save/update server inservers.json(secrets securely kept in Credential Manager)remove- Delete server from inventorylist- List all saved servers
- Standard sync execution (
command) - Sudo execution with prompt detection (
sudo_password) - Background execution (
background=True,label='...')
status- Check background job status or list all jobstail- Stream last N lines of stdout/stderrkill- Terminate running background job
upload/download- Transfer single files with configurable timeoutupload_dir/download_dir- Recursive directory transfers without manual ziplist- List remote files and directories
exec- Run sequence of interactive commands on network devicespush- Push multiline configuration block (with auto-backup)backup- Backup running config to timestamped local filerestore- Restore config (dry-run by default, confirm=True to apply)diff- Compare two configs or current config vs backup
start/stop- Persistent local port forwarding (TCP tunnels)start_socks/stop_socks- Dynamic SOCKS5 proxy serverlist- List active tunnels and proxies
Add directly to your MCP client configuration (claude_desktop_config.json, opencode.json, etc.):
{
"mcpServers": {
"ssh-mcp": {
"command": "uvx",
"args": ["--from", "git+https://github.com/Steph-ux/ssh-mcp.git", "ssh-mcp"]
}
}
}pip install git+https://github.com/Steph-ux/ssh-mcp.gitThen in your MCP config:
{
"mcpServers": {
"ssh-mcp": {
"command": "ssh-mcp"
}
}
}git clone https://github.com/Steph-ux/ssh-mcp.git
cd ssh-mcp
pip install -e .- SSHPool: Thread-safe connection pool with automatic reconnection and jump host chaining
- SSHConnection: Per-connection state with RLock for thread safety
- ThreadPoolExecutor: Async/await wrapper for Paramiko
- Rate limiter: 100ms minimum between commands to prevent device saturation
- SecretStore: Windows Credential Manager on Windows, keyring on Linux/macOS
pytest tests- CLAUDE_CODE_USAGE.md - Usage examples for AI agents
- CLAUDE_CODE_SETUP.md - Complete client configuration guide
- CHANGELOG.md - Version history and migration guide
MIT