@argosvix/mcp-server 0.26.2-alpha.1 → 0.27.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tools.js CHANGED
@@ -107,6 +107,27 @@ const TOOL_ARG_ALLOWLIST = {
107
107
  // raise は Pro+ 専用、 $5-$500 hard cap。 default $5 で 月跨ぎ で 自動 reset。
108
108
  get_llm_budget: [],
109
109
  raise_llm_budget: ["budgetUsd"],
110
+ // 2026-06-10 ランタイム制御プレーン Phase 1 = runtime 予算ゲート tools。
111
+ // user 自身の LLM 支出に対する実行前 enforce の設定 (llm_feature_budget とは別物)。
112
+ // get は Free / Pro+ 共通、 create / update / delete は Pro+ (= backend plan gate)。
113
+ // gateId は path 直前置換、 値域 validation は backend 側で最終実施。
114
+ get_budget_gate: [],
115
+ create_budget_gate: ["monthlyLimitUsd", "enforceMode", "enabled"],
116
+ update_budget_gate: ["gateId", "monthlyLimitUsd", "enforceMode", "enabled"],
117
+ delete_budget_gate: ["gateId"],
118
+ // 2026-06-10 ランタイム制御プレーン Phase 2 = ポリシーゲート tools。
119
+ // モデル allowlist / PII block / secret block の実行前 enforce 設定。
120
+ // get は Free / Pro+ 共通、 create / update / delete は Pro+ (= backend plan gate)。
121
+ // 2026-06-10 Phase 3 = 人間承認ゲート tools。 依頼と状態確認のみで、
122
+ // 承認 / 否認の tool は存在しない (= agent の自己承認を構造防止。 決定は
123
+ // dashboard /approvals か email link で人間が行う)。
124
+ request_approval: ["action", "summary", "metadata", "timeoutSeconds"],
125
+ get_approval: ["approvalId"],
126
+ list_approvals: ["status"],
127
+ get_policy_gate: [],
128
+ create_policy_gate: ["modelAllowlist", "blockPii", "blockSecrets", "enforceMode", "enabled"],
129
+ update_policy_gate: ["policyId", "modelAllowlist", "blockPii", "blockSecrets", "enforceMode", "enabled"],
130
+ delete_policy_gate: ["policyId"],
110
131
  // 2026-06-02 v1.5 Round F = prompt registry read tools。 user の保存 prompt
111
132
  // template を tap で取得、 AI agent が template + variables + labels を context
112
133
  // に取り込む path。 name / label / limit は query param、 promptId は path 直前置換。
@@ -140,6 +161,11 @@ const TOOL_ARG_ALLOWLIST = {
140
161
  aggregate_calls: ["startTime", "endTime", "groupBy", "metric", "provider", "tagKey"],
141
162
  get_percentiles: ["startTime", "endTime", "provider", "model", "metric", "groupBy"],
142
163
  list_projects: [],
164
+ // 2026-06-10 Team v1 = read-only Team tool。 list_members は GET /v1/memberships
165
+ // の wrap。 招待 / ロール変更 / 削除の mutation は権限操作 (= 人間承認ゲートで
166
+ // Bearer を 403 にした思想と整合) のため素の MCP tool にせず、 将来 chat 確認カード /
167
+ // 承認ゲート経由で設計する。
168
+ list_members: [],
143
169
  create_project: ["name", "slug"],
144
170
  rename_project: ["projectId", "name", "slug"],
145
171
  delete_project: ["projectId"],
@@ -180,6 +206,11 @@ const TOOL_ARG_ALLOWLIST = {
180
206
  "reason",
181
207
  "dryRun",
182
208
  ],
209
+ // 2026-06-07 axis 4 Tier 2 第三弾 = founder dogfood scope の Stripe mutation。
210
+ // irreversible 軸 (= trial 延長 / promo 適用)、 backend で audit emit + Stripe
211
+ // Idempotency-Key carry、 dryRun=true で preview のみ。
212
+ extend_customer_trial: ["targetAccountId", "extendDays", "reason", "dryRun", "idempotencyKey"],
213
+ apply_promo_code_to_customer: ["targetAccountId", "promoCode", "reason", "dryRun", "idempotencyKey"],
183
214
  };
184
215
  export const tools = [
185
216
  {
@@ -933,6 +964,244 @@ export const tools = [
933
964
  },
934
965
  },
935
966
  },
967
+ {
968
+ name: "get_budget_gate",
969
+ description: "runtime 予算ゲート (= ランタイム制御プレーン Phase 1) の設定一覧 + 当月 LLM 消費額を取得する。 " +
970
+ "response = { gates: [{ id, projectId, monthlyLimitUsd, enforceMode, enabled, ... }], spentUsdThisMonth, monthStart, ttlSeconds }。 monthStart は UTC 月初 (= JST では月初日 09:00 にリセット)。 " +
971
+ "SDK の budgetGate opt-in が実行前に評価するのと同じ source。 get_llm_budget (= Argosvix 内部 AI 機能の費用 cap) とは別物で、 こちらは user 自身の LLM 支出の月次上限。 " +
972
+ "用例: 「今月の予算ゲートの残りは?」 「ゲートは fail_open になってる?」",
973
+ inputSchema: {
974
+ type: "object",
975
+ additionalProperties: false,
976
+ properties: {},
977
+ },
978
+ },
979
+ {
980
+ name: "create_budget_gate",
981
+ description: "runtime 予算ゲートを作成する (= Pro+ 専用)。 account 全体の月次 LLM 支出上限 (USD) を設定し、 SDK (budgetGate opt-in) が超過呼び出しを実行前に block する。 " +
982
+ "enforceMode = fail_open (default、 backend 不達時は通す) / fail_closed (不達時も止める)。 " +
983
+ "account 全体 gate は 1 つだけ (= 既存ありで 409、 update_budget_gate を使う)。 " +
984
+ "用例: 「月 $50 で予算ゲートを作って」 「厳格モードで $10 上限のゲートを設定」",
985
+ inputSchema: {
986
+ type: "object",
987
+ additionalProperties: false,
988
+ required: ["monthlyLimitUsd"],
989
+ properties: {
990
+ monthlyLimitUsd: {
991
+ type: "number",
992
+ description: "月次上限 USD (= 0.01 - 1000000、 0.01 単位)。 例 50 / 100.5",
993
+ minimum: 0.01,
994
+ maximum: 1000000,
995
+ },
996
+ enforceMode: {
997
+ type: "string",
998
+ enum: ["fail_open", "fail_closed"],
999
+ description: "backend 不達時の挙動 (default fail_open)",
1000
+ },
1001
+ enabled: {
1002
+ type: "boolean",
1003
+ description: "gate の有効状態 (default true)",
1004
+ },
1005
+ },
1006
+ },
1007
+ },
1008
+ {
1009
+ name: "update_budget_gate",
1010
+ description: "runtime 予算ゲートを更新する (= Pro+ 専用)。 monthlyLimitUsd / enforceMode / enabled のいずれかを部分更新。 " +
1011
+ "用例: 「上限を $100 に上げて」 「ゲートを一時的に無効化して」 「fail_closed に切り替えて」",
1012
+ inputSchema: {
1013
+ type: "object",
1014
+ additionalProperties: false,
1015
+ required: ["gateId"],
1016
+ properties: {
1017
+ gateId: {
1018
+ type: "string",
1019
+ description: "対象 gate の id (= get_budget_gate で取得、 bg_ 始まり)",
1020
+ },
1021
+ monthlyLimitUsd: {
1022
+ type: "number",
1023
+ description: "新 月次上限 USD (= 0.01 - 1000000、 0.01 単位)",
1024
+ minimum: 0.01,
1025
+ maximum: 1000000,
1026
+ },
1027
+ enforceMode: {
1028
+ type: "string",
1029
+ enum: ["fail_open", "fail_closed"],
1030
+ description: "backend 不達時の挙動",
1031
+ },
1032
+ enabled: {
1033
+ type: "boolean",
1034
+ description: "gate の有効状態",
1035
+ },
1036
+ },
1037
+ },
1038
+ },
1039
+ {
1040
+ name: "delete_budget_gate",
1041
+ description: "runtime 予算ゲートを削除する (= Pro+ 専用)。 削除後は SDK の実行前 enforce が無効になる。 一時停止だけなら update_budget_gate の enabled: false を推奨。",
1042
+ inputSchema: {
1043
+ type: "object",
1044
+ additionalProperties: false,
1045
+ required: ["gateId"],
1046
+ properties: {
1047
+ gateId: {
1048
+ type: "string",
1049
+ description: "対象 gate の id (= get_budget_gate で取得、 bg_ 始まり)",
1050
+ },
1051
+ },
1052
+ },
1053
+ },
1054
+ {
1055
+ name: "request_approval",
1056
+ description: "人間承認ゲート (= ランタイム制御プレーン Phase 3) に承認依頼を作成する (= Pro+ 専用)。 危険操作 (削除 / 送金 / 退会等) の前に呼ぶと account owner へ email 通知が飛び、 人間が dashboard か email link で承認 / 否認する。 " +
1057
+ "重要: 承認 / 否認を実行する MCP tool は存在しない (= AI agent は自分の依頼を自己承認できない)。 結果は get_approval で polling して確認する。 " +
1058
+ "timeoutSeconds (default 3600) 切れは expired = 否認扱い。 " +
1059
+ "用例: 「user usr_123 の削除は危険操作なので人間の承認を取って」",
1060
+ inputSchema: {
1061
+ type: "object",
1062
+ additionalProperties: false,
1063
+ required: ["action", "summary"],
1064
+ properties: {
1065
+ action: {
1066
+ type: "string",
1067
+ description: "操作の識別子 (= 1-128 文字、 英数 ._: - と空白)。 例 delete_user",
1068
+ },
1069
+ summary: {
1070
+ type: "string",
1071
+ description: "人間が読む 1 行説明 (= 1-500 文字、 承認 email にそのまま載る)",
1072
+ },
1073
+ metadata: {
1074
+ type: "object",
1075
+ description: "補足 JSON object (= 4KB まで、 任意)",
1076
+ },
1077
+ timeoutSeconds: {
1078
+ type: "integer",
1079
+ description: "承認期限秒 (= 60-86400、 default 3600)",
1080
+ minimum: 60,
1081
+ maximum: 86400,
1082
+ },
1083
+ },
1084
+ },
1085
+ },
1086
+ {
1087
+ name: "get_approval",
1088
+ description: "承認依頼の現在状態を取得する。 status = pending / approved / denied / expired。 approved 以外なら対象操作を実行しないこと (= default-deny)。",
1089
+ inputSchema: {
1090
+ type: "object",
1091
+ additionalProperties: false,
1092
+ required: ["approvalId"],
1093
+ properties: {
1094
+ approvalId: {
1095
+ type: "string",
1096
+ description: "request_approval が返した id (= apr_ 始まり)",
1097
+ },
1098
+ },
1099
+ },
1100
+ },
1101
+ {
1102
+ name: "list_approvals",
1103
+ description: "承認依頼の一覧を取得する (= 最新 50 件)。 status filter = pending (default) / approved / denied / expired / all。",
1104
+ inputSchema: {
1105
+ type: "object",
1106
+ additionalProperties: false,
1107
+ properties: {
1108
+ status: {
1109
+ type: "string",
1110
+ enum: ["pending", "approved", "denied", "expired", "all"],
1111
+ description: "status filter (default pending)",
1112
+ },
1113
+ },
1114
+ },
1115
+ },
1116
+ {
1117
+ name: "get_policy_gate",
1118
+ description: "runtime ポリシーゲート (= ランタイム制御プレーン Phase 2) の設定を取得する。 " +
1119
+ "response = { policy: { id, modelAllowlist, blockPii, blockSecrets, enforceMode, enabled, ... } | null }。 " +
1120
+ "SDK の policyGate opt-in が LLM 呼び出し前にローカル評価する設定 (= モデル allowlist 完全一致 + PII / secret 検知で block)。 " +
1121
+ "用例: 「いまのモデル制限は?」 「PII block は有効?」",
1122
+ inputSchema: {
1123
+ type: "object",
1124
+ additionalProperties: false,
1125
+ properties: {},
1126
+ },
1127
+ },
1128
+ {
1129
+ name: "create_policy_gate",
1130
+ description: "runtime ポリシーゲートを作成する (= Pro+ 専用)。 account 全体のモデル allowlist / PII block / secret block を設定し、 SDK (policyGate opt-in) が違反呼び出しを実行前に block する。 " +
1131
+ "少なくとも 1 つのルール (modelAllowlist / blockPii / blockSecrets) が必要。 account に 1 つだけ (= 既存ありで 409)。 redact モードは未対応 (= block のみ)。 " +
1132
+ "用例: 「gpt-5.5 と claude-fable-5 だけ許可して」 「PII を含む呼び出しを止めて」",
1133
+ inputSchema: {
1134
+ type: "object",
1135
+ additionalProperties: false,
1136
+ properties: {
1137
+ modelAllowlist: {
1138
+ type: "array",
1139
+ items: { type: "string" },
1140
+ description: "許可モデル名の配列 (= 1-100 件、 完全一致)。 省略 = モデル制限なし",
1141
+ },
1142
+ blockPii: {
1143
+ type: "boolean",
1144
+ description: "PII (email / カード / 電話 / マイナンバー / IP) 検知で block",
1145
+ },
1146
+ blockSecrets: {
1147
+ type: "boolean",
1148
+ description: "API key / private key らしき token 検知で block",
1149
+ },
1150
+ enforceMode: {
1151
+ type: "string",
1152
+ enum: ["fail_open", "fail_closed"],
1153
+ description: "backend 不達時の挙動 (default fail_open)",
1154
+ },
1155
+ enabled: {
1156
+ type: "boolean",
1157
+ description: "gate の有効状態 (default true)",
1158
+ },
1159
+ },
1160
+ },
1161
+ },
1162
+ {
1163
+ name: "update_policy_gate",
1164
+ description: "runtime ポリシーゲートを更新する (= Pro+ 専用)。 modelAllowlist (= null で制限解除) / blockPii / blockSecrets / enforceMode / enabled を部分更新。 " +
1165
+ "用例: 「allowlist に gpt-4o-mini を足して」 「secret block を有効化」",
1166
+ inputSchema: {
1167
+ type: "object",
1168
+ additionalProperties: false,
1169
+ required: ["policyId"],
1170
+ properties: {
1171
+ policyId: {
1172
+ type: "string",
1173
+ description: "対象 policy の id (= get_policy_gate で取得、 pg_ 始まり)",
1174
+ },
1175
+ modelAllowlist: {
1176
+ type: ["array", "null"],
1177
+ items: { type: "string" },
1178
+ description: "新 allowlist (= null でモデル制限解除)",
1179
+ },
1180
+ blockPii: { type: "boolean" },
1181
+ blockSecrets: { type: "boolean" },
1182
+ enforceMode: {
1183
+ type: "string",
1184
+ enum: ["fail_open", "fail_closed"],
1185
+ },
1186
+ enabled: { type: "boolean" },
1187
+ },
1188
+ },
1189
+ },
1190
+ {
1191
+ name: "delete_policy_gate",
1192
+ description: "runtime ポリシーゲートを削除する (= Pro+ 専用)。 一時停止だけなら update_policy_gate の enabled: false を推奨。",
1193
+ inputSchema: {
1194
+ type: "object",
1195
+ additionalProperties: false,
1196
+ required: ["policyId"],
1197
+ properties: {
1198
+ policyId: {
1199
+ type: "string",
1200
+ description: "対象 policy の id (= get_policy_gate で取得、 pg_ 始まり)",
1201
+ },
1202
+ },
1203
+ },
1204
+ },
936
1205
  {
937
1206
  name: "test_webhook",
938
1207
  description: "指定 URL に 1 件 fabricated alert を 試送する (= Pro+ 専用、 v1.6 #13-5)。 " +
@@ -1294,6 +1563,17 @@ export const tools = [
1294
1563
  properties: {},
1295
1564
  },
1296
1565
  },
1566
+ {
1567
+ name: "list_members",
1568
+ description: "Team account のメンバー一覧を取得 (= GET /v1/memberships、 removed 除外)。 " +
1569
+ "各メンバーの email / role (admin/member/viewer) / status / 参加日時を返す read-only tool。 " +
1570
+ "招待・ロール変更・削除の mutation は権限操作のため MCP では非提供 (= dashboard か将来の承認ゲート経由)。",
1571
+ inputSchema: {
1572
+ type: "object",
1573
+ additionalProperties: false,
1574
+ properties: {},
1575
+ },
1576
+ },
1297
1577
  {
1298
1578
  name: "create_project",
1299
1579
  description: "新規 project を 作成 (= POST /v1/projects)。 name = 表示名、 slug = URL-safe 短い識別子 (= /^[a-z][a-z0-9-]{0,31}$/)。 " +
@@ -1526,6 +1806,91 @@ export const tools = [
1526
1806
  },
1527
1807
  },
1528
1808
  },
