MCP SSH Session - Reloaded
An MCP (Model Context Protocol) server that enables AI agents to establish and manage persistent SSH sessions.
Features
- 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.
Quick Start
📦 Migrating from mcp-ssh-session
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"]
}
}
}
|
Install
uvx mcp-ssh-reloaded
Development
uv venv
source .venv/bin/activate
uv pip install -e .
CLI
# 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-all
MCP Client Config
Claude Code / Desktop (~/.claude.json or claude_desktop_config.json):
{
"mcpServers": {
"ssh-session": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-ssh-reloaded", "serve", "mcp"],
"env": {}
}
}
}
Quick Examples
// 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
~/.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.
Credential Hiding (OVRD_*)
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.
Configuration
Timeouts & server settings
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 |
Via CLI (serve modes)
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 15
Via API
from mcp_ssh_reloaded import SSHService, ServerConfig
svc = SSHService(config=ServerConfig(default_timeout=60, max_timeout=600))
How It Works
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
Testing
# unit tests only - the integration suites self-skip without SSH_TEST_HOST
uv run pytest tests/
# everything, against a real host
SSH_TEST_HOST=myserver SSH_TEST_USER=admin SSH_TEST_PASSWORD=secret uv run pytest tests/
# or spin up a throw-away sshd container (the same image CI uses)
docker build -t mcp-ssh-test .github/sshd-test
docker run -d --name sshd-test -p 127.0.0.1:2222:22 mcp-ssh-test
SSH_TEST_HOST=127.0.0.1 SSH_TEST_PORT=2222 SSH_TEST_USER=root \
SSH_TEST_PASSWORD=rootpass SSH_TEST_SUDO_PASSWORD=rootpass \
uv run pytest tests/ --ignore=tests/test_mikrotik.py --ignore=tests/test_network_devices.py
test_mikrotik.py and test_network_devices.py need physical network gear.
.github/workflows/integration.yml runs the container-backed suite on every
pull request; .github/workflows/ci.yml covers unit tests, pyright, ruff and
packaging across Python 3.10-3.13.
Architecture
Types are declared in a single module (models.py); api_types.py and
datastructures.py are backward-compatible re-export shims. All execution goes
through one stack, CommandExecutor, which returns a structured
ExecutionResult. See docs/ARCHITECTURE.md for the
module map, the execution pipeline and the known gaps.
Docs
| 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 |
| docs/ARCHITECTURE.md | Module map, execution pipeline, known gaps |
License
MIT - see LICENSE.
Fork
Fork of devnullvoid/mcp-ssh-session with significant refactoring by AmritaConstant.
Metadata
Release files for mcp-ssh-reloaded 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcp_ssh_reloaded-0.3.0.tar.gz | 92.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_ssh_reloaded-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 169.2 kB
Release files / mcp_ssh_reloaded-0.3.0.tar.gz
| Download URL | mcp_ssh_reloaded-0.3.0.tar.gz |
|---|---|
| Size | 92.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7228505a00cd74e682b436614b8c95ac6fedeb8e9511f2e978e7b5dfa4a05248
|
|
BLAKE2b-256 checksum How to use checksums |
95c05b829dbb56b91a6fff5f37c73e1b921af7c5c22e2ab214daec4ae996ec4e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.22 {"installer":{"name":"uv","version":"0.12.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / mcp_ssh_reloaded-0.3.0-py3-none-any.whl
| Download URL | mcp_ssh_reloaded-0.3.0-py3-none-any.whl |
|---|---|
| Size | 77.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fc5007663b30841d25a374eb2411b109fcb96c5f2208bb37a906aee57e9bb1d8
|
|
BLAKE2b-256 checksum How to use checksums |
eb5ad13811745665e0b250688c7025cfcc3d74622adf4dc3ded28f8b890dcf37
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.22 {"installer":{"name":"uv","version":"0.12.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|