@argosvix/mcp-server 0.20.0-alpha.1 → 0.22.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
@@ -130,6 +130,24 @@ const TOOL_ARG_ALLOWLIST = {
130
130
  // は scores 同梱、 run は POST (= Pro+ で startEvalRun に渡す軸)。
131
131
  list_eval_runs: ["limit"],
132
132
  get_eval_run: ["runId"],
133
+ compare_eval_runs: ["baselineRunId", "candidateRunId"],
134
+ bulk_delete_calls: ["callIds", "dryRun"],
135
+ export_calls: ["startTime", "endTime", "provider", "model", "limit"],
136
+ list_saved_views: [],
137
+ create_saved_view: ["name", "filter"],
138
+ delete_saved_view: ["id"],
139
+ list_audit_log: ["limit", "eventType", "targetKind", "actorUserId", "from", "to", "cursor"],
140
+ aggregate_calls: ["startTime", "endTime", "groupBy", "metric", "provider", "tagKey"],
141
+ get_percentiles: ["startTime", "endTime", "provider", "model", "metric", "groupBy"],
142
+ list_projects: [],
143
+ create_project: ["name", "slug"],
144
+ rename_project: ["projectId", "name", "slug"],
145
+ delete_project: ["projectId"],
146
+ // 2026-06-05 axis 4 Tier 1 = get_account_health (= AI agent が 1 call で
147
+ // 自社 LLM infra の健康状態 サマリを取得)。 既存 4 endpoint (= aggregate /
148
+ // percentiles / llm-budget / audit) を 並列 fetch + 1 narrative response に
149
+ // 圧縮。 window 軸のみ 受け、 backend 新 endpoint 不要 (= 純 read aggregator)。
150
+ get_account_health: ["window"],
133
151
  // 2026-06-02 Codex round 2 🔴 fix = idempotencyKey 必須 path (= AI agent が
134
152
  // retry した時に backend で dedup)、 client が opaque string 64 char で carry。
135
153
  run_eval: ["name", "recentCount", "label", "promptRegistryId", "idempotencyKey"],
@@ -1205,6 +1223,379 @@ export const tools = [
1205
1223
  },
1206
1224
  },
1207
1225
  },
