Skip to main content
Glama
folexz

remnawave-mcp

by folexz

remnawave-mcp

npm version CI license node

一个用于 Remnawave 面板 API 的 MCP 服务器。

@folexz scope 发布:npm 上不带 scope 的 remnawave-mcp 属于一个不相关的项目,它面向 Remnawave 2.7.4,无法和 2.8.0+ 兼容。

npx -y @folexz/remnawave-mcp   # configured via REMNAWAVE_BASE_URL + REMNAWAVE_API_TOKEN_READ/_WRITE

它覆盖了 Remnawave API v3.3.2全部 205 个操作,纵贯 28 个控制器 —— 用户、节点、主机、配置模板、squads、订阅、节点插件、基础设施计费、系统状态 —— 全部来自面板自身的 OpenAPI 文档,由生成而来而非手写。只要把更新的 spec 指向 npm run build-spec,工具表面就会相应更新。

Highlights

  • Spec 驱动,且能自更新。 npm run update-spec 会从 Remnawave 自己发布的位置获取最新的 OpenAPI 文档,并重建整个目录。每个工具的输入 schema 都直接来自对应操作的参数和请求体。没有手写 API 的结构,而且重建过程会打印一个 diff,把每个新增、删除或重命名的操作都列出来,因此框架版本升级不会悄悄干掉任何工具。

  • 有上限的上下文成本。 205 个强类型工具会让每次请求的 tools/list 产生约 39k tokens 的开销。默认 profile 只暴露 5 个工具(约 1.4k tokens),但已经够访问所有操作 —— 参见 Why not 205 tools

  • 双 token 最小权限认证。 一个读 token,外加一个可选的写 token。GET 使用读 token;POST/PATCH/PUT/DELETE 使用写 token。没有写 token 时,变更类的工具 根本不注册 —— 服务器实际上是只读的。

  • 针对 live panel 的安全措施。 变更会被串行化,并遵循最小的间隔,同时会带退避地重试,因为每次配置写入都会让面板把配置推给所有节点并重启 Xray。批量操作和删除操作还额外要求 confirm: true

  • 内置字段说明。 下面列出的这些 hook 会挂在它们影响的操作上,从而出现在工具描述和 remnawave_describe_operation 的输出中。

  • 无注定失败的请求。 16 个端点(auth、passkeys、API token 管理)只服务登录了的管理员 JWT,拒绝 API token。它们会从 Spec 中被感知,并在本地被拒绝,同时给出解释,而不是直接触发请求。

  • Escape hatches(逃逸通道)。 remnawave_request_read / remnawave_request_write 可以访问任意路径,包括未文档化的路由以及 OpenAPI 表达不出的 query 语法。

Requirements

  • Node.js >= 18

  • 可通过 HTTPS 访问的 Remnawave 面板(3.x)

  • 来自面板的 API token:Settings → API tokens。Remnawave 3.x 支持 scoped token —— 生成一个带读作用域的 token,如果要写操作,再生成一个带写作用域的 token。

Install

最快的路径是 npx —— 参见 Register with Claude Code。从源码运行:

git clone https://github.com/folexz/remnawave-mcp.git
cd remnawave-mcp
npm install
npm run build

Configuration

所有配置都由 MCP host 提供的环境变量完成,不读取文件。

Variable

Required

Default

Description

REMNAWAVE_BASE_URL

yes

面板的 origin,如 https://panel.example.com(不要带 /api 后缀)。

REMNAWAVE_API_TOKEN_READ

yes

读 token。别名:REMNAWAVE_API_TOKEN,所以面板的 .env 也兼容。

REMNAWAVE_API_TOKEN_WRITE

no

写 token。省略则变为只读。

REMNAWAVE_TOOL_PROFILE

no

minimal

minimal | core | full —— 要展示多少个强类型工具。

REMNAWAVE_CONTROLLERS

no

逗号分隔的 controller slug 列表;覆盖 profile 的强类型工具选择。

REMNAWAVE_MAX_SCHEMA_BYTES

no

2000

输入 schema 如果超过该字节数,就会在 tools/list 中被折叠。

REMNAWAVE_WRITE_MIN_INTERVAL_MS

no

1500

两次变更操作之间的最小间隔。

REMNAWAVE_MAX_RETRIES

no

3

针对网络故障、429 和 5xx 的重试次数。

REMNAWAVE_TIMEOUT_MS

no

30000

每个请求的超时时间。

REMNAWAVE_SKIP_CONFIRM

no

0

1 会取消破坏性操作上的 confirm: true 要求。

REMNAWAVE_ALLOW_ADMIN_JWT_OPS

no

0

1 会解锁 16 个只能 admin JWT 用的端点(只在你确认 token 是 admin JWT 时再这样做)。

Register with Claude Code

只读(推荐默认):

claude mcp add remnawave --scope user \
  --env REMNAWAVE_BASE_URL=https://panel.example.com \
  --env REMNAWAVE_API_TOKEN_READ=your_read_token \
  -- npx -y @folexz/remnawave-mcp@latest

启用变更,并为日常用到的控制器开启强类型工具:

claude mcp add remnawave --scope user \
  --env REMNAWAVE_BASE_URL=https://panel.example.com \
  --env REMNAWAVE_API_TOKEN_READ=your_read_token \
  --env REMNAWAVE_API_TOKEN_WRITE=your_write_token \
  --env REMNAWAVE_TOOL_PROFILE=core \
  -- npx -y @folexz/remnawave-mcp@latest

