VPS Ops

io.github.koller-nexus由 GitHub 帳號 koller-nexus 發布v0.1.0更新於 Oct 24, 2026

Stdio MCP server that inspects and (with confirmation) mutates one VPS over SSH.

概覽

AI 產生的概覽

讓助理透過 SSH 檢查並在確認後變更單一 VPS:主機健康、Docker、Compose、Swarm、防火牆與日誌。

可以做什麼
這是一個 stdio MCP 伺服器,對單一台 VPS 執行固定的 SSH 指令。唯讀工具涵蓋主機健康(ping、資源、journal、失敗單元、top、dmesg、監聽埠、防火牆、fail2ban、SSH 強化檢查)以及 Docker/Compose/Swarm 檢查(ps、inspect、logs、stats、service 與 node 清單、compose ps)。變更工具可重新啟動、停止、啟動或刪除容器,執行 compose up、restart 與 pull-up,以及清理 Docker。每次呼叫回傳 exit_code、stdout、stderr、duration_ms 與 truncated。
適用情境
適合讓助理在不取得自由 shell 的前提下查看或操作一台遠端伺服器:讀取日誌、容器狀態、磁碟與記憶體、防火牆狀態,或在排障時重新啟動服務。若只需唯讀存取,可關閉變更功能後再註冊。
執行需求
由用戶端(Cursor 或 Codex)啟動的本機程序,不要手動常駐執行。需要 Bun、供註冊指令碼使用的 Python 3、PATH 中的 OpenSSH 用戶端,以及可讀取的 SSH 私鑰。需設定 VPS_SSH_KEY_PATH(必填)、VPS_HOST、VPS_USER、VPS_PORT、VPS_COMPOSE_DIR、VPS_COMMAND_TIMEOUT_MS、VPS_LOG_MAX_BYTES、VPS_ALLOW_MUTATIONS。遠端主機需安裝 Docker,部分防火牆、fail2ban、sshd、ss 與 dmesg 工具還需要免密碼 sudo。
安裝前請注意
此伺服器持有對真實伺服器的 SSH 存取:VPS_SSH_KEY_PATH 指向私鑰,VPS_SSH_KEY_PASSPHRASE 是選用通關密語(建議改用 ssh-agent)。變更工具會改動線上狀態:重新啟動、停止、啟動、刪除容器,compose up/restart/pull-up,以及 Docker 清理,後者可能刪除未使用的映像、網路,並在 confirm_volumes 時刪除磁碟區。變更需 confirm: true,可用 VPS_ALLOW_MUTATIONS=false 完全停用。遠端指令固定,參數經允許清單檢查並在 shell 中加上引號。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 VPS Ops,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

vps-ops-mcp

A stdio MCP server that operates one VPS over SSH. Cursor and Codex start the process with bun and call read-only tools (host health, Unix debug, Docker, Compose, Swarm, firewall) plus mutation tools (restart, stop, start, prune). Mutations require an explicit confirmation.

Transport is stdio. Do not start the server as a long-lived process by hand: the client (Cursor or Codex) launches it.

Contributing

See CONTRIBUTING.md (branches, pull requests, verification) and ISSUE.md (how to file an issue).

Requirements

  • Bun
  • Python 3 (used by the registration scripts)
  • OpenSSH client (ssh on PATH)
  • A readable SSH private key with access to the remote user
  • On the remote host: Docker (and passwordless sudo -n for ufw, fail2ban, sshd -T, ss, and dmesg if you use those tools)

Configuration

bash
cp .env.example .env

Edit .env. The file is gitignored.

VariableRequiredDefaultPurpose
VPS_SSH_KEY_PATHyes—Absolute path to the private key. The process refuses to start if the file is missing or unreadable.
VPS_HOSTnovps.example.invalidSSH host (placeholder; set your own host).
VPS_USERnoubuntuSSH user.
VPS_PORTno22SSH port.
VPS_COMPOSE_DIRno—Absolute Compose directory on the VPS. Without it, Compose tools require the dir argument.
VPS_COMMAND_TIMEOUT_MSno30000Remote command timeout. Expiry returns exit_code 124.
VPS_LOG_MAX_BYTESno200000Cap for stdout/stderr. Overflow is cut and truncated is true.
VPS_ALLOW_MUTATIONSnotruefalse, 0, no, or off disables every mutation.
VPS_SSH_KEY_PASSPHRASEno—Avoid. Prefer ssh-agent. Registration scripts do not copy this variable into the client.

VPS_COMPOSE_DIR must be absolute and match /^[a-zA-Z0-9/_.-]+$/ (it must start with /).

Installation

bash
bun install

npm package (requires Bun; the MCP Registry points at this artifact):

bash
bunx @koller-nexus/vps-ops-mcp

MCP Registry name: io.github.koller-nexus/vps-ops-mcp. The registry publishes metadata only after the package exists on public npm.

Register with clients

The scripts write MCP config from variables already exported in the shell. They do not load .env themselves. If you skip the export, the scripts fall back to their defaults (host, user, port, and a local key path).

Do this once at the repository root before each script:

bash
set -asource .envset +a

Each run backs up the destination file (*.bak.YYYYMMDDHHMMSS) and replaces only the vps-ops server. Other MCP servers stay in place.

Optional script variables:

VariableDefaultPurpose
MCP_PROJECT_DIRthis repository rootSource of the src/index.ts path.
CURSOR_MCP_JSON~/.cursor/mcp.jsonCursor file to update.
CODEX_CONFIG~/.codex/config.tomlCodex file to update.

