Metadata-Version: 2.4
Name: mcp-ssh-session
Version: 0.1.9
Summary: MCP server for managing persistent SSH sessions
Project-URL: Homepage, https://github.com/devnullvoid/mcp-ssh-session
Project-URL: Repository, https://github.com/devnullvoid/mcp-ssh-session
Project-URL: Issues, https://github.com/devnullvoid/mcp-ssh-session/issues
Author: Jon Rogers <devnullvoid>
License: Copyright © 2025 Jon Rogers
        
        Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
License-File: LICENSE
Keywords: cli,fastmcp,mcp,ssh
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Networking
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.10
Requires-Dist: fastmcp
Requires-Dist: paramiko>=3.4.0
Requires-Dist: pyte>=0.8.0
Description-Content-Type: text/markdown

# MCP SSH Session

An MCP (Model Context Protocol) server that enables AI agents to establish and manage persistent SSH sessions.

<a href="https://glama.ai/mcp/servers/@devnullvoid/mcp-ssh-session">
  <img width="380" height="200" src="https://glama.ai/mcp/servers/@devnullvoid/mcp-ssh-session/badge" alt="SSH Session MCP server" />
</a>

## Features

- **Smart Command Execution**: Never hangs the server - automatically transitions to async mode if timeout is reached
- **Persistent Sessions**: SSH connections are reused across multiple command executions
- **Async Command Execution**: Non-blocking execution for long-running commands
- **SSH Config Support**: Automatically reads and uses settings from `~/.ssh/config`
- **Multi-host Support**: Manage connections to multiple hosts simultaneously
- **Automatic Reconnection**: Dead connections are detected and automatically re-established
- **Thread-safe**: Safe for concurrent operations
- **Network Device Support**: Automatic enable mode handling for routers and switches
- **Sudo Support**: Automatic password handling for sudo commands on Unix/Linux hosts
- **File Operations**: Safe helpers to read and write remote files over SFTP
- **Command Interruption**: Send Ctrl+C to interrupt running commands

## Installation

### Using `uvx`

```bash
uvx mcp-ssh-session
```

### Using Claude Code

Add to your `~/.claude.json`:

```json
{
  "mcpServers": {
    "ssh-session": {
      "type": "stdio",
      "command": "uvx",
      "args": ["mcp-ssh-session"],
      "env": {}
    }
  }
}
```

### Using MCP Inspector

```bash
npx @modelcontextprotocol/inspector uvx mcp-ssh-session
```

### Development Installation

```bash
uv venv
source .venv/bin/activate
uv pip install -e .
```

## Usage

### Available Tools

#### `execute_command`
Execute a command on an SSH host using a persistent session.

**Smart Execution**: Starts synchronously and waits for completion. If timeout is reached, automatically transitions to async mode and returns a command ID. Server never hangs!

**Advanced Features**:
- Automatic timeout handling with async transition
- Interactive command support (use `send_input` for prompts)
- Command interruption capability (`interrupt_command_by_id`)
- Session persistence across multiple commands

**Using SSH config alias:**
```json
{
  "host": "myserver",
  "command": "uptime"
}
```

**Using explicit parameters:**
```json
{
  "host": "example.com",
  "username": "user",
  "command": "ls -la",
  "key_filename": "~/.ssh/id_rsa",
  "port": 22
}
```

**Network device with enable mode:**
```json
{
  "host": "router.example.com",
  "username": "admin",
  "password": "ssh_password",
  "enable_password": "enable_password",
  "command": "show running-config"
}
```

**Unix/Linux with sudo:**
```json
{
  "host": "server.example.com",
  "username": "user",
  "sudo_password": "user_password",
  "command": "systemctl restart nginx"
}
```

#### `list_sessions`
List all active SSH sessions.

#### `close_session`
Close a specific SSH session.

```json
{
  "host": "myserver"
}
```

#### `close_all_sessions`
Close all active SSH sessions.

#### `execute_command_async`
Execute a command asynchronously without blocking the server. Returns a command ID for tracking.

**Use with companion tools**:
- `get_command_status(command_id)` - Check progress and retrieve output
- `interrupt_command_by_id(command_id)` - Send Ctrl+C to stop execution  
- `send_input(command_id, text)` - Provide input to interactive commands