@latest 让 npx 在每次启动时解析最新发布的版本。要运行本地构建,把命令替换成:node /absolute/path/to/remnawave-mcp/dist/index.js

Register with Claude Desktop / 其他 MCP 客户端

{
  "mcpServers": {
    "remnawave": {
      "command": "npx",
      "args": ["-y", "@folexz/remnawave-mcp@latest"],
      "env": {
        "REMNAWAVE_BASE_URL": "https://panel.example.com",
        "REMNAWAVE_API_TOKEN_READ": "your_read_token"
      }
    }
  }
}

Why not 205 tools

tools/list 会在每次请求时重新发送给模型,因此它的序列化大小是永久性的 context 税。在这份 spec 上测得的(npx tsx scripts/tool-stats.ts):

Profile

Tools (read+write)

tools/list

≈ tokens

Tools (read-only)

≈ tokens

minimal

5

5.6 KB

~1.4k

4

~1.2k

core

91

71 KB

~17.8k

38

~5.9k

full

210

156 KB

~39k

92

~13.9k

Remnawave 的 DTO 数组就是 full 如此昂贵的原因:一个 dereferential host 对象本身就约 30 KB 的 JSON Schema,因为它内嵌了所有的 inbound 和安全变体。

因此,服务器不会在“一个操作一个工具”和“一个笨拙 dispatcher”二选一 —— 它同时提供两者,并让 profile 决定暴露多少:

  1. Catalog tools(一直开,3 个)。 remnawave_list_operations 浏览和搜索目录,并为每个操作返回一行紧凑摘要;remnawave_describe_operation 返回一个操作的完整 JSON Schema 和字段说明;remnawave_call 按名字执行 205 个操作中的任何一个。常见的循环就是 list → describe → call,而且随着 API 变大,它消耗的仍是同样的 1.4k tokens。这与 agent harness 推迟工具 schema 的懒加载思路一致。

  2. Typed tools(按 profile 选择)。 为你实际使用的 controllers 生成每个操作一个工具 —— core 覆盖 users、nodes、hosts、config profiles、internal squads、system 以及两个 bulk 操作 controller;full 全部支持;minimal 一个都不生成。超过 REMNAWAVE_MAX_SCHEMA_BYTES 的 schema 会保留顶层字段、丢弃嵌套,并用一个指针指向 remnawave_describe_operation 以获得完整版。

  3. Escape hatches(2 个)。 提供原始 GET 和原始写,应对任何 spec 遗漏的内容。

每个路由都走同一个执行器,所以无论你用哪个 surface,写门、破坏性 confirm 门、路径模板和 query 处理都是一致的。

按喜好选 profile:如果你连了多个 MCP server,那就选 minimal;想日常操作一次调用搞定,就选 core;不在乎 context 成本,那就 full

Field notes —— 一些 spec 没记录的行为

这些事情都是在真实 3.3.2 面板上实测出来的,并已附着在工具描述里受影响的操作上。

  • PATCH /api/config-profiles 是 replace,不是 patch。 Request body 是 {uuid, config},而 config 必须是 完整、有效 的 Xray 配置。如果只给一段 fragment,就会报错 A061: Config doesn't have inbounds。正确序列是:先 GET /api/config-profiles/{uuid} → 在返回的 config 对象上原地编辑 → 再把这个整体用 PATCH 写回。

  • 面板不会在 127.0.0.1:3000 上回应, 即使从面板主机自己访问也一样,即使 docker-proxy 在监听那里(curl 返回 exit 52,empty reply)。 一律使用带 Bearer token 的公网 HTTPS origin。

  • 每个响应都包在 {"response": ...} 里。 本 server 会去包,所以工具的 output 就是实际 payload。

  • 宿主 via 嵌套的 inbound.configProfileUuid (同时还有 inbound.configProfileInboundUuid),而不是顶层 configProfileUuid。当前测到过:嵌套存在,顶层不存在。

  • 一连若干次 PATCH 会把面板打挂。 每次配置写入都会把所有节点推送并重启 Xray;连续几次之后,面板自身的 TLS listener 会失去响应。这个 client 会串行化写操作(REMNAWAVE_WRITE_MIN_INTERVAL_MS,默认 1500ms),并带退避和抖动重试传输错误。请勿用并发批量更新绕过它。

  • POST /api/subscription-templates 只创建一个空模板。 内容是另外的 PATCH /api/subscription-templates 上传的;JSON 和 YAML body 无法在同一个调用里更新。

  • host 的 serverDescription 最多 30 个字符 (在 spec 里 maxLength 已确认)。它也是让一台 Hysteria2 host 在宿主里正确渲染,而不是输出乱七八糟 JSON 的地方。

  • 有 16 个 endpoints 是 admin JWT only —— 整个 authpasskeys controller,加上 token 管理(GET/POST /api/tokensDELETE /api/tokens/{uuid}GET /api/tokens/scopes)。面板在这些接口上用 401/403 回应 API token。本 server 会从 spec 里侦测并在本地拒绝;如果配置的 token 真的是 admin JWT,可用 REMNAWAVE_ALLOW_ADMIN_JWT_OPS=1 解锁。

  • GET /api/users/stream 返回的是 newline-delimited JSON,不是单个 document。 That endpoint 被解析为一条条 user record,返回给模型的是数组,而不是一段文本 blob。

  • PATCH /api/hosts 真正的部分 patch —— 只需 {uuid, serverDescription} 就能成功。只有 config-profile 是那种“整个替换”语义。 This was verified live.

  • 错误会这样返回:{message, errorCode};server 的错误文案里已经包含 errorCode,比如 A061 直接出现。