1809
+ {
1810
+ name: "extend_customer_trial",
1811
+ description: "(axis 4 Tier 2 = 自律 AI ops 第三弾) 自 account の Stripe subscription trial 期間を 1-30 日 延長 (= POST /v1/tier2/trial/extend)。 founder dogfood scope = 当面 自 account のみ、 paid Pro+ user は 403。 累計 60 日 上限 (= 過去 30 日 audit 集計)、 status='trialing' でなければ 409。 " +
1812
+ "R39 carry = dryRun は 必須明示 (= 暗黙 false で mutation する事故 防御)、 dryRun=false 時 は idempotencyKey も 必須 (16-128 alphanumeric+'_-')。 同 key 再呼び出しは tier2_idempotency table 経由で cached result 返却 (= retry double-extend を 構造防御)。 " +
1813
+ "dryRun=true で previousTrialEnd / newTrialEnd / 累計 narrative preview のみ (= Stripe call なし)。 dryRun=false で 実 Stripe mutation + accounts_subscription 同期 update。",
1814
+ inputSchema: {
1815
+ type: "object",
1816
+ additionalProperties: false,
1817
+ required: ["targetAccountId", "extendDays", "reason", "dryRun"],
1818
+ properties: {
1819
+ targetAccountId: {
1820
+ type: "string",
1821
+ description: "対象 account id (= 当面 自 account のみ、 別 user 指定は 403)",
1822
+ minLength: 1,
1823
+ maxLength: 64,
1824
+ },
1825
+ extendDays: {
1826
+ type: "integer",
1827
+ description: "延長 日数 (= 1-30、 累計 60 日 上限)",
1828
+ minimum: 1,
1829
+ maximum: 30,
1830
+ },
1831
+ reason: {
1832
+ type: "string",
1833
+ description: "延長 理由 (= audit log に carry、 必須、 200 char 上限)",
1834
+ minLength: 1,
1835
+ maxLength: 200,
1836
+ },
1837
+ dryRun: {
1838
+ type: "boolean",
1839
+ description: "必須明示。 true = preview のみ、 false = 実 trial 延長 + Stripe mutation",
1840
+ },
1841
+ idempotencyKey: {
1842
+ type: "string",
1843
+ description: "dryRun=false 時 必須。 16-128 char alphanumeric+'_-'、 同 key 再呼び出しは cached result 返却",
1844
+ minLength: 16,
1845
+ maxLength: 128,
1846
+ pattern: "^[A-Za-z0-9_-]+$",
1847
+ },
1848
+ },
1849
+ },
1850
+ },
1851
+ {
1852
+ name: "apply_promo_code_to_customer",
1853
+ description: "(axis 4 Tier 2 = 自律 AI ops 第三弾) 自 account の Stripe subscription に user-facing promotion code (= 既 Stripe で 登録済の 「LAUNCH50」 等) を 適用 (= POST /v1/tier2/promo/apply)。 founder dogfood scope = 当面 自 account のみ、 paid Pro+ user は 403。 既 active discount があれば 409 (= 重ね掛け 構造防御)、 status が canceled / incomplete_expired は 409。 " +
1854
+ "R39 carry = promotion_code 経由で Stripe redeem 判定を委ねる構造 (= coupon 直接適用は 制約 bypass で禁止)、 dryRun 必須明示 + dryRun=false 時 idempotencyKey 必須。 同 key 再呼び出しは tier2_idempotency table 経由で cached result 返却、 concurrent apply を 構造直列化。 " +
1855
+ "dryRun=true で resolve + 既 active 判定 + 推定 割引 narrative preview のみ (= Stripe mutation なし)。 dryRun=false で 実 promotion_code 適用。",
1856
+ inputSchema: {
1857
+ type: "object",
1858
+ additionalProperties: false,
1859
+ required: ["targetAccountId", "promoCode", "reason", "dryRun"],
1860
+ properties: {
1861
+ targetAccountId: {
1862
+ type: "string",
1863
+ description: "対象 account id (= 当面 自 account のみ、 別 user 指定は 403)",
1864
+ minLength: 1,
1865
+ maxLength: 64,
1866
+ },
1867
+ promoCode: {
1868
+ type: "string",
1869
+ description: "Stripe で 既 登録済の promotion code (= 「LAUNCH50」 等、 alphanumeric + '_-'、 64 char 上限)",
1870
+ minLength: 1,
1871
+ maxLength: 64,
1872
+ pattern: "^[A-Za-z0-9_-]+$",
1873
+ },
1874
+ reason: {
1875
+ type: "string",
1876
+ description: "適用 理由 (= audit log に carry、 必須、 200 char 上限)",
1877
+ minLength: 1,
1878
+ maxLength: 200,
1879
+ },
1880
+ dryRun: {
1881
+ type: "boolean",
1882
+ description: "必須明示。 true = preview のみ、 false = 実 promotion_code 適用 + Stripe mutation",
1883
+ },
1884
+ idempotencyKey: {
1885
+ type: "string",
1886
+ description: "dryRun=false 時 必須。 16-128 char alphanumeric+'_-'、 同 key 再呼び出しは cached result 返却",
1887
+ minLength: 16,
1888
+ maxLength: 128,
1889
+ pattern: "^[A-Za-z0-9_-]+$",
1890
+ },
1891
+ },
1892
+ },
1893
+ },
1529
1894
  {
1530
1895
  name: "detect_anomaly",
1531
1896
  description: "現 window と baseline window (= 同 length の 1 期前) を 比較して cost / latency / error_rate / call_volume の 4 軸で 異常を検出 (= axis 4 Tier 1 = AI が 1 prompt で 「何か変なことが起きてないか」 把握)。 " +
@@ -2153,6 +2518,172 @@ export async function dispatchTool(input) {
2153
2518
  jsonBody: safeArgs,
2154
2519
  });
