@carrierllc/mcp 0.13.13 → 0.13.15

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
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CARRIER_VERSION,
4
+ NETWORK_DEFAULT_MODES,
4
5
  OCS_MAX_USAGE_WINDOW_DAYS,
5
6
  OcsApiError,
6
7
  OcsClient,
@@ -28,6 +29,7 @@ import {
28
29
  formatProbes,
29
30
  generateStorefrontLogo,
30
31
  getLimitForEndpoint,
32
+ getNetworkDefault,
31
33
  getRateLimitWindowCounts,
32
34
  gyCountryHistoryFromResponse,
33
35
  imsiFromSubscriberRecord,
@@ -40,6 +42,8 @@ import {
40
42
  mccToIso,
41
43
  mergeEnvLocal,
42
44
  modifySubscriberStatusParams,
45
+ networkDefaultKey,
46
+ networkDefaultPartition,
43
47
  networkEventRows,
44
48
  networkEventsOverPeriodParams,
45
49
  ocsHlrBitrate,
@@ -53,6 +57,7 @@ import {
53
57
  packageName,
54
58
  packageTemplateCreateFromToolJson,
55
59
  packageTemplateModifyFromToolJson,
60
+ planNetworkDefault,
56
61
  prepaidPackageLimits,
57
62
  prepaidPackageRows,
58
63
  probeAll,
@@ -64,6 +69,7 @@ import {
64
69
  renderHtml,
65
70
  repairPlanFor,
66
71
  scopeListSubscriberParams,
72
+ setNetworkDefault,
67
73
  staleActivePackages,
68
74
  storefrontClerkUrls,
69
75
  subscriberIdParams,
@@ -74,7 +80,7 @@ import {
74
80
  verifyStorefront,
75
81
  withOcsListSummary,
76
82
  withTemplateResellerId
77
- } from "./chunk-OJITGTFO.js";
83
+ } from "./chunk-QXIY42R6.js";
78
84
  import "./chunk-SHKKVIIA.js";
79
85
 
80
86
  // src/index.ts
@@ -1067,22 +1073,22 @@ function boundZonesResult(raw, opts = {}) {
1067
1073
  const { verbose = false, resellerId } = opts;
1068
1074
  const maxZones = opts.maxZones ?? ZONES_MAX_ZONES;
1069
1075
  const maxOperators = opts.maxOperators ?? (verbose ? ZONES_VERBOSE_MAX_OPERATORS : ZONES_MAX_OPERATORS);
1070
- const totalOperators = all.reduce((n, z24) => n + (z24.operators?.length ?? 0), 0);
1076
+ const totalOperators = all.reduce((n, z25) => n + (z25.operators?.length ?? 0), 0);
1071
1077
  const keptZones = all.slice(0, maxZones);
1072
1078
  let budget = maxOperators;
1073
1079
  let returnedOperators = 0;
1074
- const zones = keptZones.map((z24) => {
1075
- const ops = z24.operators ?? [];
1080
+ const zones = keptZones.map((z25) => {
1081
+ const ops = z25.operators ?? [];
1076
1082
  const take = Math.max(0, Math.min(ops.length, budget));
1077
1083
  budget -= take;
1078
1084
  returnedOperators += take;
1079
1085
  const sliced = ops.slice(0, take);
1080
1086
  if (verbose) {
1081
- return { ...z24, operators: sliced, operatorCount: ops.length };
1087
+ return { ...z25, operators: sliced, operatorCount: ops.length };
1082
1088
  }
1083
1089
  return {
1084
- zoneId: z24.zoneId,
1085
- zoneName: z24.zoneName,
1090
+ zoneId: z25.zoneId,
1091
+ zoneName: z25.zoneName,
1086
1092
  operatorCount: ops.length,
1087
1093
  operators: sliced.map((o) => ({
1088
1094
  net: o.networkId,
@@ -5077,7 +5083,7 @@ function registerAllBacklogTools(server2, ctx) {
5077
5083
  }
5078
5084
 
5079
5085
  // src/tools-carrier-ask.ts
5080
- import { z as z12 } from "zod";
5086
+ import { z as z13 } from "zod";
5081
5087
 
5082
5088
  // src/tools-ui-agent.ts
5083
5089
  import { z as z4 } from "zod";
@@ -7714,6 +7720,118 @@ function registerAtlasTools(server2, ctx) {
7714
7720
  );
7715
7721
  }
7716
7722
 
7723
+ // src/tools-network-default.ts
7724
+ import { z as z12 } from "zod";
7725
+ var NETWORK_DEFAULT_TOOL_SCOPES = {
7726
+ get_network_default: "read",
7727
+ set_network_default: "write",
7728
+ plan_network_default: "read"
7729
+ };
7730
+ var choiceShape = z12.object({
7731
+ mode: z12.enum(NETWORK_DEFAULT_MODES),
7732
+ steeringListId: z12.number().int().positive().optional()
7733
+ }).describe("`cheapest_available`, `all`, or `custom` with a steeringListId from list_steering_lists");
7734
+ function ioFor(ctx, token2) {
7735
+ const kv = ctx.env.CARRIER_USERS;
7736
+ const key = networkDefaultKey(
7737
+ networkDefaultPartition({
7738
+ ...ctx.props.org_id ? { orgId: ctx.props.org_id } : {},
7739
+ sub: ctx.props.sub
7740
+ })
7741
+ );
7742
+ const io = {
7743
+ resolveResellerId: () => getDefaultResellerId(ctx.env, token2),
7744
+ // Single operator, own OCS token: the token is the tenant boundary (see
7745
+ // docs/single-tenant-model.md), so no managed scoping is needed here.
7746
+ callOcs: (method, resellerId) => new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2).call(method, resellerId),
7747
+ // No binding means no store: say so (undefined), never pretend "nothing stored".
7748
+ readStored: () => kv ? kv.get(key, "json").catch(() => null) : void 0
7749
+ };
7750
+ if (kv) {
7751
+ io.writeStored = async (record) => {
7752
+ await kv.put(key, JSON.stringify(record));
7753
+ };
7754
+ }
7755
+ return io;
7756
+ }
7757
+ function asResult(r) {
7758
+ if (r.ok) return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
7759
+ return {
7760
+ isError: true,
7761
+ content: [{ type: "text", text: JSON.stringify({ error: r.error, code: r.code, status: r.status }) }]
7762
+ };
7763
+ }
7764
+ function registerNetworkDefaultTools(server2, ctx) {
7765
+ server2.registerTool(
7766
+ "get_network_default",
7767
+ {
7768
+ title: "Get Network Default",
7769
+ description: "Use this to read the network default for this organisation: which networks a SIM is given in a country. The system default is `cheapest_available` (the lowest data rate among networks really available in the country). The org can choose `all` or a `custom` steering list instead. Returns: `systemDefault`, `orgDefault` (null when none stored), `effectiveMode`, `warnings`, and `appliesTo` (always `new_assignments_only`: existing SIMs are never changed by this setting). Do NOT use this to see which list a specific SIM has \u2014 use `get_subscriber`.",
7770
+ inputSchema: {},
7771
+ annotations: { readOnlyHint: true }
7772
+ },
7773
+ wrapHandler(
7774
+ "get_network_default",
7775
+ "getCustomerTariff",
7776
+ NETWORK_DEFAULT_TOOL_SCOPES["get_network_default"],
7777
+ ctx,
7778
+ async (_args, token2) => asResult(await getNetworkDefault(ioFor(ctx, token2)))
7779
+ )
7780
+ );
7781
+ server2.registerTool(
7782
+ "set_network_default",
7783
+ {
7784
+ title: "Set Network Default",
7785
+ 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
+ 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")
7790
+ },
7791
+ annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false }
7792
+ },
7793
+ wrapHandler(
7794
+ "set_network_default",
7795
+ "listSteeringList",
7796
+ NETWORK_DEFAULT_TOOL_SCOPES["set_network_default"],
7797
+ ctx,
7798
+ async (args, token2) => {
7799
+ const body2 = args.mode === "custom" ? { mode: args.mode, steeringListId: args.steeringListId } : { mode: args.mode };
7800
+ const io = ioFor(ctx, token2);
7801
+ if (args.dry_run === true) {
7802
+ if (!io.writeStored) return asResult(await setNetworkDefault(io, body2, ctx.props.sub, /* @__PURE__ */ new Date()));
7803
+ const probe = await setNetworkDefault({ ...io, writeStored: async () => {
7804
+ } }, body2, ctx.props.sub, /* @__PURE__ */ new Date());
7805
+ if (!probe.ok) return asResult(probe);
7806
+ return asResult({ ok: true, data: { dry_run: true, wouldStore: probe.data.orgDefault, appliesTo: probe.data.appliesTo } });
7807
+ }
7808
+ return asResult(await setNetworkDefault(io, body2, ctx.props.sub, /* @__PURE__ */ new Date()));
7809
+ }
7810
+ )
7811
+ );
7812
+ server2.registerTool(
7813
+ "plan_network_default",
7814
+ {
7815
+ title: "Plan Network Default",
7816
+ 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
+ inputSchema: {
7818
+ countries: z12.array(z12.string().length(2)).min(1).describe('ISO-3166 alpha-2 codes, e.g. ["CA"]'),
7819
+ simOverride: choiceShape.optional(),
7820
+ packageOverride: choiceShape.optional(),
7821
+ resellerId: z12.number().int().positive().optional().describe("Omit to use the token owner's reseller")
7822
+ },
7823
+ annotations: { readOnlyHint: true }
7824
+ },
7825
+ wrapHandler(
7826
+ "plan_network_default",
7827
+ "getCustomerTariff",
7828
+ NETWORK_DEFAULT_TOOL_SCOPES["plan_network_default"],
7829
+ ctx,
7830
+ async (args, token2) => asResult(await planNetworkDefault(ioFor(ctx, token2), args, /* @__PURE__ */ new Date()))
7831
+ )
7832
+ );
7833
+ }
7834
+
7717
7835
  // src/credits.ts
7718
7836
  var TIER_CREDIT_ALLOTMENTS = {
7719
7837
  free: 5e3,
@@ -9088,7 +9206,8 @@ var ALL_TOOL_SCOPES = Object.freeze({
9088
9206
  ...STOREFRONT_LOGO_TOOL_SCOPES,
9089
9207
  // Storefront deploy tools exist only in this package, not in the Worker.
9090
9208
  ...STOREFRONT_DEPLOY_TOOL_SCOPES,
9091
- ...ATLAS_TOOL_SCOPES
9209
+ ...ATLAS_TOOL_SCOPES,
9210
+ ...NETWORK_DEFAULT_TOOL_SCOPES
9092
9211
  });
9093
9212
  var TOOL_REGISTRY = new Set(Object.keys(ALL_TOOL_SCOPES));
9094
9213
  var UNREGISTERED_TOOLS = /* @__PURE__ */ new Set(["stripe_proxy_call"]);
@@ -9763,15 +9882,15 @@ function registerAllCarrierAskTools(server2, ctx) {
9763
9882
  title: "Natural Language Carrier Tool Router",
9764
9883
  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.",
9765
9884
  inputSchema: {
9766
- intent: z12.string().min(3).describe(
9885
+ intent: z13.string().min(3).describe(
9767
9886
  "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...')"
9768
9887
  ),
9769
- context: z12.object({
9770
- iccid: z12.string().optional().describe("Subscriber ICCID if already known"),
9771
- account_id: z12.number().optional().describe("Account ID if already known"),
9772
- reseller_id: z12.number().optional().describe("Reseller ID if already known")
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")
9773
9892
  }).optional().describe("Optional pre-resolved context to improve routing accuracy"),
9774
- confirm_token: z12.string().optional().describe(
9893
+ confirm_token: z13.string().optional().describe(
9775
9894
  "One-time token from a previous carrier_ask call with confirm_required=true. Providing this executes the previously staged destructive operation."
9776
9895
  )
9777
9896
  }
@@ -9975,7 +10094,7 @@ function registerAllCarrierAskTools(server2, ctx) {
9975
10094
  title: "Describe a Carrier Tool",
9976
10095
  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.",
9977
10096
  inputSchema: {
9978
- tool_name: z12.string().describe("The exact MCP tool name to describe (e.g. 'assign_package', 'hlr_set_bitrate')")
10097
+ tool_name: z13.string().describe("The exact MCP tool name to describe (e.g. 'assign_package', 'hlr_set_bitrate')")
9979
10098
  }
9980
10099
  },
9981
10100
  wrapHandler(
@@ -10068,7 +10187,7 @@ function buildExamples(toolName) {
10068
10187
  }
10069
10188
 
10070
10189
  // src/list-recent-ocs-events.ts
10071
- import { z as z13 } from "zod";
10190
+ import { z as z14 } from "zod";
10072
10191
 
10073
10192
  // src/ocs-scoping.ts
10074
10193
  function managedOwnAccountId(auth) {
@@ -10236,10 +10355,10 @@ var ALLOWED_EVENT_TYPES = [
10236
10355
  "usage.anomaly"
10237
10356
  ];
10238
10357
  var listRecentOcsEventsSchema = {
10239
- iccid: z13.string().regex(/^\d{19,20}$/).describe("ICCID, 19 or 20 digits, ITU-T E.118 format"),
10240
- limit: z13.number().int().min(1).max(50).default(20).describe("Max events to return, newest first"),
10241
- event_types: z13.array(z13.enum(ALLOWED_EVENT_TYPES)).optional().describe("Filter to specific event types"),
10242
- since: z13.string().datetime().optional().describe("ISO-8601 timestamp; only events after this point")
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")
10243
10362
  };
10244
10363
  async function listRecentOcsEvents2(iccid, limit, eventTypes, since, kv) {
10245
10364
  return listRecentOcsEvents(iccid, limit, eventTypes, since, kv);
@@ -10302,7 +10421,7 @@ function registerListRecentOcsEventsTool(server2, ctx) {
10302
10421
  }
10303
10422
 
10304
10423
  // src/tools-rate-governor.ts
10305
- import { z as z14 } from "zod";
10424
+ import { z as z15 } from "zod";
10306
10425
  var MONITORED_ENDPOINTS = [
10307
10426
  "subscriberNetworkEventsOverPeriod",
10308
10427
  // 30/min
@@ -10334,7 +10453,7 @@ function registerRateLimitStatusTool(server2, ctx) {
10334
10453
  title: "API Rate-Limit Governor Status",
10335
10454
  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.",
10336
10455
  inputSchema: {
10337
- endpoint: z14.string().optional().describe(
10456
+ endpoint: z15.string().optional().describe(
10338
10457
  "OCS method name in camelCase (e.g. 'subscriberUsageOverPeriod'). Omit to return status for all monitored endpoints."
10339
10458
  )
10340
10459
  },
@@ -10390,7 +10509,7 @@ function registerRateLimitStatusTool(server2, ctx) {
10390
10509
  }
10391
10510
 
10392
10511
  // src/tools-depletion-events.ts
10393
- import { z as z15 } from "zod";
10512
+ import { z as z16 } from "zod";
10394
10513
  function ok4(payload) {
10395
10514
  return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
10396
10515
  }
@@ -10418,10 +10537,10 @@ function registerDepletionEventsTool(server2, ctx) {
10418
10537
  title: "Subscriber Bundle Depletion Events",
10419
10538
  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.",
10420
10539
  inputSchema: {
10421
- subscriberId: z15.string().optional().describe(
10540
+ subscriberId: z16.string().optional().describe(
10422
10541
  "OCS subscriber ID to look up. Omit to list the 20 most recent fleet-wide depletions."
10423
10542
  ),
10424
- since: z15.string().optional().describe(
10543
+ since: z16.string().optional().describe(
10425
10544
  "ISO-8601 timestamp \u2014 only return events at or after this time (for single-subscriber lookup)."
10426
10545
  )
10427
10546
  },
@@ -10536,7 +10655,7 @@ function registerDepletionEventsTool(server2, ctx) {
10536
10655
  }
10537
10656
 
10538
10657
  // src/tools-country-history.ts
10539
- import { z as z16 } from "zod";
10658
+ import { z as z17 } from "zod";
10540
10659
  function isoDate(now, daysAgo = 0) {
10541
10660
  const d = new Date(now.getTime() - daysAgo * 864e5);
10542
10661
  return d.toISOString().slice(0, 10);
@@ -10559,10 +10678,10 @@ function registerCountryHistoryTool(server2, ctx) {
10559
10678
  title: "Subscriber Country History",
10560
10679
  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.",
10561
10680
  inputSchema: {
10562
- subscriberId: z16.string().regex(/^\d{19,20}$/, "subscriberId must be a 19\u201320 digit ICCID").describe("Subscriber ICCID (19\u201320 digits, ITU-T E.118)"),
10563
- startDate: z16.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)"),
10564
- endDate: z16.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)"),
10565
- limit: z16.number().int().min(1).max(50).default(20).optional().describe("Maximum number of country_events to return, newest first (default 20, max 50)")
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)")
10566
10685
  },
10567
10686
  annotations: { readOnlyHint: true, idempotentHint: true }
10568
10687
  },
@@ -10670,7 +10789,7 @@ function registerCountryHistoryTool(server2, ctx) {
10670
10789
  }
10671
10790
 
10672
10791
  // src/apps/fleet-health-app.ts
10673
- import { z as z17 } from "zod";
10792
+ import { z as z18 } from "zod";
10674
10793
  import {
10675
10794
  registerAppTool,
10676
10795
  registerAppResource,
@@ -10786,7 +10905,7 @@ function registerFleetHealthApp(server2, ctx) {
10786
10905
  title: "Fleet Health Dashboard",
10787
10906
  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.",
10788
10907
  inputSchema: {
10789
- accountId: z17.number().optional().describe("Filter to a specific account (omit for all)")
10908
+ accountId: z18.number().optional().describe("Filter to a specific account (omit for all)")
10790
10909
  },
10791
10910
  annotations: { readOnlyHint: true },
10792
10911
  _meta: {
@@ -10829,7 +10948,7 @@ function registerFleetHealthApp(server2, ctx) {
10829
10948
  }
10830
10949
 
10831
10950
  // src/apps/provisioning-wizard.ts
10832
- import { z as z18 } from "zod";
10951
+ import { z as z19 } from "zod";
10833
10952
  import {
10834
10953
  registerAppTool as registerAppTool2,
10835
10954
  registerAppResource as registerAppResource2,
@@ -10934,10 +11053,10 @@ function registerProvisioningWizard(server2, ctx) {
10934
11053
  title: "eSIM Provisioning Wizard",
10935
11054
  description: "Interactive 3-step wizard to provision an eSIM: select subscriber, choose package template, preview (dry_run) and confirm execution.",
10936
11055
  inputSchema: {
10937
- step: z18.enum(["init", "select-package", "preview", "confirm"]).describe("Current wizard step"),
10938
- wizardId: z18.string().optional().describe("Wizard session ID (absent on init)"),
10939
- subscriber_iccid: z18.string().optional().describe("Subscriber ICCID (required for select-package)"),
10940
- package_template_id: z18.number().optional().describe("Package template ID (required for preview)")
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)")
10941
11060
  },
10942
11061
  _meta: {
10943
11062
  ui: {
@@ -11207,7 +11326,7 @@ function registerProvisioningWizard(server2, ctx) {
11207
11326
  }
11208
11327
 
11209
11328
  // src/apps/balance-topup.ts
11210
- import { z as z19 } from "zod";
11329
+ import { z as z20 } from "zod";
11211
11330
  import {
11212
11331
  registerAppTool as registerAppTool3,
11213
11332
  registerAppResource as registerAppResource3,
@@ -11260,9 +11379,9 @@ function registerBalanceTopupApp(server2, ctx) {
11260
11379
  title: "Balance Top-up Form",
11261
11380
  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`.",
11262
11381
  inputSchema: {
11263
- iccid: z19.string().describe("The subscriber ICCID for the account lookup"),
11264
- delta: z19.number().describe("Amount to add (positive) or deduct (negative) from the account balance"),
11265
- preview: z19.boolean().default(true).describe("If true, return preview without writing. If false, execute the balance change.")
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.")
11266
11385
  },
11267
11386
  annotations: { destructiveHint: true },
11268
11387
  _meta: {
@@ -11345,7 +11464,7 @@ function registerAllApps(server2, ctx) {
11345
11464
  }
11346
11465
 
11347
11466
  // src/prompts.ts
11348
- import { z as z20 } from "zod";
11467
+ import { z as z21 } from "zod";
11349
11468
  function registerAllPrompts(server2) {
11350
11469
  server2.registerPrompt(
11351
11470
  "fleet_health_report",
@@ -11381,7 +11500,7 @@ Format as a structured report with sections, tables, and actionable recommendati
11381
11500
  {
11382
11501
  title: "Subscriber Deep Dive",
11383
11502
  description: "Comprehensive analysis of a single subscriber: profile, packages, usage patterns, location history, recommendations.",
11384
- argsSchema: { iccid: z20.string().describe("The subscriber ICCID to analyse") }
11503
+ argsSchema: { iccid: z21.string().describe("The subscriber ICCID to analyse") }
11385
11504
  },
11386
11505
  async ({ iccid }) => ({
11387
11506
  messages: [
@@ -11480,7 +11599,7 @@ Analyze and present:
11480
11599
  {
11481
11600
  title: "Bulk Operations Planner",
11482
11601
  description: "Plan bulk operations safely: mass package assignments, account migrations, balance adjustments, or status changes.",
11483
- argsSchema: { operation: z20.string().describe("Describe the bulk operation you want to perform") }
11602
+ argsSchema: { operation: z21.string().describe("Describe the bulk operation you want to perform") }
11484
11603
  },
11485
11604
  async ({ operation }) => ({
11486
11605
  messages: [
@@ -11513,7 +11632,7 @@ NEVER execute destructive operations without confirmation.`
11513
11632
  }
11514
11633
 
11515
11634
  // src/tools-pricing.ts
11516
- import { z as z21 } from "zod";
11635
+ import { z as z22 } from "zod";
11517
11636
  function registerAllPricingTools(server2, ctx) {
11518
11637
  const allTools = [...PRICING_TOOLS, ...PROJECTS_TOOLS];
11519
11638
  for (const tool of allTools) {
@@ -11620,19 +11739,19 @@ function buildZodSchema(schema) {
11620
11739
  for (const [key, prop] of Object.entries(properties)) {
11621
11740
  let field;
11622
11741
  if (prop.enum) {
11623
- field = z21.enum(prop.enum);
11742
+ field = z22.enum(prop.enum);
11624
11743
  } else if (prop.type === "number") {
11625
- field = z21.number();
11744
+ field = z22.number();
11626
11745
  } else if (prop.type === "boolean") {
11627
- field = z21.boolean();
11746
+ field = z22.boolean();
11628
11747
  } else if (prop.type === "array") {
11629
11748
  if (prop.items?.type === "number") {
11630
- field = z21.array(z21.number());
11749
+ field = z22.array(z22.number());
11631
11750
  } else {
11632
- field = z21.array(z21.string());
11751
+ field = z22.array(z22.string());
11633
11752
  }
11634
11753
  } else {
11635
- field = z21.string();
11754
+ field = z22.string();
11636
11755
  }
11637
11756
  const required = schema.required ?? [];
11638
11757
  if (!required.includes(key)) {
@@ -11647,7 +11766,7 @@ function buildZodSchema(schema) {
11647
11766
  }
11648
11767
 
11649
11768
  // src/tools-greenzone.ts
11650
- import { z as z22 } from "zod";
11769
+ import { z as z23 } from "zod";
11651
11770
 
11652
11771
  // src/greenzone-whitelist.ts
11653
11772
  var STATE_KEY = "greenzone:state";
@@ -11938,9 +12057,9 @@ function registerGreenzoneTools(server2, ctx) {
11938
12057
  title: "Greenzone Whitelist \u2014 Add",
11939
12058
  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.",
11940
12059
  inputSchema: {
11941
- host: z22.string().optional().describe("Hostname to whitelist (e.g. 'example.com')"),
11942
- ip: z22.string().optional().describe("IP address or CIDR to whitelist (e.g. '203.0.113.0/24')"),
11943
- dry_run: z22.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
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")
11944
12063
  },
11945
12064
  annotations: { destructiveHint: true }
11946
12065
  },
@@ -11999,9 +12118,9 @@ function registerGreenzoneTools(server2, ctx) {
11999
12118
  title: "Greenzone Whitelist \u2014 Remove",
12000
12119
  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.",
12001
12120
  inputSchema: {
12002
- host: z22.string().optional().describe("Hostname to remove from whitelist"),
12003
- ip: z22.string().optional().describe("IP address or CIDR to remove from whitelist"),
12004
- dry_run: z22.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
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")
12005
12124
  },
12006
12125
  annotations: { destructiveHint: true }
12007
12126
  },
@@ -12060,7 +12179,7 @@ function registerGreenzoneTools(server2, ctx) {
12060
12179
  title: "Greenzone Whitelist \u2014 List",
12061
12180
  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.",
12062
12181
  inputSchema: {
12063
- from_portal: z22.boolean().optional().describe(
12182
+ from_portal: z23.boolean().optional().describe(
12064
12183
  "When true, return Kapture steps to read live portal state instead of KV cache"
12065
12184
  )
12066
12185
  },
@@ -12112,7 +12231,7 @@ function registerGreenzoneTools(server2, ctx) {
12112
12231
  }
12113
12232
 
12114
12233
  // src/tools-ui-agent-generic.ts
12115
- import { z as z23 } from "zod";
12234
+ import { z as z24 } from "zod";
12116
12235
  var COST_PER_STEP_USD = 0.014;
12117
12236
  var SESSION_OVERHEAD_USD = 5e-3;
12118
12237
  function estimateCostUsd2(steps) {
@@ -12154,13 +12273,13 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12154
12273
  title: "Dispatch Generic Browsing Agent",
12155
12274
  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.",
12156
12275
  inputSchema: {
12157
- prompt: z23.string().min(1).max(4e3).describe(
12276
+ prompt: z24.string().min(1).max(4e3).describe(
12158
12277
  "Natural-language task description for the browsing agent. Be specific: include target URLs, data to extract, or actions to perform."
12159
12278
  ),
12160
- max_steps: z23.number().int().min(1).max(30).optional().describe(
12279
+ max_steps: z24.number().int().min(1).max(30).optional().describe(
12161
12280
  "Advisory step budget (1\u201330, default 15). Prices the dry-run estimate; the hosted agent sets its own depth."
12162
12281
  ),
12163
- dry_run: z23.boolean().optional().describe(
12282
+ dry_run: z24.boolean().optional().describe(
12164
12283
  "Preview cost estimate without dispatching. Returns estimated_cost_usd only \u2014 no browser session is created."
12165
12284
  )
12166
12285
  }
@@ -12297,7 +12416,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12297
12416
  title: "Poll Browsing Agent Task Status",
12298
12417
  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.",
12299
12418
  inputSchema: {
12300
- task_id: z23.string().describe("Task ID returned by ui_agent_ask")
12419
+ task_id: z24.string().describe("Task ID returned by ui_agent_ask")
12301
12420
  }
12302
12421
  },
12303
12422
  async (args) => {
@@ -12425,6 +12544,7 @@ registerAllCarrierAskTools(server, toolCtx);
12425
12544
  registerListRecentOcsEventsTool(server, toolCtx);
12426
12545
  registerRateLimitStatusTool(server, toolCtx);
12427
12546
  registerCountryHistoryTool(server, toolCtx);
12547
+ registerNetworkDefaultTools(server, toolCtx);
12428
12548
  registerDepletionEventsTool(server, toolCtx);
12429
12549
  registerAllApps(server, toolCtx);
12430
12550
  registerAllPricingTools(server, toolCtx);