An MCP (Model Context Protocol) server that enables AI agents to establish and manage persistent SSH sessions.
- Smart Command Execution - auto-transitions to async mode if timeout is reached
- Persistent Sessions - SSH connections reused across commands
- Async Commands - non-blocking execution for long-running tasks
- SSH Config Support - reads
~/.ssh/configfor aliases, ports, keys - Multi-host - manage connections to multiple hosts simultaneously
- Network Devices - enable mode handling for Cisco, Juniper, MikroTik, etc.
- Sudo Support - automatic password handling for Unix/Linux hosts
- File Operations - read/write remote files via SFTP (sudo fallback)
- Command Interruption - send Ctrl+C to stop running commands
- Thread-safe - safe for concurrent operations
- Async Native - built-in support for asynio.
This project is a drop-in replacement for the original devnullvoid/mcp-ssh-session. To migrate:
- Replace the package name -
mcp-ssh-session→mcp-ssh-reloaded - Environment variables fully inherited - all
OVRD_*andMCP_SSH_*env vars work the same - That's it - reconnect and you're done
| Before (old) | After (new) |
|---|---|
{
"mcpServers": {
"ssh-session": {
"command": "uvx",
"args": ["mcp-ssh-session", "serve", "mcp"]
}
}
} |
{
"mcpServers": {
"ssh-session": {
"command": "uvx",
"args": ["mcp-ssh-reloaded", "serve", "mcp"]
}
}
} |
uvx mcp-ssh-reloadeduv venv
source .venv/bin/activate
uv pip install -e .# MCP server (default)
mcp-ssh-reloaded serve mcp
# Direct execution (no MCP)
mcp-ssh-reloaded exec myserver "uname -a" -u admin
# List / close sessions
mcp-ssh-reloaded list
mcp-ssh-reloaded close-allClaude Code / Desktop (~/.claude.json or claude_desktop_config.json):
{
"mcpServers": {
"ssh-session": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-ssh-reloaded", "serve", "mcp"],
"env": {}
}
}
}// SSH config alias
{ "host": "myserver", "command": "uptime" }
// Explicit params
{ "host": "example.com", "username": "user", "command": "ls -la", "port": 2222 }
// Network device (Cisco enable mode)
{ "host": "router", "username": "admin", "enable_password": "secret", "command": "show run" }
// Unix with sudo
{ "host": "server", "username": "ops", "sudo_password": "secret", "command": "systemctl restart nginx" }Full API reference: API-DOCS.md - all types, all methods, all MCP tools, error handling, server config tunables.
~/.ssh/config is read automatically:
Host myserver
HostName example.com
User myuser
Port 2222
IdentityFile ~/.ssh/id_rsa
Then use "host": "myserver" - the rest is resolved for you.
For production environments, store real credentials in env vars so AI agents only see aliases:
| Variable | Description |
|---|---|
OVRD_{alias}_HOST |
Real hostname or IP |
OVRD_{alias}_PORT |
SSH port |
OVRD_{alias}_USER |
SSH username |
OVRD_{alias}_PASS |
SSH password |
OVRD_{alias}_KEY |
Path to SSH private key |
OVRD_{alias}_SUDO_PASS |
Sudo password |
OVRD_{alias}_ENABLE_PASS |
Enable password |
Example config:
{
"mcpServers": {
"ssh-session": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-ssh-reloaded", "serve", "mcp"],
"env": {
"OVRD_prod_db_HOST": "192.168.1.100",
"OVRD_prod_db_USER": "admin",
"OVRD_prod_db_PASS": "secret123",
"OVRD_prod_db_SUDO_PASS": "sudopass"
}
}
}
}The agent uses "host": "prod_db" - never sees real IPs or passwords.
All tunables live in ServerConfig. Values resolve in priority:
- Constructor kwargs (highest)
MCP_SSH_*environment variables- Defaults (lowest)
| Env Variable | Default | Description |
|---|---|---|
MCP_SSH_DEFAULT_TIMEOUT |
30 |
Command timeout (seconds) |
MCP_SSH_MAX_TIMEOUT |
300 |
Hard cap on timeout |
MCP_SSH_CONNECT_TIMEOUT |
30 |
SSH connect timeout |
MCP_SSH_MAX_WORKERS |
10 |
Thread pool size |
MCP_SSH_MAX_FILE_BYTES |
2097152 |
Max file read/write (2 MB) |
MCP_SSH_MAX_OUTPUT_BYTES |
10485760 |
Max command output (10 MB) |
MCP_SSH_INTERACTIVE_MODE |
true |
Enable PTY terminal emulation |
MCP_SSH_PTY_AWARE_VALIDATION |
false |
Relax validation for PTY inspection |
MCP_SSH_MIKROTIK_AUTO_PAGING |
true |
Auto-handle MikroTik pagers |
MCP_SSH_TERMINAL_WIDTH |
100 |
PTY columns |
MCP_SSH_TERMINAL_HEIGHT |
24 |
PTY rows |
MCP_SSH_LOG_DIR |
/tmp/mcp_ssh_session_logs |
Log directory |
MCP_SSH_BACKGROUND_MONITOR_MAX_TIMEOUT |
300 |
Background monitor max timeout |
MCP_SSH_NORMAL_IDLE_TIMEOUT |
2 |
Normal idle timeout (seconds) |
MCP_SSH_PACKAGE_MANAGER_IDLE_TIMEOUT |
10 |
Package manager idle timeout |
MCP_SSH_ASYNC_DEFAULT_TIMEOUT |
30 |
Async command default timeout |
mcp-ssh serve mcp --default-timeout 60 --max-workers 20
mcp-ssh serve http --port 8080 --interactive-mode false
mcp-ssh serve sse --port 9000 --connect-timeout 15from mcp_ssh_reloaded import SSHService, ServerConfig
svc = SSHService(config=ServerConfig(default_timeout=60, max_timeout=600))Commands run inside persistent interactive shells:
- Directory persists:
cd /tmpstays in/tmpfor the next command - Env vars persist:
export FOO=baris visible across commands - Prompt detection: completion detected via captured prompt or idle timeout (2 s)
- Session recovery: stuck shells auto-reset after repeated prompt-detection failures
| Doc | Topic |
|---|---|
| API-DOCS.md | Full API reference - types, SSHService methods, MCP tools, error model |
| docs/AGENT_GUIDE.md | Agent prompt - patterns for correct tool usage, async handling |
| docs/ASYNC_COMMANDS.md | Smart execution & async command lifecycle |
| docs/INTERACTIVE_MODE.md | Terminal emulation, screen snapshots, key sending |
| docs/SAFETY_PROTECTIONS.md | Limits, timeouts, session recovery, error handling |
| docs/DOCKER.md | Running via Docker |
MIT - see LICENSE.
Fork of devnullvoid/mcp-ssh-session with significant refactoring by AmritaConstant.