1226
+ {
1227
+ name: "get_percentiles",
1228
+ description: "calls の percentile metrics を 取得 (= POST /v1/query/percentiles)。 metric = 'latency' (= レイテンシ ms) or 'cost' (= USD)、 全期間 1 数値 or groupBy='day'/'hour'/'minute' で 時系列 series。 " +
1229
+ "AI agent が 「先週の p95 latency 推移を 日次で」 narrative で carry。 D1 SQLite (= percentile_cont 不在) で window function 経由 nearest-rank 法 計算。",
1230
+ inputSchema: {
1231
+ type: "object",
1232
+ additionalProperties: false,
1233
+ properties: {
1234
+ startTime: {
1235
+ type: "string",
1236
+ description: "範囲開始 ISO timestamp (= UTC、 省略 = 全期間)",
1237
+ },
1238
+ endTime: {
1239
+ type: "string",
1240
+ description: "範囲終了 ISO timestamp",
1241
+ },
1242
+ provider: { type: "string", description: "provider filter" },
1243
+ model: { type: "string", description: "model filter" },
1244
+ metric: {
1245
+ type: "string",
1246
+ description: "metric 種別、 default = 'latency'",
1247
+ enum: ["latency", "cost"],
1248
+ default: "latency",
1249
+ },
1250
+ groupBy: {
1251
+ type: "string",
1252
+ description: "時系列 分割 軸 (省略 = 全期間 1 数値、 'day' = 日次、 'hour' = 時間別、 'minute' = 分別)",
1253
+ enum: ["day", "hour", "minute"],
1254
+ },
1255
+ },
1256
+ },
1257
+ },
1258
+ {
1259
+ name: "list_projects",
1260
+ description: "自 account の active projects を 一覧取得 (= GET /v1/projects、 archived 除外)。 " +
1261
+ "v1.5 project switcher narrative で、 AI agent が 「dev / staging / prod 環境別 観測」 carry。 Pro 5 件 / Team unlimited、 Free は default のみ。",
1262
+ inputSchema: {
1263
+ type: "object",
1264
+ additionalProperties: false,
1265
+ properties: {},
1266
+ },
1267
+ },
1268
+ {
1269
+ name: "create_project",
1270
+ description: "新規 project を 作成 (= POST /v1/projects)。 name = 表示名、 slug = URL-safe 短い識別子 (= /^[a-z][a-z0-9-]{0,31}$/)。 " +
1271
+ "Pro 5 件上限、 Team unlimited、 Free は 不可 (= 403)。 mutation = session 認証時 Origin/Referer 強制 (= dashboard 経由前提)。",
1272
+ inputSchema: {
1273
+ type: "object",
1274
+ additionalProperties: false,
1275
+ required: ["name", "slug"],
1276
+ properties: {
1277
+ name: {
1278
+ type: "string",
1279
+ description: "project 表示名 (1-64 文字)",
1280
+ minLength: 1,
1281
+ maxLength: 64,
1282
+ },
1283
+ slug: {
1284
+ type: "string",
1285
+ description: "URL-safe 短い識別子 (= /^[a-z][a-z0-9-]{0,31}$/、 32 字以内、 先頭小文字、 hyphens 可)",
1286
+ pattern: "^[a-z][a-z0-9-]{0,31}$",
1287
+ },
1288
+ },
1289
+ },
1290
+ },
1291
+ {
1292
+ name: "rename_project",
1293
+ description: "既存 project の name / slug を 更新 (= PATCH /v1/projects/:id)。 name と slug は どちらか一方 / 両方指定可。 slug は URL-safe 制約 (= /^[a-z][a-z0-9-]{0,31}$/)。 default project の rename は 許可。",
1294
+ inputSchema: {
1295
+ type: "object",
1296
+ additionalProperties: false,
1297
+ required: ["projectId"],
1298
+ properties: {
1299
+ projectId: {
1300
+ type: "string",
1301
+ description: "対象 project の id (= list_projects で 取得した UUID)",
1302
+ minLength: 1,
1303
+ maxLength: 64,
1304
+ },
1305
+ name: {
1306
+ type: "string",
1307
+ description: "新しい表示名 (省略時 不変)",
1308
+ minLength: 1,
1309
+ maxLength: 64,
1310
+ },
1311
+ slug: {
1312
+ type: "string",
1313
+ description: "新しい slug (省略時 不変、 /^[a-z][a-z0-9-]{0,31}$/)",
1314
+ pattern: "^[a-z][a-z0-9-]{0,31}$",
1315
+ },
1316
+ },
1317
+ },
1318
+ },
1319
+ {
1320
+ name: "delete_project",
1321
+ description: "project を soft delete (= DELETE /v1/projects/:id、 archived_at 設定で 論理削除)。 default project は 削除不可 (= accounts.default_project_id 参照 整合 のため 400)。 " +
1322
+ "archived 後 calls / alerts は そのまま (= 過去観測は keep)、 新規 record は 別 project に carry する narrative。",
1323
+ inputSchema: {
1324
+ type: "object",
1325
+ additionalProperties: false,
1326
+ required: ["projectId"],
1327
+ properties: {
1328
+ projectId: {
1329
+ type: "string",
1330
+ description: "削除対象 project の id (= list_projects 経由)",
1331
+ minLength: 1,
1332
+ maxLength: 64,
1333
+ },
1334
+ },
1335
+ },
1336
+ },
1337
+ {
1338
+ name: "get_account_health",
1339
+ description: "自社 LLM infra の健康状態 サマリを 1 call で取得 (= axis 4 Tier 1 = 自律 AI ops)。 既存 4 endpoint (= aggregate_calls / get_percentiles / get_llm_budget / list_audit_log) を 並列 fetch して 1 narrative response に圧縮。 " +
1340
+ "返却 = { window, totals: {calls, costUsd, errorRate}, latency: {p50, p95, p99}, budget: {used, limit, percentUsed}, recentEvents: 件数, summary: 'ok' | 'warn' | 'critical' } 形式。 " +
1341
+ "AI agent narrative = 「今うちの LLM infra どう?」 を 1 prompt で carry。 backend 新 endpoint 不要 = 純 read aggregator。 個別 endpoint 失敗は partial で 返す (= 1 軸 timeout が summary を 止めない)。",
1342
+ inputSchema: {
1343
+ type: "object",
1344
+ additionalProperties: false,
1345
+ properties: {
1346
+ window: {
1347
+ type: "string",
1348
+ description: "観測窓 (= '1h' / '24h' / '7d'、 default '24h')",
1349
+ enum: ["1h", "24h", "7d"],
1350
+ default: "24h",
1351
+ },
1352
+ },
1353
+ },
1354
+ },
1355
+ {
1356
+ name: "aggregate_calls",
1357
+ description: "calls の 集計 cube を 取得 (= POST /v1/query/aggregate)。 groupBy (= provider / model / day / hour / minute / tag) × metric (= cost / latency / tokens / count / error_rate) で 1 call で AI agent が 「今月の cost を model 別 に集計」 narrative carry。 " +
1358
+ "tag mode は tagKey 必須 (= alphanumeric + _ - のみ、 例: 'env' / 'feature')。 hour mode は 168h / minute mode は 60min まで (= 超過 400)。 cost = SUM(cost_usd) / latency = AVG(latency_ms) / tokens = SUM(total_tokens) / count = COUNT(*) / error_rate = error ÷ total。 " +
1359
+ "返却 = { groups: [{key, value, count}], total: {value, count} } 形式。 軸 1 操作系 + 自律 AI ops の 分析 narrative の coverage 拡張。",
1360
+ inputSchema: {
1361
+ type: "object",
1362
+ additionalProperties: false,
1363
+ properties: {
1364
+ startTime: {
1365
+ type: "string",
1366
+ description: "範囲開始 ISO timestamp (= UTC、 省略 = 全期間)",
1367
+ },
1368
+ endTime: {
1369
+ type: "string",
1370
+ description: "範囲終了 ISO timestamp (= UTC、 省略 = 現在)",
1371
+ },
1372
+ groupBy: {
1373
+ type: "string",
1374
+ description: "集約軸 (= 'provider' / 'model' / 'day' / 'hour' / 'minute' / 'tag')、 default = 'provider'。 hour は 168h / minute は 60min まで",
1375
+ enum: ["provider", "model", "day", "hour", "minute", "tag"],
1376
+ default: "provider",
1377
+ },
1378
+ metric: {
1379
+ type: "string",
1380
+ description: "metric 種別 (= 'cost' / 'latency' / 'tokens' / 'count' / 'error_rate')、 default = 'cost'",
1381
+ enum: ["cost", "latency", "tokens", "count", "error_rate"],
1382
+ default: "cost",
1383
+ },
1384
+ provider: {
1385
+ type: "string",
1386
+ description: "provider filter (= 'openai' / 'anthropic' 等)、 省略 = 全 provider",
1387
+ },
1388
+ tagKey: {
1389
+ type: "string",
1390
+ description: "groupBy='tag' の時必須。 tags JSON 内 key 名 (alphanumeric + _- のみ、 1-64 文字)",
1391
+ pattern: "^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$",
1392
+ },
1393
+ },
1394
+ },
1395
+ },
1396
+ {
1397
+ name: "list_audit_log",
1398
+ description: "Phase B audit log を 一覧 取得 (= GET /v1/audit-log)。 自 account 限定、 admin role のみ許可 (= viewer/member は 403)。 " +
1399
+ "AI agent が 「最近の招待 / API key revoke / プロジェクト変更」 等の 操作履歴 を 自律参照する narrative (= axis 4 自律 AI ops)。 " +
1400
+ "filter = eventType (= 'invitation.created' / 'api_key.revoked' 等) / targetKind / actorUserId / from / to。 " +
1401
+ "cursor pagination 対応 (= nextCursor 形式 = 'created_at|id')、 max limit 200。",
1402
+ inputSchema: {
1403
+ type: "object",
1404
+ additionalProperties: false,
1405
+ properties: {
1406
+ limit: {
1407
+ type: "integer",
1408
+ description: "返却件数 (1-200、 デフォルト 50)",
1409
+ minimum: 1,
1410
+ maximum: 200,
1411
+ default: 50,
1412
+ },
1413
+ eventType: {
1414
+ type: "string",
1415
+ description: "event_type 完全一致 filter (= 'invitation.created' / 'api_key.revoked' / 'membership.removed' 等)",
1416
+ },
1417
+ targetKind: {
1418
+ type: "string",
1419
+ description: "target_kind filter (= 'invitation' / 'api_key' / 'membership' 等)",
1420
+ },
1421
+ actorUserId: {
1422
+ type: "string",
1423
+ description: "actor_user_id filter (= 特定 user の操作のみ抽出)",
1424
+ },
1425
+ from: {
1426
+ type: "string",
1427
+ description: "範囲開始 ISO timestamp (= UTC)",
1428
+ },
1429
+ to: {
1430
+ type: "string",
1431
+ description: "範囲終了 ISO timestamp (= UTC)",
1432
+ },
1433
+ cursor: {
1434
+ type: "string",
1435
+ description: "ページ送り cursor (= 前 response の nextCursor を そのまま渡す、 'created_at|id' 形式)",
1436
+ },
1437
+ },
1438
+ },
1439
+ },
1440
+ {
1441
+ name: "list_saved_views",
1442
+ description: "保存済 saved views 一覧を 取得 (= GET /v1/saved-views)。 saved view = /calls page で よく使う filter (startDate/endDate/provider/model/limit) の組み合わせを 名前付きで 保存したもの。 " +
1443
+ "AI agent は 「いつもの先週の OpenAI filter で 呼び出し見せて」 narrative で carry できる。 account 単位、 max 20 件。",
1444
+ inputSchema: {
1445
+ type: "object",
1446
+ additionalProperties: false,
1447
+ properties: {},
1448
+ },
1449
+ },
1450
+ {
1451
+ name: "create_saved_view",
1452
+ description: "新規 saved view を 作成 / 同名なら上書き (= POST /v1/saved-views)。 name は account 内一意。 filter は SavedViewFilter shape (= startDate / endDate / provider / model / limit / preset / sortBy? / sortOrder?)。 " +
1453
+ "AI agent が 自動で よく使う filter を 名前付き保存 narrative。 例: 「直近 7 日 GPT-4 のみ」 view を 作って 後で呼ぶ。",
1454
+ inputSchema: {
1455
+ type: "object",
1456
+ additionalProperties: false,
1457
+ required: ["name", "filter"],
1458
+ properties: {
1459
+ name: {
1460
+ type: "string",
1461
+ description: "saved view の名前 (1-80 文字、 改行不可)。 同名で 既存なら 上書き",
1462
+ minLength: 1,
1463
+ maxLength: 80,
1464
+ },
1465
+ filter: {
1466
+ type: "object",
1467
+ description: "filter shape = startDate (ISO) + endDate (ISO) + provider (空可) + model (空可) + limit (number) + preset (string|null) + sortBy? + sortOrder?",
1468
+ required: ["startDate", "endDate", "provider", "model", "limit", "preset"],
1469
+ properties: {
1470
+ startDate: { type: "string", description: "ISO timestamp (= 範囲開始)" },
1471
+ endDate: { type: "string", description: "ISO timestamp (= 範囲終了)" },
1472
+ provider: {
1473
+ type: "string",
1474
+ description: "プロバイダー (= 'openai' / 'anthropic' / 'google' 等)、 空 = 全 provider",
1475
+ },
1476
+ model: {
1477
+ type: "string",
1478
+ description: "モデル名、 空 = 全 model",
1479
+ },
1480
+ limit: {
1481
+ type: "integer",
1482
+ description: "返却件数 cap",
1483
+ minimum: 1,
1484
+ },
1485
+ preset: {
1486
+ type: ["string", "null"],
1487
+ description: "preset 識別子 (= dashboard 既定 filter、 null 可)",
1488
+ },
1489
+ sortBy: { type: "string", description: "ソート対象 column" },
1490
+ sortOrder: {
1491
+ type: "string",
1492
+ description: "ソート方向 ('asc' / 'desc')",
1493
+ enum: ["asc", "desc"],
1494
+ },
1495
+ },
1496
+ },
1497
+ },
1498
+ },
1499
+ },
1500
+ {
1501
+ name: "delete_saved_view",
1502
+ description: "指定 id の saved view を 削除 (= DELETE /v1/saved-views/:id)。 自 account 限定。",
1503
+ inputSchema: {
1504
+ type: "object",
1505
+ additionalProperties: false,
1506
+ required: ["id"],
1507
+ properties: {
1508
+ id: {
1509
+ type: "string",
1510
+ description: "削除対象の saved view id (= UUID)",
1511
+ minLength: 1,
1512
+ maxLength: 64,
1513
+ },
1514
+ },
1515
+ },
1516
+ },
1517
+ {
1518
+ name: "export_calls",
1519
+ description: "calls の large batch export (= POST /v1/query/export)。 query_calls より 高 limit (= plan 別 max records: Free 1000 / Pro 50000、 config/plans.ts)、 全 plan で利用可。 " +
1520
+ "filter 軸 = startTime / endTime / provider / model + limit。 AI agent が 「先月分の全 GPT-4 呼び出しを取り出して傾向分析して」 narrative で 1 call carry。 " +
1521
+ "結果 format は query_calls と 同 JSON (= AI が そのまま CSV / 統計に carry 可能)。",
1522
+ inputSchema: {
1523
+ type: "object",
1524
+ additionalProperties: false,
1525
+ properties: {
1526
+ startTime: {
1527
+ type: "string",
1528
+ description: "範囲 開始 ISO timestamp (= UTC、 省略 = 全期間)",
1529
+ },
1530
+ endTime: {
1531
+ type: "string",
1532
+ description: "範囲 終了 ISO timestamp (= UTC、 省略 = 現在)",
1533
+ },
1534
+ provider: {
1535
+ type: "string",
1536
+ description: "プロバイダー fix (= openai / anthropic / google / azure / cohere)",
1537
+ },
1538
+ model: {
1539
+ type: "string",
1540
+ description: "model 名 fix (= 部分一致なし、 完全一致 例: 'gpt-4o-mini')",
1541
+ },
1542
+ limit: {
1543
+ type: "integer",
1544
+ description: "返却件数 cap。 plan 別 max 内なら そのまま、 超過は plan max に clamp",
1545
+ minimum: 1,
1546
+ },
1547
+ },
1548
+ },
1549
+ },
1550
+ {
1551
+ name: "bulk_delete_calls",
1552
+ description: "指定 call id 一覧 (= max 100) を 自 account 限定で 一括削除する (= POST /v1/calls/bulk-delete)。 " +
1553
+ "AI agent が dogfood / dev test で 蓄積した garbage call の cleanup narrative に carry (= 軸 1 操作系)。 " +
1554
+ "dryRun=true で 削除前に matched 件数を 事前確認可能。 削除は 1 SQL atomic、 audit log に bulk_deleted event を 記録。 " +
1555
+ "FK 制約上 関連 traces / annotations / scores は ON DELETE 経由で 連鎖削除 (= 既存 schema narrative)。",
1556
+ inputSchema: {
1557
+ type: "object",
1558
+ additionalProperties: false,
1559
+ required: ["callIds"],
1560
+ properties: {
1561
+ callIds: {
1562
+ type: "array",
1563
+ description: "削除対象の call id 配列 (1-100 件、 各 1-128 文字)",
1564
+ items: { type: "string", minLength: 1, maxLength: 128 },
1565
+ minItems: 1,
1566
+ maxItems: 100,
1567
+ },
1568
+ dryRun: {
1569
+ type: "boolean",
1570
+ description: "true で 削除せず matched 件数のみ 返却 (= 確認 UX)",
1571
+ default: false,
1572
+ },
1573
+ },
1574
+ },
1575
+ },
1576
+ {
1577
+ name: "compare_eval_runs",
1578
+ description: "2 つの eval run (baseline / candidate) を 比較して per-criterion mean score delta + failed count delta + verdict を 返す (= GET /v1/eval-runs/compare)。 " +
1579
+ "AI agent は 「baseline と 比べて candidate は どう 変わったか」 を 1 call で 把握でき、 prompt 改善 効果や regress 検出 narrative に carry できる (= axis 1 操作系 + axis 4 自律 AI ops 寄与)。 " +
1580
+ "verdict = improved / regressed / mixed / unchanged。 failed count は score <= 2 を 「failed」 で 算出。 同 account 限定。",
1581
+ inputSchema: {
1582
+ type: "object",
1583
+ additionalProperties: false,
1584
+ required: ["baselineRunId", "candidateRunId"],
1585
+ properties: {
1586
+ baselineRunId: {
1587
+ type: "integer",
1588
+ description: "比較元 run の id (= list_eval_runs.runs[].id)",
1589
+ minimum: 1,
1590
+ },
1591
+ candidateRunId: {
1592
+ type: "integer",
1593
+ description: "比較先 run の id (= 同上)、 baseline と 異なる必要あり",
1594
+ minimum: 1,
1595
+ },
1596
+ },
1597
+ },
1598
+ },
1208
1599
  {
1209
1600
  name: "run_eval",
1210
1601
  description: "新規 eval run を 即時実行する (= POST /v1/eval-runs)。 直近 N 件の calls × 既定 5 criteria (+ 自作 custom criteria max 8) で gpt-4o-mini に採点させる。 " +
@@ -1605,6 +1996,287 @@ export async function dispatchTool(input) {
1605
1996
  }
1606
1997
  return await callApi(apiBase, `/v1/eval-runs/${encodeURIComponent(runId)}`, {}, apiKey);
1607
1998
  }