Tool coverage

每个 controller 都可以通过 remnawave_callescape hatches 访问。typed 列说明了在 REMNAWAVE_TOOL_PROFILE=core 下哪些才会生成具体工具。

[rest of the README continues]

控制器 slug

操作数

core 下是否类型化

users

17

node-plugins

18

nodes

15

infra-billing

12

internal-squads

12

system

12

users-bulk-actions

10

config-profiles

9

external-squads

8

auth

7

bandwidth-stats

7

connections

7

hosts

7

hwid-user-devices

7

subscription-page-configs

7

subscriptions

7

subscription-template

6

node-integrations

5

passkeys

5

snippets

5

api-tokens

4

hosts-bulk-actions

4

metadata

4

public-subscription

3

remnawave-settings

2

subscription-request-history

2

subscription-settings

2

keygen

1

总计

205

请对真实运行的服务器执行 remnawave_list_operations,以获得确切、最新的集。

示例

无需任何类型化工具即可浏览和调用:

// 1. What is there?
{ "tool": "remnawave_list_operations", "arguments": { "controller": "nodes" } }

// 2. What does it take?
{ "tool": "remnawave_describe_operation",
  "arguments": { "operation": "remnawave_post_nodes_uuid_actions_restart" } }

// 3. Do it.
{ "tool": "remnawave_call",
  "arguments": { "operation": "remnawave_post_nodes_uuid_actions_restart",
                 "params": { "uuid": "…" } } }

安全地编辑配置档案(A061 陷阱):

// Read the whole profile first — PATCH replaces the config wholesale.
{ "tool": "remnawave_call",
  "arguments": { "operation": "remnawave_get_config_profiles_uuid", "params": { "uuid": "…" } } }

// Send the full, edited config back.
{ "tool": "remnawave_call",
  "arguments": { "operation": "remnawave_patch_config_profiles",
                 "params": { "body": { "uuid": "…", "config": { /* complete Xray config */ } } } } }

规范无法表达的查询语法:

{ "tool": "remnawave_request_read",
  "arguments": { "path": "/api/users",
                 "query": { "size": 25, "start": 0,
                            "filters[0][id]": "status", "filters[0][value]": "ACTIVE" } } }

测试

npm run build
npm test            # 50 unit tests + the offline smoke suite
npm run test:unit   # unit tests alone

单元测试覆盖了这些静默失败的部分:穿过 Remnawave 递归 DTO 的 $ref 展开、工具名称派生(长度预算、确定性、冲突检测)、catalog 差异、两道写入闸门、admin-JWT 闸门、schema 折叠以及 NDJSON 解析。

针对真实面板的只读检查

如果存在可访问的面板,并且环境中有只读令牌,那么冒烟脚本还会执行 live 只读调用(绝不进行变更):

REMNAWAVE_BASE_URL=https://panel.example.com \
REMNAWAVE_API_TOKEN_READ="$REMNAWAVE_API_TOKEN" \
npm run smoke

请在令牌已经存在的地方运行它(例如在面板主机上),这样机密就不会被传输。脚本打印的是形状——类型、键名、数组长度——而不是 payload 值,因此其输出可以安全地粘贴到 issue 中。

护栏检查刻意指向 http://127.0.0.1:9,因此任何一个失败时放开访问的闸门都不可能触达真实面板。

验证写入路径

只读操作无法证明令牌路由、限流、确认闸门和 partial-patch 语义确实有效。scripts/write-check.mjs 会在没有以上关联的对象上证明这些机制,并恢复它碰过的一个预先存在的对象:

REMNAWAVE_BASE_URL=https://panel.example.com \
REMNAWAVE_API_TOKEN_READ="$T" REMNAWAVE_API_TOKEN_WRITE="$T" \
node scripts/write-check.mjs --i-understand-this-mutates [--host-uuid <uuid>]

它会创建一个没有 inbounds、没有成员的 internal squad,然后再次将其删除,并在一台主机上改写 serverDescription,最后恢复原值。没有确认标志它拒绝启动,如果留下任何残留,则会以非零退出码结束。

在真实运行的 3.3.2 面板上,它证实了:确认闸门在真实的 DELETE 上生效;partial PATCH /api/hosts 可以正常工作;面板拒绝了 31 个字符的 serverDescription;原始值(包括 null)可以往返;并且在配置了 1500 毫秒下限的情况下,连续两次变更被间隔为 1525 毫秒和 1524 毫秒。

在本地检查

REMNAWAVE_BASE_URL=https://panel.example.com REMNAWAVE_API_TOKEN_READ=xxx npm run inspect

更新 API 规范

有关 API 的一切都来自同一个文件,因此跟踪新的 Remnawave 版本只是一个命令:

npm run update-spec            # fetch the newest spec + rebuild the catalogue
npm run update-spec -- --strict  # additionally fail if any operation disappeared or was renamed
npm run build && npm test      # compile and verify

规范的来源

https://cdn.remna.st/docs/openapi.json——由 Remnawave 自己的 Build&Push OpenAPI Specs 工作流在每个上游 tag 上发布,因此它总是描述最新版本。可以使用 --url <u>REMNAWAVE_SPEC_URL 覆盖以固定到其他来源。

