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 分