@carrierllc/mcp 0.13.16 → 0.13.17

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/index.js CHANGED
@@ -80,7 +80,7 @@ import {
80
80
  verifyStorefront,
81
81
  withOcsListSummary,
82
82
  withTemplateResellerId
83
- } from "./chunk-D6VGTHND.js";
83
+ } from "./chunk-SAZFMURF.js";
84
84
  import "./chunk-SHKKVIIA.js";
85
85
 
86
86
  // src/index.ts
@@ -291,9 +291,9 @@ async function fail(resp) {
291
291
  throw new BedrockError(text || `Bedrock returned HTTP ${resp.status}`, resp.status);
292
292
  }
293
293
  function timedOut() {
294
- const err7 = new Error("Bedrock call timed out before a usable response");
295
- err7.name = "TimeoutError";
296
- return err7;
294
+ const err8 = new Error("Bedrock call timed out before a usable response");
295
+ err8.name = "TimeoutError";
296
+ return err8;
297
297
  }
298
298
  function sleep(ms, signal) {
299
299
  if (signal.aborted) return Promise.resolve();
@@ -317,9 +317,9 @@ async function sendWithRetry(aws, url, init, signal, deadlineAt) {
317
317
  let resp;
318
318
  try {
319
319
  resp = await aws.fetch(url, init);
320
- } catch (err7) {
320
+ } catch (err8) {
321
321
  if (signal.aborted && throttled) return throttled;
322
- throw err7;
322
+ throw err8;
323
323
  }
324
324
  if (!retryable(resp.status)) return resp;
325
325
  throttled = resp;
@@ -668,8 +668,8 @@ async function beginUiAgentRun(prompt, title, env, opts) {
668
668
  ...opts?.model ? { model: opts.model } : {},
669
669
  ...opts?.outputSchema ? { outputSchema: opts.outputSchema } : {}
670
670
  });
671
- } catch (err7) {
672
- const message = err7 instanceof Error ? err7.message : String(err7);
671
+ } catch (err8) {
672
+ const message = err8 instanceof Error ? err8.message : String(err8);
673
673
  await putTask(env, {
674
674
  ...base,
675
675
  status: "error",
@@ -681,7 +681,7 @@ async function beginUiAgentRun(prompt, title, env, opts) {
681
681
  screenshots_taken: 0
682
682
  }
683
683
  });
684
- throw err7;
684
+ throw err8;
685
685
  }
686
686
  const dispatched = {
687
687
  ...base,
@@ -695,8 +695,8 @@ async function beginUiAgentRun(prompt, title, env, opts) {
695
695
  let run;
696
696
  try {
697
697
  run = await waitForRun(env, created.runId);
698
- } catch (err7) {
699
- const message = err7 instanceof Error ? err7.message : String(err7);
698
+ } catch (err8) {
699
+ const message = err8 instanceof Error ? err8.message : String(err8);
700
700
  const failed = {
701
701
  success: false,
702
702
  summary: `Browser Use run failed: ${message}`,
@@ -837,8 +837,8 @@ function writeAgentAudit(env, entry) {
837
837
  ],
838
838
  indexes: [entry.task_id]
839
839
  });
840
- } catch (err7) {
841
- console.error(`[browser-use-agent] audit write failed: ${err7.message}`);
840
+ } catch (err8) {
841
+ console.error(`[browser-use-agent] audit write failed: ${err8.message}`);
842
842
  }
843
843
  }
844
844
 