```json
{
  "host": "myserver",
  "command": "sleep 60 && echo 'Done'",
  "timeout": 300
}
```

#### `get_command_status`
Get the status and output of an async command.

```json
{
  "command_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

#### `interrupt_command_by_id`
Interrupt a running async command by sending Ctrl+C.

```json
{
  "command_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

#### `list_running_commands`
List all currently running async commands.

#### `list_command_history`
List recent command history (completed, failed, interrupted commands).

```json
{
  "limit": 50
}
```

#### `read_file`
Read the contents of a remote file via SFTP, with optional sudo support.

**Basic usage:**
```json
{
  "host": "myserver",
  "remote_path": "/etc/nginx/nginx.conf",
  "max_bytes": 131072
}
```

**With passwordless sudo (NOPASSWD in sudoers):**
```json
{
  "host": "myserver",
  "remote_path": "/etc/shadow",
  "use_sudo": true
}
```

**With sudo password:**
```json
{
  "host": "myserver",
  "remote_path": "/etc/shadow",
  "sudo_password": "user_password"
}
```

- Attempts SFTP first for best performance
- Falls back to `sudo cat` via shell if permission denied and `use_sudo=true` or `sudo_password` provided
- Supports both passwordless sudo (NOPASSWD) and password-based sudo
- Enforces a 2 MB maximum per request (configurable per call up to that limit)
- Returns truncated notice when the content size exceeds the requested limit

#### `write_file`
Write text content to a remote file via SFTP, with optional sudo support.

**Basic usage:**
```json
{
  "host": "myserver",
  "remote_path": "/tmp/app.env",
  "content": "DEBUG=true\n",
  "append": true,
  "make_dirs": true
}
```

**With passwordless sudo (NOPASSWD in sudoers):**
```json
{
  "host": "myserver",
  "remote_path": "/etc/nginx/nginx.conf",
  "content": "server { ... }",
  "use_sudo": true,
  "permissions": 420
}
```

**With sudo password:**
```json
{
  "host": "myserver",
  "remote_path": "/etc/nginx/nginx.conf",
  "content": "server { ... }",
  "sudo_password": "user_password",
  "permissions": 420
}
```

- Uses SFTP when `use_sudo=false` and no `sudo_password` provided
- Uses `sudo tee` via shell when `use_sudo=true` or `sudo_password` is provided
- Supports both passwordless sudo (NOPASSWD) and password-based sudo
- Content larger than 2 MB is rejected for safety
- Optional `append` mode to add to existing files
- Optional `make_dirs` flag will create missing parent directories
- Supports `permissions` to set octal file modes after write (e.g., `420` for `0644`)
- Note: Shell fallback is slower than SFTP but enables writing to protected files

## SSH Config Support

The server automatically reads `~/.ssh/config` and supports:
- Host aliases
- Hostname mappings
- Port configurations
- User specifications
- IdentityFile settings

Example `~/.ssh/config`:
```
Host myserver
    HostName example.com
    User myuser
    Port 2222
    IdentityFile ~/.ssh/id_rsa
```

Then simply use:
```json
{
  "host": "myserver",
  "command": "uptime"
}
```

## How It Works

### Persistent Shell Sessions
Commands execute in persistent interactive shells that maintain state:
- Current directory persists across commands (`cd /tmp` stays in `/tmp`)
- Environment variables remain set
- Shell history is maintained

### Smart Command Completion Detection
Commands complete when either:
1. **Prompt detected**: Standard shell prompts (`$`, `#`, `>`, `%`) at end of output
2. **Idle timeout**: No output for 2 seconds after receiving data

**Why idle timeout?** Custom themed prompts may not match standard patterns. The 2-second idle timeout ensures commands complete even with non-standard prompts.

**Long-running commands**: The idle timer resets every time new output arrives, so builds or scripts that output sporadically continue running until naturally complete or the overall timeout is reached.

## Documentation

- [ASYNC_COMMANDS.md](/docs/ASYNC_COMMANDS.md) - Smart execution and async commands
- [SAFETY_PROTECTIONS.md](/docs/SAFETY_PROTECTIONS.md)

## License

Distributed under the MIT License. See `LICENSE` for details.