2155
2520
  }
2521
+ case "get_budget_gate": {
2522
+ return await callApi(apiBase, "/v1/gate/budget", {}, apiKey);
2523
+ }
2524
+ case "create_budget_gate": {
2525
+ if (typeof safeArgs["monthlyLimitUsd"] !== "number") {
2526
+ return errorResponse("monthlyLimitUsd required (number, 0.01-1000000)");
2527
+ }
2528
+ // safeArgs 丸投げでなく明示 field 構築 (= 将来 allowlist 拡張で意図しない
2529
+ // field が素通りしない防御、 R65a MEDIUM 3)
2530
+ const body = {
2531
+ monthlyLimitUsd: safeArgs["monthlyLimitUsd"],
2532
+ };
2533
+ if (typeof safeArgs["enforceMode"] === "string") {
2534
+ body["enforceMode"] = safeArgs["enforceMode"];
2535
+ }
2536
+ if (typeof safeArgs["enabled"] === "boolean") {
2537
+ body["enabled"] = safeArgs["enabled"];
2538
+ }
2539
+ return await callApi(apiBase, "/v1/gate/budget", {}, apiKey, {
2540
+ method: "POST",
2541
+ jsonBody: body,
2542
+ });
2543
+ }
2544
+ case "update_budget_gate": {
2545
+ const gateId = validateBudgetGateId(safeArgs["gateId"]);
2546
+ if (!gateId) {
2547
+ return errorResponse("gateId required (pattern: bg_[a-f0-9]{32})");
2548
+ }
2549
+ const body = {};
2550
+ if (typeof safeArgs["monthlyLimitUsd"] === "number") {
2551
+ body["monthlyLimitUsd"] = safeArgs["monthlyLimitUsd"];
2552
+ }
2553
+ if (typeof safeArgs["enforceMode"] === "string") {
2554
+ body["enforceMode"] = safeArgs["enforceMode"];
2555
+ }
2556
+ if (typeof safeArgs["enabled"] === "boolean") {
2557
+ body["enabled"] = safeArgs["enabled"];
2558
+ }
2559
+ if (Object.keys(body).length === 0) {
2560
+ return errorResponse("at least one of monthlyLimitUsd / enforceMode / enabled is required");
2561
+ }
2562
+ return await callApi(apiBase, `/v1/gate/budget/${encodeURIComponent(gateId)}`, {}, apiKey, { method: "PATCH", jsonBody: body });
2563
+ }
2564
+ case "delete_budget_gate": {
2565
+ const gateId = validateBudgetGateId(safeArgs["gateId"]);
2566
+ if (!gateId) {
2567
+ return errorResponse("gateId required (pattern: bg_[a-f0-9]{32})");
2568
+ }
2569
+ return await callApi(apiBase, `/v1/gate/budget/${encodeURIComponent(gateId)}`, {}, apiKey, { method: "DELETE" });
2570
+ }
2571
+ case "request_approval": {
2572
+ if (typeof safeArgs["action"] !== "string" || typeof safeArgs["summary"] !== "string") {
2573
+ return errorResponse("action and summary are required (strings)");
2574
+ }
2575
+ const body = {
2576
+ action: safeArgs["action"],
2577
+ summary: safeArgs["summary"],
2578
+ };
2579
+ if ("metadata" in safeArgs) {
2580
+ if (typeof safeArgs["metadata"] !== "object" ||
2581
+ safeArgs["metadata"] === null ||
2582
+ Array.isArray(safeArgs["metadata"])) {
2583
+ return errorResponse("metadata must be a JSON object");
2584
+ }
2585
+ body["metadata"] = safeArgs["metadata"];
2586
+ }
2587
+ if ("timeoutSeconds" in safeArgs) {
2588
+ if (typeof safeArgs["timeoutSeconds"] !== "number") {
2589
+ return errorResponse("timeoutSeconds must be an integer (60-86400)");
2590
+ }
2591
+ body["timeoutSeconds"] = safeArgs["timeoutSeconds"];
2592
+ }
2593
+ return await callApi(apiBase, "/v1/gate/approvals", {}, apiKey, {
2594
+ method: "POST",
2595
+ jsonBody: body,
2596
+ });
2597
+ }
2598
+ case "get_approval": {
2599
+ const approvalId = validateApprovalId(safeArgs["approvalId"]);
2600
+ if (!approvalId) {
2601
+ return errorResponse("approvalId required (pattern: apr_[a-f0-9]{32})");
2602
+ }
2603
+ return await callApi(apiBase, `/v1/gate/approvals/${encodeURIComponent(approvalId)}`, {}, apiKey);
2604
+ }
2605
+ case "list_approvals": {
2606
+ const q = {};
2607
+ if (typeof safeArgs["status"] === "string")
2608
+ q["status"] = safeArgs["status"];
2609
+ return await callApi(apiBase, "/v1/gate/approvals", q, apiKey);
2610
+ }
2611
+ case "get_policy_gate": {
2612
+ return await callApi(apiBase, "/v1/gate/policy", {}, apiKey);
2613
+ }
2614
+ case "create_policy_gate": {
2615
+ const body = {};
2616
+ // 型不一致は黙って drop せず即 reject (= R66a MEDIUM 3、 agent が
2617
+ // 「設定した」と誤認する部分更新を防ぐ)
2618
+ if ("modelAllowlist" in safeArgs) {
2619
+ if (!Array.isArray(safeArgs["modelAllowlist"])) {
2620
+ return errorResponse("modelAllowlist must be an array of model names");
2621
+ }
2622
+ body["modelAllowlist"] = safeArgs["modelAllowlist"];
2623
+ }
2624
+ for (const key of ["blockPii", "blockSecrets", "enabled"]) {
2625
+ if (key in safeArgs) {
2626
+ if (typeof safeArgs[key] !== "boolean") {
2627
+ return errorResponse(`${key} must be a boolean`);
2628
+ }
2629
+ body[key] = safeArgs[key];
2630
+ }
2631
+ }
2632
+ if ("enforceMode" in safeArgs) {
2633
+ if (typeof safeArgs["enforceMode"] !== "string") {
2634
+ return errorResponse("enforceMode must be 'fail_open' or 'fail_closed'");
2635
+ }
2636
+ body["enforceMode"] = safeArgs["enforceMode"];
2637
+ }
2638
+ if (body["modelAllowlist"] === undefined &&
2639
+ body["blockPii"] !== true &&
2640
+ body["blockSecrets"] !== true) {
2641
+ return errorResponse("at least one rule is required (modelAllowlist / blockPii / blockSecrets)");
2642
+ }
2643
+ return await callApi(apiBase, "/v1/gate/policy", {}, apiKey, {
2644
+ method: "POST",
2645
+ jsonBody: body,
2646
+ });
2647
+ }
2648
+ case "update_policy_gate": {
2649
+ const policyId = validatePolicyGateId(safeArgs["policyId"]);
2650
+ if (!policyId) {
2651
+ return errorResponse("policyId required (pattern: pg_[a-f0-9]{32})");
2652
+ }
2653
+ const body = {};
2654
+ if ("modelAllowlist" in safeArgs) {
2655
+ const v = safeArgs["modelAllowlist"];
2656
+ if (v !== null && !Array.isArray(v)) {
2657
+ return errorResponse("modelAllowlist must be an array of model names, or null to remove the restriction");
2658
+ }
2659
+ body["modelAllowlist"] = v;
2660
+ }
2661
+ for (const key of ["blockPii", "blockSecrets", "enabled"]) {
2662
+ if (key in safeArgs) {
2663
+ if (typeof safeArgs[key] !== "boolean") {
2664
+ return errorResponse(`${key} must be a boolean`);
2665
+ }
2666
+ body[key] = safeArgs[key];
2667
+ }
2668
+ }
2669
+ if ("enforceMode" in safeArgs) {
2670
+ if (typeof safeArgs["enforceMode"] !== "string") {
2671
+ return errorResponse("enforceMode must be 'fail_open' or 'fail_closed'");
2672
+ }
2673
+ body["enforceMode"] = safeArgs["enforceMode"];
2674
+ }
2675
+ if (Object.keys(body).length === 0) {
2676
+ return errorResponse("at least one of modelAllowlist / blockPii / blockSecrets / enforceMode / enabled is required");
2677
+ }
2678
+ return await callApi(apiBase, `/v1/gate/policy/${encodeURIComponent(policyId)}`, {}, apiKey, { method: "PATCH", jsonBody: body });
2679
+ }
2680
+ case "delete_policy_gate": {
2681
+ const policyId = validatePolicyGateId(safeArgs["policyId"]);
2682
+ if (!policyId) {
2683
+ return errorResponse("policyId required (pattern: pg_[a-f0-9]{32})");
2684
+ }
2685
+ return await callApi(apiBase, `/v1/gate/policy/${encodeURIComponent(policyId)}`, {}, apiKey, { method: "DELETE" });
2686
+ }
2156
2687
  case "list_prompts": {
2157
2688
  const q = {};
2158
2689
  if (typeof safeArgs["label"] === "string")
@@ -2264,6 +2795,9 @@ export async function dispatchTool(input) {
2264
2795
  case "list_projects": {
2265
2796
  return await callApi(apiBase, "/v1/projects", {}, apiKey);
2266
2797
  }
2798
+ case "list_members": {
2799
+ return await callApi(apiBase, "/v1/memberships", {}, apiKey);
2800
+ }
2267
2801
  case "create_project": {
2268
2802
  const name = safeArgs["name"];
2269
2803
  const slug = safeArgs["slug"];
@@ -2481,6 +3015,95 @@ export async function dispatchTool(input) {
2481
3015
  body["dryRun"] = dryRunRaw === true;
2482
3016
  return await callApi(apiBase, "/v1/tier2/alerts/auto-silence", {}, apiKey, { method: "POST", jsonBody: body });
2483
3017
  }
3018
+ // 2026-06-07 axis 4 Tier 2 第三弾 = extend_customer_trial + apply_promo_code_to_customer。
3019
+ // founder dogfood scope + Stripe API mutation 軸、 dryRun 必須、 backend で Stripe
3020
+ // Idempotency-Key carry。
3021
+ case "extend_customer_trial": {
3022
+ const targetAccountIdRaw = safeArgs["targetAccountId"];
3023
+ if (typeof targetAccountIdRaw !== "string" ||
3024
+ targetAccountIdRaw.length === 0 ||
3025
+ targetAccountIdRaw.length > 64) {
3026
+ return errorResponse("targetAccountId must be non-empty string <= 64 chars");
3027
+ }
3028
+ const extendDaysRaw = safeArgs["extendDays"];
3029
+ if (typeof extendDaysRaw !== "number" ||
3030
+ !Number.isInteger(extendDaysRaw) ||
3031
+ extendDaysRaw < 1 ||
3032
+ extendDaysRaw > 30) {
3033
+ return errorResponse("extendDays must be integer 1-30");
3034
+ }
3035
+ const reasonRaw = safeArgs["reason"];
3036
+ if (typeof reasonRaw !== "string" ||
3037
+ reasonRaw.length === 0 ||
3038
+ reasonRaw.length > 200) {
3039
+ return errorResponse("reason must be non-empty string <= 200 chars");
3040
+ }
3041
+ // R39 HIGH 1 carry: dryRun を MCP layer でも 必須明示 化。
3042
+ const dryRunRaw = safeArgs["dryRun"];
3043
+ if (typeof dryRunRaw !== "boolean") {
3044
+ return errorResponse("dryRun must be explicit boolean (= R39 safety carry)");
3045
+ }
3046
+ const body = {
3047
+ targetAccountId: targetAccountIdRaw,
3048
+ extendDays: extendDaysRaw,
3049
+ reason: reasonRaw,
3050
+ dryRun: dryRunRaw,
3051
+ };
3052
+ // R39 SB2 carry: dryRun=false 時 idempotencyKey 必須。
3053
+ if (dryRunRaw === false) {
3054
+ const ikRaw = safeArgs["idempotencyKey"];
3055
+ if (typeof ikRaw !== "string" ||
3056
+ ikRaw.length < 16 ||
3057
+ ikRaw.length > 128 ||
3058
+ !/^[A-Za-z0-9_-]+$/.test(ikRaw)) {
3059
+ return errorResponse("idempotencyKey required for dryRun=false (16-128 alphanumeric + '_-' chars)");
3060
+ }
3061
+ body["idempotencyKey"] = ikRaw;
3062
+ }
3063
+ return await callApi(apiBase, "/v1/tier2/trial/extend", {}, apiKey, { method: "POST", jsonBody: body });
3064
+ }
3065
+ case "apply_promo_code_to_customer": {
3066
+ const targetAccountIdRaw = safeArgs["targetAccountId"];
3067
+ if (typeof targetAccountIdRaw !== "string" ||
3068
+ targetAccountIdRaw.length === 0 ||
3069
+ targetAccountIdRaw.length > 64) {
3070
+ return errorResponse("targetAccountId must be non-empty string <= 64 chars");
3071
+ }
3072
+ const promoCodeRaw = safeArgs["promoCode"];
3073
+ if (typeof promoCodeRaw !== "string" ||
3074
+ promoCodeRaw.length === 0 ||
3075
+ promoCodeRaw.length > 64 ||
3076
+ !/^[A-Za-z0-9_-]+$/.test(promoCodeRaw)) {
3077
+ return errorResponse("promoCode must be non-empty alphanumeric (+ '_-') string <= 64 chars");
3078
+ }
3079
+ const reasonRaw = safeArgs["reason"];
3080
+ if (typeof reasonRaw !== "string" ||
3081
+ reasonRaw.length === 0 ||
3082
+ reasonRaw.length > 200) {
3083
+ return errorResponse("reason must be non-empty string <= 200 chars");
3084
+ }
3085
+ const dryRunRaw = safeArgs["dryRun"];
3086
+ if (typeof dryRunRaw !== "boolean") {
3087
+ return errorResponse("dryRun must be explicit boolean (= R39 safety carry)");
3088
+ }
3089
+ const body = {
3090
+ targetAccountId: targetAccountIdRaw,
3091
+ promoCode: promoCodeRaw,
3092
+ reason: reasonRaw,
3093
+ dryRun: dryRunRaw,
3094
+ };
3095
+ if (dryRunRaw === false) {
3096
+ const ikRaw = safeArgs["idempotencyKey"];
3097
+ if (typeof ikRaw !== "string" ||
3098
+ ikRaw.length < 16 ||
3099
+ ikRaw.length > 128 ||
3100
+ !/^[A-Za-z0-9_-]+$/.test(ikRaw)) {
3101
+ return errorResponse("idempotencyKey required for dryRun=false (16-128 alphanumeric + '_-' chars)");
3102
+ }
3103
+ body["idempotencyKey"] = ikRaw;
3104
+ }
3105
+ return await callApi(apiBase, "/v1/tier2/promo/apply", {}, apiKey, { method: "POST", jsonBody: body });
3106
+ }
2484
3107
  case "detect_anomaly": {
2485
3108
  // 2026-06-05 axis 4 Tier 1 = 現 window vs baseline window (= 同 length
2486
3109
  // の 1 期前) 比較で 4 軸 異常検出。 pure MCP-side aggregator、 backend
@@ -3252,6 +3875,33 @@ function validateAlertId(value) {
3252
3875
  return null;
3253
3876
  return value;
3254
3877
  }
3878
+ /**
3879
+ * budget gate id = backend budgetGateHandler の `bg_` + UUID hex 32。
3880
+ * path injection 防御で形式を固定する。
3881
+ */
3882
+ function validateBudgetGateId(value) {
3883
+ if (typeof value !== "string")
3884
+ return null;
3885
+ if (!/^bg_[a-f0-9]{32}$/.test(value))
3886
+ return null;
3887
+ return value;
3888
+ }
3889
+ /** approval id = backend approvalsHandler の `apr_` + UUID hex 32。 */
3890
+ function validateApprovalId(value) {
3891
+ if (typeof value !== "string")
3892
+ return null;
3893
+ if (!/^apr_[a-f0-9]{32}$/.test(value))
3894
+ return null;
3895
+ return value;
3896
+ }
3897
+ /** policy gate id = backend policyGateHandler の `pg_` + UUID hex 32。 */
3898
+ function validatePolicyGateId(value) {
3899
+ if (typeof value !== "string")
3900
+ return null;
3901
+ if (!/^pg_[a-f0-9]{32}$/.test(value))
3902
+ return null;
3903
+ return value;
3904
+ }
3255
3905
  /**
3256
3906
  * eventId は backend regex (= [A-Za-z0-9_-]{1,64}、 alert_events.id) で validate、
3257
3907
  * path injection 防御。 alertId と異なり _ (underscore) も accept (= UUID v4 dashed