@@ -1073,22 +1073,22 @@ function boundZonesResult(raw, opts = {}) {
1073
1073
  const { verbose = false, resellerId } = opts;
1074
1074
  const maxZones = opts.maxZones ?? ZONES_MAX_ZONES;
1075
1075
  const maxOperators = opts.maxOperators ?? (verbose ? ZONES_VERBOSE_MAX_OPERATORS : ZONES_MAX_OPERATORS);
1076
- const totalOperators = all.reduce((n, z25) => n + (z25.operators?.length ?? 0), 0);
1076
+ const totalOperators = all.reduce((n, z26) => n + (z26.operators?.length ?? 0), 0);
1077
1077
  const keptZones = all.slice(0, maxZones);
1078
1078
  let budget = maxOperators;
1079
1079
  let returnedOperators = 0;
1080
- const zones = keptZones.map((z25) => {
1081
- const ops = z25.operators ?? [];
1080
+ const zones = keptZones.map((z26) => {
1081
+ const ops = z26.operators ?? [];
1082
1082
  const take = Math.max(0, Math.min(ops.length, budget));
1083
1083
  budget -= take;
1084
1084
  returnedOperators += take;
1085
1085
  const sliced = ops.slice(0, take);
1086
1086
  if (verbose) {
1087
- return { ...z25, operators: sliced, operatorCount: ops.length };
1087
+ return { ...z26, operators: sliced, operatorCount: ops.length };
1088
1088
  }
1089
1089
  return {
1090
- zoneId: z25.zoneId,
1091
- zoneName: z25.zoneName,
1090
+ zoneId: z26.zoneId,
1091
+ zoneName: z26.zoneName,
1092
1092
  operatorCount: ops.length,
1093
1093
  operators: sliced.map((o) => ({
1094
1094
  net: o.networkId,
@@ -1278,9 +1278,9 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler, options)
1278
1278
  try {
1279
1279
  const token2 = await ctx.getUserToken(ctx.props.sub);
1280
1280
  result2 = await handler(args, token2);
1281
- } catch (err7) {
1281
+ } catch (err8) {
1282
1282
  try {
1283
- Sentry.captureException(err7, {
1283
+ Sentry.captureException(err8, {
1284
1284
  tags: {
1285
1285
  tool: toolName,
1286
1286
  feature: "mcp",
@@ -1289,8 +1289,8 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler, options)
1289
1289
  });
1290
1290
  } catch {
1291
1291
  }
1292
- const message = err7 instanceof Error ? err7.message : String(err7);
1293
- const ocsCode = err7 instanceof OcsApiError ? err7.code : void 0;
1292
+ const message = err8 instanceof Error ? err8.message : String(err8);
1293
+ const ocsCode = err8 instanceof OcsApiError ? err8.code : void 0;
1294
1294
  ctx.audit({
1295
1295
  tool_name: toolName,
1296
1296
  ocs_method: ocsMethod,
@@ -3668,8 +3668,8 @@ async function safeCall(env, token2, method, params = {}) {
3668
3668
  const client2 = new OcsClient(env.CARRIER_OCS_BASE_URL, token2);
3669
3669
  const data = await client2.call(method, params);
3670
3670
  return { data, error: null };
3671
- } catch (err7) {
3672
- return { data: null, error: err7 instanceof Error ? err7.message : String(err7) };
3671
+ } catch (err8) {
3672
+ return { data: null, error: err8 instanceof Error ? err8.message : String(err8) };
3673
3673
  }
3674
3674
  }
3675
3675
  async function fetchActiveSubscribers(env, token2, accountId, resellerId) {
@@ -5083,7 +5083,7 @@ function registerAllBacklogTools(server2, ctx) {
5083
5083
  }
5084
5084
 
5085
5085
  // src/tools-carrier-ask.ts
5086
- import { z as z13 } from "zod";
5086
+ import { z as z14 } from "zod";
5087
5087
 
5088
5088
  // src/tools-ui-agent.ts
5089
5089
  import { z as z4 } from "zod";
@@ -5244,7 +5244,7 @@ var RunBudget = class {
5244
5244
  }
5245
5245
  };
5246
5246
  function createUiAgentRuntime(processEnv = process.env) {
5247
- const apiKey2 = processEnv.BROWSER_USE_API_KEY;
5247
+ const apiKey3 = processEnv.BROWSER_USE_API_KEY;
5248
5248
  const spend = { runs: 0, costUsd: 0 };
5249
5249
  const audit2 = (row) => {
5250
5250
  const cost = row.doubles?.[1];
@@ -5255,13 +5255,13 @@ function createUiAgentRuntime(processEnv = process.env) {
5255
5255
  };
5256
5256
  return {
5257
5257
  env: {
5258
- ...apiKey2 ? { apiKey: apiKey2 } : {},
5258
+ ...apiKey3 ? { apiKey: apiKey3 } : {},
5259
5259
  store: createMemoryStore(),
5260
5260
  audit: audit2
5261
5261
  },
5262
5262
  budget: new RunBudget(maxRunsPerProcess(processEnv)),
5263
5263
  spend,
5264
- configured: Boolean(apiKey2),
5264
+ configured: Boolean(apiKey3),
5265
5265
  dispatchedTaskIds: /* @__PURE__ */ new Set()
5266
5266
  };
5267
5267
  }
@@ -5356,8 +5356,8 @@ function wrapPortalHandler(toolName, gapId, requiredScope, ctx, runtime, optIn,
5356
5356
  }
5357
5357
  try {
5358
5358
  runtime.budget.reserve();
5359
- } catch (err7) {
5360
- if (err7 instanceof SpendLimitError) {
5359
+ } catch (err8) {
5360
+ if (err8 instanceof SpendLimitError) {
5361
5361
  ctx.audit({
5362
5362
  tool_name: toolName,
5363
5363
  ocs_method: ocsMethod,
@@ -5369,11 +5369,11 @@ function wrapPortalHandler(toolName, gapId, requiredScope, ctx, runtime, optIn,
5369
5369
  event_type: "ui_agent_dispatch"
5370
5370
  });
5371
5371
  return jsonResult(
5372
- { error: "run_limit_reached", message: err7.message },
5372
+ { error: "run_limit_reached", message: err8.message },
5373
5373
  true
5374
5374
  );
5375
5375
  }
5376
- throw err7;
5376
+ throw err8;
5377
5377
  }
5378
5378
  try {
5379
5379
  const result2 = await runUiAgent(
@@ -5436,7 +5436,7 @@ function wrapPortalHandler(toolName, gapId, requiredScope, ctx, runtime, optIn,
5436
5436
  note: "The run completed in-process \u2014 a local CLI has no background runtime, so this call blocked for its duration.",
5437
5437
  portal_password_note: PORTAL_PASSWORD_RUN_NOTE
5438
5438
  });
5439
- } catch (err7) {
5439
+ } catch (err8) {
5440
5440
  ctx.audit({
5441
5441
  tool_name: toolName,
5442
5442
  ocs_method: ocsMethod,
@@ -5451,7 +5451,7 @@ function wrapPortalHandler(toolName, gapId, requiredScope, ctx, runtime, optIn,
5451
5451
  {
5452
5452
  type: "text",
5453
5453
  text: redact(
5454
- `Error dispatching the UI agent: ${err7 instanceof Error ? err7.message : String(err7)}`
5454
+ `Error dispatching the UI agent: ${err8 instanceof Error ? err8.message : String(err8)}`
5455
5455
  )
5456
5456
  }
5457
5457
  ]
@@ -5944,13 +5944,13 @@ function registerUiAgentAskTools(server2, ctx, runtime) {
5944
5944
  taskIds = listing.keys.map(
5945
5945
  (k) => k.name.slice(PENDING_ASK_PREFIX.length)
5946
5946
  );
5947
- } catch (err7) {
5947
+ } catch (err8) {
5948
5948
  return {
5949
5949
  isError: true,
5950
5950
  content: [
5951
5951
  {
5952
5952
  type: "text",
5953
- text: `Error listing pending tasks: ${err7 instanceof Error ? err7.message : String(err7)}`
5953
+ text: `Error listing pending tasks: ${err8 instanceof Error ? err8.message : String(err8)}`
5954
5954
  }
5955
5955
  ]
5956
5956
  };
@@ -7601,9 +7601,103 @@ function registerStorefrontDeployTools(server2, props2) {
7601
7601
  );
7602
7602
  }
7603
7603
 
7604
- // src/tools-atlas.ts
7604
+ // src/tools-storefront-plan.ts
7605
7605
  import { z as z11 } from "zod";
7606
7606
 
7607
+ // src/lib/storefront-plan-update.ts
7608
+ function storefrontPlanUpdate(input) {
7609
+ if (input.priceCents === void 0 && input.active === void 0 && input.sortOrder === void 0) {
7610
+ return { ok: false, message: "Send a price, whether the plan is for sale, or a sort order." };
7611
+ }
7612
+ if (input.priceCents !== void 0 && (!Number.isSafeInteger(input.priceCents) || input.priceCents <= 0)) {
7613
+ return { ok: false, message: "Price must be a positive number of minor units." };
7614
+ }
7615
+ if (input.sortOrder !== void 0 && (!Number.isSafeInteger(input.sortOrder) || input.sortOrder < 0)) {
7616
+ return { ok: false, message: "sort_order must be a non-negative integer." };
7617
+ }
7618
+ const body2 = {};
7619
+ if (input.priceCents !== void 0) body2.price_cents = input.priceCents;
7620
+ if (input.active !== void 0) body2.active = input.active;
7621
+ if (input.sortOrder !== void 0) body2.sort_order = input.sortOrder;
7622
+ return { ok: true, body: body2 };
7623
+ }
7624
+
7625
+ // src/tools-storefront-plan.ts
7626
+ var STOREFRONT_PLAN_TOOL_SCOPES = {
7627
+ update_storefront_plan: "write"
7628
+ };
7629
+ var ok4 = (text) => ({ content: [{ type: "text", text }] });
7630
+ var err5 = (text) => ({ isError: true, content: [{ type: "text", text }] });
7631
+ var DEFAULT_API = "https://api.carrier.llc";
7632
+ function apiOrigin() {
7633
+ return (process.env.CARRIER_API_URL ?? DEFAULT_API).replace(/\/$/, "");
7634
+ }
7635
+ function apiKey2() {
7636
+ return process.env.CARRIER_API_KEY?.trim() || process.env.CARRIER_ORG_API_KEY?.trim() || null;
7637
+ }
7638
+ async function patchStorefrontPlan(storefrontId, slug, body2) {
7639
+ const key = apiKey2();
7640
+ if (!key) {
7641
+ return { ok: false, status: 401, body: { error: "not_signed_in", message: "Not signed in." } };
7642
+ }
7643
+ const res = await fetch(
7644
+ `${apiOrigin()}/v1/storefronts/${encodeURIComponent(storefrontId)}/plans/${encodeURIComponent(slug)}`,
7645
+ {
7646
+ method: "PATCH",
7647
+ headers: {
7648
+ Authorization: `Bearer ${key}`,
7649
+ Accept: "application/json",
7650
+ "Content-Type": "application/json"
7651
+ },
7652
+ body: JSON.stringify(body2)
7653
+ }
7654
+ );
7655
+ const raw = await res.text();
7656
+ let parsed = raw;
7657
+ try {
7658
+ parsed = raw.trim() === "" ? {} : JSON.parse(raw);
7659
+ } catch {
7660
+ parsed = { message: raw };
7661
+ }
7662
+ return { ok: res.ok, status: res.status, body: parsed };
7663
+ }
7664
+ function registerStorefrontPlanTools(server2, props2, call = (path, body2) => {
7665
+ const [storefrontId, slug] = path.split("/");
7666
+ return patchStorefrontPlan(storefrontId ?? "", slug ?? "", body2);
7667
+ }) {
7668
+ server2.registerTool(
7669
+ "update_storefront_plan",
7670
+ {
7671
+ title: "Update Storefront Plan",
7672
+ description: "Change the retail price, whether a plan is for sale, or its display order on the caller's own hosted storefront. The plan must already exist. A price of zero or less is refused. A plan on another storefront is not found and is left unchanged. This writes the shop's own price. It does not create or change a Stripe price.",
7673
+ inputSchema: {
7674
+ storefront_id: z11.string().min(1).describe("Id of the hosted storefront"),
7675
+ slug: z11.string().min(1).describe("Plan slug, such as eu-1gb"),
7676
+ price_cents: z11.number().int().positive().optional().describe("Retail price in minor units. Must be greater than zero. Omit to leave the price."),
7677
+ active: z11.boolean().optional().describe("Whether the plan is for sale. Omit to leave it."),
7678
+ sort_order: z11.number().int().nonnegative().optional().describe("Display order, lowest first. Omit to leave it.")
7679
+ },
7680
+ annotations: annotationsFor("update_storefront_plan", "write")
7681
+ },
7682
+ async ({ storefront_id, slug, price_cents, active, sort_order }) => {
7683
+ const denied = denyUnlessScoped(props2, "update_storefront_plan", STOREFRONT_PLAN_TOOL_SCOPES);
7684
+ if (denied) return denied;
7685
+ const update = storefrontPlanUpdate({ priceCents: price_cents, active, sortOrder: sort_order });
7686
+ if (!update.ok) return err5(update.message);
7687
+ try {
7688
+ const result2 = await call(`${storefront_id}/${slug}`, update.body);
7689
+ const text = JSON.stringify(result2.body);
7690
+ return result2.ok ? ok4(text) : err5(text);
7691
+ } catch (e) {
7692
+ return err5(e instanceof Error ? e.message : "plan update failed");
7693
+ }
7694
+ }
7695
+ );
7696
+ }
7697
+
7698
+ // src/tools-atlas.ts
7699
+ import { z as z12 } from "zod";
7700
+
7607
7701
  // src/atlas-public.ts
7608
7702
  var ATLAS_PUBLIC_BASE_URL = "https://atlas.carrier.llc";
7609
7703
  function atlasBaseUrl(env) {
@@ -7634,8 +7728,8 @@ async function atlasPublicGet(baseUrl2, path) {
7634
7728
  headers: { Accept: "application/json" },
7635
7729
  signal: AbortSignal.timeout(8e3)
7636
7730
  });
7637
- } catch (err7) {
7638
- const message = err7 instanceof Error ? err7.message : String(err7);
7731
+ } catch (err8) {
7732
+ const message = err8 instanceof Error ? err8.message : String(err8);
7639
7733
  throw new Error(`Atlas request failed: ${message}`);
7640
7734
  }
7641
7735
  const text = await response.text();
@@ -7660,13 +7754,13 @@ var ATLAS_TOOL_SCOPES = {
7660
7754
  get_atlas_network_status: "read"
7661
7755
  };
7662
7756
  var coverageSchema = {
7663
- country: z11.string().regex(/^[A-Za-z]{2}$/, "country must be a 2-letter ISO code").describe("ISO 3166-1 alpha-2 country code, for example JP or NL"),
7664
- status: z11.enum(["up", "degraded", "down", "unknown"]).optional().describe("Keep only networks whose latest probe has this status"),
7665
- technology: z11.enum(["2G", "3G", "4G", "5G"]).optional().describe("Keep only networks of this radio generation")
7757
+ country: z12.string().regex(/^[A-Za-z]{2}$/, "country must be a 2-letter ISO code").describe("ISO 3166-1 alpha-2 country code, for example JP or NL"),
7758
+ status: z12.enum(["up", "degraded", "down", "unknown"]).optional().describe("Keep only networks whose latest probe has this status"),
7759
+ technology: z12.enum(["2G", "3G", "4G", "5G"]).optional().describe("Keep only networks of this radio generation")
7666
7760
  };
7667
7761
  var networkSchema = {
7668
- mcc: z11.string().regex(/^\d{3}$/, "mcc must be 3 digits").describe("Mobile Country Code"),
7669
- mnc: z11.string().regex(/^\d{2,3}$/, "mnc must be 2 or 3 digits").describe("Mobile Network Code")
7762
+ mcc: z12.string().regex(/^\d{3}$/, "mcc must be 3 digits").describe("Mobile Country Code"),
7763
+ mnc: z12.string().regex(/^\d{2,3}$/, "mnc must be 2 or 3 digits").describe("Mobile Network Code")
7670
7764
  };
7671
7765
  function jsonResult2(body2) {
7672
7766
  return {
@@ -7721,15 +7815,15 @@ function registerAtlasTools(server2, ctx) {
7721
7815
  }
7722
7816
 
7723
7817
  // src/tools-network-default.ts
7724
- import { z as z12 } from "zod";
7818
+ import { z as z13 } from "zod";
7725
7819
  var NETWORK_DEFAULT_TOOL_SCOPES = {
7726
7820
  get_network_default: "read",
7727
7821
  set_network_default: "write",
7728
7822
  plan_network_default: "read"
7729
7823
  };
7730
- var choiceShape = z12.object({
7731
- mode: z12.enum(NETWORK_DEFAULT_MODES),
7732
- steeringListId: z12.number().int().positive().optional()
7824
+ var choiceShape = z13.object({
7825
+ mode: z13.enum(NETWORK_DEFAULT_MODES),
7826
+ steeringListId: z13.number().int().positive().optional()
7733
7827
  }).describe("`cheapest_available`, `all`, or `custom` with a steeringListId from list_steering_lists");
7734
7828
  function ioFor(ctx, token2) {
7735
7829
  const kv = ctx.env.CARRIER_USERS;
@@ -7784,9 +7878,9 @@ function registerNetworkDefaultTools(server2, ctx) {
7784
7878
  title: "Set Network Default",
7785
7879
  description: "Use this to set the organisation's network default to `cheapest_available`, `all`, or `custom` (a steering list id from `list_steering_lists`, which must exist). This only stores the setting. It changes no SIM, writes nothing to OCS and does not migrate existing customers: it applies to NEW assignments. A single assignment can still override it with `plan_network_default`'s `simOverride`. Params: `mode` (required), `steeringListId` (required when mode is `custom`), `dry_run` (preview only). Do NOT use this to assign a list to one SIM \u2014 use `modify_subscriber_steering_list`.",
7786
7880
  inputSchema: {
7787
- mode: z12.enum(NETWORK_DEFAULT_MODES).describe("cheapest_available | all | custom"),
7788
- steeringListId: z12.number().int().positive().optional().describe("Required when mode is custom"),
7789
- dry_run: z12.boolean().optional().describe("If true, validate and return what would be stored; store nothing")
7881
+ mode: z13.enum(NETWORK_DEFAULT_MODES).describe("cheapest_available | all | custom"),
7882
+ steeringListId: z13.number().int().positive().optional().describe("Required when mode is custom"),
7883
+ dry_run: z13.boolean().optional().describe("If true, validate and return what would be stored; store nothing")
7790
7884
  },
7791
7885
  annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false }
7792
7886
  },
@@ -7815,10 +7909,10 @@ function registerNetworkDefaultTools(server2, ctx) {
7815
7909
  title: "Plan Network Default",
7816
7910
  description: "Use this to see which networks a SIM would be given in one or more countries, before anything is assigned. By default that is the cheapest available network per country (lowest data rate above zero, among rules really in force). Precedence: `simOverride`, then `packageOverride`, then the org default, then `cheapest_available`. Read only: `applied` is always false. If the tariff is missing or cannot be compared it falls back to ALL networks and says so in `warnings` and `summary` (`fellBackToAll: true`); never present that as a cheapest choice. The price basis is the wholesale data rate per MB, not the end price. Creating a steering list has no OCS REST method, so when no existing list matches the result is `needs_list`, not a created list. Params: `countries` (ISO-3166 alpha-2 codes, required), `simOverride`, `packageOverride`, `resellerId` (optional).",
7817
7911
  inputSchema: {
7818
- countries: z12.array(z12.string().length(2)).min(1).describe('ISO-3166 alpha-2 codes, e.g. ["CA"]'),
7912
+ countries: z13.array(z13.string().length(2)).min(1).describe('ISO-3166 alpha-2 codes, e.g. ["CA"]'),
7819
7913
  simOverride: choiceShape.optional(),
7820
7914
  packageOverride: choiceShape.optional(),
7821
- resellerId: z12.number().int().positive().optional().describe("Omit to use the token owner's reseller")
7915
+ resellerId: z13.number().int().positive().optional().describe("Omit to use the token owner's reseller")
7822
7916
  },
7823
7917
  annotations: { readOnlyHint: true }
7824
7918
  },
@@ -9206,6 +9300,7 @@ var ALL_TOOL_SCOPES = Object.freeze({
9206
9300
  ...STOREFRONT_LOGO_TOOL_SCOPES,
9207
9301
  // Storefront deploy tools exist only in this package, not in the Worker.
9208
9302
  ...STOREFRONT_DEPLOY_TOOL_SCOPES,
9303
+ ...STOREFRONT_PLAN_TOOL_SCOPES,
9209
9304
  ...ATLAS_TOOL_SCOPES,
9210
9305
  ...NETWORK_DEFAULT_TOOL_SCOPES
9211
9306
  });
@@ -9768,16 +9863,16 @@ Pre-resolved context: ${JSON.stringify(definedContext)}` : "";
9768
9863
  ...onUsage ? { onUsage } : {}
9769
9864
  }
9770
9865
  );
9771
- } catch (err7) {
9772
- if (err7 instanceof BedrockRateLimitError) {
9773
- const parsedSeconds = err7.retryAfter ? parseInt(err7.retryAfter, 10) : 60;
9866
+ } catch (err8) {
9867
+ if (err8 instanceof BedrockRateLimitError) {
9868
+ const parsedSeconds = err8.retryAfter ? parseInt(err8.retryAfter, 10) : 60;
9774
9869
  return {
9775
9870
  match: "rate_limited",
9776
9871
  retry_after_seconds: Number.isFinite(parsedSeconds) ? parsedSeconds : 60,
9777
9872
  suggestion: "Bedrock rate limit reached. Please retry after the indicated delay."
9778
9873
  };
9779
9874
  }
9780
- throw err7;
9875
+ throw err8;
9781
9876
  }
9782
9877
  if (!toolCall) {
9783
9878
  return {
@@ -9882,15 +9977,15 @@ function registerAllCarrierAskTools(server2, ctx) {
9882
9977
  title: "Natural Language Carrier Tool Router",
9883
9978
  description: "Use this when you want to perform a carrier operation but don't know which specific tool to call. Describe your intent in plain language and carrier_ask will identify the correct tool(s) and suggest the required parameters. For simple read intents, it executes directly and returns results. For destructive operations it returns a confirm_token that you must pass in a second call. Params: `intent` (string \u2014 natural language description of what you want to do), `context` (optional: iccid, account_id, reseller_id if already known). Returns: RouteResult \u2014 confirmed | pending_confirm | ambiguous | none. Do NOT use this when you know the right tool \u2014 direct calls are faster and cheaper.",
9884
9979
  inputSchema: {
9885
- intent: z13.string().min(3).describe(
9980
+ intent: z14.string().min(3).describe(
9886
9981
  "Natural-language description of the operation to perform (e.g. 'suspend ICCID 89316 until next month', 'show fleet health', 'assign Europe 5GB package to subscriber 89316...')"
9887
9982
  ),
9888
- context: z13.object({
9889
- iccid: z13.string().optional().describe("Subscriber ICCID if already known"),
9890
- account_id: z13.number().optional().describe("Account ID if already known"),
9891
- reseller_id: z13.number().optional().describe("Reseller ID if already known")
9983
+ context: z14.object({
9984
+ iccid: z14.string().optional().describe("Subscriber ICCID if already known"),
9985
+ account_id: z14.number().optional().describe("Account ID if already known"),
9986
+ reseller_id: z14.number().optional().describe("Reseller ID if already known")
9892
9987
  }).optional().describe("Optional pre-resolved context to improve routing accuracy"),
9893
- confirm_token: z13.string().optional().describe(
9988
+ confirm_token: z14.string().optional().describe(
9894
9989
  "One-time token from a previous carrier_ask call with confirm_required=true. Providing this executes the previously staged destructive operation."
9895
9990
  )
9896
9991
  }
@@ -9991,8 +10086,8 @@ function registerAllCarrierAskTools(server2, ctx) {
9991
10086
  modelRan = true;
9992
10087
  }
9993
10088
  });
9994
- } catch (err7) {
9995
- if (err7 instanceof AskRouterNotConfiguredError) {
10089
+ } catch (err8) {
10090
+ if (err8 instanceof AskRouterNotConfiguredError) {
9996
10091
  writeCarrierAskAudit(ctx.env, {
9997
10092
  intent_hash: intentHash,
9998
10093
  match: "error",
@@ -10012,8 +10107,8 @@ function registerAllCarrierAskTools(server2, ctx) {
10012
10107
  type: "text",
10013
10108
  text: JSON.stringify({
10014
10109
  error: "router_not_configured",
10015
- missing: err7.missing,
10016
- message: err7.message
10110
+ missing: err8.missing,
10111
+ message: err8.message
10017
10112
  })
10018
10113
  }
10019
10114
  ],
@@ -10043,7 +10138,7 @@ function registerAllCarrierAskTools(server2, ctx) {
10043
10138
  type: "text",
10044
10139
  text: JSON.stringify({
10045
10140
  error: "routing_error",
10046
- message: err7 instanceof Error ? err7.message : "An unexpected error occurred during routing."
10141
+ message: err8 instanceof Error ? err8.message : "An unexpected error occurred during routing."
10047
10142
  })
10048
10143
  }
10049
10144
  ],
@@ -10094,7 +10189,7 @@ function registerAllCarrierAskTools(server2, ctx) {
10094
10189
  title: "Describe a Carrier Tool",
10095
10190
  description: "Get full documentation for any registered Carrier MCP tool: description, parameters, 2-3 example invocations, required scope, destructive flag, and guidance. Params: `tool_name` (exact MCP tool name, e.g. 'assign_package'). Returns: structured tool metadata. Does NOT execute the tool.",
10096
10191
  inputSchema: {
10097
- tool_name: z13.string().describe("The exact MCP tool name to describe (e.g. 'assign_package', 'hlr_set_bitrate')")
10192
+ tool_name: z14.string().describe("The exact MCP tool name to describe (e.g. 'assign_package', 'hlr_set_bitrate')")
10098
10193
  }
10099
10194
  },
10100
10195
  wrapHandler(
@@ -10187,7 +10282,7 @@ function buildExamples(toolName) {
10187
10282
  }
10188
10283
 
10189
10284
  // src/list-recent-ocs-events.ts
10190
- import { z as z14 } from "zod";
10285
+ import { z as z15 } from "zod";
10191
10286
 
10192
10287
  // src/ocs-scoping.ts
10193
10288
  function managedOwnAccountId(auth) {
@@ -10220,10 +10315,10 @@ async function assertManagedIccidAccess(baseUrl2, auth, iccid) {
10220
10315
  return { ok: false, error: "Subscriber not found" };
10221
10316
  }
10222
10317
  return { ok: true };
10223
- } catch (err7) {
10318
+ } catch (err8) {
10224
10319
  return {
10225
10320
  ok: false,
10226
- error: err7 instanceof Error ? err7.message : String(err7)
10321
+ error: err8 instanceof Error ? err8.message : String(err8)
10227
10322
  };
10228
10323
  }
10229
10324
  }
@@ -10355,10 +10450,10 @@ var ALLOWED_EVENT_TYPES = [
10355
10450
  "usage.anomaly"
10356
10451
  ];
10357
10452
  var listRecentOcsEventsSchema = {
10358
- iccid: z14.string().regex(/^\d{19,20}$/).describe("ICCID, 19 or 20 digits, ITU-T E.118 format"),
10359
- limit: z14.number().int().min(1).max(50).default(20).describe("Max events to return, newest first"),
10360
- event_types: z14.array(z14.enum(ALLOWED_EVENT_TYPES)).optional().describe("Filter to specific event types"),
10361
- since: z14.string().datetime().optional().describe("ISO-8601 timestamp; only events after this point")
10453
+ iccid: z15.string().regex(/^\d{19,20}$/).describe("ICCID, 19 or 20 digits, ITU-T E.118 format"),
10454
+ limit: z15.number().int().min(1).max(50).default(20).describe("Max events to return, newest first"),
10455
+ event_types: z15.array(z15.enum(ALLOWED_EVENT_TYPES)).optional().describe("Filter to specific event types"),
10456
+ since: z15.string().datetime().optional().describe("ISO-8601 timestamp; only events after this point")
10362
10457
  };
10363
10458
  async function listRecentOcsEvents2(iccid, limit, eventTypes, since, kv) {
10364
10459
  return listRecentOcsEvents(iccid, limit, eventTypes, since, kv);
@@ -10409,8 +10504,8 @@ function registerListRecentOcsEventsTool(server2, ctx) {
10409
10504
  return {
10410
10505
  content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
10411
10506
  };
10412
- } catch (err7) {
10413
- const message = err7 instanceof Error ? err7.message : String(err7);
10507
+ } catch (err8) {
10508
+ const message = err8 instanceof Error ? err8.message : String(err8);
10414
10509
  return {
10415
10510
  isError: true,
10416
10511
  content: [{ type: "text", text: `Error reading OCS event buffer: ${message}` }]
@@ -10421,7 +10516,7 @@ function registerListRecentOcsEventsTool(server2, ctx) {
10421
10516
  }
10422
10517
 
10423
10518
  // src/tools-rate-governor.ts
10424
- import { z as z15 } from "zod";
10519
+ import { z as z16 } from "zod";
10425
10520
  var MONITORED_ENDPOINTS = [
10426
10521
  "subscriberNetworkEventsOverPeriod",
10427
10522
  // 30/min
@@ -10453,7 +10548,7 @@ function registerRateLimitStatusTool(server2, ctx) {
10453
10548
  title: "API Rate-Limit Governor Status",
10454
10549
  description: "Inspect Bridge4IP OCS rate-limit governor state \u2014 bucket fill levels, per-endpoint limits, and 80% alert thresholds. Use this to answer: 'are we rate limited', 'check ocs quota', 'how full is the rate limit bucket', 'rate limit status'. Params: `endpoint` (optional \u2014 OCS camelCase method name to inspect a single bucket, e.g. 'subscriberUsageOverPeriod'; omit for all monitored endpoints). Returns: per-endpoint { limit_per_min, batch_cap_per_min, calls_in_window, batch_calls_in_window, utilisation_pct, alert (true when \u226580% of limit) }. Note: counts come from the in-process sliding-window log (same key as OCS calls: API token + endpoint) \u2014 they reset when the Worker isolate recycles. Use as a relative signal, not an exact counter. Do NOT use this to adjust limits \u2014 limits are set by Bridge4IP contract and cannot be changed via MCP.",
10455
10550
  inputSchema: {
10456
- endpoint: z15.string().optional().describe(
10551
+ endpoint: z16.string().optional().describe(
10457
10552
  "OCS method name in camelCase (e.g. 'subscriberUsageOverPeriod'). Omit to return status for all monitored endpoints."
10458
10553
  )
10459
10554
  },
@@ -10509,11 +10604,11 @@ function registerRateLimitStatusTool(server2, ctx) {
10509
10604
  }
10510
10605
 
10511
10606
  // src/tools-depletion-events.ts
10512
- import { z as z16 } from "zod";
10513
- function ok4(payload) {
10607
+ import { z as z17 } from "zod";
10608
+ function ok5(payload) {
10514
10609
  return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
10515
10610
  }
10516
- function err5(message) {
10611
+ function err6(message) {
10517
10612
  return { isError: true, content: [{ type: "text", text: message }] };
10518
10613
  }
10519
10614
  async function ownershipToken(ctx) {
@@ -10537,10 +10632,10 @@ function registerDepletionEventsTool(server2, ctx) {
10537
10632
  title: "Subscriber Bundle Depletion Events",
10538
10633
  description: "Look up bundle depletion events for a subscriber (or list recent fleet-wide depletions). Returns the most recent bundle.depleted event stored when the prepaid usage webhook received a last_slice notification. Events are retained for 7 days. Use subscriberId to check a specific subscriber, or omit to list the 20 most recent fleet-wide depletions. Requires 'read' scope.",
10539
10634
  inputSchema: {
10540
- subscriberId: z16.string().optional().describe(
10635
+ subscriberId: z17.string().optional().describe(
10541
10636
  "OCS subscriber ID to look up. Omit to list the 20 most recent fleet-wide depletions."
10542
10637
  ),
10543
- since: z16.string().optional().describe(
10638
+ since: z17.string().optional().describe(
10544
10639
  "ISO-8601 timestamp \u2014 only return events at or after this time (for single-subscriber lookup)."
10545
10640
  )
10546
10641
  },
@@ -10548,13 +10643,13 @@ function registerDepletionEventsTool(server2, ctx) {
10548
10643
  },
10549
10644
  async (args) => {
10550
10645
  if (!ctx.props.scope.includes("read")) {
10551
- return err5(
10646
+ return err6(
10552
10647
  `Scope denied: subscriber_depletion_events requires 'read' scope. Your token has: [${ctx.props.scope.join(", ")}].`
10553
10648
  );
10554
10649
  }
10555
10650
  const kv = ctx.env.RATE_LIMIT_KV;
10556
10651
  if (!kv) {
10557
- return err5(
10652
+ return err6(
10558
10653
  "subscriber_depletion_events cannot answer here. Depletion events are written by the Carrier webhook receiver as they arrive from the network and kept for 7 days in hosted storage; a local stdio server receives no webhooks and has nowhere to keep them, so there is no history on this machine to read. There is nothing to set in your environment. Use the hosted MCP at https://mcp.carrier.llc/mcp for this tool. To see a subscriber's current bundle state instead of its depletion history, `get_subscriber` and `list_subscriber_packages` query OCS directly and work here."
10559
10654
  );
10560
10655
  }
@@ -10563,14 +10658,14 @@ function registerDepletionEventsTool(server2, ctx) {
10563
10658
  try {
10564
10659
  scopeToken = await ownershipToken(ctx);
10565
10660
  } catch (e) {
10566
- return err5(
10661
+ return err6(
10567
10662
  `Could not resolve the OCS credential needed to scope this read: ${e instanceof Error ? e.message : String(e)}`
10568
10663
  );
10569
10664
  }
10570
10665
  if (subscriberId) {
10571
10666
  const raw = await kv.get(`bundle-depleted:${subscriberId}`, "text");
10572
10667
  if (!raw) {
10573
- return ok4({
10668
+ return ok5({
10574
10669
  subscriber_id: subscriberId,
10575
10670
  depleted: false,
10576
10671
  event: null,
@@ -10581,7 +10676,7 @@ function registerDepletionEventsTool(server2, ctx) {
10581
10676
  try {
10582
10677
  event = JSON.parse(raw);
10583
10678
  } catch {
10584
- return ok4({
10679
+ return ok5({
10585
10680
  subscriber_id: subscriberId,
10586
10681
  depleted: false,
10587
10682
  event: null,
@@ -10589,7 +10684,7 @@ function registerDepletionEventsTool(server2, ctx) {
10589
10684
  });
10590
10685
  }
10591
10686
  if (!await ownsEvent(ctx, scopeToken, event.iccid)) {
10592
- return ok4({
10687
+ return ok5({
10593
10688
  subscriber_id: subscriberId,
10594
10689
  depleted: false,
10595
10690
  event: null,
@@ -10597,14 +10692,14 @@ function registerDepletionEventsTool(server2, ctx) {
10597
10692
  });
10598
10693
  }
10599
10694
  if (since && event.depletedAt < since) {
10600
- return ok4({
10695
+ return ok5({
10601
10696
  subscriber_id: subscriberId,
10602
10697
  depleted: false,
10603
10698
  event: null,
10604
10699
  note: `No bundle depletion after ${since}.`
10605
10700
  });
10606
10701
  }
10607
- return ok4({
10702
+ return ok5({
10608
10703
  subscriber_id: subscriberId,
10609
10704
  depleted: true,
10610
10705
  event: {
@@ -10618,7 +10713,7 @@ function registerDepletionEventsTool(server2, ctx) {
10618
10713
  }
10619
10714
  const listResult = await kv.list({ prefix: "bundle-depleted:", limit: 20 });
10620
10715
  if (listResult.keys.length === 0) {
10621
- return ok4({
10716
+ return ok5({
10622
10717
  depletions: [],
10623
10718
  total: 0,
10624
10719
  note: "No bundle depletion events in the last 7 days."
@@ -10645,7 +10740,7 @@ function registerDepletionEventsTool(server2, ctx) {
10645
10740
  })
10646
10741
  );
10647
10742
  events.sort((a, b) => b.depleted_at.localeCompare(a.depleted_at));
10648
- return ok4({
10743
+ return ok5({
10649
10744
  depletions: events,
10650
10745
  total: events.length,
10651
10746
  list_truncated: !listResult.list_complete
@@ -10655,7 +10750,7 @@ function registerDepletionEventsTool(server2, ctx) {
10655
10750
  }
10656
10751
 
10657
10752
  // src/tools-country-history.ts
10658
- import { z as z17 } from "zod";
10753
+ import { z as z18 } from "zod";
10659
10754
  function isoDate(now, daysAgo = 0) {
10660
10755
  const d = new Date(now.getTime() - daysAgo * 864e5);
10661
10756
  return d.toISOString().slice(0, 10);
@@ -10678,10 +10773,10 @@ function registerCountryHistoryTool(server2, ctx) {
10678
10773
  title: "Subscriber Country History",
10679
10774
  description: "Return cross-border movement history for a subscriber, reconstructed from OCS Gy charging events. Reads `gyAnswer.events` from `subscriberNetworkEventsOverPeriod` \u2014 every Gy record carries its own country, operator and MCC \u2014 and collapses consecutive same-country records into stays with first-seen and last-seen timestamps. This needs no webhook: it works with Relay LU and Relay Gy off. Relay `country.entered` events from the 24h ring-buffer are merged in as a supplement when present. Use this to answer: 'where has subscriber X traveled', 'show country history for ICCID', 'border crossings for this SIM'. Params: `subscriberId` (ICCID, 19\u201320 digits), `startDate`/`endDate` (YYYY-MM-DD, max 7 days apart, default the last 7 days), `limit` (1\u201350, default 20, newest first). Returns: `stays` ({ country, country_name, first_seen, last_seen, operators, mccs, event_count, used_bytes }), `transitions` ({ from, to, at }), and `country_events` in the legacy flat shape. `used_bytes` already handles the string-typed `usedUnits` and its 'N/A' values. Do NOT use this for raw per-session records \u2014 use `subscriber_network_events` for those.",
10680
10775
  inputSchema: {
10681
- subscriberId: z17.string().regex(/^\d{19,20}$/, "subscriberId must be a 19\u201320 digit ICCID").describe("Subscriber ICCID (19\u201320 digits, ITU-T E.118)"),
10682
- startDate: z17.string().regex(/^\d{4}-\d{2}-\d{2}$/, "startDate must be YYYY-MM-DD").optional().describe("Start of the period, YYYY-MM-DD inclusive (default: 7 days ago)"),
10683
- endDate: z17.string().regex(/^\d{4}-\d{2}-\d{2}$/, "endDate must be YYYY-MM-DD").optional().describe("End of the period, YYYY-MM-DD inclusive, max 7 days from start (default: today)"),
10684
- limit: z17.number().int().min(1).max(50).default(20).optional().describe("Maximum number of country_events to return, newest first (default 20, max 50)")
10776
+ subscriberId: z18.string().regex(/^\d{19,20}$/, "subscriberId must be a 19\u201320 digit ICCID").describe("Subscriber ICCID (19\u201320 digits, ITU-T E.118)"),
10777
+ startDate: z18.string().regex(/^\d{4}-\d{2}-\d{2}$/, "startDate must be YYYY-MM-DD").optional().describe("Start of the period, YYYY-MM-DD inclusive (default: 7 days ago)"),
10778
+ endDate: z18.string().regex(/^\d{4}-\d{2}-\d{2}$/, "endDate must be YYYY-MM-DD").optional().describe("End of the period, YYYY-MM-DD inclusive, max 7 days from start (default: today)"),
10779
+ limit: z18.number().int().min(1).max(50).default(20).optional().describe("Maximum number of country_events to return, newest first (default 20, max 50)")
10685
10780
  },
10686
10781
  annotations: { readOnlyHint: true, idempotentHint: true }
10687
10782
  },
@@ -10712,8 +10807,8 @@ function registerCountryHistoryTool(server2, ctx) {
10712
10807
  networkEventsOverPeriodParams(subscriberId, start, end)
10713
10808
  );
10714
10809
  gy = gyCountryHistoryFromResponse(events);
10715
- } catch (err7) {
10716
- gyError = err7 instanceof Error ? err7.message : String(err7);
10810
+ } catch (err8) {
10811
+ gyError = err8 instanceof Error ? err8.message : String(err8);
10717
10812
  }
10718
10813
  let syntheticRingIccid = null;
10719
10814
  const msisdnRaw = msisdnFieldFromSubscriberPayload(subscriberPayload);
@@ -10789,7 +10884,7 @@ function registerCountryHistoryTool(server2, ctx) {
10789
10884
  }
10790
10885
 
10791
10886
  // src/apps/fleet-health-app.ts
10792
- import { z as z18 } from "zod";
10887
+ import { z as z19 } from "zod";
10793
10888
  import {
10794
10889
  registerAppTool,
10795
10890
  registerAppResource,
@@ -10840,8 +10935,8 @@ var FLEET_APP_RESOURCE_URI = "ui://fleet-health-dashboard";
10840
10935
  async function safeCall2(client2, method, params) {
10841
10936
  try {
10842
10937
  return { data: await client2.call(method, params), error: null };
10843
- } catch (err7) {
10844
- return { data: null, error: err7 instanceof Error ? err7.message : String(err7) };
10938
+ } catch (err8) {
10939
+ return { data: null, error: err8 instanceof Error ? err8.message : String(err8) };
10845
10940
  }
10846
10941
  }
10847
10942
  async function loadFleet(ctx, accountId) {
@@ -10873,7 +10968,7 @@ function registerFleetHealthApp(server2, ctx) {
10873
10968
  let html;
10874
10969
  try {
10875
10970
  html = renderHtml(fleetScreen(await loadFleet(ctx)));
10876
- } catch (err7) {
10971
+ } catch (err8) {
10877
10972
  html = renderHtml({
10878
10973
  id: "fleet-health",
10879
10974
  title: "Fleet health",
@@ -10881,7 +10976,7 @@ function registerFleetHealthApp(server2, ctx) {
10881
10976
  {
10882
10977
  kind: "note",
10883
10978
  tone: "critical",
10884
- text: `Could not load fleet data: ${err7 instanceof Error ? err7.message : String(err7)}`
10979
+ text: `Could not load fleet data: ${err8 instanceof Error ? err8.message : String(err8)}`
10885
10980
  }
10886
10981
  ]
10887
10982
  });
@@ -10905,7 +11000,7 @@ function registerFleetHealthApp(server2, ctx) {
10905
11000
  title: "Fleet Health Dashboard",
10906
11001
  description: "Draw the fleet overview as an interactive panel: eSIM status, per-account breakdown and low-balance alerts. Returns structuredContent for the panel, so it only helps a client that can render MCP UI resources. Do NOT use this when the answer has to be readable text, or in any headless or scripted context \u2014 `fleet_health` returns the same data as prose.",
10907
11002
  inputSchema: {
10908
- accountId: z18.number().optional().describe("Filter to a specific account (omit for all)")
11003
+ accountId: z19.number().optional().describe("Filter to a specific account (omit for all)")
10909
11004
  },
10910
11005
  annotations: { readOnlyHint: true },
10911
11006
  _meta: {
@@ -10948,7 +11043,7 @@ function registerFleetHealthApp(server2, ctx) {
10948
11043
  }
10949
11044
 
10950
11045
  // src/apps/provisioning-wizard.ts
10951
- import { z as z19 } from "zod";
11046
+ import { z as z20 } from "zod";
10952
11047
  import {
10953
11048
  registerAppTool as registerAppTool2,
10954
11049
  registerAppResource as registerAppResource2,
@@ -11001,10 +11096,10 @@ async function deleteWizardSession(env, sub, wizardId) {
11001
11096
  async function safeCallWithToken(client2, _token, method, params = {}) {
11002
11097
  try {
11003
11098
  return { data: await client2.call(method, params), error: null };
11004
- } catch (err7) {
11099
+ } catch (err8) {
11005
11100
  return {
11006
11101
  data: null,
11007
- error: err7 instanceof Error ? err7.message : String(err7)
11102
+ error: err8 instanceof Error ? err8.message : String(err8)
11008
11103
  };
11009
11104
  }
11010
11105
  }
@@ -11053,10 +11148,10 @@ function registerProvisioningWizard(server2, ctx) {
11053
11148
  title: "eSIM Provisioning Wizard",
11054
11149
  description: "Interactive 3-step wizard to provision an eSIM: select subscriber, choose package template, preview (dry_run) and confirm execution.",
11055
11150
  inputSchema: {
11056
- step: z19.enum(["init", "select-package", "preview", "confirm"]).describe("Current wizard step"),
11057
- wizardId: z19.string().optional().describe("Wizard session ID (absent on init)"),
11058
- subscriber_iccid: z19.string().optional().describe("Subscriber ICCID (required for select-package)"),
11059
- package_template_id: z19.number().optional().describe("Package template ID (required for preview)")
11151
+ step: z20.enum(["init", "select-package", "preview", "confirm"]).describe("Current wizard step"),
11152
+ wizardId: z20.string().optional().describe("Wizard session ID (absent on init)"),
11153
+ subscriber_iccid: z20.string().optional().describe("Subscriber ICCID (required for select-package)"),
11154
+ package_template_id: z20.number().optional().describe("Package template ID (required for preview)")
11060
11155
  },
11061
11156
  _meta: {
11062
11157
  ui: {
@@ -11326,7 +11421,7 @@ function registerProvisioningWizard(server2, ctx) {
11326
11421
  }
11327
11422
 
11328
11423
  // src/apps/balance-topup.ts
11329
- import { z as z20 } from "zod";
11424
+ import { z as z21 } from "zod";
11330
11425
  import {
11331
11426
  registerAppTool as registerAppTool3,
11332
11427
  registerAppResource as registerAppResource3,
@@ -11335,10 +11430,10 @@ import {
11335
11430
  async function safeCallWithToken2(client2, _token, method, params = {}) {
11336
11431
  try {
11337
11432
  return { data: await client2.call(method, params), error: null };
11338
- } catch (err7) {
11433
+ } catch (err8) {
11339
11434
  return {
11340
11435
  data: null,
11341
- error: err7 instanceof Error ? err7.message : String(err7)
11436
+ error: err8 instanceof Error ? err8.message : String(err8)
11342
11437
  };
11343
11438
  }
11344
11439
  }
@@ -11379,9 +11474,9 @@ function registerBalanceTopupApp(server2, ctx) {
11379
11474
  title: "Balance Top-up Form",
11380
11475
  description: "Adjust the OCS reseller-account balance behind one subscriber, through a form panel the user can review before it commits. Defaults to preview: a call that omits `preview` returns a projection and writes nothing. Only `preview: false` performs the OCS write. Params: `iccid` (subscriber identifier used to find the account), `delta` (amount to add when positive, deduct when negative), `preview` (boolean, default true). Requires `admin` scope plus recent MFA, and a client that can render UI panels. Do NOT use this in a headless or scripted context \u2014 call `modify_account_balance` directly, which takes an `accountId` and needs no panel. Do NOT use this for a subscriber's own balance \u2014 the `iccid` here only locates the account; per-subscriber money is `modify_subscriber_balance`. Do NOT use this for the Carrier prepaid wallet or MCP credits \u2014 see `wallet_topup_checkout` and `credit_balance`.",
11381
11476
  inputSchema: {
11382
- iccid: z20.string().describe("The subscriber ICCID for the account lookup"),
11383
- delta: z20.number().describe("Amount to add (positive) or deduct (negative) from the account balance"),
11384
- preview: z20.boolean().default(true).describe("If true, return preview without writing. If false, execute the balance change.")
11477
+ iccid: z21.string().describe("The subscriber ICCID for the account lookup"),
11478
+ delta: z21.number().describe("Amount to add (positive) or deduct (negative) from the account balance"),
11479
+ preview: z21.boolean().default(true).describe("If true, return preview without writing. If false, execute the balance change.")
11385
11480
  },
11386
11481
  annotations: { destructiveHint: true },
11387
11482
  _meta: {
@@ -11464,7 +11559,7 @@ function registerAllApps(server2, ctx) {
11464
11559
  }
11465
11560
 
11466
11561
  // src/prompts.ts
11467
- import { z as z21 } from "zod";
11562
+ import { z as z22 } from "zod";
11468
11563
  function registerAllPrompts(server2) {
11469
11564
  server2.registerPrompt(
11470
11565
  "fleet_health_report",
@@ -11500,7 +11595,7 @@ Format as a structured report with sections, tables, and actionable recommendati
11500
11595
  {
11501
11596
  title: "Subscriber Deep Dive",
11502
11597
  description: "Comprehensive analysis of a single subscriber: profile, packages, usage patterns, location history, recommendations.",
11503
- argsSchema: { iccid: z21.string().describe("The subscriber ICCID to analyse") }
11598
+ argsSchema: { iccid: z22.string().describe("The subscriber ICCID to analyse") }
11504
11599
  },
11505
11600
  async ({ iccid }) => ({
11506
11601
  messages: [
@@ -11599,7 +11694,7 @@ Analyze and present:
11599
11694
  {
11600
11695
  title: "Bulk Operations Planner",
11601
11696
  description: "Plan bulk operations safely: mass package assignments, account migrations, balance adjustments, or status changes.",
11602
- argsSchema: { operation: z21.string().describe("Describe the bulk operation you want to perform") }
11697
+ argsSchema: { operation: z22.string().describe("Describe the bulk operation you want to perform") }
11603
11698
  },
11604
11699
  async ({ operation }) => ({
11605
11700
  messages: [
@@ -11632,7 +11727,7 @@ NEVER execute destructive operations without confirmation.`
11632
11727
  }
11633
11728
 
11634
11729
  // src/tools-pricing.ts
11635
- import { z as z22 } from "zod";
11730
+ import { z as z23 } from "zod";
11636
11731
  function registerAllPricingTools(server2, ctx) {
11637
11732
  const allTools = [...PRICING_TOOLS, ...PROJECTS_TOOLS];
11638
11733
  for (const tool of allTools) {
@@ -11714,8 +11809,8 @@ function buildPricingHandler(tool, ctx) {
11714
11809
  }
11715
11810
  ]
11716
11811
  };
11717
- } catch (err7) {
11718
- const message = err7 instanceof Error ? err7.message : String(err7);
11812
+ } catch (err8) {
11813
+ const message = err8 instanceof Error ? err8.message : String(err8);
11719
11814
  ctx.audit({
11720
11815
  tool_name: tool.name,
11721
11816
  ocs_method: "billing",
@@ -11739,19 +11834,19 @@ function buildZodSchema(schema) {
11739
11834
  for (const [key, prop] of Object.entries(properties)) {
11740
11835
  let field;
11741
11836
  if (prop.enum) {
11742
- field = z22.enum(prop.enum);
11837
+ field = z23.enum(prop.enum);
11743
11838
  } else if (prop.type === "number") {
11744
- field = z22.number();
11839
+ field = z23.number();
11745
11840
  } else if (prop.type === "boolean") {
11746
- field = z22.boolean();
11841
+ field = z23.boolean();
11747
11842
  } else if (prop.type === "array") {
11748
11843
  if (prop.items?.type === "number") {
11749
- field = z22.array(z22.number());
11844
+ field = z23.array(z23.number());
11750
11845
  } else {
11751
- field = z22.array(z22.string());
11846
+ field = z23.array(z23.string());
11752
11847
  }
11753
11848
  } else {
11754
- field = z22.string();
11849
+ field = z23.string();
11755
11850
  }
11756
11851
  const required = schema.required ?? [];
11757
11852
  if (!required.includes(key)) {
@@ -11766,7 +11861,7 @@ function buildZodSchema(schema) {
11766
11861
  }
11767
11862
 
11768
11863
  // src/tools-greenzone.ts
11769
- import { z as z23 } from "zod";
11864
+ import { z as z24 } from "zod";
11770
11865
 
11771
11866
  // src/greenzone-whitelist.ts
11772
11867
  var STATE_KEY = "greenzone:state";
@@ -12043,10 +12138,10 @@ function resolvePortalBaseUrl(env) {
12043
12138
  function getGreenzoneKv(env) {
12044
12139
  return env.GREENZONE_STATE_KV ?? null;
12045
12140
  }
12046
- function ok5(payload) {
12141
+ function ok6(payload) {
12047
12142
  return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
12048
12143
  }
12049
- function err6(message) {
12144
+ function err7(message) {
12050
12145
  return { isError: true, content: [{ type: "text", text: message }] };
12051
12146
  }
12052
12147
  var KAPTURE_UNAVAILABLE_MSG = "Execute each Kapture step using mcp__kapture__* tools in sequence. Ensure the Kapture extension is active in Chrome before invoking.";
@@ -12057,21 +12152,21 @@ function registerGreenzoneTools(server2, ctx) {
12057
12152
  title: "Greenzone Whitelist \u2014 Add",
12058
12153
  description: "Add a hostname and/or IP address to the Bridge4IP Greenzone whitelist. The Greenzone whitelist controls which hosts/IPs are exempt from data charging after a subscriber's bundle is depleted. This operates via browser automation (Kapture) against the OCS portal \u2014 no REST API exists for this surface. Provide `host` (e.g. 'example.com') and/or `ip` (e.g. '203.0.113.0/24'). At least one of host or ip must be provided. Returns Kapture step sequence for Claude Code to execute. Requires 'write' scope and Kapture running in Sam's Chrome.",
12059
12154
  inputSchema: {
12060
- host: z23.string().optional().describe("Hostname to whitelist (e.g. 'example.com')"),
12061
- ip: z23.string().optional().describe("IP address or CIDR to whitelist (e.g. '203.0.113.0/24')"),
12062
- dry_run: z23.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
12155
+ host: z24.string().optional().describe("Hostname to whitelist (e.g. 'example.com')"),
12156
+ ip: z24.string().optional().describe("IP address or CIDR to whitelist (e.g. '203.0.113.0/24')"),
12157
+ dry_run: z24.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
12063
12158
  },
12064
12159
  annotations: { destructiveHint: true }
12065
12160
  },
12066
12161
  async (args) => {
12067
12162
  if (!ctx.props.scope.includes("write")) {
12068
- return err6(
12163
+ return err7(
12069
12164
  `Scope denied: greenzone_whitelist_add requires 'write' scope. Your token has: [${ctx.props.scope.join(", ")}].`
12070
12165
  );
12071
12166
  }
12072
12167
  const { host, ip, dry_run } = args;
12073
12168
  if (!host && !ip) {
12074
- return err6("At least one of 'host' or 'ip' must be provided.");
12169
+ return err7("At least one of 'host' or 'ip' must be provided.");
12075
12170
  }
12076
12171
  const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
12077
12172
  const steps = [
@@ -12079,7 +12174,7 @@ function registerGreenzoneTools(server2, ctx) {
12079
12174
  ...ip ? buildAddIpSteps(portalBaseUrl, ip) : []
12080
12175
  ];
12081
12176
  if (dry_run) {
12082
- return ok5({
12177
+ return ok6({
12083
12178
  dry_run: true,
12084
12179
  message: "Would execute the following Kapture steps against the OCS portal:",
12085
12180
  kapture_steps: steps,
@@ -12102,7 +12197,7 @@ function registerGreenzoneTools(server2, ctx) {
12102
12197
  } else {
12103
12198
  kvWarning = "GREENZONE_STATE_KV not bound in stdio mode \u2014 state not persisted.";
12104
12199
  }
12105
- return ok5({
12200
+ return ok6({
12106
12201
  requires_kapture: true,
12107
12202
  message: "Execute the Kapture steps below to apply the change in the OCS portal. " + KAPTURE_UNAVAILABLE_MSG,
12108
12203
  kapture_steps: steps,
@@ -12118,21 +12213,21 @@ function registerGreenzoneTools(server2, ctx) {
12118
12213
  title: "Greenzone Whitelist \u2014 Remove",
12119
12214
  description: "Remove a hostname and/or IP address from the Bridge4IP Greenzone whitelist. This operates via browser automation (Kapture) against the OCS portal. Provide `host` and/or `ip` to remove \u2014 at least one required. Returns Kapture step sequence for Claude Code to execute. Requires 'write' scope and Kapture running in Sam's Chrome.",
12120
12215
  inputSchema: {
12121
- host: z23.string().optional().describe("Hostname to remove from whitelist"),
12122
- ip: z23.string().optional().describe("IP address or CIDR to remove from whitelist"),
12123
- dry_run: z23.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
12216
+ host: z24.string().optional().describe("Hostname to remove from whitelist"),
12217
+ ip: z24.string().optional().describe("IP address or CIDR to remove from whitelist"),
12218
+ dry_run: z24.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
12124
12219
  },
12125
12220
  annotations: { destructiveHint: true }
12126
12221
  },
12127
12222
  async (args) => {
12128
12223
  if (!ctx.props.scope.includes("write")) {
12129
- return err6(
12224
+ return err7(
12130
12225
  `Scope denied: greenzone_whitelist_remove requires 'write' scope. Your token has: [${ctx.props.scope.join(", ")}].`
12131
12226
  );
12132
12227
  }
12133
12228
  const { host, ip, dry_run } = args;
12134
12229
  if (!host && !ip) {
12135
- return err6("At least one of 'host' or 'ip' must be provided.");
12230
+ return err7("At least one of 'host' or 'ip' must be provided.");
12136
12231
  }
12137
12232
  const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
12138
12233
  const steps = [
@@ -12140,7 +12235,7 @@ function registerGreenzoneTools(server2, ctx) {
12140
12235
  ...ip ? buildRemoveIpSteps(portalBaseUrl, ip) : []
12141
12236
  ];
12142
12237
  if (dry_run) {
12143
- return ok5({
12238
+ return ok6({
12144
12239
  dry_run: true,
12145
12240
  message: "Would execute the following Kapture steps to remove from OCS portal:",
12146
12241
  kapture_steps: steps,
@@ -12163,7 +12258,7 @@ function registerGreenzoneTools(server2, ctx) {
12163
12258
  } else {
12164
12259
  kvWarning = "GREENZONE_STATE_KV not bound in stdio mode \u2014 state not persisted.";
12165
12260
  }
12166
- return ok5({
12261
+ return ok6({
12167
12262
  requires_kapture: true,
12168
12263
  message: "Execute the Kapture steps below to apply the removal in the OCS portal. " + KAPTURE_UNAVAILABLE_MSG,
12169
12264
  kapture_steps: steps,
@@ -12179,7 +12274,7 @@ function registerGreenzoneTools(server2, ctx) {
12179
12274
  title: "Greenzone Whitelist \u2014 List",
12180
12275
  description: "List current Greenzone whitelist entries from the KV cache. Returns all whitelisted hosts and IPs, when the state was last synced, and whether the last hourly reconciliation detected drift from the portal. This is a fast read from KV \u2014 no browser automation required. Use `from_portal: true` to trigger a live portal read via Kapture instead of KV. Requires 'read' scope.",
12181
12276
  inputSchema: {
12182
- from_portal: z23.boolean().optional().describe(
12277
+ from_portal: z24.boolean().optional().describe(
12183
12278
  "When true, return Kapture steps to read live portal state instead of KV cache"
12184
12279
  )
12185
12280
  },
@@ -12187,14 +12282,14 @@ function registerGreenzoneTools(server2, ctx) {
12187
12282
  },
12188
12283
  async (args) => {
12189
12284
  if (!ctx.props.scope.includes("read")) {
12190
- return err6(
12285
+ return err7(
12191
12286
  `Scope denied: greenzone_whitelist_list requires 'read' scope. Your token has: [${ctx.props.scope.join(", ")}].`
12192
12287
  );
12193
12288
  }
12194
12289
  const { from_portal } = args;
12195
12290
  if (from_portal) {
12196
12291
  const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
12197
- return ok5({
12292
+ return ok6({
12198
12293
  requires_kapture: true,
12199
12294
  message: "Execute the Kapture steps below to read live Greenzone whitelist state from the OCS portal.",
12200
12295
  kapture_steps: buildListSteps(portalBaseUrl),
@@ -12203,7 +12298,7 @@ function registerGreenzoneTools(server2, ctx) {
12203
12298
  }
12204
12299
  const kv = getGreenzoneKv(ctx.env);
12205
12300
  if (!kv) {
12206
- return ok5({
12301
+ return ok6({
12207
12302
  source: "kv_cache",
12208
12303
  hosts: [],
12209
12304
  ips: [],
@@ -12215,7 +12310,7 @@ function registerGreenzoneTools(server2, ctx) {
12215
12310
  }
12216
12311
  try {
12217
12312
  const state = await readState(kv);
12218
- return ok5({
12313
+ return ok6({
12219
12314
  source: "kv_cache",
12220
12315
  hosts: state.hosts,
12221
12316
  ips: state.ips,
@@ -12224,14 +12319,14 @@ function registerGreenzoneTools(server2, ctx) {
12224
12319
  reconcile_drift: state.reconcile_drift ?? false
12225
12320
  });
12226
12321
  } catch (e) {
12227
- return err6(`Failed to read Greenzone state from KV: ${e instanceof Error ? e.message : String(e)}`);
12322
+ return err7(`Failed to read Greenzone state from KV: ${e instanceof Error ? e.message : String(e)}`);
12228
12323
  }
12229
12324
  }
12230
12325
  );
12231
12326
  }
12232
12327
 
12233
12328
  // src/tools-ui-agent-generic.ts
12234
- import { z as z24 } from "zod";
12329
+ import { z as z25 } from "zod";
12235
12330
  var COST_PER_STEP_USD = 0.014;
12236
12331
  var SESSION_OVERHEAD_USD = 5e-3;
12237
12332
  function estimateCostUsd2(steps) {
@@ -12273,13 +12368,13 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12273
12368
  title: "Dispatch Generic Browsing Agent",
12274
12369
  description: "Send a plain-language task to a hosted browsing agent, which drives a real browser on the open web \u2014 navigating pages, searching, extracting content, filling forms outside the OCS portal. Each run costs roughly $0.20-1.00 in model spend against your own BROWSER_USE_API_KEY, so treat it as the expensive last resort, not a lookup. Limited to CARRIER_BROWSER_USE_MAX_RUNS runs per CLI session (default 10). Params: `prompt` (1-4000 chars \u2014 name the target URL and the exact data or action), `max_steps` (1-30, default 15; prices the dry-run estimate only), `dry_run` (true returns only `estimated_cost_usd` and creates no session). Returns the finished result: unlike the hosted Worker, a local CLI has no background runtime, so this call blocks for the length of the run. Requires `write` scope. Do NOT use this to work out which Carrier tool answers a request \u2014 that is `carrier_ask`, which costs nothing. Do NOT use this for anything the OCS API already covers; every other tool here is faster, free, and audited.",
12275
12370
  inputSchema: {
12276
- prompt: z24.string().min(1).max(4e3).describe(
12371
+ prompt: z25.string().min(1).max(4e3).describe(
12277
12372
  "Natural-language task description for the browsing agent. Be specific: include target URLs, data to extract, or actions to perform."
12278
12373
  ),
12279
- max_steps: z24.number().int().min(1).max(30).optional().describe(
12374
+ max_steps: z25.number().int().min(1).max(30).optional().describe(
12280
12375
  "Advisory step budget (1\u201330, default 15). Prices the dry-run estimate; the hosted agent sets its own depth."
12281
12376
  ),
12282
- dry_run: z24.boolean().optional().describe(
12377
+ dry_run: z25.boolean().optional().describe(
12283
12378
  "Preview cost estimate without dispatching. Returns estimated_cost_usd only \u2014 no browser session is created."
12284
12379
  )
12285
12380
  }
@@ -12322,8 +12417,8 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12322
12417
  }
12323
12418
  try {
12324
12419
  runtime.budget.reserve();
12325
- } catch (err7) {
12326
- if (err7 instanceof SpendLimitError) {
12420
+ } catch (err8) {
12421
+ if (err8 instanceof SpendLimitError) {
12327
12422
  ctx.audit({
12328
12423
  tool_name: toolName,
12329
12424
  ocs_method: "[ui-agent:generic-ask]",
@@ -12342,13 +12437,13 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12342
12437
  type: "text",
12343
12438
  text: JSON.stringify({
12344
12439
  error: "run_limit_reached",
12345
- message: err7.message
12440
+ message: err8.message
12346
12441
  })
12347
12442
  }
12348
12443
  ]
12349
12444
  };
12350
12445
  }
12351
- throw err7;
12446
+ throw err8;
12352
12447
  }
12353
12448
  try {
12354
12449
  const result2 = await runUiAgent(
@@ -12386,7 +12481,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12386
12481
  }
12387
12482
  ]
12388
12483
  };
12389
- } catch (err7) {
12484
+ } catch (err8) {
12390
12485
  ctx.audit({
12391
12486
  tool_name: toolName,
12392
12487
  ocs_method: "[ui-agent:generic-ask]",
@@ -12402,7 +12497,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12402
12497
  type: "text",
12403
12498
  text: JSON.stringify({
12404
12499
  error: "ui_agent_failed",
12405
- message: err7 instanceof Error ? err7.message : String(err7)
12500
+ message: err8 instanceof Error ? err8.message : String(err8)
12406
12501
  })
12407
12502
  }
12408
12503
  ]
@@ -12416,7 +12511,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12416
12511
  title: "Poll Browsing Agent Task Status",
12417
12512
  description: "Returns the current state of a browsing-agent task by `task_id`, including its result once finished. In the local CLI, ui_agent_ask already returns the finished result, so this is mainly for re-reading a task within the same session. Task records do not survive the CLI process exiting. Requires `read` scope.",
12418
12513
  inputSchema: {
12419
- task_id: z24.string().describe("Task ID returned by ui_agent_ask")
12514
+ task_id: z25.string().describe("Task ID returned by ui_agent_ask")
12420
12515
  }
12421
12516
  },
12422
12517
  async (args) => {
@@ -12564,6 +12659,7 @@ registerStorefrontLogoTools(
12564
12659
  props
12565
12660
  );
12566
12661
  registerStorefrontDeployTools(server, props);
12662
+ registerStorefrontPlanTools(server, props);
12567
12663
  registerAtlasTools(server, toolCtx);
12568
12664
  registerAllPrompts(server);
12569
12665
  var transport = new StdioServerTransport();