面板实例不是可用的来源:除非部署时启用了文档,否则文档是被禁用的。而且即使启用了,Swagger 也被挂载在 /backend-tools/swagger 下,而普通的反向代理不会路由该路径。探测真实运行的 3.3.2 面板时,所有传统 spec 路径都返回 404。

下载只有在成功解析为包含非空 paths 的 OpenAPI 文档时才会写入磁盘,因此错误页或强制门户无法破坏一份可用的 spec。

之后要检查什么

npm run build-spec 会对新 catalog 和之前的 catalog 进行 diff,并打印所有变更:

build-spec: Remnawave API v3.4.0 -> 211 operations, 28 controllers, 315 KB
  methods: DELETE=22 GET=90 PATCH=19 POST=78 PUT=2  admin-JWT-only: 16
  diff: API version 3.3.2 -> 3.4.0
  REMOVED — tools that will disappear (1):
    remnawave_get_old_thing  (GET /api/old-thing)
  added (7):
    ...
  • REMOVED / RENAMED 对于任何在 prompt 或脚本中引用这些工具的人来说都是破坏性的。--strict 会将它们变为非零退出状态,这是自动化应该使用的标志。

  • added 则是安全的;新操作可以通过 remnawave_call 立即触达,并且如果它们的 controller 位于活动 profile 中,则还会获得类型化工具。

  • schema changed 对于你实际使用的操作值得瞥一眼。

然后,npm test 会重新检查磁盘上的 catalog 是否与新构建匹配,所有工具名是否唯一且都在 64 字符范围内,以及护栏是否仍然成立。

自动化它

npm run update-spec -- --strict   # exits non-zero on a breaking catalogue change
npm test
npm version minor --no-git-tag-version
git commit -am "chore: Remnawave API 3.4.0" && git push
git tag "v$(node -p "require('./package.json').version")" && git push --tags

推送的 tag 会触发 release 工作流,该工作流重新发布到 npm。注册了 @folexz/remnawave-latest 的客户端会在下次启动时获取新版本。

发布(维护者)

首次发布——必须手动

