Skip to content
Conduit

mcp server

mcp-ssh-reloaded

MCP server for persistent SSH sessions but refactored version.

AmritaBot/mcp-ssh-reloadedcommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "mcp-ssh-reloaded": {
      "command": "uvx",
      "args": [
        "mcp-ssh-reloaded"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 27, 2026

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/config for 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:

  1. Replace the package name - mcp-ssh-session → mcp-ssh-reloaded
  2. Environment variables fully inherited - all OVRD_* and MCP_SSH_* env vars work the same
  3. That's it - reconnect and you're done
{
  "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:

VariableDescription
OVRD_{alias}_HOSTReal hostname or IP
OVRD_{alias}_PORTSSH port
OVRD_{alias}_USERSSH username
OVRD_{alias}_PASSSSH password
OVRD_{alias}_KEYPath to SSH private key
OVRD_{alias}_SUDO_PASSSudo password
OVRD_{alias}_ENABLE_PASSEnable 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:

  1. Constructor kwargs (highest)
  2. MCP_SSH_* environment variables
  3. Defaults (lowest)
Env VariableDefaultDescription
MCP_SSH_DEFAULT_TIMEOUT30Command timeout (seconds)
MCP_SSH_MAX_TIMEOUT300Hard cap on timeout
MCP_SSH_CONNECT_TIMEOUT30SSH connect timeout
MCP_SSH_MAX_WORKERS10Thread pool size
MCP_SSH_MAX_FILE_BYTES2097152Max file read/write (2 MB)
MCP_SSH_MAX_OUTPUT_BYTES10485760Max command output (10 MB)
MCP_SSH_INTERACTIVE_MODEtrueEnable PTY terminal emulation
MCP_SSH_PTY_AWARE_VALIDATIONfalseRelax validation for PTY inspection
MCP_SSH_MIKROTIK_AUTO_PAGINGtrueAuto-handle MikroTik pagers
MCP_SSH_TERMINAL_WIDTH100PTY columns
MCP_SSH_TERMINAL_HEIGHT24PTY rows
MCP_SSH_LOG_DIR/tmp/mcp_ssh_session_logsLog directory
MCP_SSH_BACKGROUND_MONITOR_MAX_TIMEOUT300Background monitor max timeout
MCP_SSH_NORMAL_IDLE_TIMEOUT2Normal idle timeout (seconds)
MCP_SSH_PACKAGE_MANAGER_IDLE_TIMEOUT10Package manager idle timeout
MCP_SSH_ASYNC_DEFAULT_TIMEOUT30Async 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 /tmp stays in /tmp for the next command
  • Env vars persist: export FOO=bar is 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

DocTopic
API-DOCS.mdFull API reference - types, SSHService methods, MCP tools, error model
docs/AGENT_GUIDE.mdAgent prompt - patterns for correct tool usage, async handling
docs/ASYNC_COMMANDS.mdSmart execution & async command lifecycle
docs/INTERACTIVE_MODE.mdTerminal emulation, screen snapshots, key sending
docs/SAFETY_PROTECTIONS.mdLimits, timeouts, session recovery, error handling
docs/DOCKER.mdRunning via Docker
docs/ARCHITECTURE.mdModule map, execution pipeline, known gaps

License

MIT - see LICENSE.

Fork

Fork of devnullvoid/mcp-ssh-session with significant refactoring by AmritaConstant.

↑↓ navigate openesc close