VPS_COMPOSE_DIR is written into the client config only when it is set and non-empty.

Cursor

Global registration (applies in every workspace):

bash
./scripts/register-cursor-mcp.sh

The script writes ~/.cursor/mcp.json in this shape:

json
{  "mcpServers": {    "vps-ops": {      "command": "bun",      "args": ["/absolute/path/vps-ops-mcp/src/index.ts"],      "env": {        "VPS_HOST": "your.host",        "VPS_USER": "ubuntu",        "VPS_PORT": "22",        "VPS_SSH_KEY_PATH": "/absolute/path/to/key",        "VPS_COMMAND_TIMEOUT_MS": "30000",        "VPS_LOG_MAX_BYTES": "200000",        "VPS_ALLOW_MUTATIONS": "true"      }    }  }}

To scope it to one project, point the script at that project's mcp.json:

bash
CURSOR_MCP_JSON="/absolute/path/to/project/.cursor/mcp.json" ./scripts/register-cursor-mcp.sh

Then reload the Cursor window (Command Palette → Developer: Reload Window) or restart the server under Settings → MCP. The server appears as vps-ops.

Codex

bash
./scripts/register-codex-mcp.sh

The script writes ~/.codex/config.toml:

toml
[mcp_servers.vps-ops]command = "bun"args = ["/absolute/path/vps-ops-mcp/src/index.ts"]
[mcp_servers.vps-ops.env]VPS_HOST = "your.host"VPS_USER = "ubuntu"VPS_PORT = "22"VPS_SSH_KEY_PATH = "/absolute/path/to/key"VPS_COMMAND_TIMEOUT_MS = "30000"VPS_LOG_MAX_BYTES = "200000"VPS_ALLOW_MUTATIONS = "true"

Close and reopen the Codex session so it rereads config.toml. If the CLI is on PATH, codex mcp list should show vps-ops.

Verify

Test SSH outside MCP with the same flags the server uses:

bash
ssh -i "$VPS_SSH_KEY_PATH" \  -o BatchMode=yes \  -o IdentitiesOnly=yes \  -o StrictHostKeyChecking=accept-new \  -p "${VPS_PORT:-22}" \  "${VPS_USER}@${VPS_HOST}" \  'uname -a'

In Cursor or Codex, ask the client to call vps_ping. The response is JSON:

json
{  "exit_code": 0,  "stdout": "...",  "stderr": "",  "duration_ms": 0,  "truncated": false}

A non-zero exit_code is an MCP error. If the process exits immediately with VPS_SSH_KEY_PATH is required or missing or unreadable, the variable never reached the client env — rerun the registration script with .env exported.

Tools

Every call returns exit_code, stdout, stderr, duration_ms, and truncated.

Read-only

ToolArgumentsWhat it does
vps_ping—uname -a, uptime, hostname.
vps_resources—df -h, free -h, load average.
vps_journalunit, n? (1–500, default 100)journalctl -u. Unit from the allowlist (docker, sshd, fail2ban, ufw, cron, with or without .service) or a safe name ending in .service.
docker_ps—docker ps -a as JSON lines.
docker_inspectnamedocker inspect.
docker_logsname, n? (1–1000, default 200), since?docker logs --tail --timestamps.
docker_stats—docker stats --no-stream.
docker_service_ls—docker service ls em JSON lines (Swarm).
docker_node_ls—docker node ls em JSON lines (Swarm).
compose_psdir?, file?docker compose ps em dir ou VPS_COMPOSE_DIR (file, ex. stack.yml, vira -f).
compose_config_varsdir?, file?Renderiza o stack (docker compose config) e devolve só os nomes das variáveis de ambiente por serviço, nunca os valores.
host_deps—Versões de docker, docker compose (v2) e jq.
host_listen—ss -lntup (sudo -n, senão sem sudo).
host_failed_units—systemctl --failed --no-pager --full.
host_top—Top 30 processes by memory (ps aux --sort=-%mem).
host_dmesgn? (1–200, default 100)dmesg -T + tail (sudo -n, otherwise without sudo).
host_firewall—ufw status verbose (sudo -n, otherwise without sudo).
host_fail2banjail?fail2ban-client status (sudo -n).
ssh_hardening_check—Filtered sshd -T: port, password, root login, pubkey.

Container, service, image, and jail names must match ^[a-zA-Z0-9][a-zA-Z0-9_.-]*$.

Mutation

These require confirm: true. With VPS_ALLOW_MUTATIONS=false, all of them are rejected.

ToolExtra argumentsRemote command
docker_restartnamedocker restart
docker_stopnamedocker stop
docker_startnamedocker start
compose_updir?, services?docker compose up -d
compose_restartdir?, services?docker compose restart
compose_pull_updir?, services?docker compose pull then up -d
docker_rmname, force_namedocker rm -f. force_name must equal name.
disk_cleanup_dockerconfirm_volumes?docker system prune -f. Volumes only with confirm_volumes: true.

Security

  • Remote commands are fixed. There is no free-form shell tool.
  • Name and path arguments go through an allowlist and are quoted in the shell.
  • SSH uses BatchMode=yes, IdentitiesOnly=yes, and StrictHostKeyChecking=accept-new.
  • A mutation without confirm: true is rejected. docker_rm asks for the name twice. Volume prune requires confirm_volumes: true.
  • For a read-only client, register with VPS_ALLOW_MUTATIONS=false.

來源:README.md,提交 1f4735f

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v0.1.0最新Sep 23, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分