Skip to main content
Glama

hq-mcp

俄语版 · English

MCP 服务器,让 AI 代理能够访问 VPN 业务:计费系统 SHM 和面板 Remnawave,两者被缝合在一起,使得横跨两套系统的问题可以用一次调用回答。

没有任何工具会在被请求的同一调用中改变任何东西:写操作首先返回一个计划,而应用是第二次调用,携带该计划的标识符。

实际运行效果

在任何安装上的第一次调用是 platform_probe。它回答这个部署中到底有什么、其中哪些是活的;这里的一切都从它报告的内容派生而来。下面的回答被截断了,数值是虚构的。

platform_probe {}
{
  "shm":   { "configured": true, "reachable": true, "version": "2.19.4", "live": true },
  "remna": { "configured": true, "reachable": true, "version": "3.2.3",
             "runtime": { "instances": 6, "youngestUptimeSeconds": 54294 } },
  "capabilities": { "shm.filter": false, "remna.realtimeBandwidth": true,
                    "tunnel.mysql": false, "…": "…" },
  "warnings": [{ "code": "specs_are_stale", "message": "…" }]
}

接下来是一个两套系统单独都无法回答的问题:「客户说付了款,但配置不存在」。

client_resolve { "query": "kot@example.com" }
{
  "shm":   { "count": 1, "matches": [{ "user_id": 4821, "email": "kot@example.com",
                                      "blocked": false }] },
  "remna": { "count": 0, "ambiguous": false,
             "paths": [{ "path": "email",   "tried": true, "found": 0, "note": null },
                       { "path": "service", "tried": true, "found": 0, "note": "…" }] }
}

面板对它一无所知,但这里的 count: 0 不是「账户不存在」:paths 列出了每一次经过的搜索以及它看不到什么。实际发生了什么,由第二双眼睛来说明:

provisioning_diagnose { "shm_user_id": 4821 }
{
  "verdict": "panel_user_missing",
  "services": { "items": 1, "diagnosed": [{
    "user_service_id": 90210,
    "status": "ACTIVE",
    "verdict": "panel_user_missing",
    "storage": { "name": "vpn_mrzb_90210", "present": true, "checked": true },
    "panel":   { "username": "HQVPN_90210", "id": 11274, "found": false, "checked": true },
    "spool":   { "total": 0, "stuck": 0, "failed": 0, "succeeded": 0 },
    "history": { "total": 1, "success": 1 }
  }] }
}

服务是 ACTIVE,配置快照就位,供应(provisioning)报告成功——但属于这个成功的用户却不在面板里。计费和面板单独都无法显示这一点。

读取两套系统的工具有三十四个。rw 模式增加了十五个写工具:十三个修改真实数据,一个应用计划,还有一个读取本地变更日志。

Related MCP server: xendit-mcp

为什么是复合工具,而不是端点代理

显而易见的构造是每个 HTTP 端点一个工具,大约一百五十个。它被写出来又扔掉了,原因有两个。

原始代理会清零任何禁止列表。如果模型能调用 GET <任意路径>,那么你决定不给的操作清单就只是装饰:到被禁止的路径只有一行之遥。这里的工具按名称调用指定的路由,而构建阶段的扫描器会在被禁止的路径以字面量形式出现在源码中时使构建失败。

而且端点不是一个问题。上面的例子涉及 SHM 的四个路由和面板的两个路由,而其中有趣的地方恰恰是接缝client_overviewsync_auditprovisioning_diagnose 之所以存在,是因为 bug 就活在这条接缝上。

决定其他一切的原则

空响应绝不能被当作已证实的「不存在」。

当后端拒绝时,工具会降级:拒绝进入 degraded,警告 partial_result 指出缺失的一半,而任何依赖那一半的发现都会被抑制,而不是从不完整的数据中计算出来。当列表被截断时,服务器端的 total 会一起返回——这样「没有这样的服务」就不会建立在未声明的窗口上。

