Secure VPS Operations MCP Server
Enables secure, read-only inspection of a VPS over SSH through approved operations such as system health, disk usage, container logs, and service status, without giving the AI unrestricted shell access.
README
Secure VPS Operations MCP Server
A secure, read-only Model Context Protocol (MCP) server for inspecting a VPS over SSH without giving an AI assistant unrestricted shell access.
The core idea is simple: the AI selects approved operations; it never constructs or executes arbitrary shell commands.
What This Project Does
This project exposes a small set of MCP tools for safe VPS inspection:
get_system_healthget_disk_usagelist_containersget_container_logsget_service_statuscheck_nginx_configurationcheck_ssl_expirycheck_database_health
Each MCP tool validates input locally, checks configured allowlists, then calls a fixed SSH gateway command on the VPS. The gateway maps operation names to root-owned scripts under /usr/local/lib/mcp/scripts.
There is intentionally no generic execute_shell tool.
Architecture
AI Client
|
v
MCP Client
|
v
Local Python MCP Server
|
v
SSH key authentication
|
v
Dedicated VPS user
|
v
/usr/local/bin/mcp-command-gateway
|
v
Root-owned allowlisted scripts
Why Python
This first version uses Python 3.11+ because the official MCP Python SDK, asyncssh, and Pydantic make a small control-plane server straightforward to build and audit.
Java would also be viable, especially inside an existing Spring-based internal platform, but Python keeps this MCP adapter smaller and simpler for the first release.
Security Model
The security boundary is based on constraints rather than prompting:
- No unrestricted shell tool
- Dedicated SSH user, not root
- SSH key authentication only
- Host-key verification through
known_hosts - Fixed gateway command on the VPS
- Operation allowlist in the gateway
- Resource allowlists for containers, services, domains, and databases
- Root-owned scripts that the SSH user cannot edit
- Narrow sudo rules only where required
- Output sanitisation before returning data to the model
- Append-only JSONL audit logs
Local Setup
Create a virtual environment:
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e '.[test]'
Create local configuration:
cp .env.example .env
Edit .env:
SSH_HOST=your-vps-host-or-ip
SSH_PORT=22
SSH_USERNAME=mcp-operator
SSH_PRIVATE_KEY_FILE=/path/to/private/key
SSH_KNOWN_HOSTS_FILE=/path/to/known_hosts
ALLOWED_CONTAINERS=grafana,prometheus,opensearch
ALLOWED_SERVICES=app.service,docker.service,nginx.service
ALLOWED_DOMAINS=api-dev.example.com,api-staging.example.com
ALLOWED_DATABASES=appdb
Pin the VPS host key:
ssh-keyscan -H your-vps-host-or-ip >> ~/.ssh/known_hosts
Run tests:
python -m pytest
Run the MCP server locally:
contabo-ops-mcp
For stdio MCP usage, the server will appear to wait for input. That is expected. An MCP client starts the process and communicates with it over stdin/stdout.
VPS Setup
Create a dedicated user on the VPS:
sudo useradd --create-home --shell /bin/bash mcp-operator
sudo mkdir -p /home/mcp-operator/.ssh
sudo chmod 700 /home/mcp-operator/.ssh
sudo chown -R mcp-operator:mcp-operator /home/mcp-operator
Create an SSH key locally:
ssh-keygen -t ed25519 -f ~/.ssh/vps_mcp -C "vps-operations-mcp"
Add the public key to the VPS:
sudo tee /home/mcp-operator/.ssh/authorized_keys > /dev/null <<'EOF'
PASTE_PUBLIC_KEY_HERE
EOF
sudo chown -R mcp-operator:mcp-operator /home/mcp-operator/.ssh
sudo chmod 700 /home/mcp-operator/.ssh
sudo chmod 600 /home/mcp-operator/.ssh/authorized_keys
sudo chmod 755 /home/mcp-operator
Test SSH from your local machine:
ssh -i ~/.ssh/vps_mcp mcp-operator@your-vps-host-or-ip
Gateway Installation
Copy the gateway to the VPS:
scp -i ~/.ssh/vps_mcp vps/mcp-command-gateway mcp-operator@your-vps-host-or-ip:/tmp/mcp-command-gateway
Install it on the VPS:
sudo install -o root -g root -m 755 /tmp/mcp-command-gateway /usr/local/bin/mcp-command-gateway
sudo mkdir -p /usr/local/lib/mcp/scripts
sudo chown -R root:root /usr/local/lib/mcp
sudo chmod -R 755 /usr/local/lib/mcp
The gateway accepts structured JSON like this:
{
"operation": "system.health",
"arguments": {}
}
It rejects unknown operations and never accepts raw shell commands.
Example Script
Create /usr/local/lib/mcp/scripts/system-health on the VPS:
sudo tee /usr/local/lib/mcp/scripts/system-health > /dev/null <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail
load_average="$(cut -d ' ' -f1-3 /proc/loadavg)"
uptime_seconds="$(cut -d. -f1 /proc/uptime)"
hostname="$(hostname)"
printf '{"success":true,"hostname":"%s","uptimeSeconds":%s,"loadAverage":"%s"}\n' \
"$hostname" \
"$uptime_seconds" \
"$load_average"
EOF
sudo chown root:root /usr/local/lib/mcp/scripts/system-health
sudo chmod 755 /usr/local/lib/mcp/scripts/system-health
Test directly on the VPS:
printf '{"operation":"system.health","arguments":{}}' | /usr/local/bin/mcp-command-gateway
Test from your local machine over SSH:
ssh -i ~/.ssh/vps_mcp mcp-operator@your-vps-host-or-ip \
'printf '\''{"operation":"system.health","arguments":{}}'\'' | /usr/local/bin/mcp-command-gateway'
MCP Client Configuration
For an MCP client that supports local stdio servers, configure the command as:
{
"mcp": {
"vps-ops": {
"type": "local",
"command": [
"/absolute/path/to/project/.venv/bin/python",
"/absolute/path/to/project/src/contabo_mcp/main.py"
],
"enabled": true,
"env": {
"PYTHONPATH": "/absolute/path/to/project/src"
}
}
}
}
Restart the MCP client after changing its config.
Example prompts:
- "Check system health on the VPS."
- "Show disk usage on the VPS."
- "Show the last 20 logs for the Grafana container."
- "Check SSL expiry for
api-dev.example.com." - "Check status of
nginx.service."
Development
Run tests:
source .venv/bin/activate
python -m pytest
Check config loading:
python -c "from contabo_mcp.config import get_settings; print(get_settings().ssh_host)"
Check the SSH gateway from Python:
python - <<'PY'
import asyncio
from contabo_mcp.config import get_settings
from contabo_mcp.ssh_gateway import SshGateway
async def main():
gateway = SshGateway(get_settings())
result = await gateway.execute("system.health", {})
print(result.exit_code)
print(result.stdout)
print(result.stderr)
asyncio.run(main())
PY
Current Scope
This repository is intentionally read-only for the first release.
Write operations such as service restarts, Nginx reloads, certificate renewal, backups, deployments, and rollbacks should be added only after implementing:
- Explicit approval handling
- Risk classification
- Narrow sudo rules
- Idempotency checks
- Post-action health verification
- Stronger audit records
- Failure and rollback behavior
License
This project is licensed under the MIT License. See LICENSE for details.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。