README.md•8.98 kB
# SSH Read-Only MCP Server
A secure Model Context Protocol (MCP) server that enables remote SSH command execution with strict read-only enforcement. Perfect for safely delegating SSH access to Claude while preventing accidental or malicious write operations.
## Features
✅ **Read-Only Command Enforcement** – Only allows safe, read-only commands
✅ **SSH Connection Pooling** – Support multiple simultaneous connections
✅ **Command Validation** – Blocks dangerous patterns and write operations
✅ **Multicast Discovery** – Auto-announces on network for easy discovery
✅ **Flexible Transport** – Stdio, HTTP, or Streamable-HTTP modes
✅ **Comprehensive Logging** – Full audit trail in `ssh_mcp.log`
✅ **Environment Configuration** – Fully configurable via .env
## Installation
### Prerequisites
- Python 3.8+
- `uv` package manager
### Setup
```bash
# Clone or create project directory
mkdir ssh-mcp-server
cd ssh-mcp-server
# Initialize uv project (if needed)
uv init
# Install dependencies
uv pip install fastmcp paramiko python-dotenv
```
Or use `pyproject.toml`:
```toml
[project]
name = "ssh-mcp-server"
version = "0.1.0"
dependencies = [
"fastmcp>=0.1.0",
"paramiko>=3.0.0",
"python-dotenv>=1.0.0",
]
```
Then install:
```bash
uv sync
```
## Configuration
Create a `.env` file in the project root:
```env
# Transport mode: stdio (default), http, or streamable-http
MCP_TRANSPORT=stdio
# Server identification
MCP_SERVER_NAME=SSH Read-Only MCP Server
# HTTP mode settings (if using http/streamable-http)
MCP_HOST=0.0.0.0
MCP_PORT=3000
# Multicast discovery
MCP_ENABLE_BROADCAST=true
MCP_BROADCAST_INTERVAL=30
```
### Environment Variables
| Variable | Default | Description |
| ------------------------ | -------------------------- | -------------------------------------------------------------- |
| `MCP_TRANSPORT` | `stdio` | Communication transport: `stdio`, `http`, or `streamable-http` |
| `MCP_SERVER_NAME` | `SSH Read-Only MCP Server` | Display name for the server |
| `MCP_HOST` | `0.0.0.0` | Bind address for HTTP mode |
| `MCP_PORT` | `3000` | Port for HTTP mode |
| `MCP_ENABLE_BROADCAST` | `true` | Enable multicast discovery announcements |
| `MCP_BROADCAST_INTERVAL` | `30` | Seconds between broadcast announcements |
## Usage
### Start the Server
**Stdio mode (default):**
```bash
uv run ssh_readonly_fastmcp_mcast.py
```
**HTTP mode with multicast discovery:**
```bash
MCP_TRANSPORT=http MCP_PORT=3000 uv run ssh_readonly_fastmcp_mcast.py
```
**HTTP mode without broadcasting:**
```bash
MCP_ENABLE_BROADCAST=false MCP_TRANSPORT=http MCP_PORT=3000 uv run ssh_readonly_fastmcp_mcast.py
```
### Available Tools
#### 1. `ssh_connect`
Establish an SSH connection to a remote machine.
**Parameters:**
- `host` (required) – Remote host IP or hostname
- `username` (required) – SSH username
- `port` (optional, default: 22) – SSH port
- `key_filename` (optional) – Path to private key file (recommended)
- `password` (optional) – SSH password (fallback)
**Example:**
```
Connect to 192.168.1.100 as user 'admin' with private key
host: 192.168.1.100
username: admin
key_filename: /home/user/.ssh/id_rsa
```
#### 2. `ssh_execute`
Execute a read-only command on the connected remote machine.
**Parameters:**
- `host` (required) – Remote host (must be connected first)
- `username` (required) – SSH username
- `command` (required) – Read-only command to execute
- `port` (optional, default: 22) – SSH port
**Example:**
```
Run 'ls -la /home' on connected server
host: 192.168.1.100
username: admin
command: ls -la /home
```
#### 3. `ssh_disconnect`
Close an SSH connection.
**Parameters:**
- `host` (required) – Remote host
- `username` (required) – SSH username
- `port` (optional, default: 22) – SSH port
#### 4. `ssh_list_connections`
View all active SSH connections.
**Parameters:** None
#### 5. `ssh_get_allowed_commands`
Retrieve the list of allowed read-only commands.
**Parameters:** None
## Allowed Commands
The server permits the following read-only operations:
- **File operations:** `cat`, `ls`, `file`, `head`, `tail`, `find`, `locate`
- **System info:** `ps`, `top`, `df`, `du`, `free`, `uname`, `hostname`, `uptime`
- **Network:** `netstat`, `ss`, `ifconfig`, `ip`, `curl`, `wget`, `dig`, `nslookup`, `ping`, `traceroute`
- **Process management:** `lsof`, `systemctl`, `service`
- **Text processing:** `grep`, `awk`, `sed`, `wc`
- And many more read-only utilities
**Blocked operations:** `rm`, `mv`, `cp`, `chmod`, `chown`, `mkdir`, `touch`, `kill`, `shutdown`, `reboot`, `sudo`, and any write/modify commands.
## Multicast Discovery
When running in HTTP mode with broadcasting enabled, the server announces itself on the multicast group:
- **Address:** `239.255.255.250`
- **Port:** `5353`
- **Interval:** Configurable (default: 30 seconds)
Discovery announcement includes:
- Server UUID
- Server name
- Local IP and port
- Transport type
- Protocol version
## Logging
All activity is logged to `ssh_mcp.log`:
```
2025-10-17 10:30:45,123 [INFO] ssh_mcp - Starting MCP server with transport=http
2025-10-17 10:30:46,456 [INFO] ssh_mcp - Starting multicast broadcaster on 239.255.255.250:5353
2025-10-17 10:30:47,789 [INFO] ssh_mcp - Successfully connected to admin@192.168.1.100:22
```
## Security Considerations
🔒 **Read-Only Enforcement:**
- Only whitelisted commands are allowed
- Dangerous patterns (pipes, redirects, subshells) are blocked
- Write operations are prevented at the command level
⏱️ **Timeouts:**
- 30-second execution timeout per command
- Prevents hanging commands from blocking the server
🔐 **Authentication:**
- SSH key authentication recommended over passwords
- Passwords stored in memory only, never persisted
📋 **Audit Trail:**
- All connections and commands are logged
- Review `ssh_mcp.log` for security audits
## Troubleshooting
### Connection Refused
```
Error: Connection failed: [Errno 111] Connection refused
```
- Verify the remote host is reachable: `ping <host>`
- Check SSH is running on the remote machine
- Verify port number (default 22)
### Authentication Failed
```
Error: Connection failed: Authentication failed
```
- Verify username is correct
- For key auth: check key file path and permissions (`chmod 600`)
- For password auth: verify credentials
- Ensure SSH public key is authorized on remote (`~/.ssh/authorized_keys`)
### Command Not Allowed
```
Error: Command not allowed for security reasons
```
- The command contains a blocked pattern or is not in the allowed list
- Use `ssh_get_allowed_commands` to see permitted commands
- For write operations, use SSH directly instead
### Broadcast Not Working
- Verify `MCP_ENABLE_BROADCAST=true`
- Check network supports multicast (most corporate networks block it)
- Verify firewall allows UDP on port 5353
- Check `ssh_mcp.log` for broadcast errors
## Development
### Running in Debug Mode
```bash
DEBUG=true uv run ssh_readonly_fastmcp_mcast.py
```
### Testing
```bash
# Test connection
uv run -c "from ssh_readonly_fastmcp_mcast import is_command_safe; print(is_command_safe('ls -la'))"
# Should print: True
# Test blocked command
uv run -c "from ssh_readonly_fastmcp_mcast import is_command_safe; print(is_command_safe('rm -rf /'))"
# Should print: False
```
## Project Structure
```
ssh-mcp-server/
├── ssh_readonly_fastmcp_mcast.py # Main server implementation
├── .env # Configuration file
├── .env.example # Configuration template
├── ssh_mcp.log # Server logs (auto-generated)
├── pyproject.toml # Project metadata
└── README.md # This file
```
## API Response Format
All tools return consistent JSON responses:
**Success:**
```json
{
"status": "success",
"host": "192.168.1.100",
"command": "ls -la /home",
"exit_code": 0,
"output": "total 24\ndrwxr-xr-x 3 root root 4096...",
"error": null
}
```
**Error:**
```json
{
"status": "error",
"message": "Command not allowed for security reasons",
"reason": "Only read-only commands are permitted"
}
```
## License
MIT
## Contributing
Contributions welcome! Please ensure:
- All changes maintain read-only enforcement
- Code is logged appropriately
- Tests pass for security validations
## Support
For issues or questions:
1. Check `ssh_mcp.log` for error details
2. Review the Troubleshooting section
3. Verify environment configuration
4. Check network connectivity to remote hosts