@argosvix/mcp-server 0.26.2-alpha.1 → 0.28.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/README.md +3 -3
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +694 -1
- package/dist/tools.js.map +1 -1
- package/dist/tools.test.js +213 -16
- package/dist/tools.test.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/tools.js
CHANGED
|
@@ -29,7 +29,9 @@ import { isDebugEnabled } from "./debug.js";
|
|
|
29
29
|
* させる二重防御。
|
|
30
30
|
*/
|
|
31
31
|
const TOOL_ARG_ALLOWLIST = {
|
|
32
|
-
|
|
32
|
+
// R74 HIGH 1 fix: latencyMin/Max を allowlist に追加 (= schema + dispatcher に
|
|
33
|
+
// 追加しただけでは safeArgs で落ちて backend に渡らず、 silent 未フィルタだった)
|
|
34
|
+
query_calls: ["limit", "provider", "model", "rangePreset", "latencyMin", "latencyMax"],
|
|
33
35
|
get_cost_summary: ["rangePreset", "groupBy"],
|
|
34
36
|
list_alerts: ["includeTriggered"],
|
|
35
37
|
// Phase 2 write tools = alertId は path 直前置換、 body field のみ allowlist
|
|
@@ -107,6 +109,27 @@ const TOOL_ARG_ALLOWLIST = {
|
|
|
107
109
|
// raise は Pro+ 専用、 $5-$500 hard cap。 default $5 で 月跨ぎ で 自動 reset。
|
|
108
110
|
get_llm_budget: [],
|
|
109
111
|
raise_llm_budget: ["budgetUsd"],
|
|
112
|
+
// 2026-06-10 ランタイム制御プレーン Phase 1 = runtime 予算ゲート tools。
|
|
113
|
+
// user 自身の LLM 支出に対する実行前 enforce の設定 (llm_feature_budget とは別物)。
|
|
114
|
+
// get は Free / Pro+ 共通、 create / update / delete は Pro+ (= backend plan gate)。
|
|
115
|
+
// gateId は path 直前置換、 値域 validation は backend 側で最終実施。
|
|
116
|
+
get_budget_gate: [],
|
|
117
|
+
create_budget_gate: ["monthlyLimitUsd", "enforceMode", "enabled"],
|
|
118
|
+
update_budget_gate: ["gateId", "monthlyLimitUsd", "enforceMode", "enabled"],
|
|
119
|
+
delete_budget_gate: ["gateId"],
|
|
120
|
+
// 2026-06-10 ランタイム制御プレーン Phase 2 = ポリシーゲート tools。
|
|
121
|
+
// モデル allowlist / PII block / secret block の実行前 enforce 設定。
|
|
122
|
+
// get は Free / Pro+ 共通、 create / update / delete は Pro+ (= backend plan gate)。
|
|
123
|
+
// 2026-06-10 Phase 3 = 人間承認ゲート tools。 依頼と状態確認のみで、
|
|
124
|
+
// 承認 / 否認の tool は存在しない (= agent の自己承認を構造防止。 決定は
|
|
125
|
+
// dashboard /approvals か email link で人間が行う)。
|
|
126
|
+
request_approval: ["action", "summary", "metadata", "timeoutSeconds"],
|
|
127
|
+
get_approval: ["approvalId"],
|
|
128
|
+
list_approvals: ["status"],
|
|
129
|
+
get_policy_gate: [],
|
|
130
|
+
create_policy_gate: ["modelAllowlist", "blockPii", "blockSecrets", "enforceMode", "enabled"],
|
|
131
|
+
update_policy_gate: ["policyId", "modelAllowlist", "blockPii", "blockSecrets", "enforceMode", "enabled"],
|
|
132
|
+
delete_policy_gate: ["policyId"],
|
|
110
133
|
// 2026-06-02 v1.5 Round F = prompt registry read tools。 user の保存 prompt
|
|
111
134
|
// template を tap で取得、 AI agent が template + variables + labels を context
|
|
112
135
|
// に取り込む path。 name / label / limit は query param、 promptId は path 直前置換。
|
|
@@ -140,6 +163,11 @@ const TOOL_ARG_ALLOWLIST = {
|
|
|
140
163
|
aggregate_calls: ["startTime", "endTime", "groupBy", "metric", "provider", "tagKey"],
|
|
141
164
|
get_percentiles: ["startTime", "endTime", "provider", "model", "metric", "groupBy"],
|
|
142
165
|
list_projects: [],
|
|
166
|
+
// 2026-06-10 Team v1 = read-only Team tool。 list_members は GET /v1/memberships
|
|
167
|
+
// の wrap。 招待 / ロール変更 / 削除の mutation は権限操作 (= 人間承認ゲートで
|
|
168
|
+
// Bearer を 403 にした思想と整合) のため素の MCP tool にせず、 将来 chat 確認カード /
|
|
169
|
+
// 承認ゲート経由で設計する。
|
|
170
|
+
list_members: [],
|
|
143
171
|
create_project: ["name", "slug"],
|
|
144
172
|
rename_project: ["projectId", "name", "slug"],
|
|
145
173
|
delete_project: ["projectId"],
|
|
@@ -180,6 +208,11 @@ const TOOL_ARG_ALLOWLIST = {
|
|
|
180
208
|
"reason",
|
|
181
209
|
"dryRun",
|
|
182
210
|
],
|
|
211
|
+
// 2026-06-07 axis 4 Tier 2 第三弾 = founder dogfood scope の Stripe mutation。
|
|
212
|
+
// irreversible 軸 (= trial 延長 / promo 適用)、 backend で audit emit + Stripe
|
|
213
|
+
// Idempotency-Key carry、 dryRun=true で preview のみ。
|
|
214
|
+
extend_customer_trial: ["targetAccountId", "extendDays", "reason", "dryRun", "idempotencyKey"],
|
|
215
|
+
apply_promo_code_to_customer: ["targetAccountId", "promoCode", "reason", "dryRun", "idempotencyKey"],
|
|
183
216
|
};
|
|
184
217
|
export const tools = [
|
|
185
218
|
{
|
|
@@ -212,6 +245,16 @@ export const tools = [
|
|
|
212
245
|
enum: ["24h", "7d", "30d", "90d"],
|
|
213
246
|
default: "24h",
|
|
214
247
|
},
|
|
248
|
+
latencyMin: {
|
|
249
|
+
type: "number",
|
|
250
|
+
description: "応答時間の下限 (ms、 0 以上)。 外れ値 drill 用 (= 「2 秒超の呼び出しだけ」)",
|
|
251
|
+
minimum: 0,
|
|
252
|
+
},
|
|
253
|
+
latencyMax: {
|
|
254
|
+
type: "number",
|
|
255
|
+
description: "応答時間の上限 (ms、 0 以上)。 latencyMin と併用で範囲指定",
|
|
256
|
+
minimum: 0,
|
|
257
|
+
},
|
|
215
258
|
},
|
|
216
259
|
},
|
|
217
260
|
},
|
|
@@ -933,6 +976,245 @@ export const tools = [
|
|
|
933
976
|
},
|
|
934
977
|
},
|
|
935
978
|
},
|
|
979
|
+
{
|
|
980
|
+
name: "get_budget_gate",
|
|
981
|
+
description: "runtime 予算ゲート (= ランタイム制御プレーン Phase 1) の設定一覧 + 当月 LLM 消費額を取得する。 " +
|
|
982
|
+
"response = { gates: [{ id, projectId, monthlyLimitUsd, enforceMode, enabled, ... }], spentUsdThisMonth, monthStart, ttlSeconds }。 monthStart は UTC 月初 (= JST では月初日 09:00 にリセット)。 " +
|
|
983
|
+
"SDK の budgetGate opt-in が実行前に評価するのと同じ source。 get_llm_budget (= Argosvix 内部 AI 機能の費用 cap) とは別物で、 こちらは user 自身の LLM 支出の月次上限。 " +
|
|
984
|
+
"用例: 「今月の予算ゲートの残りは?」 「ゲートは fail_open になってる?」",
|
|
985
|
+
inputSchema: {
|
|
986
|
+
type: "object",
|
|
987
|
+
additionalProperties: false,
|
|
988
|
+
properties: {},
|
|
989
|
+
},
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
name: "create_budget_gate",
|
|
993
|
+
description: "runtime 予算ゲートを作成する (= Pro+ 専用)。 account 全体の月次 LLM 支出上限 (USD) を設定し、 SDK (budgetGate opt-in) が超過呼び出しを実行前に block する。 " +
|
|
994
|
+
"enforce は楽観方式 (= 消費額は 60 秒 cache + 実行中の呼び出し分は通るため、 上限は厳密な hard cap ではなく目安として超過しうる)。 " +
|
|
995
|
+
"enforceMode = fail_open (default、 backend 不達時は通す) / fail_closed (不達時も止める。 SDK が設定を一度も取得できていない cold start は SDK 側の failClosed opt-in が別途必要)。 " +
|
|
996
|
+
"account 全体 gate は 1 つだけ (= 既存ありで 409、 update_budget_gate を使う)。 " +
|
|
997
|
+
"用例: 「月 $50 で予算ゲートを作って」 「厳格モードで $10 上限のゲートを設定」",
|
|
998
|
+
inputSchema: {
|
|
999
|
+
type: "object",
|
|
1000
|
+
additionalProperties: false,
|
|
1001
|
+
required: ["monthlyLimitUsd"],
|
|
1002
|
+
properties: {
|
|
1003
|
+
monthlyLimitUsd: {
|
|
1004
|
+
type: "number",
|
|
1005
|
+
description: "月次上限 USD (= 0.01 - 1000000、 0.01 単位)。 例 50 / 100.5",
|
|
1006
|
+
minimum: 0.01,
|
|
1007
|
+
maximum: 1000000,
|
|
1008
|
+
},
|
|
1009
|
+
enforceMode: {
|
|
1010
|
+
type: "string",
|
|
1011
|
+
enum: ["fail_open", "fail_closed"],
|
|
1012
|
+
description: "backend 不達時の挙動 (default fail_open)",
|
|
1013
|
+
},
|
|
1014
|
+
enabled: {
|
|
1015
|
+
type: "boolean",
|
|
1016
|
+
description: "gate の有効状態 (default true)",
|
|
1017
|
+
},
|
|
1018
|
+
},
|
|
1019
|
+
},
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
name: "update_budget_gate",
|
|
1023
|
+
description: "runtime 予算ゲートを更新する (= Pro+ 専用)。 monthlyLimitUsd / enforceMode / enabled のいずれかを部分更新。 " +
|
|
1024
|
+
"用例: 「上限を $100 に上げて」 「ゲートを一時的に無効化して」 「fail_closed に切り替えて」",
|
|
1025
|
+
inputSchema: {
|
|
1026
|
+
type: "object",
|
|
1027
|
+
additionalProperties: false,
|
|
1028
|
+
required: ["gateId"],
|
|
1029
|
+
properties: {
|
|
1030
|
+
gateId: {
|
|
1031
|
+
type: "string",
|
|
1032
|
+
description: "対象 gate の id (= get_budget_gate で取得、 bg_ 始まり)",
|
|
1033
|
+
},
|
|
1034
|
+
monthlyLimitUsd: {
|
|
1035
|
+
type: "number",
|
|
1036
|
+
description: "新 月次上限 USD (= 0.01 - 1000000、 0.01 単位)",
|
|
1037
|
+
minimum: 0.01,
|
|
1038
|
+
maximum: 1000000,
|
|
1039
|
+
},
|
|
1040
|
+
enforceMode: {
|
|
1041
|
+
type: "string",
|
|
1042
|
+
enum: ["fail_open", "fail_closed"],
|
|
1043
|
+
description: "backend 不達時の挙動",
|
|
1044
|
+
},
|
|
1045
|
+
enabled: {
|
|
1046
|
+
type: "boolean",
|
|
1047
|
+
description: "gate の有効状態",
|
|
1048
|
+
},
|
|
1049
|
+
},
|
|
1050
|
+
},
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
name: "delete_budget_gate",
|
|
1054
|
+
description: "runtime 予算ゲートを削除する (= Pro+ 専用)。 削除後は SDK の実行前 enforce が無効になる。 一時停止だけなら update_budget_gate の enabled: false を推奨。",
|
|
1055
|
+
inputSchema: {
|
|
1056
|
+
type: "object",
|
|
1057
|
+
additionalProperties: false,
|
|
1058
|
+
required: ["gateId"],
|
|
1059
|
+
properties: {
|
|
1060
|
+
gateId: {
|
|
1061
|
+
type: "string",
|
|
1062
|
+
description: "対象 gate の id (= get_budget_gate で取得、 bg_ 始まり)",
|
|
1063
|
+
},
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
name: "request_approval",
|
|
1069
|
+
description: "人間承認ゲート (= ランタイム制御プレーン Phase 3) に承認依頼を作成する (= Pro+ 専用)。 危険操作 (削除 / 送金 / 退会等) の前に呼ぶと account owner へ email 通知が飛び、 人間が dashboard か email link で承認 / 否認する。 " +
|
|
1070
|
+
"重要: 承認 / 否認を実行する MCP tool は存在しない (= AI agent は自分の依頼を自己承認できない)。 結果は get_approval で polling して確認する。 " +
|
|
1071
|
+
"timeoutSeconds (default 3600) 切れは expired = 否認扱い。 " +
|
|
1072
|
+
"用例: 「user usr_123 の削除は危険操作なので人間の承認を取って」",
|
|
1073
|
+
inputSchema: {
|
|
1074
|
+
type: "object",
|
|
1075
|
+
additionalProperties: false,
|
|
1076
|
+
required: ["action", "summary"],
|
|
1077
|
+
properties: {
|
|
1078
|
+
action: {
|
|
1079
|
+
type: "string",
|
|
1080
|
+
description: "操作の識別子 (= 1-128 文字、 英数 ._: - と空白)。 例 delete_user",
|
|
1081
|
+
},
|
|
1082
|
+
summary: {
|
|
1083
|
+
type: "string",
|
|
1084
|
+
description: "人間が読む 1 行説明 (= 1-500 文字、 承認 email にそのまま載る)",
|
|
1085
|
+
},
|
|
1086
|
+
metadata: {
|
|
1087
|
+
type: "object",
|
|
1088
|
+
description: "補足 JSON object (= 4KB まで、 任意)",
|
|
1089
|
+
},
|
|
1090
|
+
timeoutSeconds: {
|
|
1091
|
+
type: "integer",
|
|
1092
|
+
description: "承認期限秒 (= 60-86400、 default 3600)",
|
|
1093
|
+
minimum: 60,
|
|
1094
|
+
maximum: 86400,
|
|
1095
|
+
},
|
|
1096
|
+
},
|
|
1097
|
+
},
|
|
1098
|
+
},
|
|
1099
|
+
{
|
|
1100
|
+
name: "get_approval",
|
|
1101
|
+
description: "承認依頼の現在状態を取得する。 status = pending / approved / denied / expired。 approved 以外なら対象操作を実行しないこと (= default-deny)。",
|
|
1102
|
+
inputSchema: {
|
|
1103
|
+
type: "object",
|
|
1104
|
+
additionalProperties: false,
|
|
1105
|
+
required: ["approvalId"],
|
|
1106
|
+
properties: {
|
|
1107
|
+
approvalId: {
|
|
1108
|
+
type: "string",
|
|
1109
|
+
description: "request_approval が返した id (= apr_ 始まり)",
|
|
1110
|
+
},
|
|
1111
|
+
},
|
|
1112
|
+
},
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
name: "list_approvals",
|
|
1116
|
+
description: "承認依頼の一覧を取得する (= 最新 50 件)。 status filter = pending (default) / approved / denied / expired / all。",
|
|
1117
|
+
inputSchema: {
|
|
1118
|
+
type: "object",
|
|
1119
|
+
additionalProperties: false,
|
|
1120
|
+
properties: {
|
|
1121
|
+
status: {
|
|
1122
|
+
type: "string",
|
|
1123
|
+
enum: ["pending", "approved", "denied", "expired", "all"],
|
|
1124
|
+
description: "status filter (default pending)",
|
|
1125
|
+
},
|
|
1126
|
+
},
|
|
1127
|
+
},
|
|
1128
|
+
},
|
|
1129
|
+
{
|
|
1130
|
+
name: "get_policy_gate",
|
|
1131
|
+
description: "runtime ポリシーゲート (= ランタイム制御プレーン Phase 2) の設定を取得する。 " +
|
|
1132
|
+
"response = { policy: { id, modelAllowlist, blockPii, blockSecrets, enforceMode, enabled, ... } | null }。 " +
|
|
1133
|
+
"SDK の policyGate opt-in が LLM 呼び出し前にローカル評価する設定 (= モデル allowlist 完全一致 + PII / secret 検知で block)。 " +
|
|
1134
|
+
"用例: 「いまのモデル制限は?」 「PII block は有効?」",
|
|
1135
|
+
inputSchema: {
|
|
1136
|
+
type: "object",
|
|
1137
|
+
additionalProperties: false,
|
|
1138
|
+
properties: {},
|
|
1139
|
+
},
|
|
1140
|
+
},
|
|
1141
|
+
{
|
|
1142
|
+
name: "create_policy_gate",
|
|
1143
|
+
description: "runtime ポリシーゲートを作成する (= Pro+ 専用)。 account 全体のモデル allowlist / PII block / secret block を設定し、 SDK (policyGate opt-in) が違反呼び出しを実行前に block する。 " +
|
|
1144
|
+
"少なくとも 1 つのルール (modelAllowlist / blockPii / blockSecrets) が必要。 account に 1 つだけ (= 既存ありで 409)。 redact モードは未対応 (= block のみ)。 " +
|
|
1145
|
+
"用例: 「gpt-5.5 と claude-fable-5 だけ許可して」 「PII を含む呼び出しを止めて」",
|
|
1146
|
+
inputSchema: {
|
|
1147
|
+
type: "object",
|
|
1148
|
+
additionalProperties: false,
|
|
1149
|
+
properties: {
|
|
1150
|
+
modelAllowlist: {
|
|
1151
|
+
type: "array",
|
|
1152
|
+
items: { type: "string" },
|
|
1153
|
+
description: "許可モデル名の配列 (= 1-100 件、 完全一致)。 省略 = モデル制限なし",
|
|
1154
|
+
},
|
|
1155
|
+
blockPii: {
|
|
1156
|
+
type: "boolean",
|
|
1157
|
+
description: "PII 検知で block。 検知範囲 = email / カード番号 (Luhn 検証付) / 区切りあり電話番号 / 区切りあり個人番号 / IPv4 / IPv6 (完全形 + 主要圧縮形)。 区切りなし連続数字の電話・個人番号は誤遮断防止のため対象外",
|
|
1158
|
+
},
|
|
1159
|
+
blockSecrets: {
|
|
1160
|
+
type: "boolean",
|
|
1161
|
+
description: "API key / private key らしき token 検知で block",
|
|
1162
|
+
},
|
|
1163
|
+
enforceMode: {
|
|
1164
|
+
type: "string",
|
|
1165
|
+
enum: ["fail_open", "fail_closed"],
|
|
1166
|
+
description: "backend 不達時の挙動 (default fail_open)",
|
|
1167
|
+
},
|
|
1168
|
+
enabled: {
|
|
1169
|
+
type: "boolean",
|
|
1170
|
+
description: "gate の有効状態 (default true)",
|
|
1171
|
+
},
|
|
1172
|
+
},
|
|
1173
|
+
},
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
name: "update_policy_gate",
|
|
1177
|
+
description: "runtime ポリシーゲートを更新する (= Pro+ 専用)。 modelAllowlist (= null で制限解除) / blockPii / blockSecrets / enforceMode / enabled を部分更新。 " +
|
|
1178
|
+
"用例: 「allowlist に gpt-4o-mini を足して」 「secret block を有効化」",
|
|
1179
|
+
inputSchema: {
|
|
1180
|
+
type: "object",
|
|
1181
|
+
additionalProperties: false,
|
|
1182
|
+
required: ["policyId"],
|
|
1183
|
+
properties: {
|
|
1184
|
+
policyId: {
|
|
1185
|
+
type: "string",
|
|
1186
|
+
description: "対象 policy の id (= get_policy_gate で取得、 pg_ 始まり)",
|
|
1187
|
+
},
|
|
1188
|
+
modelAllowlist: {
|
|
1189
|
+
type: ["array", "null"],
|
|
1190
|
+
items: { type: "string" },
|
|
1191
|
+
description: "新 allowlist (= null でモデル制限解除)",
|
|
1192
|
+
},
|
|
1193
|
+
blockPii: { type: "boolean" },
|
|
1194
|
+
blockSecrets: { type: "boolean" },
|
|
1195
|
+
enforceMode: {
|
|
1196
|
+
type: "string",
|
|
1197
|
+
enum: ["fail_open", "fail_closed"],
|
|
1198
|
+
},
|
|
1199
|
+
enabled: { type: "boolean" },
|
|
1200
|
+
},
|
|
1201
|
+
},
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
name: "delete_policy_gate",
|
|
1205
|
+
description: "runtime ポリシーゲートを削除する (= Pro+ 専用)。 一時停止だけなら update_policy_gate の enabled: false を推奨。",
|
|
1206
|
+
inputSchema: {
|
|
1207
|
+
type: "object",
|
|
1208
|
+
additionalProperties: false,
|
|
1209
|
+
required: ["policyId"],
|
|
1210
|
+
properties: {
|
|
1211
|
+
policyId: {
|
|
1212
|
+
type: "string",
|
|
1213
|
+
description: "対象 policy の id (= get_policy_gate で取得、 pg_ 始まり)",
|
|
1214
|
+
},
|
|
1215
|
+
},
|
|
1216
|
+
},
|
|
1217
|
+
},
|
|
936
1218
|
{
|
|
937
1219
|
name: "test_webhook",
|
|
938
1220
|
description: "指定 URL に 1 件 fabricated alert を 試送する (= Pro+ 専用、 v1.6 #13-5)。 " +
|
|
@@ -1294,6 +1576,17 @@ export const tools = [
|
|
|
1294
1576
|
properties: {},
|
|
1295
1577
|
},
|
|
1296
1578
|
},
|
|
1579
|
+
{
|
|
1580
|
+
name: "list_members",
|
|
1581
|
+
description: "Team account のメンバー一覧を取得 (= GET /v1/memberships、 removed 除外)。 " +
|
|
1582
|
+
"各メンバーの email / role (admin/member/viewer) / status / 参加日時を返す read-only tool。 " +
|
|
1583
|
+
"招待・ロール変更・削除の mutation は権限操作のため MCP では非提供 (= dashboard か将来の承認ゲート経由)。",
|
|
1584
|
+
inputSchema: {
|
|
1585
|
+
type: "object",
|
|
1586
|
+
additionalProperties: false,
|
|
1587
|
+
properties: {},
|
|
1588
|
+
},
|
|
1589
|
+
},
|
|
1297
1590
|
{
|
|
1298
1591
|
name: "create_project",
|
|
1299
1592
|
description: "新規 project を 作成 (= POST /v1/projects)。 name = 表示名、 slug = URL-safe 短い識別子 (= /^[a-z][a-z0-9-]{0,31}$/)。 " +
|
|
@@ -1526,6 +1819,91 @@ export const tools = [
|
|
|
1526
1819
|
},
|
|
1527
1820
|
},
|
|
1528
1821
|
},
|
|
1822
|
+
{
|
|
1823
|
+
name: "extend_customer_trial",
|
|
1824
|
+
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。 " +
|
|
1825
|
+
"R39 carry = dryRun は 必須明示 (= 暗黙 false で mutation する事故 防御)、 dryRun=false 時 は idempotencyKey も 必須 (16-128 alphanumeric+'_-')。 同 key 再呼び出しは tier2_idempotency table 経由で cached result 返却 (= retry double-extend を 構造防御)。 " +
|
|
1826
|
+
"dryRun=true で previousTrialEnd / newTrialEnd / 累計 narrative preview のみ (= Stripe call なし)。 dryRun=false で 実 Stripe mutation + accounts_subscription 同期 update。",
|
|
1827
|
+
inputSchema: {
|
|
1828
|
+
type: "object",
|
|
1829
|
+
additionalProperties: false,
|
|
1830
|
+
required: ["targetAccountId", "extendDays", "reason", "dryRun"],
|
|
1831
|
+
properties: {
|
|
1832
|
+
targetAccountId: {
|
|
1833
|
+
type: "string",
|
|
1834
|
+
description: "対象 account id (= 当面 自 account のみ、 別 user 指定は 403)",
|
|
1835
|
+
minLength: 1,
|
|
1836
|
+
maxLength: 64,
|
|
1837
|
+
},
|
|
1838
|
+
extendDays: {
|
|
1839
|
+
type: "integer",
|
|
1840
|
+
description: "延長 日数 (= 1-30、 累計 60 日 上限)",
|
|
1841
|
+
minimum: 1,
|
|
1842
|
+
maximum: 30,
|
|
1843
|
+
},
|
|
1844
|
+
reason: {
|
|
1845
|
+
type: "string",
|
|
1846
|
+
description: "延長 理由 (= audit log に carry、 必須、 200 char 上限)",
|
|
1847
|
+
minLength: 1,
|
|
1848
|
+
maxLength: 200,
|
|
1849
|
+
},
|
|
1850
|
+
dryRun: {
|
|
1851
|
+
type: "boolean",
|
|
1852
|
+
description: "必須明示。 true = preview のみ、 false = 実 trial 延長 + Stripe mutation",
|
|
1853
|
+
},
|
|
1854
|
+
idempotencyKey: {
|
|
1855
|
+
type: "string",
|
|
1856
|
+
description: "dryRun=false 時 必須。 16-128 char alphanumeric+'_-'、 同 key 再呼び出しは cached result 返却",
|
|
1857
|
+
minLength: 16,
|
|
1858
|
+
maxLength: 128,
|
|
1859
|
+
pattern: "^[A-Za-z0-9_-]+$",
|
|
1860
|
+
},
|
|
1861
|
+
},
|
|
1862
|
+
},
|
|
1863
|
+
},
|
|
1864
|
+
{
|
|
1865
|
+
name: "apply_promo_code_to_customer",
|
|
1866
|
+
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。 " +
|
|
1867
|
+
"R39 carry = promotion_code 経由で Stripe redeem 判定を委ねる構造 (= coupon 直接適用は 制約 bypass で禁止)、 dryRun 必須明示 + dryRun=false 時 idempotencyKey 必須。 同 key 再呼び出しは tier2_idempotency table 経由で cached result 返却、 concurrent apply を 構造直列化。 " +
|
|
1868
|
+
"dryRun=true で resolve + 既 active 判定 + 推定 割引 narrative preview のみ (= Stripe mutation なし)。 dryRun=false で 実 promotion_code 適用。",
|
|
1869
|
+
inputSchema: {
|
|
1870
|
+
type: "object",
|
|
1871
|
+
additionalProperties: false,
|
|
1872
|
+
required: ["targetAccountId", "promoCode", "reason", "dryRun"],
|
|
1873
|
+
properties: {
|
|
1874
|
+
targetAccountId: {
|
|
1875
|
+
type: "string",
|
|
1876
|
+
description: "対象 account id (= 当面 自 account のみ、 別 user 指定は 403)",
|
|
1877
|
+
minLength: 1,
|
|
1878
|
+
maxLength: 64,
|
|
1879
|
+
},
|
|
1880
|
+
promoCode: {
|
|
1881
|
+
type: "string",
|
|
1882
|
+
description: "Stripe で 既 登録済の promotion code (= 「LAUNCH50」 等、 alphanumeric + '_-'、 64 char 上限)",
|
|
1883
|
+
minLength: 1,
|
|
1884
|
+
maxLength: 64,
|
|
1885
|
+
pattern: "^[A-Za-z0-9_-]+$",
|
|
1886
|
+
},
|
|
1887
|
+
reason: {
|
|
1888
|
+
type: "string",
|
|
1889
|
+
description: "適用 理由 (= audit log に carry、 必須、 200 char 上限)",
|
|
1890
|
+
minLength: 1,
|
|
1891
|
+
maxLength: 200,
|
|
1892
|
+
},
|
|
1893
|
+
dryRun: {
|
|
1894
|
+
type: "boolean",
|
|
1895
|
+
description: "必須明示。 true = preview のみ、 false = 実 promotion_code 適用 + Stripe mutation",
|
|
1896
|
+
},
|
|
1897
|
+
idempotencyKey: {
|
|
1898
|
+
type: "string",
|
|
1899
|
+
description: "dryRun=false 時 必須。 16-128 char alphanumeric+'_-'、 同 key 再呼び出しは cached result 返却",
|
|
1900
|
+
minLength: 16,
|
|
1901
|
+
maxLength: 128,
|
|
1902
|
+
pattern: "^[A-Za-z0-9_-]+$",
|
|
1903
|
+
},
|
|
1904
|
+
},
|
|
1905
|
+
},
|
|
1906
|
+
},
|
|
1529
1907
|
{
|
|
1530
1908
|
name: "detect_anomaly",
|
|
1531
1909
|
description: "現 window と baseline window (= 同 length の 1 期前) を 比較して cost / latency / error_rate / call_volume の 4 軸で 異常を検出 (= axis 4 Tier 1 = AI が 1 prompt で 「何か変なことが起きてないか」 把握)。 " +
|
|
@@ -1907,6 +2285,14 @@ export async function dispatchTool(input) {
|
|
|
1907
2285
|
if (typeof safeArgs["limit"] === "number") {
|
|
1908
2286
|
body["limit"] = safeArgs["limit"];
|
|
1909
2287
|
}
|
|
2288
|
+
// 2026-06-11 外れ値 drill = latency 範囲 (ms)。 validation は backend 共有
|
|
2289
|
+
// (= 非負有限数 + min <= max、 違反は 400 narrative がそのまま返る)。
|
|
2290
|
+
if (typeof safeArgs["latencyMin"] === "number") {
|
|
2291
|
+
body["latencyMin"] = safeArgs["latencyMin"];
|
|
2292
|
+
}
|
|
2293
|
+
if (typeof safeArgs["latencyMax"] === "number") {
|
|
2294
|
+
body["latencyMax"] = safeArgs["latencyMax"];
|
|
2295
|
+
}
|
|
1910
2296
|
return await callApi(apiBase, "/v1/query/calls", {}, apiKey, {
|
|
1911
2297
|
method: "POST",
|
|
1912
2298
|
jsonBody: body,
|
|
@@ -2153,6 +2539,172 @@ export async function dispatchTool(input) {
|
|
|
2153
2539
|
jsonBody: safeArgs,
|
|
2154
2540
|
});
|
|
2155
2541
|
}
|
|
2542
|
+
case "get_budget_gate": {
|
|
2543
|
+
return await callApi(apiBase, "/v1/gate/budget", {}, apiKey);
|
|
2544
|
+
}
|
|
2545
|
+
case "create_budget_gate": {
|
|
2546
|
+
if (typeof safeArgs["monthlyLimitUsd"] !== "number") {
|
|
2547
|
+
return errorResponse("monthlyLimitUsd required (number, 0.01-1000000)");
|
|
2548
|
+
}
|
|
2549
|
+
// safeArgs 丸投げでなく明示 field 構築 (= 将来 allowlist 拡張で意図しない
|
|
2550
|
+
// field が素通りしない防御、 R65a MEDIUM 3)
|
|
2551
|
+
const body = {
|
|
2552
|
+
monthlyLimitUsd: safeArgs["monthlyLimitUsd"],
|
|
2553
|
+
};
|
|
2554
|
+
if (typeof safeArgs["enforceMode"] === "string") {
|
|
2555
|
+
body["enforceMode"] = safeArgs["enforceMode"];
|
|
2556
|
+
}
|
|
2557
|
+
if (typeof safeArgs["enabled"] === "boolean") {
|
|
2558
|
+
body["enabled"] = safeArgs["enabled"];
|
|
2559
|
+
}
|
|
2560
|
+
return await callApi(apiBase, "/v1/gate/budget", {}, apiKey, {
|
|
2561
|
+
method: "POST",
|
|
2562
|
+
jsonBody: body,
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
case "update_budget_gate": {
|
|
2566
|
+
const gateId = validateBudgetGateId(safeArgs["gateId"]);
|
|
2567
|
+
if (!gateId) {
|
|
2568
|
+
return errorResponse("gateId required (pattern: bg_[a-f0-9]{32})");
|
|
2569
|
+
}
|
|
2570
|
+
const body = {};
|
|
2571
|
+
if (typeof safeArgs["monthlyLimitUsd"] === "number") {
|
|
2572
|
+
body["monthlyLimitUsd"] = safeArgs["monthlyLimitUsd"];
|
|
2573
|
+
}
|
|
2574
|
+
if (typeof safeArgs["enforceMode"] === "string") {
|
|
2575
|
+
body["enforceMode"] = safeArgs["enforceMode"];
|
|
2576
|
+
}
|
|
2577
|
+
if (typeof safeArgs["enabled"] === "boolean") {
|
|
2578
|
+
body["enabled"] = safeArgs["enabled"];
|
|
2579
|
+
}
|
|
2580
|
+
if (Object.keys(body).length === 0) {
|
|
2581
|
+
return errorResponse("at least one of monthlyLimitUsd / enforceMode / enabled is required");
|
|
2582
|
+
}
|
|
2583
|
+
return await callApi(apiBase, `/v1/gate/budget/${encodeURIComponent(gateId)}`, {}, apiKey, { method: "PATCH", jsonBody: body });
|
|
2584
|
+
}
|
|
2585
|
+
case "delete_budget_gate": {
|
|
2586
|
+
const gateId = validateBudgetGateId(safeArgs["gateId"]);
|
|
2587
|
+
if (!gateId) {
|
|
2588
|
+
return errorResponse("gateId required (pattern: bg_[a-f0-9]{32})");
|
|
2589
|
+
}
|
|
2590
|
+
return await callApi(apiBase, `/v1/gate/budget/${encodeURIComponent(gateId)}`, {}, apiKey, { method: "DELETE" });
|
|
2591
|
+
}
|
|
2592
|
+
case "request_approval": {
|
|
2593
|
+
if (typeof safeArgs["action"] !== "string" || typeof safeArgs["summary"] !== "string") {
|
|
2594
|
+
return errorResponse("action and summary are required (strings)");
|
|
2595
|
+
}
|
|
2596
|
+
const body = {
|
|
2597
|
+
action: safeArgs["action"],
|
|
2598
|
+
summary: safeArgs["summary"],
|
|
2599
|
+
};
|
|
2600
|
+
if ("metadata" in safeArgs) {
|
|
2601
|
+
if (typeof safeArgs["metadata"] !== "object" ||
|
|
2602
|
+
safeArgs["metadata"] === null ||
|
|
2603
|
+
Array.isArray(safeArgs["metadata"])) {
|
|
2604
|
+
return errorResponse("metadata must be a JSON object");
|
|
2605
|
+
}
|
|
2606
|
+
body["metadata"] = safeArgs["metadata"];
|
|
2607
|
+
}
|
|
2608
|
+
if ("timeoutSeconds" in safeArgs) {
|
|
2609
|
+
if (typeof safeArgs["timeoutSeconds"] !== "number") {
|
|
2610
|
+
return errorResponse("timeoutSeconds must be an integer (60-86400)");
|
|
2611
|
+
}
|
|
2612
|
+
body["timeoutSeconds"] = safeArgs["timeoutSeconds"];
|
|
2613
|
+
}
|
|
2614
|
+
return await callApi(apiBase, "/v1/gate/approvals", {}, apiKey, {
|
|
2615
|
+
method: "POST",
|
|
2616
|
+
jsonBody: body,
|
|
2617
|
+
});
|
|
2618
|
+
}
|
|
2619
|
+
case "get_approval": {
|
|
2620
|
+
const approvalId = validateApprovalId(safeArgs["approvalId"]);
|
|
2621
|
+
if (!approvalId) {
|
|
2622
|
+
return errorResponse("approvalId required (pattern: apr_[a-f0-9]{32})");
|
|
2623
|
+
}
|
|
2624
|
+
return await callApi(apiBase, `/v1/gate/approvals/${encodeURIComponent(approvalId)}`, {}, apiKey);
|
|
2625
|
+
}
|
|
2626
|
+
case "list_approvals": {
|
|
2627
|
+
const q = {};
|
|
2628
|
+
if (typeof safeArgs["status"] === "string")
|
|
2629
|
+
q["status"] = safeArgs["status"];
|
|
2630
|
+
return await callApi(apiBase, "/v1/gate/approvals", q, apiKey);
|
|
2631
|
+
}
|
|
2632
|
+
case "get_policy_gate": {
|
|
2633
|
+
return await callApi(apiBase, "/v1/gate/policy", {}, apiKey);
|
|
2634
|
+
}
|
|
2635
|
+
case "create_policy_gate": {
|
|
2636
|
+
const body = {};
|
|
2637
|
+
// 型不一致は黙って drop せず即 reject (= R66a MEDIUM 3、 agent が
|
|
2638
|
+
// 「設定した」と誤認する部分更新を防ぐ)
|
|
2639
|
+
if ("modelAllowlist" in safeArgs) {
|
|
2640
|
+
if (!Array.isArray(safeArgs["modelAllowlist"])) {
|
|
2641
|
+
return errorResponse("modelAllowlist must be an array of model names");
|
|
2642
|
+
}
|
|
2643
|
+
body["modelAllowlist"] = safeArgs["modelAllowlist"];
|
|
2644
|
+
}
|
|
2645
|
+
for (const key of ["blockPii", "blockSecrets", "enabled"]) {
|
|
2646
|
+
if (key in safeArgs) {
|
|
2647
|
+
if (typeof safeArgs[key] !== "boolean") {
|
|
2648
|
+
return errorResponse(`${key} must be a boolean`);
|
|
2649
|
+
}
|
|
2650
|
+
body[key] = safeArgs[key];
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
if ("enforceMode" in safeArgs) {
|
|
2654
|
+
if (typeof safeArgs["enforceMode"] !== "string") {
|
|
2655
|
+
return errorResponse("enforceMode must be 'fail_open' or 'fail_closed'");
|
|
2656
|
+
}
|
|
2657
|
+
body["enforceMode"] = safeArgs["enforceMode"];
|
|
2658
|
+
}
|
|
2659
|
+
if (body["modelAllowlist"] === undefined &&
|
|
2660
|
+
body["blockPii"] !== true &&
|
|
2661
|
+
body["blockSecrets"] !== true) {
|
|
2662
|
+
return errorResponse("at least one rule is required (modelAllowlist / blockPii / blockSecrets)");
|
|
2663
|
+
}
|
|
2664
|
+
return await callApi(apiBase, "/v1/gate/policy", {}, apiKey, {
|
|
2665
|
+
method: "POST",
|
|
2666
|
+
jsonBody: body,
|
|
2667
|
+
});
|
|
2668
|
+
}
|
|
2669
|
+
case "update_policy_gate": {
|
|
2670
|
+
const policyId = validatePolicyGateId(safeArgs["policyId"]);
|
|
2671
|
+
if (!policyId) {
|
|
2672
|
+
return errorResponse("policyId required (pattern: pg_[a-f0-9]{32})");
|
|
2673
|
+
}
|
|
2674
|
+
const body = {};
|
|
2675
|
+
if ("modelAllowlist" in safeArgs) {
|
|
2676
|
+
const v = safeArgs["modelAllowlist"];
|
|
2677
|
+
if (v !== null && !Array.isArray(v)) {
|
|
2678
|
+
return errorResponse("modelAllowlist must be an array of model names, or null to remove the restriction");
|
|
2679
|
+
}
|
|
2680
|
+
body["modelAllowlist"] = v;
|
|
2681
|
+
}
|
|
2682
|
+
for (const key of ["blockPii", "blockSecrets", "enabled"]) {
|
|
2683
|
+
if (key in safeArgs) {
|
|
2684
|
+
if (typeof safeArgs[key] !== "boolean") {
|
|
2685
|
+
return errorResponse(`${key} must be a boolean`);
|
|
2686
|
+
}
|
|
2687
|
+
body[key] = safeArgs[key];
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
if ("enforceMode" in safeArgs) {
|
|
2691
|
+
if (typeof safeArgs["enforceMode"] !== "string") {
|
|
2692
|
+
return errorResponse("enforceMode must be 'fail_open' or 'fail_closed'");
|
|
2693
|
+
}
|
|
2694
|
+
body["enforceMode"] = safeArgs["enforceMode"];
|
|
2695
|
+
}
|
|
2696
|
+
if (Object.keys(body).length === 0) {
|
|
2697
|
+
return errorResponse("at least one of modelAllowlist / blockPii / blockSecrets / enforceMode / enabled is required");
|
|
2698
|
+
}
|
|
2699
|
+
return await callApi(apiBase, `/v1/gate/policy/${encodeURIComponent(policyId)}`, {}, apiKey, { method: "PATCH", jsonBody: body });
|
|
2700
|
+
}
|
|
2701
|
+
case "delete_policy_gate": {
|
|
2702
|
+
const policyId = validatePolicyGateId(safeArgs["policyId"]);
|
|
2703
|
+
if (!policyId) {
|
|
2704
|
+
return errorResponse("policyId required (pattern: pg_[a-f0-9]{32})");
|
|
2705
|
+
}
|
|
2706
|
+
return await callApi(apiBase, `/v1/gate/policy/${encodeURIComponent(policyId)}`, {}, apiKey, { method: "DELETE" });
|
|
2707
|
+
}
|
|
2156
2708
|
case "list_prompts": {
|
|
2157
2709
|
const q = {};
|
|
2158
2710
|
if (typeof safeArgs["label"] === "string")
|
|
@@ -2264,6 +2816,31 @@ export async function dispatchTool(input) {
|
|
|
2264
2816
|
case "list_projects": {
|
|
2265
2817
|
return await callApi(apiBase, "/v1/projects", {}, apiKey);
|
|
2266
2818
|
}
|
|
2819
|
+
case "list_members": {
|
|
2820
|
+
// R72c MEDIUM 2 fix: backend の full shape (= userId / accountId /
|
|
2821
|
+
// invitedBy / suspendedAt / removedAt / updatedAt 等の内部 ID・管理 field)
|
|
2822
|
+
// を MCP に素通ししない。 description どおり email / role / status /
|
|
2823
|
+
// 参加日時の最小 projection に絞る (= dashboard team UI は full shape を
|
|
2824
|
+
// 使うため backend response 自体は変えない)。
|
|
2825
|
+
const res = await callApi(apiBase, "/v1/memberships", {}, apiKey);
|
|
2826
|
+
if (res.isError)
|
|
2827
|
+
return res;
|
|
2828
|
+
try {
|
|
2829
|
+
const parsed = JSON.parse(res.content[0]?.text ?? "{}");
|
|
2830
|
+
const members = (parsed.members ?? []).map((m) => ({
|
|
2831
|
+
email: typeof m["email"] === "string" ? m["email"] : null,
|
|
2832
|
+
role: m["role"] ?? null,
|
|
2833
|
+
status: m["status"] ?? null,
|
|
2834
|
+
joinedAt: m["acceptedAt"] ?? m["createdAt"] ?? null,
|
|
2835
|
+
}));
|
|
2836
|
+
return {
|
|
2837
|
+
content: [{ type: "text", text: JSON.stringify({ members }) }],
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
catch {
|
|
2841
|
+
return errorResponse("unexpected memberships response shape");
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2267
2844
|
case "create_project": {
|
|
2268
2845
|
const name = safeArgs["name"];
|
|
2269
2846
|
const slug = safeArgs["slug"];
|
|
@@ -2481,6 +3058,95 @@ export async function dispatchTool(input) {
|
|
|
2481
3058
|
body["dryRun"] = dryRunRaw === true;
|
|
2482
3059
|
return await callApi(apiBase, "/v1/tier2/alerts/auto-silence", {}, apiKey, { method: "POST", jsonBody: body });
|
|
2483
3060
|
}
|
|
3061
|
+
// 2026-06-07 axis 4 Tier 2 第三弾 = extend_customer_trial + apply_promo_code_to_customer。
|
|
3062
|
+
// founder dogfood scope + Stripe API mutation 軸、 dryRun 必須、 backend で Stripe
|
|
3063
|
+
// Idempotency-Key carry。
|
|
3064
|
+
case "extend_customer_trial": {
|
|
3065
|
+
const targetAccountIdRaw = safeArgs["targetAccountId"];
|
|
3066
|
+
if (typeof targetAccountIdRaw !== "string" ||
|
|
3067
|
+
targetAccountIdRaw.length === 0 ||
|
|
3068
|
+
targetAccountIdRaw.length > 64) {
|
|
3069
|
+
return errorResponse("targetAccountId must be non-empty string <= 64 chars");
|
|
3070
|
+
}
|
|
3071
|
+
const extendDaysRaw = safeArgs["extendDays"];
|
|
3072
|
+
if (typeof extendDaysRaw !== "number" ||
|
|
3073
|
+
!Number.isInteger(extendDaysRaw) ||
|
|
3074
|
+
extendDaysRaw < 1 ||
|
|
3075
|
+
extendDaysRaw > 30) {
|
|
3076
|
+
return errorResponse("extendDays must be integer 1-30");
|
|
3077
|
+
}
|
|
3078
|
+
const reasonRaw = safeArgs["reason"];
|
|
3079
|
+
if (typeof reasonRaw !== "string" ||
|
|
3080
|
+
reasonRaw.length === 0 ||
|
|
3081
|
+
reasonRaw.length > 200) {
|
|
3082
|
+
return errorResponse("reason must be non-empty string <= 200 chars");
|
|
3083
|
+
}
|
|
3084
|
+
// R39 HIGH 1 carry: dryRun を MCP layer でも 必須明示 化。
|
|
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
|
+
extendDays: extendDaysRaw,
|
|
3092
|
+
reason: reasonRaw,
|
|
3093
|
+
dryRun: dryRunRaw,
|
|
3094
|
+
};
|
|
3095
|
+
// R39 SB2 carry: dryRun=false 時 idempotencyKey 必須。
|
|
3096
|
+
if (dryRunRaw === false) {
|
|
3097
|
+
const ikRaw = safeArgs["idempotencyKey"];
|
|
3098
|
+
if (typeof ikRaw !== "string" ||
|
|
3099
|
+
ikRaw.length < 16 ||
|
|
3100
|
+
ikRaw.length > 128 ||
|
|
3101
|
+
!/^[A-Za-z0-9_-]+$/.test(ikRaw)) {
|
|
3102
|
+
return errorResponse("idempotencyKey required for dryRun=false (16-128 alphanumeric + '_-' chars)");
|
|
3103
|
+
}
|
|
3104
|
+
body["idempotencyKey"] = ikRaw;
|
|
3105
|
+
}
|
|
3106
|
+
return await callApi(apiBase, "/v1/tier2/trial/extend", {}, apiKey, { method: "POST", jsonBody: body });
|
|
3107
|
+
}
|
|
3108
|
+
case "apply_promo_code_to_customer": {
|
|
3109
|
+
const targetAccountIdRaw = safeArgs["targetAccountId"];
|
|
3110
|
+
if (typeof targetAccountIdRaw !== "string" ||
|
|
3111
|
+
targetAccountIdRaw.length === 0 ||
|
|
3112
|
+
targetAccountIdRaw.length > 64) {
|
|
3113
|
+
return errorResponse("targetAccountId must be non-empty string <= 64 chars");
|
|
3114
|
+
}
|
|
3115
|
+
const promoCodeRaw = safeArgs["promoCode"];
|
|
3116
|
+
if (typeof promoCodeRaw !== "string" ||
|
|
3117
|
+
promoCodeRaw.length === 0 ||
|
|
3118
|
+
promoCodeRaw.length > 64 ||
|
|
3119
|
+
!/^[A-Za-z0-9_-]+$/.test(promoCodeRaw)) {
|
|
3120
|
+
return errorResponse("promoCode must be non-empty alphanumeric (+ '_-') string <= 64 chars");
|
|
3121
|
+
}
|
|
3122
|
+
const reasonRaw = safeArgs["reason"];
|
|
3123
|
+
if (typeof reasonRaw !== "string" ||
|
|
3124
|
+
reasonRaw.length === 0 ||
|
|
3125
|
+
reasonRaw.length > 200) {
|
|
3126
|
+
return errorResponse("reason must be non-empty string <= 200 chars");
|
|
3127
|
+
}
|
|
3128
|
+
const dryRunRaw = safeArgs["dryRun"];
|
|
3129
|
+
if (typeof dryRunRaw !== "boolean") {
|
|
3130
|
+
return errorResponse("dryRun must be explicit boolean (= R39 safety carry)");
|
|
3131
|
+
}
|
|
3132
|
+
const body = {
|
|
3133
|
+
targetAccountId: targetAccountIdRaw,
|
|
3134
|
+
promoCode: promoCodeRaw,
|
|
3135
|
+
reason: reasonRaw,
|
|
3136
|
+
dryRun: dryRunRaw,
|
|
3137
|
+
};
|
|
3138
|
+
if (dryRunRaw === false) {
|
|
3139
|
+
const ikRaw = safeArgs["idempotencyKey"];
|
|
3140
|
+
if (typeof ikRaw !== "string" ||
|
|
3141
|
+
ikRaw.length < 16 ||
|
|
3142
|
+
ikRaw.length > 128 ||
|
|
3143
|
+
!/^[A-Za-z0-9_-]+$/.test(ikRaw)) {
|
|
3144
|
+
return errorResponse("idempotencyKey required for dryRun=false (16-128 alphanumeric + '_-' chars)");
|
|
3145
|
+
}
|
|
3146
|
+
body["idempotencyKey"] = ikRaw;
|
|
3147
|
+
}
|
|
3148
|
+
return await callApi(apiBase, "/v1/tier2/promo/apply", {}, apiKey, { method: "POST", jsonBody: body });
|
|
3149
|
+
}
|
|
2484
3150
|
case "detect_anomaly": {
|
|
2485
3151
|
// 2026-06-05 axis 4 Tier 1 = 現 window vs baseline window (= 同 length
|
|
2486
3152
|
// の 1 期前) 比較で 4 軸 異常検出。 pure MCP-side aggregator、 backend
|
|
@@ -3252,6 +3918,33 @@ function validateAlertId(value) {
|
|
|
3252
3918
|
return null;
|
|
3253
3919
|
return value;
|
|
3254
3920
|
}
|
|
3921
|
+
/**
|
|
3922
|
+
* budget gate id = backend budgetGateHandler の `bg_` + UUID hex 32。
|
|
3923
|
+
* path injection 防御で形式を固定する。
|
|
3924
|
+
*/
|
|
3925
|
+
function validateBudgetGateId(value) {
|
|
3926
|
+
if (typeof value !== "string")
|
|
3927
|
+
return null;
|
|
3928
|
+
if (!/^bg_[a-f0-9]{32}$/.test(value))
|
|
3929
|
+
return null;
|
|
3930
|
+
return value;
|
|
3931
|
+
}
|
|
3932
|
+
/** approval id = backend approvalsHandler の `apr_` + UUID hex 32。 */
|
|
3933
|
+
function validateApprovalId(value) {
|
|
3934
|
+
if (typeof value !== "string")
|
|
3935
|
+
return null;
|
|
3936
|
+
if (!/^apr_[a-f0-9]{32}$/.test(value))
|
|
3937
|
+
return null;
|
|
3938
|
+
return value;
|
|
3939
|
+
}
|
|
3940
|
+
/** policy gate id = backend policyGateHandler の `pg_` + UUID hex 32。 */
|
|
3941
|
+
function validatePolicyGateId(value) {
|
|
3942
|
+
if (typeof value !== "string")
|
|
3943
|
+
return null;
|
|
3944
|
+
if (!/^pg_[a-f0-9]{32}$/.test(value))
|
|
3945
|
+
return null;
|
|
3946
|
+
return value;
|
|
3947
|
+
}
|
|
3255
3948
|
/**
|
|
3256
3949
|
* eventId は backend regex (= [A-Za-z0-9_-]{1,64}、 alert_events.id) で validate、
|
|
3257
3950
|
* path injection 防御。 alertId と異なり _ (underscore) も accept (= UUID v4 dashed
|