MCP server for managing VPS via SSH - gives AI agents secure access to remote servers
Project description
🔐 SSH MCP Server
Give AI agents secure access to your VPS servers via SSH
Execute commands, transfer files, manage processes — all through Model Context Protocol
Quick Start • Features • IDE Integration • Tools • Security
🚀 Quick Start
Install
# Via pip
pip install mcp-ssh-vps
# Via uvx (recommended for MCP)
uvx mcp-ssh-vps
Add Your First Server
# Interactive setup
uvx mcp-ssh-vps --help
# Or use the CLI
sshmcp-cli server add --name prod --host 192.168.1.100 --user deploy
sshmcp-cli server test prod
Connect to Your AI Agent
Add to your AI IDE config and start managing servers with natural language!
"Deploy my app to the production server"
"Check disk space on all servers"
"Restart nginx on web1 and web2"
✨ Features
| Feature | Description |
|---|---|
| 🖥️ Execute Commands | Run any shell command on remote servers |
| 📁 File Operations | Read, write, and list files via SFTP |
| 📂 Directory Transfer | rsync-like upload/download/sync directories |
| 🔗 SSH Tunnels | Local and remote port forwarding |
| 💻 Interactive Shell | PTY-based shell sessions for interactive work |
| ⚙️ Process Management | Control systemd, pm2, supervisor services |
| 📊 Command History | Track and search executed commands |
| 🚨 Monitoring Alerts | CPU/memory/disk usage alerts |
| 🏷️ Server Tags | Group servers with tags for batch operations |
| 🔄 Batch Execution | Run commands on multiple servers in parallel |
| 🔒 Security Profiles | Strict, moderate, or full access levels |
| 📝 Audit Logging | Track all operations for compliance |
| 🔑 SSH Keys & Passwords | Key, password, and SSH-agent authentication |
🔌 IDE Integration
Note: Config is auto-loaded from
~/.sshmcp/machines.jsonby default. No env variables required!
Claude Code
claude mcp add ssh-vps -- uvx mcp-ssh-vps
Or add to ~/.claude.json:
{
"mcpServers": {
"ssh-vps": {
"command": "uvx",
"args": ["mcp-ssh-vps"]
}
}
}
Factory Droid
droid mcp add ssh-vps "uvx mcp-ssh-vps"
Or add to ~/.factory/mcp.json:
{
"mcpServers": {
"ssh-vps": {
"command": "uvx",
"args": ["mcp-ssh-vps"]
}
}
}
Cursor
Add to ~/.cursor/mcp.json:
{
"mcpServers": {
"ssh-vps": {
"command": "uvx",
"args": ["mcp-ssh-vps"]
}
}
}
Qwen Code
qwen mcp add ssh-vps uvx mcp-ssh-vps
Or add to ~/.qwen/settings.json:
{
"mcpServers": {
"ssh-vps": {
"command": "uvx",
"args": ["mcp-ssh-vps"]
}
}
}
Claude Desktop
Add to Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"ssh-vps": {
"command": "uvx",
"args": ["mcp-ssh-vps"]
}
}
}
VS Code + Continue
Add to .continue/config.json:
{
"experimental": {
"modelContextProtocolServers": [
{
"transport": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-ssh-vps"]
}
}
]
}
}
Windsurf
Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"ssh-vps": {
"command": "uvx",
"args": ["mcp-ssh-vps"]
}
}
}
OpenAI Codex CLI
codex --mcp-config '{"ssh-vps": {"command": "uvx", "args": ["mcp-ssh-vps"]}}'
Any MCP-Compatible Client
Generic stdio configuration:
{
"command": "uvx",
"args": ["mcp-ssh-vps"]
}
Custom config path? Add
"env": {"SSHMCP_CONFIG_PATH": "/custom/path.json"}
🛠️ Tools Reference
Command Execution
| Tool | Description | Example |
|---|---|---|
execute_command |
Run command on server | execute_command("prod", "docker ps") |
execute_command_streaming |
Run with progress updates | execute_command_streaming("prod", "apt update") |
execute_on_multiple |
Run on multiple servers | execute_on_multiple(["*"], "uptime") |
Server Management
| Tool | Description | Example |
|---|---|---|
list_servers |
List all servers | list_servers() or list_servers(tag="web") |
add_server |
Add new server | add_server("web1", "1.2.3.4", "root") |
remove_server |
Remove server | remove_server("old-server") |
test_connection |
Test SSH connection | test_connection("prod") |
File Operations
| Tool | Description | Example |
|---|---|---|
read_file |
Read remote file | read_file("prod", "/var/log/app.log") |
upload_file |
Upload file | upload_file("prod", "/tmp/script.sh", "#!/bin/bash\n...") |
list_files |
List directory | list_files("prod", "/var/log") |
Process Management
| Tool | Description | Example |
|---|---|---|
manage_process |
Control services | manage_process("prod", "restart", "nginx") |
Help & Info
| Tool | Description | Example |
|---|---|---|
get_help |
Get documentation | get_help("examples") |
get_allowed_commands |
View security config | get_allowed_commands("prod") |
get_server_info |
Server details | get_server_info("prod") |
Interactive Shell Sessions
| Tool | Description | Example |
|---|---|---|
shell_start |
Start PTY shell | shell_start("prod") |
shell_send |
Send to shell | shell_send(session_id, "ls -la") |
shell_recv |
Receive output | shell_recv(session_id) |
shell_close |
Close session | shell_close(session_id) |
shell_list |
List sessions | shell_list() |
Command History
| Tool | Description | Example |
|---|---|---|
get_command_history |
View history | get_command_history(host="prod", limit=50) |
search_command_history |
Search | search_command_history("docker") |
get_command_stats |
Statistics | get_command_stats() |
Directory Transfer (rsync-like)
| Tool | Description | Example |
|---|---|---|
upload_directory |
Upload dir to remote | upload_directory("prod", "./dist", "/var/www") |
download_directory |
Download dir | download_directory("prod", "/var/log", "./logs") |
sync_directory |
Sync with delete | sync_directory("prod", "./app", "/opt/app", "upload") |
Port Forwarding (SSH Tunnels)
| Tool | Description | Example |
|---|---|---|
forward_local |
Local tunnel | forward_local("prod", 5433, "localhost", 5432) |
forward_remote |
Reverse tunnel | forward_remote("prod", 8080, "localhost", 3000) |
list_forwards |
List tunnels | list_forwards() |
close_forward |
Close tunnel | close_forward("local-1") |
Monitoring & Alerts
| Tool | Description | Example |
|---|---|---|
get_alerts |
View alerts | get_alerts(host="prod") |
get_active_alerts |
Active only | get_active_alerts() |
clear_alert |
Clear alert | clear_alert("prod", "cpu") |
🏷️ Server Tags & Batch Operations
Add servers with tags
add_server("web1", "192.168.1.10", "deploy", tags=["production", "web"])
add_server("web2", "192.168.1.11", "deploy", tags=["production", "web"])
add_server("db1", "192.168.1.20", "deploy", tags=["production", "database"])
Filter by tag
list_servers(tag="web") # Only web servers
list_servers(tag="production") # All production servers
Execute on tagged servers
execute_on_multiple(["tag:web"], "nginx -t") # All web servers
execute_on_multiple(["tag:production"], "uptime") # All production
execute_on_multiple(["*"], "df -h") # ALL servers
🔒 Security
Security Profiles
| Profile | Allowed Commands | Use Case |
|---|---|---|
strict |
git, ls, cat, df, uptime |
Read-only monitoring |
moderate |
+ docker, npm, systemctl, pm2 |
Standard DevOps |
full |
All commands (except rm -rf /) |
Full access |
Set security level
add_server("prod", "1.2.3.4", "root", security_level="full")
Audit Logging
All commands are logged with timestamps, user, and results:
2024-01-15T10:30:00Z | prod | root | docker ps | exit_code=0 | 150ms
📁 Configuration
Config Location
~/.sshmcp/machines.json
Example Config
{
"machines": [
{
"name": "production",
"host": "192.168.1.100",
"port": 22,
"user": "deploy",
"auth": {
"type": "key",
"key_path": "~/.ssh/id_rsa"
},
"security": {
"allowed_commands": [".*"],
"forbidden_commands": [".*rm\\s+-rf\\s+/$"],
"timeout_seconds": 120
},
"tags": ["production", "web"],
"description": "Main production server"
}
]
}
Environment Variable
export SSHMCP_CONFIG_PATH=/custom/path/machines.json
📖 Usage Examples
Check status of all servers
User: Check the status of all my servers
AI: I'll run uptime on all configured servers...
execute_on_multiple(["*"], "uptime && df -h | head -5")
Deploy application
User: Deploy the latest code to production
AI: I'll pull the latest changes and restart the service...
execute_command("prod", "cd /app && git pull origin main")
execute_command("prod", "npm install --production")
execute_command("prod", "pm2 restart all")
Debug server issues
User: Why is the web server slow?
AI: Let me check system resources and logs...
execute_command("web1", "top -bn1 | head -20")
execute_command("web1", "tail -50 /var/log/nginx/error.log")
execute_command("web1", "free -m && df -h")
🚀 Advanced Features
Transport Modes
# Default stdio (for MCP clients like Claude Code)
uvx mcp-ssh-vps
# SSE transport for web integration
uvx mcp-ssh-vps --transport sse
# Streamable HTTP for production
uvx mcp-ssh-vps --transport streamable-http
WebSocket Shell (Real-time)
For real-time interactive shells, integrate with your web app:
from fastapi import FastAPI, WebSocket
from sshmcp.ws import ShellWebSocketHandler
app = FastAPI()
handler = ShellWebSocketHandler()
@app.websocket("/ws/shell/{host}")
async def shell(websocket: WebSocket, host: str):
await handler.handle(websocket, host)
Client protocol:
// Connect
const ws = new WebSocket("ws://localhost:8000/ws/shell/prod");
// Send command
ws.send(JSON.stringify({type: "input", data: "ls -la\n"}));
// Resize terminal
ws.send(JSON.stringify({type: "resize", width: 120, height: 40}));
// Receive output
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "output") terminal.write(msg.data);
};
Streaming Commands
For long-running commands with progress updates:
# Regular (waits for completion)
execute_command("prod", "apt update")
# Streaming (reports progress)
execute_command_streaming("prod", "apt update")
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
git clone https://github.com/LuxVTZ/sshmcp.git
cd sshmcp
pip install -e ".[dev]"
pytest
📄 License
MIT License - see LICENSE for details.
Made with ❤️ for the AI agent ecosystem
PyPI •
GitHub •
Issues
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_ssh_vps-0.7.0.tar.gz.
File metadata
- Download URL: mcp_ssh_vps-0.7.0.tar.gz
- Upload date:
- Size: 101.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9de788202f0042b48b5b9465ed359c46069e57ea9a1fd841136a79a297daa9b0
|
|
| MD5 |
3f506cdc82b75dc2ebffe7632cba383f
|
|
| BLAKE2b-256 |
1ce0676d35dc427629d8dee36dd929b5bbf23ea7c80f81520f594c88b7707e9a
|
Provenance
The following attestation bundles were made for mcp_ssh_vps-0.7.0.tar.gz:
Publisher:
publish.yml on data-zen-ru/sshmcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_ssh_vps-0.7.0.tar.gz -
Subject digest:
9de788202f0042b48b5b9465ed359c46069e57ea9a1fd841136a79a297daa9b0 - Sigstore transparency entry: 804642781
- Sigstore integration time:
-
Permalink:
data-zen-ru/sshmcp@043c82a40f889f4197d85d3259bf79217386dcdc -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/data-zen-ru
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@043c82a40f889f4197d85d3259bf79217386dcdc -
Trigger Event:
release
-
Statement type:
File details
Details for the file mcp_ssh_vps-0.7.0-py3-none-any.whl.
File metadata
- Download URL: mcp_ssh_vps-0.7.0-py3-none-any.whl
- Upload date:
- Size: 91.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b9008dac9e96e5efa8929541f04cc6b038fe4882fd4b230e9e1b9a5ebd532f3
|
|
| MD5 |
9e532dafb1e7e7250549cb8148440314
|
|
| BLAKE2b-256 |
85ed65dda0074dc10a4186a2100850952433e3752929f5c74b3fabbc4fdb9a99
|
Provenance
The following attestation bundles were made for mcp_ssh_vps-0.7.0-py3-none-any.whl:
Publisher:
publish.yml on data-zen-ru/sshmcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_ssh_vps-0.7.0-py3-none-any.whl -
Subject digest:
9b9008dac9e96e5efa8929541f04cc6b038fe4882fd4b230e9e1b9a5ebd532f3 - Sigstore transparency entry: 804642782
- Sigstore integration time:
-
Permalink:
data-zen-ru/sshmcp@043c82a40f889f4197d85d3259bf79217386dcdc -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/data-zen-ru
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@043c82a40f889f4197d85d3259bf79217386dcdc -
Trigger Event:
release
-
Statement type: