Skip to main content
Glama

hq-mcp

ロシア語版 · English

AIエージェントにVPNビジネスへのアクセスを提供するMCPサーバー。ビリング SHMとパネル Remnawaveを統合し、両システムにまたがる質問に 1回の呼び出しで答えられるようにしたもの。

どのツールも、依頼された同じ呼び出しでは何も変更しません。書き込み系はまず計画を返し、 適用はその計画のIDを運ぶ2回目の呼び出しで行われます。

動作の様子

どの環境でも最初の呼び出しは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、設定のスナップショットはその場にあり、プロビジョニングは成功を報告 — しかし、その成功の持ち主であるユーザーがパネルにいません。ビリングもパネルも、単独では これを表示しません。

両システムを読み取るツールは34個あります。rwモードでは書き込み系が15個追加されます: 13個は実際のデータを変更し、1個は計画を適用し、もう1個はローカルの変更ログを読み取ります。

Related MCP server: xendit-mcp

エンドポイントのプロキシではなく複合ツールである理由

明白な構成は、HTTPエンドポイントごとに1ツール、約150個です。これは書かれて捨てられました。 理由は2つあります。

生のプロキシは、あらゆる禁止リストを無効化します。モデルがGET <任意のパス>を呼べるなら、 与えないと決めた操作の一覧は飾りに過ぎません。禁止されたパスまでは1行です。ここでは ツールは名前付きのルートを呼び、ビルド段階のスキャナは、禁止されたパスがソース内に リテラルとして現れた場合、実行を落とします。

そして、エンドポイントは質問ではありません。上記の例はSHMの4つのルートとパネルの2つの ルートに触れており、興味深いのはまさに接合部です。client_overviewsync_auditprovisioning_diagnoseは、バグがこの継ぎ目に住んでいるから存在します。

他のすべてを決定づけたルール

空の応答は、決して証明された不在として受け取られるべきではない。

バックエンドが拒否した場合、ツールは劣化します。拒否はdegradedに入り、警告 partial_resultは欠落した半分を指名し、その半分に依存していた発見は抑制され、 生き残ったものから計算されることはありません。リストが切り詰められた場合、サーバー側の totalも一緒に届きます — 「そのようなサービスは存在しない」が宣言されていない窓に 依存しないように。

これは理論上の慎重さではありません。開発中、あるツールがパネルの全レコードを読み、 フィールドが相手側で改名されたためにすべてを捨て、その後、数百人の顧客が再プロビジョニングを 必要としていると報告しました — 空集合から導き出された、自信を持って述べられた破壊的な 推奨です。修正は改名されたフィールドだけではありませんでした。不適切な入力から計算された バスケットは、発見であることを拒否しなければならない、ということでした。

互換性: あなたの環境で動くか

SHM 2.19.4Remnawave 3.2.3で検証済み — 両方の数値は仕様からではなく、 稼働中のデプロイから取得したものです。

最低要件はSHM 2.18.0とRemnawave 3.0.0。 公式のdanuk/shmで十分です。ツールが 呼ぶすべてのルートはアップストリームのもので、フォークは不要です。そのデプロイのパッチが ツールに見えていた唯一の場所は、GET /user/password-authの4番目のフラグでした。 現在、その欠如はsign_in_flag_absentという警告として報告され、診断として偽装されることは ありません。両システムの全ルート一覧、各ルートの登場バージョン、フォークに関する詳細な回答は COMPATIBILITY.mdにあります。

検証は1回の呼び出し — 同じplatform_probeで完了します。バージョンが最低要件を下回る場合、 backend_version_below_minimumという警告で応答し、バージョン、最低要件、そして具体的に 何が壊れるかを示します。その際、何も無効化されません。古いバージョンは、静かな空の応答 ではなく、特定のルートで大きな拒否を返します。

バージョン

失われるもの

影響を受ける対象

SHM < 2.18.0

GET /healthcheck — 認証なしの唯一のルート

platform_probeのみ: shm.livenullのままで、「ビリングがダウン」と「パスワードが違う」が区別できなくなる(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

ユーザーは数値のidではなくuuidでアドレスされる

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は5つのサンプルのうち2つを失う

パネル < 3.2.0

GET /api/system/configurationなし

platform_probeのみ: remna.subscriptionRequestHistoryunknownのまま — 意図的であり、falseではない

Remnawave 3.xは2.x向けに書かれたすべてとの互換性を壊し、静かに壊しません。 ユーザーオブジェクトからuuidが削除され、それとともにby-telegram-idby-emailby-tagルートが消えました。しかも/api/users/{uuid}は404ではなく400で応答するため、 拒否は「そのようなユーザーはいない」にすら似ていません。ここにはこれらのルートはまったく ありません。サーバーが継承された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、必ずrunを付けてください。pnpm setupはpnpm自体の組み込み コマンドです。シェルプロファイルを編集し、このリポジトリには到達しません。

マスターが存在するのは、それが置き換えるステップ — .envを手書きすること — が静かに 失敗するからです。パネルトークンのタイポはサーバーの起動を妨げず、後で無関係な質問の 最中にツールエラーとして浮上します。そのため、マスターは各クレデンシャルを稼働中の システムで検証し、3つの拒否を区別します — ホストがまったく応答しない(DNS、TLS、 閉じたポート)、ホストが応答してクレデンシャルを拒否した、ホストが何も証明しないもので 応答した(502、429): これらは異なる修正が必要であり、単一の「login failed」では 間違ったものを修正することになります。

マスターは、あなたが持っているシステムとアクセスモードについてのみ尋ねます。その他は すべてConfigure the optional settings? [y/N]という1つの質問にまとめられています。 タイムゾーンは推測ではなく、稼働中の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"]
    }
  }
}

2番目のトランスポート: 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リバインディング対策です。「制限」を参照してください。

隣の/v1/toolsはMCPではなく、ai-bot用の内部RESTファサードです。リスト用のハンドルが 1つと、呼び出し用が1つで、独自の応答エンベロープと独自のリクエスト上限を持ちます。

Production HTTP image

Production イメージは、検証済みの40文字のlowercaseコミットSHAからのみビルドされます。このSHAは、OCIラベル、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文字のタグを固定し、host portsを宣言しません。コンテナはUID/GID 10001で動作し、bot+roでは/healthzとRESTファサードのみを公開し、共通のnon-secret HQ_MCP_DEPLOYMENT_CONFIG_REVISIONをlowercase UUID形式で要求します。healthには両方のrevisionが含まれ、不一致時にai-botがカタログを読む前に終了できるようにします。

Productionは、シークレットを正確にmode 0600の3つのregular non-symlinkファイル(SHM_ADMIN_AUTH_FILEREMNA_API_TOKEN_FILEHQ_MCP_HTTP_TOKENS_FILE)経由でのみ渡します。最後のファイルにはai-bot:<dedicated token>のみが含まれます。これはサーバー専用のトークンです。SHMとRemnawaveのクレデンシャルもこのdeployment専用に別途割り当てられ、support botから再利用されることはありません。

自環境への適合

アップストリームのdanuk/shmは「Remnawave」という言葉を知りません。文字列すらありません。課金とパネルの間のブリッジは、完全にあなたのプロビジョニングテンプレートに存在します。パネルの1ユーザーにつきuser_service_idが1つ、名前は<NAME_PREFIX><user_service_id>、SHMのストレージ内の設定スナップショットは<STORAGE_PREFIX><user_service_id>の下に置かれます。両方のプレフィックスは、サーバーがruntimeでSHMのconfig.remnawaveから読み取り、上書きを許可します(HQ_MCP_STORAGE_PREFIXHQ_MCP_PANEL_PREFIXES)。オペレーターは、設定キーが「SHMが明日構築するもの」を記述するよりも、今日パネルにあるものをよく知っています。

パネルのユーザー名は唯一のリンクキーであり、何にも一致しないプレフィックスはエラーを出しません。確信を持った誤った回答を返し、すべてのサービスがプロビジョニングされていないように見えます。そのため、これを証明できるツールは、**prefix_unverified**コードで応答し、影響を受ける発見を抑制します。sync_auditmissingPanelUserバケットをまったく返さず、provisioning_diagnoseは同じコードまたはpanel_username_guessedで結果をマークします。この規約は正確に3つのツール(sync_auditprovisioning_diagnose、ミューテーターstorage_edit)にのみ必要です。client_overviewremna_user_idをオプションパラメータとして受け取り、それがなければパネルの半分を単に表示しません。規約がない場合、他のすべてのツールは通常どおり動作し、これら3つは発見を捏造しません。プレフィックスの順序と継承名を含む完全な解説は、COMPATIBILITY.mdにあります。

ツール

34個がroで表示されます。rwモードは最後のテーブルから15個を追加し、何も削除しません。数字はhumanプロファイル用です。botが何を見るかは、セキュリティモデルに記載されています。

プラットフォームと単一クライアント

ツール

何に答えるか

platform_probe

今まさに生きているもの:バージョン、機能、トンネル、そして障害が事故なのかクレデンシャルなのか

client_resolve

任意の識別子(telegram id、email、ログイン、id、パネル内の名前)を両システムの正規IDに変換 — 最初の一致ではなくすべての一致

client_search

フラグメントによるSHMクライアント検索、サーバー側の一致数付き

client_overview

1回の呼び出しで両システムのクライアント全体

client_account_state

アカウントがどうログインするか:emailとその確認、OTP、passkey、パスワードログインが可能か、リファラル

client_billing_view

クライアントの目から見たお金:今後の引き落としと、実際に提示される支払い方法

client_catalog_view

1人のクライアントの目から見たカタログとプロモコード — その割引、そのボーナス、隠された料金プラン

お金、カタログ、設定

ツール

何に答えるか

billing_ledger

支払い、ボーナス、引き落とし、および2つの独立した照合(残高とボーナスは更新経路が異なる別の列)

autopay_inspect

自動支払いの状態とすべての差し引かれた手数料 — それはuser.settingsではなく支払い行のJSONフィールドcommentにある

promo_read

プロモコードとその利用:これらは別の行であり、一緒に読むことはできない

catalog_read

料金プラン、注文価格、子サービス、イベントマップ、カテゴリ — 有効なservice_idのソース

config_read

限定リストからのSHM設定キー1つ、シークレットはマスクされる。設定全体の読み取りは存在しない

template_read

テンプレートのリストまたは正確に1つの本文 — 通知またはプロビジョニングスクリプトを生成するファイル

サービスとプロビジョニング

ツール

何に答えるか

service_inspect

クライアントのサービス:ステータス、期限、予定されている次の料金プラン、各サービスのスプールタスク

spool_inspect

プロビジョニングキュー:スタック、失敗、一時停止、実際の深さ

provisioning_diagnose

「支払い済みなのに設定がない」— クライアント単位ではなくサービス単位

sync_audit

課金とパネルの一括照合、両側を最後まで読み取る

notify_history

クライアントに実際に伝えたか、伝えていないならその理由。他では表示されない配信判定

server_inventory

SHM自身のトランスポートとそのグループ(ssh、http、mail、telegram)、およびプロビジョニングを黙って停止させる断絶。Remnawaveノードのリストではない

パネル — 最初にクライアント側、次にフリート側

ツール

何に答えるか

subscription_inspect

Remnawaveカード:ステータス、期限、トラフィック、HWIDデバイス、最近のサブスクリプション取得。キーは決して —

subpage_read

サブスクリプションページがクライアントに実際に表示するもの:プラットフォーム、アプリ、インストール手順、ボタンリンク

client_reach

このクライアントが実際に到達できるノードと、それが与えるインバウンドのスクワッドとタグ

device_inventory

フリート全体のHWIDの全体像 — これなしでは1人のクライアントのデバイス数は意味を持たないベース

traffic_stats

ノードとスクワッド別の日次トラフィック。これは時系列であり、カードのカウンターではない

connections_inspect

今まさに接続している人。パネルはジョブでこれに応答し、ツールはポーリングを自ら行う

infra_map

ノード × 設定プロファイル × インバウンド × ホスト × スクワッド、およびそれらの間の断絶

infra_costs

インフラのコスト、パネルとの接点:支払われたが誰も到達しないノードは、流出するお金

country_health

1つの国のノード、オンライン、トラフィック、ホスト

node_config_audit

プロファイルが宣言するものと、パネルが実際にノードに配信するもの

squads_read

両方のスクワッドファミリー:内部はアクセスを決定し、外部はサブスクリプションの提示方法

panel_activity

パネル自体で起こっていること:サマリー、ウィンドウのダイジェスト、どのルートが呼び出されているか、サブスクリプション取得履歴

torrent_reports

トレントブロッカーの証拠 — そして、別途、それがそもそもインストールされ監視しているか

トンネルの向こう側(これら2つはトンネルなしでは正確な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

スタックしたスプールタスク1件の retry、resume、または pause

template_edit

既存の SHM テンプレートの本文を上書きする

storage_edit

このインストール用に出力されたキーリストに基づいて、SHM のカスタムストレージを書き込む

server_edit

SHM のトランスポート行またはトランスポートグループ — ウェブフック、SSH プロビジョニングポイント、メール送信者

user_flags

クライアントをブロックするか、カードの安全なフィールド(full_namephonecomment)を編集する

ops_confirm

その plan_id に基づいてプランを適用する。計画されたツールが書き込む内容を書き込む

ops_audit

何もしない。ローカルの変更ジャーナルを読み取る — rw なのは、ジャーナルが変更サーフェスの一部だからである

ミューテーション

何も、それを要求した呼び出しによって適用されることはない。 plan_id のないミューテーターは現在の状態を読み取り、目標状態を構築してプランを返す: beforeafter、フィールドごとの diff、副作用、存在する場合の rollback、そして識別子。何も書き込まない。適用は2回目の呼び出しである:

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

プランは、それを構築したプロフィール、そのために構築されたツール、そして引数のハッシュに結び付けられている: 別の呼び出し元からでも、別のツールによってでも、1つの数値が変更された同じツールによってでも、それを消費することはできない。有効期間は10分。一回限り性は、ディスク上のアトミックな rename であり、「読み取って削除」ではない: 20件の同時確認のうち、勝つのは正確に1件だけで、残りは「見つかりません」を受け取る。失敗 は有効なプランを消費しない — すべてのチェックは取得後に行われ、失敗したものはファイルを元の場所に戻す; 消費するのは試行自体であり、バックエンドが落ちた場合、プランは消費済みとなる。これは意図的であり、これが1回の引き落としと3回の引き落としの違いである。適用前に、ツールは世界を再読み取りし、プランが構築されたスナップショットと照合する: オブジェクトが動いていれば、プランは他人の変更の上に重ねて適用されるのではなく、拒否される。

すべての試行は HQ_MCP_AUDIT_PATH にジャーナルされる(JSONL、権限0600): 誰が、何で、どのような引数で、オブジェクトが前後でどう見えたか、そして何で終わったか — plannedapplyingappliedfailed、または rejected; 失敗も成功と同様に記録される。applying はバックエンドへのアクセスに書き込まれ、これがこの構造のすべての意味である: 対になる終端レコードのないレコードは、プロセスが途中で死んだこと、プランのスナップショットがすでに破棄されたこと、そしてお金が動いた可能性があることを意味する。ops_audit は、要求されたウィンドウに関係なく、ジャーナル全体でそのような未完了レコードを探し、それらを最初に報告する; 解析できない行は黙ってスキップされるのではなく、カウントされる。

上限はフレームワークが保持し、ツールの作者ではない。 HQ_MCP_MAX_OP_AMOUNT を超えるミューテーションは、プラン構築前に拒否され、フレームワークは、金銭エンドポイントを宣言したが、その入力から金額を読み取る方法を述べなかったツールの登録を拒否する。上限は両方の種類のお金の動きをカバーし、2つ目は見落としやすい: 呼び出し元が金額を指定する支払いとボーナス、そしてクライアントの残高を消費するライフサイクルアクション(givetouchchange_planactivate)— 金額がカタログの料金価格である場合。価格を読み取れなかったプランは発行されない: 数字を知らないことが引き落としを無料にはしない。HQ_MCP_MAX_BULK_USERS は、1回の一括操作がパネルのクライアントを何人まで触れることができるかを制限し、パネルでその数を確立できなかったプランは、目分量で評価されるのではなく拒否される。上限を超えると、操作は全体として拒否される — 決して切り詰められない。

上限はプラン構築時のみ、そしてそこでだけチェックされる: 適用はすでに構築されたプランに基づいて動作し、それを再測定しない。これによって上限を回避することはできない — 引数はハッシュで固定されている — が、プラン発行後に .env で上限を下げても、そのプランには影響しない。

template_editstorage_edit は、まず独自のロールバックを HQ_MCP_BACKUP_DIR に書き込む(ディレクトリ0700、ファイル0600); パスはレスポンスで返され、restore_from はバイトを元に戻し、スナップショットを取らずに書き込むものはどちらもない。バックアップは意図的にプランのスナップショットから分離されている: テンプレート本文と設定スナップショットは、マスクするフィールド名のない裸の部分文字列としてシークレットを運ぶ — つまり、それらを before/rollback 内でモデルに戻して送ることはできない; そしてロールバックは変更を生き延びなければならないが、プランのスナップショットは1時間以内に掃除される。

他に2つのことについて、書き込み系は拒否する。<redacted:…> マーカーを含む本文は決して書き込まれない: それは読み取りツールの出力であり、それを書き込むと、生きたクレデンシャルが、それを隠すために使われた単語に置き換わることになる。そして、パネルの生のブロブ(finalMaskxhttpExtraParamsmuxParamssockoptParams)は、ホストへのあらゆるパッチから除外される — 稼働中のインストールでは、ホストのかなりの部分が finalMask 内に動作中の Hysteria2 パスワードを運んでいる。

実際に証明されていることと、証明されていないこと

host_edit は、適用ブランチが稼働中のシステムで実行された唯一のミューテーターである: 稼働中の Remnawave 3.2.3 パネルでホストのラベルを変更し、finalMask 内のパスワードが無傷であり、宣言されたフィールド以外に何も変更されていないことを確認し、元に戻した。他のすべてはプランまでを含めて証明されている: プランは実際のデータで構築され、適用部分はテストでカバーされているが、稼働中のシステムではそのブランチは実行されていない。これは文字通りに読むべきである。正しく見えるプランは、プランについての証明書である。

セキュリティモデル

2つのプロフィール。 human は信頼されたオペレーターであり、トンネルが閉じているときの正確な ssh コマンドを含む、具体的で実行可能な失敗を受け取る。bot は信頼されていないチャネルである: どの失敗も同じメッセージに潰され、レジストリを探索して、どの名前が異なる応答をするかを探ることができないようにする。書き込みツールが bot に提供されることは決してない: rw プロフィールでは、botro と同じ20の読み取りツールを見る。

単に危険なものとは別の、禁止されたクラス。 これらの操作はゲートで閉じられているのではない — それらは存在せず、ビルド時のスキャナーは、そのパスがソース内にリテラルとして現れた場合、実行を落とす。ノードの identity および keygen ルート(レスポンス本文に秘密鍵がある GET)。トークン、認証、passkey のルート(パネルはトークンを平文で返し、作成されたトークンはすべてのゲートを迂回する永続的な管理者である)。パネル設定とサブスクリプション。/admin/config 全体のエクスポート。プロビジョニングタスクの手動での成功マーク — それは作業を実行するのではなく、パネルにユーザーが依然として存在しないまま、サービスを ACTIVE に移すだけである。支払い、ボーナス、または引き落としの削除 — レジストリに対する裸の DELETE FROM であり、users.balance は再計算されない。すぐに使えるサブスクリプションリンクと connection-keys。restart-allreorder、スクワッドの一括アクション、および job_users を伴う PUT /admin/spool — キャンセルなしの全クライアントへのブロードキャスト。

クラスは狭くなり、それぞれの狭小化は緩和ではなく修正であった。テンプレートの読み取りは書き込みとともに禁止されていたが、理由 — git がない、ロールバックがない — は書き込みについてのみ述べていた; その広さは理論上のものではなかった: 観測された1つのウィンドウでの通知のかなりの割合が空にレンダリングされ、何も送信されず、タスクは SUCCESS を報告したが、沈黙の原因はテンプレート本文の内部にある。現在、読み取りは開かれており、POST はロールバックをもたらした template_edit の下にあり、PUTDELETE は閉じられている: 現れたばかりのテンプレートと消えたばかりのテンプレートには、取得できる以前の状態がない。/api/sub の禁止はプレフィックスベースであり、/api/subscription-page-configs/api/subscription-request-history — キーを発行しない2つの読み取りコントローラー — も同時にカバーしていた; 現在は exact に加えて /api/sub/prefix である。パネルのクライアントに対する一括操作は、表示用のリストなしにデータベース全体に適用されるため禁止されていた — 誰もカウントしない限り正しい: bulk_ops は適用前にパネルでカウントし、数を確立できない場合や HQ_MCP_MAX_BULK_USERS を超える場合に拒否し、bot には提供されない。

POST /api/users/bulk/delete-by-statusその形式によって禁止されたままである: その本文にはステータスがあり、人のリストではない。パネルはタスクをキューに入れ、実行時点で該当する人を削除する — オペレーターが閲覧した人ではなく — 空の本文とカウンターなしで 202 を返すため、その間に期限切れになったアカウントは不可視に削除される。この機能は bulk_ops delete_by_status として保持されている: それは具体的な id を列挙し、それらを表示し、bulk/delete を通じて正確にそれらを削除する。他のエンティティ — ホスト、ノード、スクワッド、スプールブロードキャスト — の一括ルートにはそのようなカウントステップがなく、存在しないままである。

シークレットは出力時にマスクされる — キー名によっても、値の形式によっても。 名前によって: クレデンシャルキーのクローズドリスト、明示的な除外リストを持つ token|secret|key|password|auth との一致、いくつかの末尾マスキング、および bot プロフィールの PII マスキング。これでは不十分で、1日で3回失敗した: Telegram ボットのトークンがスプール行の response.request.url 内を移動していた(キーは url と呼ばれる)、同じものが SHM トランスポート行の host 列にあった、そしてテンプレート本文はフィールド名がまったく隣にない裸の部分文字列としてクレデンシャルを運ぶ。したがって、リダクターは通過するすべての行を値の形式のルールにも通す: JWT; NAME=<値> で、名前がシークレットを約束し、値がプレースホルダーのように見えないもの; 周囲のパスがある場合とない場合の Telegram ボットトークン; URL 内の user:password@。それは redact 内に存在し、両方の HTTP クライアントが入力で、エグゼキューターが出力で呼び出す — 個々のツールはそれを覚えておく必要はない。

ルールは推測ではなく較正されており、較正はソース内で直接宣言されている: 「不透明な実行」のしきい値(ランダムに見える32+文字)は実際のテンプレート本文で測定され、data-URI アイコンと支払いの hex uniq_id がそれを超える構造化 API レスポンスではオフにされている — それらを切り出すと、クリーニングはツールが書かれたまさにそのフィールドを消してしまうだろう。これはいずれにせよセキュリティ境界ではなく、ソースもそう述べている: 言葉で書かれたシークレットには形式がない; フィルターを通過するものはすべて human プロフィール内に残る。同じルールを scripts/no-secrets.test.ts が使用し、公開コミットにシークレットが入るのを防ぐ: 「シークレットがどのように見えるか」という知識の2つのコピーは黙って乖離し、2つ目は機能しているように見え続ける

sql_query は何も実行しません。 検証して拒否するだけで、そのことはソースコード自体に明記されています。字句チェックは安価な最初のフィルターであり、明らかにセキュリティの境界ではありません。モジュールはそれを通過する回避策を列挙し、テストはそれらを開いたままにして、誰もフィルターを保証と誤解しないようにしています。実行が接続されていない間、前提条件は同じファイルに宣言されています。読み取り専用ロール、読み取り専用トランザクション、クエリタイムアウト、禁止列リストです。

制限について知っておくべきこと

  • HTTPトランスポートはMCP(/mcp、ストリーミング可能なHTTP)で通信し、stdioと同じツールセットを提供します。1つの関数が両方のトランスポートにそれらを公開します。意図的にできないこと:セッション(Mcp-Session-Idは発行されない)、サーバー主導のメッセージ、それに伴うGETでのSSEストリームとLast-Event-IDによる再開です。各呼び出しは自己完結型であるため、サーバーとトランスポートはリクエストごとに新しく作成されます。これはSDK自体も要求しており、そのセッションレストランスポートは再利用が禁止されています。

  • /mcpルートでは、エグゼキュータの2つの結果に到達できず、/metricsカウンターはそのルートで4つのうち2つしか認識しません。無効な入力はSDKがツールの前に解析し、自ら-32602で応答します。存在しない名前もレジストリに到達する前にSDK自身が拒否します。したがって、invalid_inputnot_foundはこのルートでは応答にもレポートにも現れません。RESTファサードでは両方に到達可能です。

  • Originヘッダー付きの/mcpへのリクエストは、無条件に403で拒否されます。サーバーはループバックをリッスンしており、ブラウザのページが自分のドメインを127.0.0.1に向けて、オペレーターの代わりにここへアクセスする可能性があるためです。ブラウザはクロスオリジンのPOSTにOriginを付与しますが、本物のMCPクライアントは決して付与しません。サーバーはCORSヘッダーを返さないため、ブラウザクライアントは存在せず、存在することもできません。組み込みのallowedHosts/allowedOriginsはこれには適していません。このSDKバージョンでは、外部ミドルウェアを優先して非推奨とマークされており、originリストが空の場合「チェック無効」を意味し、「どのoriginも不適格」ではありません。

  • 2つのツールは内部ネットワークへのトンネルを必要とし、それがなければ拒否します。それらは意図的に表示されたままです。消えたツールはモデルに「そのような機能は存在しない」と教えますが、実際にはポートが閉じられているだけです。

  • 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