1999
+ case "get_percentiles": {
2000
+ const body = {};
2001
+ if (typeof safeArgs["startTime"] === "string")
2002
+ body["startTime"] = safeArgs["startTime"];
2003
+ if (typeof safeArgs["endTime"] === "string")
2004
+ body["endTime"] = safeArgs["endTime"];
2005
+ if (typeof safeArgs["provider"] === "string")
2006
+ body["provider"] = safeArgs["provider"];
2007
+ if (typeof safeArgs["model"] === "string")
2008
+ body["model"] = safeArgs["model"];
2009
+ if (typeof safeArgs["metric"] === "string")
2010
+ body["metric"] = safeArgs["metric"];
2011
+ if (typeof safeArgs["groupBy"] === "string")
2012
+ body["groupBy"] = safeArgs["groupBy"];
2013
+ return await callApi(apiBase, "/v1/query/percentiles", {}, apiKey, {
2014
+ method: "POST",
2015
+ jsonBody: body,
2016
+ });
2017
+ }
2018
+ case "list_projects": {
2019
+ return await callApi(apiBase, "/v1/projects", {}, apiKey);
2020
+ }
2021
+ case "create_project": {
2022
+ const name = safeArgs["name"];
2023
+ const slug = safeArgs["slug"];
2024
+ if (typeof name !== "string" || name.length === 0 || name.length > 64) {
2025
+ return errorResponse("name required (1-64 chars)");
2026
+ }
2027
+ if (typeof slug !== "string" || !/^[a-z][a-z0-9-]{0,31}$/.test(slug)) {
2028
+ return errorResponse("slug required (lowercase alphanumeric + hyphens, max 32 chars, starts with letter)");
2029
+ }
2030
+ return await callApi(apiBase, "/v1/projects", {}, apiKey, {
2031
+ method: "POST",
2032
+ jsonBody: { name, slug },
2033
+ });
2034
+ }
2035
+ case "rename_project": {
2036
+ const projectId = safeArgs["projectId"];
2037
+ if (typeof projectId !== "string" || projectId.length === 0 || projectId.length > 64) {
2038
+ return errorResponse("projectId required (1-64 chars)");
2039
+ }
2040
+ const body = {};
2041
+ if (typeof safeArgs["name"] === "string")
2042
+ body["name"] = safeArgs["name"];
2043
+ if (typeof safeArgs["slug"] === "string") {
2044
+ if (!/^[a-z][a-z0-9-]{0,31}$/.test(safeArgs["slug"])) {
2045
+ return errorResponse("slug must match /^[a-z][a-z0-9-]{0,31}$/");
2046
+ }
2047
+ body["slug"] = safeArgs["slug"];
2048
+ }
2049
+ if (Object.keys(body).length === 0) {
2050
+ return errorResponse("at least one of name / slug required");
2051
+ }
2052
+ return await callApi(apiBase, `/v1/projects/${encodeURIComponent(projectId)}`, {}, apiKey, { method: "PATCH", jsonBody: body });
2053
+ }
2054
+ case "delete_project": {
2055
+ const projectId = safeArgs["projectId"];
2056
+ if (typeof projectId !== "string" || projectId.length === 0 || projectId.length > 64) {
2057
+ return errorResponse("projectId required (1-64 chars)");
2058
+ }
2059
+ return await callApi(apiBase, `/v1/projects/${encodeURIComponent(projectId)}`, {}, apiKey, { method: "DELETE" });
2060
+ }
2061
+ case "get_account_health": {
2062
+ // 2026-06-05 axis 4 Tier 1 = 自社 LLM infra 健康状態 サマリ。 既存
2063
+ // 4 endpoint を 並列 fetch + 1 narrative 圧縮。 個別 fail は partial
2064
+ // で carry (= 1 軸 timeout で summary を 止めない設計)。
2065
+ const winRaw = typeof safeArgs["window"] === "string" ? safeArgs["window"] : "24h";
2066
+ const window = winRaw === "1h" || winRaw === "24h" || winRaw === "7d" ? winRaw : "24h";
2067
+ const now = Date.now();
2068
+ const windowMs = window === "1h"
2069
+ ? 60 * 60 * 1000
2070
+ : window === "24h"
2071
+ ? 24 * 60 * 60 * 1000
2072
+ : 7 * 24 * 60 * 60 * 1000;
2073
+ const startTime = new Date(now - windowMs).toISOString();
2074
+ const endTime = new Date(now).toISOString();
2075
+ const aggregateBody = { startTime, endTime, groupBy: "provider", metric: "count" };
2076
+ const errorBody = { startTime, endTime, groupBy: "provider", metric: "error_rate" };
2077
+ const costBody = { startTime, endTime, groupBy: "provider", metric: "cost" };
2078
+ const percentileBody = { startTime, endTime, metric: "latency" };
2079
+ const [countsRes, errorRes, costRes, percentilesRes, budgetRes, auditRes,] = await Promise.allSettled([
2080
+ callApi(apiBase, "/v1/query/aggregate", {}, apiKey, {
2081
+ method: "POST",
2082
+ jsonBody: aggregateBody,
2083
+ }),
2084
+ callApi(apiBase, "/v1/query/aggregate", {}, apiKey, {
2085
+ method: "POST",
2086
+ jsonBody: errorBody,
2087
+ }),
2088
+ callApi(apiBase, "/v1/query/aggregate", {}, apiKey, {
2089
+ method: "POST",
2090
+ jsonBody: costBody,
2091
+ }),
2092
+ callApi(apiBase, "/v1/query/percentiles", {}, apiKey, {
2093
+ method: "POST",
2094
+ jsonBody: percentileBody,
2095
+ }),
2096
+ callApi(apiBase, "/v1/account/llm-feature-budget", {}, apiKey),
2097
+ callApi(apiBase, "/v1/audit-log", { limit: 10 }, apiKey),
2098
+ ]);
2099
+ const extractJson = (r) => {
2100
+ if (r.status !== "fulfilled" || r.value.isError)
2101
+ return null;
2102
+ const txt = r.value.content[0]?.text ?? "";
2103
+ try {
2104
+ return JSON.parse(txt);
2105
+ }
2106
+ catch {
2107
+ return null;
2108
+ }
2109
+ };
2110
+ const counts = extractJson(countsRes);
2111
+ const errors = extractJson(errorRes);
2112
+ const cost = extractJson(costRes);
2113
+ const percentiles = extractJson(percentilesRes);
2114
+ const budget = extractJson(budgetRes);
2115
+ const audit = extractJson(auditRes);
2116
+ const totalCalls = counts?.total?.value ?? 0;
2117
+ const errorRate = errors?.total?.value !== undefined && errors.total.value !== null
2118
+ ? errors.total.value
2119
+ : null;
2120
+ const costUsd = cost?.total?.value ?? 0;
2121
+ const p50 = percentiles?.p50 ?? null;
2122
+ const p95 = percentiles?.p95 ?? null;
2123
+ const p99 = percentiles?.p99 ?? null;
2124
+ const budgetUsed = budget?.usedUsd ?? null;
2125
+ const budgetLimit = budget?.monthlyLimitUsd ?? null;
2126
+ const budgetPercent = budgetUsed !== null && budgetLimit !== null && budgetLimit > 0
2127
+ ? Math.round((budgetUsed / budgetLimit) * 1000) / 10
2128
+ : null;
2129
+ const recentEvents = Array.isArray(audit?.events) ? audit.events.length : 0;
2130
+ let summary = "ok";
2131
+ if ((errorRate !== null && errorRate >= 0.1) ||
2132
+ (budgetPercent !== null && budgetPercent >= 90) ||
2133
+ (p95 !== null && p95 >= 10000)) {
2134
+ summary = "critical";
2135
+ }
2136
+ else if ((errorRate !== null && errorRate >= 0.03) ||
2137
+ (budgetPercent !== null && budgetPercent >= 70) ||
2138
+ (p95 !== null && p95 >= 3000)) {
2139
+ summary = "warn";
2140
+ }
2141
+ const partialFailures = [];
2142
+ if (countsRes.status !== "fulfilled" || countsRes.value.isError)
2143
+ partialFailures.push("counts");
2144
+ if (errorRes.status !== "fulfilled" || errorRes.value.isError)
2145
+ partialFailures.push("errorRate");
2146
+ if (costRes.status !== "fulfilled" || costRes.value.isError)
2147
+ partialFailures.push("cost");
2148
+ if (percentilesRes.status !== "fulfilled" || percentilesRes.value.isError)
2149
+ partialFailures.push("percentiles");
2150
+ if (budgetRes.status !== "fulfilled" || budgetRes.value.isError)
2151
+ partialFailures.push("budget");
2152
+ if (auditRes.status !== "fulfilled" || auditRes.value.isError)
2153
+ partialFailures.push("auditLog");
2154
+ return {
2155
+ content: [
2156
+ {
2157
+ type: "text",
2158
+ text: JSON.stringify({
2159
+ window,
2160
+ totals: { calls: totalCalls, costUsd, errorRate },
2161
+ latency: { p50, p95, p99 },
2162
+ budget: { used: budgetUsed, limit: budgetLimit, percentUsed: budgetPercent },
2163
+ recentEvents,
2164
+ summary,
2165
+ partialFailures: partialFailures.length > 0 ? partialFailures : undefined,
2166
+ }),
2167
+ },
2168
+ ],
2169
+ };
2170
+ }
2171
+ case "aggregate_calls": {
2172
+ const body = {};
2173
+ if (typeof safeArgs["startTime"] === "string")
2174
+ body["startTime"] = safeArgs["startTime"];
2175
+ if (typeof safeArgs["endTime"] === "string")
2176
+ body["endTime"] = safeArgs["endTime"];
2177
+ if (typeof safeArgs["groupBy"] === "string")
2178
+ body["groupBy"] = safeArgs["groupBy"];
2179
+ if (typeof safeArgs["metric"] === "string")
2180
+ body["metric"] = safeArgs["metric"];
2181
+ if (typeof safeArgs["provider"] === "string")
2182
+ body["provider"] = safeArgs["provider"];
2183
+ if (typeof safeArgs["tagKey"] === "string")
2184
+ body["tagKey"] = safeArgs["tagKey"];
2185
+ return await callApi(apiBase, "/v1/query/aggregate", {}, apiKey, {
2186
+ method: "POST",
2187
+ jsonBody: body,
2188
+ });
2189
+ }
2190
+ case "list_audit_log": {
2191
+ const q = {};
2192
+ if (typeof safeArgs["limit"] === "number")
2193
+ q["limit"] = safeArgs["limit"];
2194
+ if (typeof safeArgs["eventType"] === "string")
2195
+ q["eventType"] = safeArgs["eventType"];
2196
+ if (typeof safeArgs["targetKind"] === "string")
2197
+ q["targetKind"] = safeArgs["targetKind"];
2198
+ if (typeof safeArgs["actorUserId"] === "string")
2199
+ q["actorUserId"] = safeArgs["actorUserId"];
2200
+ if (typeof safeArgs["from"] === "string")
2201
+ q["from"] = safeArgs["from"];
2202
+ if (typeof safeArgs["to"] === "string")
2203
+ q["to"] = safeArgs["to"];
2204
+ if (typeof safeArgs["cursor"] === "string")
2205
+ q["cursor"] = safeArgs["cursor"];
2206
+ return await callApi(apiBase, "/v1/audit-log", q, apiKey);
2207
+ }
2208
+ case "list_saved_views": {
2209
+ return await callApi(apiBase, "/v1/saved-views", {}, apiKey);
2210
+ }
2211
+ case "create_saved_view": {
2212
+ const name = safeArgs["name"];
2213
+ const filter = safeArgs["filter"];
2214
+ if (typeof name !== "string" || name.length === 0 || name.length > 80) {
2215
+ return errorResponse("name required (1-80 chars)");
2216
+ }
2217
+ if (!filter || typeof filter !== "object") {
2218
+ return errorResponse("filter required (object)");
2219
+ }
2220
+ return await callApi(apiBase, "/v1/saved-views", {}, apiKey, {
2221
+ method: "POST",
2222
+ jsonBody: { name, filter },
2223
+ });
2224
+ }
2225
+ case "delete_saved_view": {
2226
+ const id = safeArgs["id"];
2227
+ if (typeof id !== "string" || id.length === 0 || id.length > 64) {
2228
+ return errorResponse("id required (1-64 chars)");
2229
+ }
2230
+ return await callApi(apiBase, `/v1/saved-views/${encodeURIComponent(id)}`, {}, apiKey, { method: "DELETE" });
2231
+ }
2232
+ case "export_calls": {
2233
+ const body = {};
2234
+ if (typeof safeArgs["startTime"] === "string")
2235
+ body["startTime"] = safeArgs["startTime"];
2236
+ if (typeof safeArgs["endTime"] === "string")
2237
+ body["endTime"] = safeArgs["endTime"];
2238
+ if (typeof safeArgs["provider"] === "string")
2239
+ body["provider"] = safeArgs["provider"];
2240
+ if (typeof safeArgs["model"] === "string")
2241
+ body["model"] = safeArgs["model"];
2242
+ if (typeof safeArgs["limit"] === "number")
2243
+ body["limit"] = safeArgs["limit"];
2244
+ return await callApi(apiBase, "/v1/query/export", {}, apiKey, {
2245
+ method: "POST",
2246
+ jsonBody: body,
2247
+ });
2248
+ }
2249
+ case "bulk_delete_calls": {
2250
+ const ids = safeArgs["callIds"];
2251
+ if (!Array.isArray(ids) || ids.length === 0 || ids.length > 100) {
2252
+ return errorResponse("callIds required (non-empty string array, max 100)");
2253
+ }
2254
+ const strIds = [];
2255
+ for (const id of ids) {
2256
+ if (typeof id !== "string" || id.length === 0 || id.length > 128) {
2257
+ return errorResponse("each callId must be non-empty string up to 128 chars");
2258
+ }
2259
+ strIds.push(id);
2260
+ }
2261
+ const body = { callIds: strIds };
2262
+ if (safeArgs["dryRun"] === true)
2263
+ body["dryRun"] = true;
2264
+ return await callApi(apiBase, "/v1/calls/bulk-delete", {}, apiKey, {
2265
+ method: "POST",
2266
+ jsonBody: body,
2267
+ });
2268
+ }
2269
+ case "compare_eval_runs": {
2270
+ const baselineId = validateAnnotationId(safeArgs["baselineRunId"]);
2271
+ const candidateId = validateAnnotationId(safeArgs["candidateRunId"]);
2272
+ if (!baselineId || !candidateId) {
2273
+ return errorResponse("baselineRunId + candidateRunId required (positive integers up to 10 digits)");
2274
+ }
2275
+ if (baselineId === candidateId) {
2276
+ return errorResponse("baselineRunId and candidateRunId must differ");
2277
+ }
2278
+ return await callApi(apiBase, "/v1/eval-runs/compare", { baseline: baselineId, candidate: candidateId }, apiKey);
2279
+ }
1608
2280
  case "run_eval": {
1609
2281
  const body = {};
1610
2282
  if (typeof safeArgs["name"] === "string")