npm 无法为尚不存在的包配置受信任发布者:该设置位于包自己的 settings 页面上。这是一个已知且仍然未关闭的限制(npm/cli#8544),并且它同样适用于 scope 包。因此,版本 0.1.0 只能从已登录的机器上发布:

npm whoami            # must print the account that owns the @folexz scope
npm publish --access public

必须使用 --access public:scoped 包默认为 restricted

然后切换为无令牌发布

一旦包存在,在 npmjs.com → @folexz/remnmal-t → Settings → Trusted Publisher 添加一个 GitHub Actions 发布者,仓库为 folexz/remnawave-mcp,工作流为 release.ymlpackage.json 中的 repository.url 必须与 GitHub 仓库完全匹配——它确实完全匹配。

之后,.github/workflows/release.yml 会在任何推送的 vX.Y.Z tag 上通过 OIDC 发布——无需令牌、无需机密,自动附带 provenance:

npm version patch --no-git-tag-version
git commit -am "chore: v0.1.1"
git push
git tag v0.1.1 && git push origin v0.1.1

工作流会根据 lockfile 重新安装依赖、重新构建、运行单元测试和离线冒烟测试套件,并在 tag 与 package.json 不匹配时快速失败。

注册了 @folexz/remnawave-mcp@latest 的客户端会在下次启动时获取新版本。

安全说明

  • 令牌仅从环境中读取,绝不记录。日志进入 stderr;stdout 是 MCP JSON-RPC 通道。

  • 优先只配置 REMNAWAVE_API_TOKEN_READ。没有写令牌就没有变更类型化工具的存在,因此受损或已混淆的客户端无法改变面板。

  • 订阅端点返回可工作的客户端配置。请将其输出当作机密。

  • 绝不提交真实令牌。.env 被 gitignore 忽略;.env.example 显示了形状。

已知限制

  • body 校验被委托给面板。 该服务器只检查必需的参数和一个必需的 body 是否存在;它不会针对 schema 验证 body 的内部结构。这是有意为之——面板已经验证了每个字段,并返回精确的 message + errorCode(例如 A061),在本地复制这些规则将意味着要附带一个 JSON Schema 验证器以及另一批不可避免会漂移的规则副本。其代价是一个损坏的 body 需要一个往返才能发现。

  • 逃生舱会绕过按操作划分的门禁。 remnawave_request_write 是两个逃生舱中更原始的一个:它仍然要求写令牌,并且仍然经过 throttling 和重试逻辑,但由于无法从无写令牌中查找,它不能应用破坏性的 confirm 门或 admin-JWT 检查。除非你绝对需要一个 spec 未描述的路由,否则优先使用 remnawave_call

  • 工具只与随附的 spec(v3.3.2)一样新。 一个不同的 minor 版本的面板可能会暴露新路由,该 spec 没有描述这些路由;这正是逃生舱存在的意义。请参阅 更新 API spec

  • admin-JWT 端点是有门禁的,而不是实现的。 此服务器携带 API 阶段;它不执行管理登录、不持有管理页会话,也不刷新 JWT。如果你用 admin JWT 作为令牌,并设置 REMNAWAVE_ALLOW_ADMIN_JWT_OPS=1,那么这 16 个端点可以访问,但过期和续期是你的问题。

  • The Prometheus basic-auth metrics endpoint 不是本 spec 的一部分,并且没有公开。

  • write-check.mjs 执行 mutation。 这是一个维护者工具,从 npm test 中剔除,并且在没有显式确认标志的情况下拒绝运行。

License

MIT — 请参阅 LICENSE| 控制器 slug | 操作数 | 在 core 下是否类型化 | | --- | --- | --- | | users | 17 | 是 | | node-plugins | 18 | — | | nodes | 15 | 是 | | infra-billing | 12 | — | | internal-squads | 12 | 是 | | system | 12 | 是 | | users-bulk-actions | 10 | 是 | | config-profiles | 9 | 是 | | external-squads | 8 | — | | auth | 7 | — | | bandwidth-stats | 7 | — | | connections | 7 | — | | hosts | 7 | 是 | | hwid-user-devices | 7 | — | | subscription-page-configs | 7 | — | | subscriptions | 7 | — | | subscription-template | 6 | — | | node-integrations | 5 | — | | passkeys | 5 | — | | snippets | 5 | — | | api-tokens | 4 | — | | hosts-bulk-actions | 4 | 是 | | metadata | 4 | — | | public-subscription | 3 | — | | remnawave-settings | 2 | — | | subscription-request-history | 2 | — | | subscription-settings | 2 | — | | keygen | 1 | — | | 总计 | 205 | |

请对真实运行的服务器执行 remnawave_list_operations,以获取精确、当前的集合。

示例

无需任何类型化工具即可浏览和调用:

// 1. What is there?
{ "tool": "remnawave_list_operations", "arguments": { "controller": "nodes" } }

// 2. What does it take?
{ "tool": "remnawave_describe_operation",
  "arguments": { "operation": "remnawave_post_nodes_uuid_actions_restart" } }

// 3. Do it.
{ "tool": "remnawave_call",
  "arguments": { "operation": "remnawave_post_nodes_uuid_actions_restart",
                 "params": { "uuid": "…" } } }

安全地编辑配置档案(A061 陷阱):

// Read the whole profile first — PATCH replaces the config wholesale.
{ "tool": "remnawave_call",
  "arguments": { "operation": "remnawave_get_config_profiles_uuid", "params": { "uuid": "…" } } }

// Send the full, edited config back.
{ "tool": "remnawave_call",
  "arguments": { "operation": "remnawave_patch_config_profiles",
                 "params": { "body": { "uuid": "…", "config": { /* complete Xray config */ } } } } }

规范无法表达的查询语法:

{ "tool": "remnawave_request_read",
  "arguments": { "path": "/api/users",
                 "query": { "size": 25, "start": 0,
                            "filters[0][id]": "status", "filters[0][value]": "ACTIVE" } } }

测试

npm run build
npm test            # 50 unit tests + the offline smoke suite
npm run test:unit   # unit tests alone

单元测试覆盖了那些静默失败的部分:通过 Remnawave 递归 DTO 的 $ref 展开、工具名称派生(长度预算、确定性、冲突检测)、catalog 差异、两道写入闸门、admin-JWT 闸门、schema 折叠和 NDJSON 解析。

针对真实面板的只读检查

既有面板可访问,且在环境中具有只读令牌时,冒烟脚本还会运行实时的只读调用(绝不进行变更):

REMNAWAVE_BASE_URL=https://panel.example.com \
REMNAWAVE_API_TOKEN_READ="$REMNAWAVE_API_TOKEN" \
npm run smoke

请在令牌已经存在的位置运行它(例如在面板主机上),这样机密就不会传输。脚本打印的是形状——类型、键名、数组长度——而不是 payload 值,因此其输出可以安全粘贴到 issue 中。

护栏检查刻意指向 http://127.0.0.1:9,因此一个失败放行的闸门也不可能触达真实面板。

验证写入路径

只读操作无法证明标志路由、限流、确认闸门和 partial-patch 语义确实有效。scripts/write-check.mjs 在没有人关注的对象上证明了这些,并将它所触碰的一个已有对象还原:

REMNAWAVE_BASE_URL=https://panel.example.com \
REMNAWAVE_API_TOKEN_READ="$T" REMNAWAVE_API_TOKEN_WRITE="$T" \
node scripts/write-check.mjs --i-understand-this-mutates [--host-uuid <uuid>]

它会创建一个没有 inbounds、没有成员的内部 squad,然后再次将其删除,再在一台主机上重写 serverDescription 并恢复原始值。没有确认标志它拒绝启动,并且如果有任何残留,会报告非零退出状态。

在真实运行的 3.3.2 面板上,它确认了:确认闸门在真实的 DELETE 上生效;partial PATCH /api/hosts 可用;面板拒绝 31 个字符的 serverDescription;原始值(包括 null)能够往返;在配置了 1500 毫秒下限的情况下,连续两次变更分别间隔了 1525 毫秒和 1524 毫秒。

本地检查

REMNAWAVE_BASE_URL=https://panel.example.com REMNAWAVE_API_TOKEN_READ=xxx npm run inspect

更新 API 规范

关于 API 的一切都来自一个文件,因此跟踪新的 Remnawave 版本只需一个命令:

npm run update-spec            # fetch the newest spec + rebuild the catalogue
npm run update-spec -- --strict  # additionally fail if any operation disappeared or was renamed
npm run build && npm test      # compile and verify

规范的来源

https://cdn.remna.st/docs/openapi.json —— 由 Remnawave 自己的 Build&Push OpenAPI Specs 工作流在每次上游标签上发布,因此它始终描述最新版本。可以使用 --url <u>REMNAWAVE_SPEC_URL 覆盖以固定到其他源。

面板实例不是可用的源:除非部署中启用了文档,否则文档是被禁用的;而且即使启用了,Swagger 也挂载在 /backend-tools/swagger 上,常规反向代理不会路由。对真实运行的 3.3.2 面板进行探测,在所有常规规范路径都返回了 404。

下载内容只有在被解析为具有非空 paths 的 OpenAPI 文档后才会写入磁盘,因此错误页或强制门户网站无法损坏正在工作的 spec。

之后要检查什么

build-spec 会对比上一目录的目录结构,并打印每个变更:

build-spec: Remnawave API v3.4.0 -> 211 operations, 28 controllers, 315 KB
  methods: DELETE=22 GET=90 PATCH=19 POST=78 PUT=2  admin-JWT-only: 16
  diff: API version 3.3.2 -> 3.4.0
  REMOVED — tools that will disappear (1):
    remnawave_get_old_thing  (GET /api/old-thing)
  added (7):
    ...
  • REMOVED / RENAMED 对这种双方是破坏性的;对于在提示或脚本中命名这些工具的人来说,更是破坏性。--strict 会将它们变成非零退出,这是自动化应该使用的标志。

  • added 是安全的;新操作可以通过 remnawave_call 立即访问它们的 controller 位于活动配置档中时会获得类型化工具。

  • schema changed 对于你实际使用的操作值得一看。

npm test 然后会再次检查磁盘上的目录是否与新的构建匹配,所有工具名称是否唯一且不超过 64 字符预算,并且护栏仍然有效。

自动化

npm run update-spec -- --strict   # exits non-zero on a breaking catalogue change
npm test
npm version minor --no-git-tag-version
git commit -am "chore: Remnawave API 3.4.0" && git push
git tag "v$(node -p "require('./package.json').version")" && git push --tags

推送的标签会触发发布工作流,该工作流会重新发布到 npm。注册了 @folexz/remnawave-mcp@latest 的客户端会在下次启动时获取新的版本。

发布(维护者)

首次发布 —— 必然手动

npm 无法为某个不存在的包配置受信任的发布者:该设置位于包自己的设置页面上。这是一个已知且仍存未解决的问题(npm/cli#8544),同样适用于作用域包。所以 0.1.0 版本就讲必须从已登录的机器上发布:

npm whoami            # must print the account that owns the @folexz scope
npm publish --access public

注意:必须使用 --access public:scoped 包默认是 restricted 的。

然后切换到无令牌发布

包一旦存在,在 npmjs.com → @folexz/remnawave-mcp → Settings → Trusted Publisher 中,选择添加 GitHub Actions publisher,repository 为 folexz/remnawave-mcp,workflow 为 release.ymlpackage.json 中的 repository.url 必须与 GitHub 仓库完全一致即可。

在那之后,.github/workflows/release.yml 会在任何推送的 vX.Y.Z 标签上通过 OIDC 发布——无需 token,无需 secret,自动附带 provenance 出处:

npm version patch --no-git-tag-version
git commit -am "chore: v0.1.1"
git push
git tag v0.1.1 && git push origin v0.1.1

该工作流会根据 lockfile 重新安装、重新构建、运行单元测试和离线冒烟测试套件,并在标签与 package.json 不匹配时快速失败。

注册了 @folexz/remnawave-mcp@latest 的客户端下次启动时会选择新版本。

安全注意事项

  • 令牌从环境中读取,并且永不记录。日志输出到 stderr;stdout 是 MCP JSON-RPC 通道。

  • 最好只配置 REMNAWAVE_API_TOKEN_READ。没有写令牌就不存在变更类工具,因此受感染或困惑的客户端也无法改变面板。

  • 订阅端点返回的是工作配置。请将其输出视为机密。

  • 绝不要提交真实的令牌。.env 已被 gitignore;.env.example 说明了格式。

已知限制

  • body 的参数验证被委托给了面板。 此服务器只检查赋值和必需的 body 是否存在,不检查 body 内部结构是否符合 schema。这是有意为之——面板已经验证了每个字段并返回精确的 messageerrorCode(例如 A061),在本地重复验证意味着要引入 JSON Schema 校验器,并且再加上一套必然漂移的重复规则。这样做的代价是,m is malformed 的 body 需要一次往返才能发现。

  • 逃生通道会绕过按操作分组定义的闸门。 remnawave_request_write 是 raw 设计;它仍然要求写入令牌,并且仍然经过限速和重试规范,但因为它没有可操作的操作来辨识,它不应用破坏性的 confirm 网关或 admin-JWT 检查。除非需要 spec 未描述的路由,否则应优先使用 remnawave_call

  • 工具只与附带的 spec(v3.3.2)保持一致。 在另一个小版本的面板上,可能会暴露没有描述的 route;这正是逃生通道的作用。见 更新 API spec

  • Admin-JWT 端点是被闸门,而不是实现。 服务器携带 API 凭据;它不执行维护者登录、不持有会话,也不刷新 JWT。如果提供 admin JWT 作为 token 并设置 REMNAWAVE_ALLOW_ADMIN_JWT_OPS=1,那 16 个端点可以调用,但过期和续期就是你的问题。

  • Prometheus basic-auth metrics 端点 不是这个 spec 的一部分,同时也未暴露。

  • write-check.mjs 会变更。 它是维护者工具,已从 npm test 中排除,并且没有确认标志时拒绝运行。

许可证

MIT — 请见 LICENSE。| 控制器 slug | Operations | 在 core 下是否类型化 | | ------------------------------ | ---------- | ------------------ | | users | 17 | 是 | | node-plugins | 18 | — | | nodes | 15 | 是 | | infra-billing | 12 | — | | internal-squads | 12 | 是 | | system | 12 | 是 | | users-bulk-actions | 10 | 是 | | config-profiles | 9 | 是 | | external-squads | 8 | — | | auth | 7 | — | | bandwidth-stats | 7 | — | | connections | 7 | — | | hosts | 7 | 是 | | hwid-user-devices | 7 | — | | subscription-page-configs | 7 | — | | subscriptions | 7 | — | | subscription-template | 6 | — | | node-integrations | 5 | — | | passkeys | 5 | — | | snippets | 5 | — | | api-tokens | 4 | — | | hosts-bulk-actions | 4 | 是 | | metadata | 4 | — | | public-subscription | 3 | — | | remnawave-settings | 2 | — | | subscription-request-history | 2 | — | | subscription-settings | 2 | — | | keygen | 1 | — | | 总计 | 205 | |

请对真实运行的服务器执行 remnawave_list_operations,以获取精确、当前的操作集。

示例

无需任何类型化工具即可浏览和调用:

// 1. What is there?
{ "tool": "remnawave_list_operations", "arguments": { "controller": "nodes" } }

// 2. What does it take?
{ "tool": "remnawave_describe_operation",
  "arguments": { "operation": "remnawave_post_nodes_uuid_actions_restart" } }

// 3. Do it.
{ "tool": "remnawave_call",
  "arguments": { "operation": "remnawave_post_nodes_uuid_actions_restart",
                 "params": { "uuid": "…" } } }

安全地编辑配置档案(A061 陷阱):

// Read the whole profile first — PATCH replaces the config wholesale.
{ "tool": "remnawave_call",
  "arguments": { "operation": "remnawave_get_config_profiles_uuid", "params": { "uuid": "…" } } }

// Send the full, edited config back.
{ "tool": "remnawave_call",
  "arguments": { "operation": "remnawave_patch_config_profiles",
                 "params": { "body": { "uuid": "…", "config": { /* complete Xray config */ } } } } }

规范无法表达的查询语法:

{ "tool": "remnawave_request_read",
  "arguments": { "path": "/api/users",
                 "query": { "size": 25, "start": 0,
                            "filters[0][id]": "status", "filters[0][value]": "ACTIVE" } } }

测试

npm run build
npm test            # 50 unit tests + the offline smoke suite
npm run test:unit   # unit tests alone

单元测试覆盖了那些悄悄失败的环节:$ref 在 Remnawave 递归 DTO 中的展开、工具名派生(长度预算、确定性、冲突检测)、catalog 差异、两道写入闸门、admin-JWT 闸门、schema 折叠和 NDJSON 解析。

对真实面板的只读检查

在可访问到面板且环境中已存在只读令牌的情况下,冒烟脚本还会运行实时的只读调用(绝不执行变更):

REMNAWAVE_BASE_URL=https://panel.example.com \
REMNAWAVE_API_TOKEN_READ="$REMNAWAVE_API_TOKEN" \
npm run smoke

请在令牌已经存在的位置(如面板主机上)运行它,这样秘密信息就不会被传输。脚本打印形状——类型、键名、数组长度——而不打印所设置的值,因此其输出可以安全地粘贴到 issue 中。

护栏检查特意指向 http://127.0.0.1:9,因此如果一个关卡失败但被开放,也不可能触达真实面板。

验证写入路径

读取操作无法证明令牌路由、限流、确认闸门和 partial-patch 语义真正有效。scripts/write-check.mjs 可以在无任何附加对象的对象上验证它们,并恢复它触碰过的一个已有对象:

REMNAWAVE_BASE_URL=https://panel.example.com \
REMNAWAVE_API_TOKEN_READ="$T" REMNAWAVE_API_TOKEN_WRITE="$T" \
node scripts/write-check.mjs --i-understand-this-mutates [--host-uuid <uuid>]

它会创建一个无入站、无成员的内部 squad,然后删除它,接着改写一台主机上的 serverDescription 并恢复原始值。它拒绝启动时必须带有确认标志,并在有残留时返回退出代码非零。

在真实的 3.3.2 面板上,它确认了:确认闸门在真实的 DELETE 上生效;partial PATCH /api/hosts 可用;面板拒绝长度为 31 的 serverDescription;原始值(包括 null)署往返;以及连续变更在配置的 1500 ms 下限下,实际间隔分别为 1525 ms 和 1524 ms。

在本地检查

REMNAWAVE_BASE_URL=https://panel.example.com REMNAWAVE_API_TOKEN_READ=xxx npm run inspect

更新 API 规范

关于 API 的一切都来自一个文件,因此跟踪新的 Remnawave 版本只需一个命令:

npm run update-spec            # fetch the newest spec + rebuild the catalogue
npm run update-spec -- --strict  # additionally fail if any operation disappeared or was renamed
npm run build && npm test      # compile and verify

规范来自哪里

https://cdn.remna.st/docs/openapi.json —— 由 Remnawave 自己的 Build&Push OpenAPI Specs 工作流在每一个上游 tag 上自动发布,因此描述的是最新版本。可以用 --url <u>REMNAWAVE_SPEC_URL 覆盖,以固定到不同的源。

它不是一个可用的来源:文档在未经部署时被禁用,并且即使启用,Swagger 被挂载在 /backend-tools/swagger,常规反向代理也不会路由。对实时的 3.3.2 面板进行探测,所有常规 spec 路径都返回 404。

当且仅当选中的下载能被解析为具有非空 paths 的 OpenAPI 文档时,该文件才会写入磁盘,因此一个错误页面或强制门户不能覆盖已有的可用 spec。

之后要检查什么

build-spec 将新目录与之前的目录进行 diff,并打印每次更改:

build-spec: Remnawave API v3.4.0 -> 211 operations, 28 controllers, 315 KB
  methods: DELETE=22 GET=90 PATCH=19 POST=78 PUT=2  admin-JWT-only: 16
  diff: API version 3.3.2 -> 3.4.0
  REMOVED — tools that will disappear (1):
    remnawave_get_old_thing  (GET /api/old-thing)
  added (7):
    ...
  • REMOVED / RENAMED 对任何在提示词或脚本中引用这些工具的人来说都是破坏性的。--strict 把它们变成非零退出码,这正是自动化应该使用的标志。

  • added 是安全的;新操作立即可通过 remnawave_call 访问,并且如果它们的控制器在活动档案中,会获得类型化工具。

  • schema changed 对于你实际使用的操作,值得一看。

然后 npm test 会重新检查磁盘上的目录是否与新构建匹配、所有工具名是否唯一且符合 64 字符预算,以及护栏仍然坚持。

自动化

npm run update-spec -- --strict   # exits non-zero on a breaking catalogue change
npm test
npm version minor --no-git-tag-version
git commit -am "chore: Remnawave API 3.4.0" && git push
git tag "v$(node -p "require('./package.json').version")" && git push --tags

被推送的 tag 会触发 release 工作流并重新发布到 npm。注册了 @folexz/remnawave-mcp@latest 的客户端下次启动时会接管新版本。

发布(维护者)

第一次发布——必须手动

npm 无法为一个尚不存在的包配置受信任的发布者:该设置在包自己的设置页中。这是一个已知且未解决的限制(npm/cli#8544),并且它也适用于 scoped 包。所以 0.1.0 版本必须在已登录的机器上发布:

npm whoami            # must print the account that owns the @folexz scope
npm publish --access public

必须使用 --access public:作用域包默认是 restricted

然后切换到无令牌发布

包一旦存在,在 npmjs.com → @folexz/remnawave-mcp → Settings → Trusted Publisher 中,添加一个 GitHub Actions publisher,设置 repository 为 folexz/remnawave-mcp,workflow 为 release.yml。而 package.json 中的 repository.url 必须严格匹配 GitHub 仓库——确实如此。

此后,.github/workflows/release.yml 会在任何推送的 vX.Y.Z tag 上通过 OIDC 发布——无需 token、无 secret,让自动挂上 provenance 出处,发布完毕。

npm version patch --no-git-tag-version
git commit -am "chore: v0.1.1"
git push
git tag v0.1.1 && git push origin v0.1.1

该工作流会根据 lockfile 重新安装依赖、重新构建、运行单元测试和离线冒烟测试套件,并在标签与 package.json 不匹配时快速失败。

注册了 @folexz/remotool-mcp 的客户端会在下一次启动时获取新版本。

安全说明

  • 令牌仅从环境变量中读取,绝不记录。日志输出到 stderr;stdout 是 MCP JSON-RPC 通道。

  • 请优先只配置 REMNAWAVE_API_TOKEN_READ。有的令牌没有写入 token,因此不存在变异工具,受到感染的客户端也无法篡改面板。

  • 订阅端点返回可用的客户端配置。请将其输出视为机密。

  • 绝不提交真实令牌。.env 位于 gitignore;.env.example 展示的是格式。

已知限制

  • body 校验被委托给后端。 本服务器只要求必需参数和必需的 body 存在;它不会根据 schema 验证 body 的语义。这是有意为之——面板已经验证了每个字段并返回精确的 message + errorCode(例如 A061),在本地重复该功能将意味着附带一个 JSON Schema 校验器,以及第二份不可避免地会漂移的校验规则。这意味着一个格式不正确的 body 需要一次往返才能失败。

  • 逃生口绕过了逐操作的确认闸门。 remnawave_request_write 设计成 raw:它仍然需要写入的 token,并且仍然通过 throttle 和重试逻辑,但由于没有对应的操作可查,它不采用破坏性的 confirm gate 或 admin-JWT 检查。只有在需要 spec 不描述的路由时,才最好使用 remnawave_call

  • 工具只能与附带的 spec(v3.3.2)一样新。 使用不同小版本的面板可能暴露未描述的 spec 路由,这正是逃生门的存在意义。 参见 更新 API 规范

  • Admin-JWT 端点被 gate,但未实现。 该服务器仅携带 API JWT,不执行管理员登录、不保留会话,也不刷新 JWT。如果您把 admin JWT 作为令牌并提供,并设置 REMNAWAVE_ALLOW_ADMIN_JWT_OPS=1,该 16 个端点变为可调用,但过期和续签是您的问题。

  • Prometheus basic-auth 指标端点 不是此规范的一部分,也并未公开。

  • write-check.mjs 会执行改写。 它是维护者工具,被排除在 npm test 之外,并且如果没有显式确认指令就会拒绝运行。

许可证

MIT —— 见 LICENSE

-
license - not tested
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • 34 production API tools over one hosted MCP endpoint.

  • Official Sevalla MCP — full PaaS API access through just 2 tools.

  • MCP Server for agents to onboard, pay, and provision services autonomously with InFlow

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/folexz/remnawave-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server