这不是理论上的谨慎。在开发过程中,一个工具读取了面板的所有记录,把它们全部丢弃,因为字段在那一侧被重命名了,然后报告说数百个客户需要重新供应——这是一个破坏性的建议,说得信心满满,却是从空集合推导出来的。修复不仅仅是重命名字段:修复在于,从不可用的输入计算出来的结果必须拒绝成为发现。

兼容性:这能在你那里运行吗

已在 SHM 2.19.4Remnawave 3.2.3 上验证——两个数字都来自运行中的部署,而不是取自规范。

最低要求是 SHM 2.18.0 和 Remnawave 3.0.0。 官方 danuk/shm 适用:工具调用的所有路由都是上游的,不需要 fork。工具能看到该部署补丁的唯一地方是 GET /user/password-auth 的第四个标志;现在它的缺失被称为警告 sign_in_flag_absent,而不是被当作诊断。两套系统的完整路由清单、每个路由出现的版本以及关于 fork 的详细回答,见 COMPATIBILITY.md

检查只需一次调用——同样是 platform_probe。如果版本低于最低要求,它会以警告 backend_version_below_minimum 回应,指出版本、最低要求以及具体会失效什么。此时不会关闭任何东西:旧版本在特定路由上给出响亮的拒绝,而不是安静的空白响应。

版本

丢失的内容

影响谁

SHM < 2.18.0

GET /healthcheck — 唯一无需授权的路由

platform_probeshm.live 保持 null,「计费宕机」和「密码不对」不再可区分(shm_healthcheck_route_absent)。其他工具不受影响

SHM < 2.11.3

GET /admin/user/search

client_searchclient_resolve — 拒绝,而非空列表

SHM < 2.9.0

GET /user/referrals

client_account_state 丢失推荐人计数器

SHM < 2.4.0

GET /user/email

client_account_state 丢失地址和确认标志

面板 < 3.0.0

用户以 uuid 而非数字 id 寻址

client_overviewsubscription_inspecttraffic_statsprovisioning_diagnosesubscription_ops/api/users/{id} 被验证以 400 拒绝

面板 < 3.0.0

没有 /api/connections/*

connections_inspect — 整个工具

面板 < 3.0.0

没有 POST /api/users/{id}/actions/extend

subscription_ops 丢失续期(面板只剩批量续期)

面板 < 3.0.0

没有 /api/system/stats/digest/stats/http

panel_activity 丢失五个采样中的两个

面板 < 3.2.0

没有 GET /api/system/configuration

platform_proberemna.subscriptionRequestHistory 保持 unknown — 这是有意的,而不是 false

Remnawave 3.x 破坏了与所有为 2.x 编写的代码的兼容性,而且破坏得不安静。 用户对象中移除了 uuid,随之消失的还有 by-telegram-idby-emailby-tag 路由,而且 /api/users/{uuid} 返回 400 而不是 404——所以拒绝甚至不像「没有这样的用户」。这里完全没有这些路由;在服务器确实遇到遗留 uuid 的地方(例如在 SHM 存储的旧快照中),它会在响应中说明这一点,而不是默默滑向猜测。

OpenAPI 规范落后于运行中的系统,因此 platform_probe 在每次调用时都带有警告 specs_are_stale;SHM 比通常更糟——它的规范在运行时用配置中的 info.version 盖章,也就是说它描述的是导出时所在的站点,而不是你的站点。因此探测根本不从文件读取版本,而是向运行中的系统询问——并在同一处确定对这个部署来说什么是正确的:SHM 的服务器端 filter 是否会缩小范围,面板在用户列表中是否尊重 filters(两者都返回 200 并默默丢弃未知参数),面板是否记录订阅请求日志,realtime 流量路由是否存在,哪些 ssh 隧道是开放的。并且区分「后端宕机」和「我们的凭据不对」:401/403 被报告为 credentialsRejected

安装

需要 Node 22.12+ 和 pnpm,以及两套系统中至少一套——SHM 或 Remnawave。两者不是都必须:每套都可以单独配置,单独就是完整的配置。不存在的那个系统的工具根本不会发布——不是「返回空」,而是不存在,platform_probe 会直接说明配置了什么。因此工具数量取决于安装:只有面板 — 16 个,只有 SHM — 18 个,两者都有 — 34 个(在 rw 模式下更多)。

pnpm install
pnpm build
pnpm run setup

pnpm run setup,必须带 runpnpm setup 是 pnpm 本身的内置命令:它修改你的 shell 配置文件,不会到达这个仓库。

向导存在是因为它替代的步骤——手写 .env——会默默失败:面板令牌中的拼写错误不会阻止服务器启动,而是稍后以工具错误的形式出现在一个不相关的问题中间。因此它在运行中的系统上验证每个凭据并区分三种失败——主机完全没有响应(DNS、TLS、端口关闭),主机响应了但拒绝了凭据,主机响应了但证明不了任何东西(502、429):它们的修复方式不同,而一个「login failed」会让人去修错地方。

它只询问你拥有的那套系统以及访问模式;其他一切都收进一个问题 Configure the optional settings? [y/N]。时区从运行中的 SHM 读取,而不是猜测:SHM 用自己本地时间写日期而不带偏移,错误的时区会默默偏移每个年龄。不打印秘密。默认设置 ro;在 rw 上要求输入 rw 这个词并单独确认——在此之前说明会出现多少工具、其中多少写入真实的计费和真实的面板,按同一时刻的注册表计算。.env 以 0600 权限写入,覆盖旧副本,转移它没有询问的变量,并打印 Claude Code、Codex 和 opencode 的连接命令——但不修改别人的配置:一个重写 JSONC 的向导总有一天会弄坏某人的工作配置。可以随时重新运行,Enter 保留现有值。没有终端它拒绝启动:MCP 客户端启动的是服务器,没有 TTY,而能在那里醒来的向导会挂在一个没人看得见的问题上。

或者手动

cp .env.example .env && chmod 600 .env    # и заполнить

每个变量都在 .env.example 中描述。缺失或错误会导致启动失败,并指出变量名和期望值,而不是稍后以难以理解的工具错误出现。

{
  "mcpServers": {
    "hq": {
      "command": "node",
      "args": ["/absolute/path/to/hq-mcp/apps/stdio/dist/index.js"]
    }
  }
}

第二种传输:基于 HTTP 的 MCP

同一组工具也可以通过 HTTP 使用——当客户端无法自行启动进程时需要:它在容器里、在另一台机器上,或者有多个。一个独立的应用程序,配置来自同一个 .env

# метка произвольная (её показывает /metrics), токен — не короче 24 символов:
# openssl rand -hex 24
HQ_MCP_HTTP_TOKENS='<label>:<token>' pnpm --filter @hq/http start
# hq-mcp http ready: url=http://127.0.0.1:42480 mode=ro profile=human tools=34 …

没有 HQ_MCP_HTTP_TOKENS 它根本不会启动,而且会在建立到计费和面板的客户端之前就拒绝。监听回环;把它开放到网络——HQ_MCP_HTTP_HOST=0.0.0.0,并且会打印警告,因为服务器和网络之间将只剩下这个令牌。端口是 HQ_MCP_HTTP_PORT。客户端连接到 /mcp,用普通的 Authorization: Bearer 传递令牌:

{
  "mcpServers": {
    "hq": {
      "type": "http",
      "url": "http://127.0.0.1:42480/mcp",
      "headers": { "Authorization": "Bearer <тот же токен>" }
    }
  }
}

该路由是无会话的:Mcp-Session-Id 不发出也不要求,因此可以在反向代理后面运行多个进程副本而无需粘性连接。它没有服务器消息,因此对 SSE 流的 GET 和对关闭会话的 DELETE 返回 405——MCP 客户端能理解这一点。带有 Origin 头的请求会被 403 拒绝:防止 DNS rebinding,见「限制」。

旁边的 /v1/tools 不是 MCP,而是面向 ai-bot 的内部 REST 门面:一个列表句柄和一个调用句柄,有自己的响应信封和自己的请求上限。

Production HTTP image

Production-образ собирается только из проверенного 40-символьного lowercase commit SHA。该 SHA 同时被封入 OCI label、root 所有的只读文件和 /healthz;entrypoint 从文件中恢复该值,因此 runtime 覆盖 HQ_MCP_IMAGE_REVISION 不会改变 health evidence。

pnpm test && pnpm test:guards && pnpm typecheck && pnpm build
HQ_MCP_COMMIT_SHA="$(git rev-parse HEAD)"
test "${#HQ_MCP_COMMIT_SHA}" -eq 40
docker build --build-arg "HQ_MCP_DEPLOYMENT_REVISION=${HQ_MCP_COMMIT_SHA}" --tag "hq-mcp-http:${HQ_MCP_COMMIT_SHA}" .
scripts/http-container-smoke.sh "hq-mcp-http:${HQ_MCP_COMMIT_SHA}"

Compose 消费者固定使用这个 40 字符的 tag,并且不声明 host ports。容器以 UID/GID 10001 运行,在 bot+ro 模式下仅发布 /healthz 和 REST 门面,并要求共享的 non-secret HQ_MCP_DEPLOYMENT_CONFIG_REVISION,格式为 lowercase UUID。health 中包含两个 revision,以便 ai-bot 在不匹配时能在读取目录之前关闭。

Production 仅通过三个 regular non-symlink 文件传递机密,文件权限精确为 0600SHM_ADMIN_AUTH_FILEREMNA_API_TOKEN_FILEHQ_MCP_HTTP_TOKENS_FILE。最后一个只包含 ai-bot:<dedicated token>。这是服务器的独立 token;SHM 和 Remnawave 的凭据也单独分配给该 deployment,不会从 support bot 复用。

适配自己的安装环境

上游的 danuk/shm 完全不认识“Remnawave”这个词——一行都没有。计费与面板之间的桥接完全存在于您的 provisioning 模板中:面板的一个用户对应一个 user_service_id,名称为 <NAME_PREFIX><user_service_id>,配置快照存放在 SHM 的 storage 中,路径为 <STORAGE_PREFIX><user_service_id>。服务器在运行时从您的 SHM 的 config.remnawave 中读取这两个前缀,并允许覆盖(HQ_MCP_STORAGE_PREFIXHQ_MCP_PANEL_PREFIXES)——操作员比描述 SHM 明天将构建什么的配置键更清楚面板中今天有什么。

面板用户名是唯一的关联键,而一个不匹配任何内容的前缀不会产生错误:它会产生一个确定性的错误答案,其中每项服务看起来都未 provisioning。因此,能够证明这一点的工具会返回代码 prefix_unverified 并抑制受影响的发现——sync_audit 完全不返回 missingPanelUser 桶,provisioning_diagnose 用相同代码或 panel_username_guessed 标记结果。该约定恰好只对三个工具有意义(sync_auditprovisioning_diagnose、mutator storage_edit);client_overviewremna_user_id 作为可选参数接受,没有它时只是不显示面板的一半。如果您没有此约定,所有其他工具照常工作,而这三个工具不会编造发现。完整解析,包括前缀顺序和继承名称,请参见 COMPATIBILITY.md

工具

三十四个在 ro 模式下可见;rw 模式从最后一个表格中增加十五个,且不删除任何内容。这些数字针对 human 配置文件;bot 能看到什么在安全模型中说明。

平台与单个客户端

工具

回答什么

platform_probe

当前什么还活着:版本、能力、隧道,以及故障是事故还是凭据问题

client_resolve

将任何标识符(telegram id、email、登录名、id、面板中的名称)解析为两个系统的规范 id——返回所有匹配项,而非第一个

client_search

按片段搜索 SHM 客户端,带服务端匹配数

client_overview

一次调用查看两个系统中的完整客户端

client_account_state

账户如何登录:email 及其确认、OTP、passkey、是否可用密码登录、推荐

client_billing_view

客户端视角看钱:即将扣款以及实际提供给该客户端的支付方式

client_catalog_view

从单个客户端视角看目录和促销码——他的折扣、他的奖励、对他隐藏的套餐

资金、目录、配置

工具

回答什么

billing_ledger

付款、奖励、扣款以及两个独立的对账(余额和奖励是不同的列,更新路径不同)

autopay_inspect

自动付款状态及所有被扣的手续费——它位于支付行的 JSON 字段 comment 中,而非 user.settings

promo_read

促销码及其核销:这是不同的行,不能从同一处读取

catalog_read

套餐、订单价格、子服务、事件映射、分类——合法 service_id 的来源

config_read

从受限列表中读取一个 SHM 配置键,机密被掩码。不存在整体读取配置的操作

template_read

模板列表或恰好一个模板的正文——即产生通知或 provisioning 脚本的那个文件

服务与 provisioning

工具

回答什么

service_inspect

客户端服务:状态、期限、计划的下一个套餐、每项服务的 spool 任务

spool_inspect

provisioning 队列:卡住的、失败的、暂停的以及实际深度

provisioning_diagnose

“已付款但没有配置”——按每项服务而非按客户端

sync_audit

计费与面板的批量对账,两侧都完整读取

notify_history

是否真的告知了客户端,如果没有——为什么;任何其他工具都不显示的投递判定

server_inventory

SHM 自身的传输及其分组(ssh、http、mail、telegram)以及静默阻止 provisioning 的断裂。这不是 Remnawave 节点列表

面板——先客户端视角,再集群视角

工具

回答什么

subscription_inspect

Remnawave 卡片:状态、期限、流量、HWID 设备、最近的订阅请求。密钥——绝不

subpage_read

订阅页面实际向客户端展示什么:平台、应用、安装步骤、按钮链接

client_reach

该客户端实际能到达哪些节点,以及哪些入站 squad 和标签提供了这种可达性

device_inventory

整个集群的 HWID 视图——没有这个基础,单个客户端的设备数毫无意义

traffic_stats

按天、按节点和 squad 的流量;这是时间序列,而非卡片计数器

connections_inspect

当前谁已连接。面板通过 job 回答此问题,工具自行轮询

infra_map

节点 × 配置配置文件 × 入站 × 主机 × squad 以及它们之间的断裂

infra_costs

基础设施成本,与面板对接:已付费但无人可达的节点就是流失的资金

country_health

一个国家的节点、在线状态、流量和主机

node_config_audit

配置文件声明的内容与面板实际会提供给节点的内容之间的对比

squads_read

两类 squad:内部的决定访问权限,外部的决定订阅如何呈现

panel_activity

面板本身发生了什么:摘要、窗口期内的简报、哪些路由被调用、订阅请求历史

torrent_reports

种子封锁器的证据——以及,单独地,它是否已安装并正在监控

隧道之后(这两个没有隧道就会失败,并给出精确的 ssh 命令)

工具

回答什么

abuse_report

反滥用钩子的发现以及面板排行。代价高:对计费 MySQL 的无限制扫描,5 分钟内最多 5 次调用

sql_query

只读 SQL——仅预检,仅此而已,见下文

写入类(仅 rw,仅 human 配置文件,先计划)

工具

作用

billing_adjust

调整 SHM 客户的余额或奖励

billing_refund_service

将 SHM 记录为当前已付费期间扣除的金额退回余额

bulk_ops

对面板客户执行批量操作——按指定的 id 集合或整个机群

host_edit

单个 Remnawave 主机:标签、地址、端口、SNI/host/path/ALPN/fingerprint、安全层、标签、启用与隐藏

host_cleanup

按明确的 uuid 列表删除主机。不可逆

node_manage

单个节点:enable、disable、restart、reset_traffic、update、create

subscription_ops

面板中的单个订阅:enable、disable、extend、reset_traffic、revoke、set_limits、移除设备

service_lifecycle

客户服务:give、touch、change_plan、schedule_change、stop、activate、delete

provisioning_repair

retry、resume 或 pause 单个卡住的 spool 任务

template_edit

覆写现有 SHM 模板的内容

storage_edit

按此安装实例列出的键列表写入自定义 SHM storage

server_edit

SHM 的传输线路或传输组——webhook、ssh 配置端点、邮件发送器

user_flags

锁定客户或编辑卡片的安全字段(full_namephonecomment

ops_confirm

plan_id 应用计划。写入所计划工具会写入的内容

ops_audit

不写入任何内容。读取本地变更日志——标记为 rw 是因为日志本身是变更面的一部分

变更操作

没有任何操作是由请求它的那次调用直接应用的。 没有 plan_id 的变更器会读取当前状态、构建目标状态并返回计划:beforeafter、字段级 diff、副作用、存在时的 rollback,以及标识符。不写入任何内容。应用是第二次调用:

ops_confirm { "plan_id": "…" }          # либо: тот же мутатор, ТЕ ЖЕ аргументы, плюс plan_id

计划绑定到构建它的配置文件为其构建的工具以及参数的哈希:既不能由其他调用者执行,也不能由其他工具执行,也不能由同一工具以任何修改过的数字执行。计划存活 10 分钟。一次性是通过磁盘上的原子 rename 实现的,而不是"读取后删除":二十个并发确认中恰好有一个胜出,其余的都收到"未找到"。失败不会消耗有效的计划——所有检查都在获取之后进行,失败的计划会恢复到原位置;消耗计划的是尝试本身,如果后端崩溃,计划即被耗尽。这是有意为之,这就是一次扣款和三次扣款之间的区别。应用前,工具会重新读取世界状态,并与构建计划时的快照进行核对:对象发生移动——计划被拒绝,而不是覆盖在别人的变更之上。

每次尝试都会记录日志HQ_MCP_AUDIT_PATH(JSONL,权限 0600):谁、用什么、带什么参数、对象前后状态如何、结果如何——plannedapplyingappliedfailedrejected;失败与成功同等记录。applying访问后端之前写入,这正是此设计的全部意义:没有配对终止记录的日志条目意味着进程中途死亡,计划快照已被销毁,而资金可能已经转出。ops_audit 会在整个日志中搜索此类未关闭的记录,无论请求的时间窗口如何,并首先报告它们;无法解析的行会被计数,而不是被静默跳过。

上限由框架而非工具作者来维护。 超过 HQ_MCP_MAX_OP_AMOUNT 的变更操作在构建计划之前就被拒绝,框架拒绝注册声明了货币端点但未说明如何从其输入中读取金额的工具。上限覆盖两种资金流动方式,第二种很容易被忽略:既有调用者指定金额的支付和奖励,也有消耗客户余额的生命周期操作(givetouchchange_planactivate),其中金额是目录中的套餐价格。无法读取价格的计划不会发出:不知道数字并不会让扣款免费。HQ_MCP_MAX_BULK_USERS 限制单个批量操作可以影响的面板客户数量,无法从面板获取此数字的计划会被拒绝,而不是凭估计。超过上限的操作会被整体拒绝——绝不会被截断。

上限在构建计划时且仅在此处检查:应用按已构建的计划执行,不会重新测量。无法借此绕过上限——参数被哈希固定——但在计划发出后降低 .env 中的上限不会影响该计划。

template_editstorage_edit 首先将自身的回滚写入 HQ_MCP_BACKUP_DIR(目录 0700,文件 0600);路径在响应中返回,restore_from 将字节放回原处,没有拍摄快照两者都不会写入。备份与计划快照有意分离:模板主体和配置快照以裸子字符串形式携带机密,这些子字符串没有字段名可以遮蔽——因此它们不能通过 before/rollback 返回给模型;而且回滚必须能跨变更存活,而计划快照在一小时内就会被清理。

还有两件事写入操作拒绝执行。带有 <redacted:…> 标记的主体永远不会被写入:这是读取工具的输出,写入它会用隐藏凭据的词替换实时凭据。面板的原始 blob(finalMaskxhttpExtraParamsmuxParamssockoptParams)被排除在任何主机补丁之外——在运行中的安装实例中,相当一部分主机在 finalMask 内携带有效的 Hysteria2 密码。

实际验证了什么,什么没有

host_edit 是唯一一个应用分支在运行系统上测试过的变更器:在运行中的 Remnawave 3.2.3 面板上更改了主机标签,验证了 finalMask 中的密码完好无损,且除声明的字段外没有其他变化,然后回滚。所有其他变更器都验证到计划为止:计划基于真实数据构建,应用部分有测试覆盖,但其分支未在运行系统上测试过。这应该按字面理解。看起来正确的计划只是关于计划的证明。

安全模型

两个配置文件。 human 是受信任的操作员,获得具体可操作的失败信息,包括隧道关闭时的精确 ssh 命令。bot 是不可信通道:任何失败都被压缩为同一条消息,以防止通过探测哪些名称响应不同来枚举注册表。任何写入工具都不会提供给 bot:在 rw 配置文件中,bot 看到与 ro 中相同的二十个读取工具。

禁止类别,与单纯危险的操作分开。 这些操作不是被门禁关闭的——它们根本不存在,构建阶段的扫描器如果在源码中以字面量形式遇到它们的路径,就会使构建失败。节点的 identity 和 keygen 路由(GET,响应体包含私钥)。令牌、授权和 passkey 路由(面板以明文返回令牌,而创建的令牌是绕过所有门禁的永久管理员)。面板和订阅设置。完整导出 /admin/config。手动将 provisioning 任务标记为成功——它不执行工作,只是在面板中用户仍然不存在的情况下将服务转为 ACTIVE。删除支付、奖励或扣款——对注册表的裸 DELETE FROM,不重新计算 users.balance。现成的订阅链接和 connection-keys。restart-allreorder、squad 的 bulk-actions 以及带 job_usersPUT /admin/spool——向所有客户群发而无法取消。

该类别收窄了,每次收窄都是修复而非放宽。模板读取曾与写入一起被禁止,尽管原因——没有 git、没有回滚——只涉及写入;这种宽泛并非理论上的代价:在观察到的某个时间窗口内,相当一部分通知渲染为空且未发送任何内容,任务却报告 SUCCESS,而沉默的原因就在模板主体内部。现在读取已开放,POSTtemplate_edit 管理,它带来了回滚,而 PUTDELETE 被关闭:刚出现和刚消失的模板都没有可拍摄的先前状态。对 /api/sub 的禁令是前缀式的,同时覆盖了 /api/subscription-page-configs/api/subscription-request-history——两个不发放密钥的读取控制器;现在改为 exactprefix 指向 /api/sub/。对面板客户的批量操作曾被禁止,因为它们在没有可查看列表的情况下应用于整个数据库——这只有在没人计数时才成立:bulk_ops 在应用前向面板计数,无法确定数字或数字超过 HQ_MCP_MAX_BULK_USERS 时拒绝,且不提供给 bot。

POST /api/users/bulk/delete-by-status 仍然按其形式被禁止:其主体中是状态而非人员列表。面板将任务放入队列,并删除任务执行时匹配的人——而不是操作员查看过的人——并以空主体且无计数器的 202 响应,因此在此期间过期的账户会被不可见地删除。该能力保留为 bulk_ops delete_by_status:它枚举具体 id、显示它们,并通过 bulk/delete 精确删除它们。其他实体上的批量路由——主机、节点、squad、spool 群发——没有这样的计数步骤,仍然不存在。

机密在输出时被遮蔽——既按键名,也按值的形式。 按名称:凭据键的封闭列表,匹配 token|secret|key|password|auth 并带有明确的排除列表,多个键的尾部遮蔽,以及 bot 配置文件的 PII 遮蔽。这还不够,在一天内就三次失效:Telegram bot 令牌在 spool 字符串的 response.request.url 中传输(键名为 url),它也存在于 SHM 传输行的 host 列中,而模板主体以裸子字符串形式携带凭据,旁边根本没有字段名。因此,清理器还会对每条通过的字符串运行值形式规则:JWT;NAME=<值>,其中名称暗示机密而值不像占位符;带或不带周围路径的 Telegram bot 令牌;URL 中的 user:password@。它存在于 redact 内部,两个 HTTP 客户端在入口处和执行器在出口处都会调用它——任何单个工具都不需要记住这一点。

规则是校准过的而非猜测的,校准直接在源码中声明:"不透明运行"阈值(32+ 个看起来随机的字符)是在真实模板主体上测量的,并在结构化 API 响应上关闭,因为 data-URI 图标和支付的 hex uniq_id 会超过它——如果切掉它们,清理就会恰好抹掉工具为之而写的那些字段。这仍然不是安全边界,源码也这么说了:用文字写成的机密没有形式;通过过滤器的所有内容都保留在 human 配置文件内。scripts/no-secrets.test.ts 使用相同的规则,防止机密进入公开提交:关于"机密长什么样"的两份知识副本会静默分歧,而第二份继续看起来在起作用。

sql_query 不执行任何操作。 它只做校验并拒绝执行,这一点在其源码中已有说明。词法检查是一个廉价的首道过滤器,显然不是安全边界;模块列出了能绕过它的方式,测试也保持这些绕过路径开放,以免有人把过滤器当作保证。在执行功能尚未接入之前,前置条件已在同一文件中声明:只读角色、只读事务、查询超时和禁用列清单。

关于限制需要了解的内容

  • HTTP 传输层使用 MCP 协议(/mcp,streamable HTTP),提供与 stdio 相同的工具集:一个函数同时向两种传输方式发布这些工具。它刻意不支持的功能包括:会话(不发放 Mcp-Session-Id)、服务器主动发起的消息,以及随之而来的 GET 上的 SSE 流和基于 Last-Event-ID 的恢复。每次调用都是自包含的,因此服务器和传输层在每次请求时都会重新创建;SDK 本身也要求如此,其无会话传输层禁止被复用。

  • /mcp 路由上,执行器的两种结果不可达,/metrics 计数器在该路由上只能看到四种中的两种。无效输入由 SDK 在到达工具之前就解析并自行返回 -32602;不存在的名称也会被 SDK 自行拦截,不会到达注册表。因此,invalid_inputnot_found 在该路由上既不会出现在响应中,也不会出现在报告中。在 REST 外观上,两者均可达。

  • 带有 Origin 头的 /mcp 请求会被无条件拒绝并返回 403:服务器只监听回环地址,而浏览器中的页面可以将自己的域名指向 127.0.0.1,以操作者的身份访问这里。浏览器会在任何跨源 POST 请求上自动添加 Origin 头,而真正的 MCP 客户端永远不会这样做;服务器也不返回 CORS 头,因此它没有也不可能有浏览器客户端。内置的 allowedHosts/allowedOrigins 不适用于此场景:在该版本的 SDK 中,它们已被标记为弃用,转而推荐使用外部中间件;而且它们的空 origin 列表表示「检查已关闭」,而不是「任何 origin 都不合格」。

  • 有两个工具需要通往内部网络的隧道,没有隧道它们就会拒绝工作。它们故意保持可见:一个消失的工具会让模型认为这种能力不存在,而实际上只是端口被关闭了。

  • sync_audit 会完整读取两个系统,是这里唯一的高成本调用——正因如此,它有自己的请求配额。

  • 面板的页面大小是在运行时测量的,而不是假设的:API 不声明最大值,实际值在不同版本之间会变化。

  • 面板的批量路由返回 202 或 204 及空响应体,部分工作会进入队列,因此「已应用」意味着「已被面板接受」,而不是「已对所有对象完成」。计划中预先设定的数字是这里唯一真实可信的数字。

  • 在面板中进行的修改不会同步回 SHM 计费系统,执行这些修改的工具会明确说明这一点。没有对账步骤;差异之后会由 sync_audit 显示给您。

开发

pnpm test          # модульные тесты
pnpm typecheck
pnpm test:guards   # сканер секретов и предохранители скрипта захвата фикстур

测试使用模拟真实响应格式的夹具运行。在缺陷仅在运行中的系统上可见的地方,用于固化该缺陷的测试也会明确说明这一点。

许可证

MIT.

Maintenance

ActivityMaintained
ResponsivenessSyncing

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

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    A read-only MCP server for securely accessing Xendit payment platform data. It enables querying balances, invoices, transactions, disbursements, refunds, and virtual account payments while preventing any money-moving operations.
    13
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    MCP server for the DataGate billing platform API, providing read-only tools to manage customers, invoices, products, agreements, sites, and payments.
    13
    MIT

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/qwertyhq/hq-mcp'

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