@carrierllc/mcp 0.13.18 → 0.13.20

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-VXROP2SU.js";
83
+ } from "./chunk-VM2X3RHP.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 err8 = new Error("Bedrock call timed out before a usable response");
295
- err8.name = "TimeoutError";
296
- return err8;
294
+ const err9 = new Error("Bedrock call timed out before a usable response");
295
+ err9.name = "TimeoutError";
296
+ return err9;
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 (err8) {
320
+ } catch (err9) {
321
321
  if (signal.aborted && throttled) return throttled;
322
- throw err8;
322
+ throw err9;
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 (err8) {
672
- const message = err8 instanceof Error ? err8.message : String(err8);
671
+ } catch (err9) {
672
+ const message = err9 instanceof Error ? err9.message : String(err9);
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 err8;
684
+ throw err9;
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 (err8) {
699
- const message = err8 instanceof Error ? err8.message : String(err8);
698
+ } catch (err9) {
699
+ const message = err9 instanceof Error ? err9.message : String(err9);
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 (err8) {
841
- console.error(`[browser-use-agent] audit write failed: ${err8.message}`);
840
+ } catch (err9) {
841
+ console.error(`[browser-use-agent] audit write failed: ${err9.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, z26) => n + (z26.operators?.length ?? 0), 0);
1076
+ const totalOperators = all.reduce((n, z27) => n + (z27.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((z26) => {
1081
- const ops = z26.operators ?? [];
1080
+ const zones = keptZones.map((z27) => {
1081
+ const ops = z27.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 { ...z26, operators: sliced, operatorCount: ops.length };
1087
+ return { ...z27, operators: sliced, operatorCount: ops.length };
1088
1088
  }
1089
1089
  return {
1090
- zoneId: z26.zoneId,
1091
- zoneName: z26.zoneName,
1090
+ zoneId: z27.zoneId,
1091
+ zoneName: z27.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 (err8) {
1281
+ } catch (err9) {
1282
1282
  try {
1283
- Sentry.captureException(err8, {
1283
+ Sentry.captureException(err9, {
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 = err8 instanceof Error ? err8.message : String(err8);
1293
- const ocsCode = err8 instanceof OcsApiError ? err8.code : void 0;
1292
+ const message = err9 instanceof Error ? err9.message : String(err9);
1293
+ const ocsCode = err9 instanceof OcsApiError ? err9.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 (err8) {
3672
- return { data: null, error: err8 instanceof Error ? err8.message : String(err8) };
3671
+ } catch (err9) {
3672
+ return { data: null, error: err9 instanceof Error ? err9.message : String(err9) };
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 z14 } from "zod";
5086
+ import { z as z15 } from "zod";
5087
5087
 
5088
5088
  // src/tools-ui-agent.ts
5089
5089
  import { z as z4 } from "zod";
@@ -5356,8 +5356,8 @@ function wrapPortalHandler(toolName, gapId, requiredScope, ctx, runtime, optIn,
5356
5356
  }
5357
5357
  try {
5358
5358
  runtime.budget.reserve();
5359
- } catch (err8) {
5360
- if (err8 instanceof SpendLimitError) {
5359
+ } catch (err9) {
5360
+ if (err9 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: err8.message },
5372
+ { error: "run_limit_reached", message: err9.message },
5373
5373
  true
5374
5374
  );
5375
5375
  }
5376
- throw err8;
5376
+ throw err9;
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 (err8) {
5439
+ } catch (err9) {
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: ${err8 instanceof Error ? err8.message : String(err8)}`
5454
+ `Error dispatching the UI agent: ${err9 instanceof Error ? err9.message : String(err9)}`
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 (err8) {
5947
+ } catch (err9) {
5948
5948
  return {
5949
5949
  isError: true,
5950
5950
  content: [
5951
5951
  {
5952
5952
  type: "text",
5953
- text: `Error listing pending tasks: ${err8 instanceof Error ? err8.message : String(err8)}`
5953
+ text: `Error listing pending tasks: ${err9 instanceof Error ? err9.message : String(err9)}`
5954
5954
  }
5955
5955
  ]
5956
5956
  };
@@ -6832,12 +6832,12 @@ var STRIPE_API_VERSION = "2025-08-27.basil";
6832
6832
  function isStripeOrgKey(stripeKey) {
6833
6833
  return stripeKey.startsWith("sk_org_");
6834
6834
  }
6835
+ var ORG_KEY_NEEDS_PLATFORM_ACCOUNT = "STRIPE_ORG_SECRET_KEY is set but STRIPE_PLATFORM_ACCOUNT_ID is not. Organization keys reach a connected account only through Stripe-Context <platform>/<connected>, so set STRIPE_PLATFORM_ACCOUNT_ID to your platform's acct_ id (or unset STRIPE_ORG_SECRET_KEY to use STRIPE_SECRET_KEY).";
6835
6836
  function stripeAccountHeaders(stripeKey, connectedAccountId, platformAccountId) {
6836
6837
  if (isStripeOrgKey(stripeKey)) {
6837
6838
  if (connectedAccountId) {
6838
- return {
6839
- "Stripe-Context": platformAccountId ? `${platformAccountId}/${connectedAccountId}` : connectedAccountId
6840
- };
6839
+ if (!platformAccountId) throw new Error(ORG_KEY_NEEDS_PLATFORM_ACCOUNT);
6840
+ return { "Stripe-Context": `${platformAccountId}/${connectedAccountId}` };
6841
6841
  }
6842
6842
  return platformAccountId ? { "Stripe-Context": platformAccountId } : {};
6843
6843
  }
@@ -6845,6 +6845,9 @@ function stripeAccountHeaders(stripeKey, connectedAccountId, platformAccountId)
6845
6845
  return {};
6846
6846
  }
6847
6847
  function buildStripeHeaders(stripeKey, opts = {}) {
6848
+ if (opts.requirePlatformScope && isStripeOrgKey(stripeKey) && !opts.platformAccountId) {
6849
+ throw new Error(ORG_KEY_NEEDS_PLATFORM_ACCOUNT);
6850
+ }
6848
6851
  const headers = {
6849
6852
  Authorization: `Bearer ${stripeKey}`,
6850
6853
  "Stripe-Version": STRIPE_API_VERSION,
@@ -6890,7 +6893,8 @@ function err2(text) {
6890
6893
  async function stripeGet(stripeKey, path, connectedAccountId) {
6891
6894
  const headers = buildStripeHeaders(stripeKey, {
6892
6895
  connectedAccountId,
6893
- platformAccountId: process.env.STRIPE_PLATFORM_ACCOUNT_ID
6896
+ platformAccountId: process.env.STRIPE_PLATFORM_ACCOUNT_ID,
6897
+ requirePlatformScope: path.startsWith("/v1/accounts/")
6894
6898
  });
6895
6899
  const res = await fetch(`https://api.stripe.com${path}`, { headers });
6896
6900
  const data = await res.json();
@@ -7753,8 +7757,8 @@ async function atlasPublicGet(baseUrl2, path) {
7753
7757
  headers: { Accept: "application/json" },
7754
7758
  signal: AbortSignal.timeout(8e3)
7755
7759
  });
7756
- } catch (err8) {
7757
- const message = err8 instanceof Error ? err8.message : String(err8);
7760
+ } catch (err9) {
7761
+ const message = err9 instanceof Error ? err9.message : String(err9);
7758
7762
  throw new Error(`Atlas request failed: ${message}`);
7759
7763
  }
7760
7764
  const text = await response.text();
@@ -7951,6 +7955,176 @@ function registerNetworkDefaultTools(server2, ctx) {
7951
7955
  );
7952
7956
  }
7953
7957
 
7958
+ // src/affiliate-tools.ts
7959
+ import { z as z14 } from "zod";
7960
+ var AFFILIATE_TOOL_SCOPES = {
7961
+ affiliate_status: "read",
7962
+ affiliate_commissions: "read",
7963
+ affiliate_join: "write"
7964
+ };
7965
+ var REF_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
7966
+ var LINK_BASE = "https://app.carrier.llc/sign-up";
7967
+ function newRefCode() {
7968
+ const bytes = crypto.getRandomValues(new Uint8Array(8));
7969
+ return Array.from(bytes, (b) => REF_ALPHABET[b % REF_ALPHABET.length]).join("");
7970
+ }
7971
+ var linkFor = (code) => `${LINK_BASE}?ref=${code}`;
7972
+ function ok5(text) {
7973
+ return { content: [{ type: "text", text }] };
7974
+ }
7975
+ function err6(text) {
7976
+ return { isError: true, content: [{ type: "text", text }] };
7977
+ }
7978
+ function registerAffiliateTools(server2, ctx) {
7979
+ const { env, props: props2 } = ctx;
7980
+ const operatorId = props2.org_id ?? props2.sub;
7981
+ server2.registerTool(
7982
+ "affiliate_status",
7983
+ {
7984
+ title: "Affiliate Status",
7985
+ description: "Read-only: returns the affiliate profile, referral code, link, counts, money owed and paid, and Connect payouts status for the authenticated operator.",
7986
+ inputSchema: {},
7987
+ annotations: annotationsFor("affiliate_status", "read")
7988
+ },
7989
+ async () => {
7990
+ const denied = denyUnlessScoped(props2, "affiliate_status", AFFILIATE_TOOL_SCOPES);
7991
+ if (denied) return denied;
7992
+ try {
7993
+ requireBinding(env.BILLING_DB, "affiliate_status", "BILLING_DB", "affiliate data");
7994
+ const me = await env.BILLING_DB.prepare("SELECT ref_code, status, created_at FROM affiliates WHERE org_id = ?").bind(operatorId).first();
7995
+ if (!me) {
7996
+ return err6("Not an affiliate yet. Call affiliate_join to join.");
7997
+ }
7998
+ const stats = await env.BILLING_DB.prepare(
7999
+ `SELECT (SELECT COUNT(*) FROM affiliate_referrals WHERE affiliate_org_id = ?) AS referrals,
8000
+ COALESCE((SELECT SUM(commission_cents) FROM affiliate_commissions WHERE affiliate_org_id = ? AND status <> 'paid'), 0) AS pending_cents,
8001
+ COALESCE((SELECT SUM(commission_cents) FROM affiliate_commissions WHERE affiliate_org_id = ? AND status = 'paid'), 0) AS paid_cents,
8002
+ COALESCE((SELECT payouts_enabled FROM operator_stripe_accounts WHERE operator_id = ?), 0) AS payouts_enabled`
8003
+ ).bind(operatorId, operatorId, operatorId, operatorId).first();
8004
+ return ok5(
8005
+ JSON.stringify(
8006
+ {
8007
+ ref_code: me.ref_code,
8008
+ link: linkFor(me.ref_code),
8009
+ status: me.status,
8010
+ referrals: stats?.referrals ?? 0,
8011
+ pending_cents: stats?.pending_cents ?? 0,
8012
+ paid_cents: stats?.paid_cents ?? 0,
8013
+ payouts_enabled: Boolean(stats?.payouts_enabled)
8014
+ },
8015
+ null,
8016
+ 2
8017
+ )
8018
+ );
8019
+ } catch (e) {
8020
+ return err6(e instanceof Error ? e.message : "unknown");
8021
+ }
8022
+ }
8023
+ );
8024
+ server2.registerTool(
8025
+ "affiliate_commissions",
8026
+ {
8027
+ title: "Affiliate Commissions",
8028
+ description: "Read-only: returns the list of commission records for the authenticated operator.",
8029
+ inputSchema: {
8030
+ limit: z14.number().int().min(1).max(200).optional().default(50).describe("Maximum number of commission records to return (1-200, default: 50).")
8031
+ },
8032
+ annotations: annotationsFor("affiliate_commissions", "read")
8033
+ },
8034
+ async ({ limit }) => {
8035
+ const denied = denyUnlessScoped(props2, "affiliate_commissions", AFFILIATE_TOOL_SCOPES);
8036
+ if (denied) return denied;
8037
+ try {
8038
+ requireBinding(env.BILLING_DB, "affiliate_commissions", "BILLING_DB", "affiliate data");
8039
+ const rawLimit = limit ?? 50;
8040
+ const clampedLimit = Number.isFinite(rawLimit) ? Math.min(200, Math.max(1, rawLimit)) : 50;
8041
+ const rows = await env.BILLING_DB.prepare(
8042
+ `SELECT period, referred_org_id, base_cents, commission_bps, commission_cents, status, unmeasured_rows
8043
+ FROM affiliate_commissions
8044
+ WHERE affiliate_org_id = ?
8045
+ ORDER BY period DESC, id DESC
8046
+ LIMIT ?`
8047
+ ).bind(operatorId, clampedLimit).all();
8048
+ return ok5(
8049
+ JSON.stringify(
8050
+ {
8051
+ commissions: rows.results ?? []
8052
+ },
8053
+ null,
8054
+ 2
8055
+ )
8056
+ );
8057
+ } catch (e) {
8058
+ return err6(e instanceof Error ? e.message : "unknown");
8059
+ }
8060
+ }
8061
+ );
8062
+ server2.registerTool(
8063
+ "affiliate_join",
8064
+ {
8065
+ title: "Join Affiliate Program",
8066
+ description: "Join the Carrier affiliate program and generate a personal referral code and link.",
8067
+ inputSchema: {},
8068
+ annotations: annotationsFor("affiliate_join", "write")
8069
+ },
8070
+ async () => {
8071
+ const denied = denyUnlessScoped(props2, "affiliate_join", AFFILIATE_TOOL_SCOPES);
8072
+ if (denied) return denied;
8073
+ try {
8074
+ requireBinding(env.BILLING_DB, "affiliate_join", "BILLING_DB", "affiliate data");
8075
+ const existing = await env.BILLING_DB.prepare("SELECT ref_code FROM affiliates WHERE org_id = ?").bind(operatorId).first();
8076
+ if (existing) {
8077
+ return ok5(
8078
+ JSON.stringify(
8079
+ {
8080
+ ref_code: existing.ref_code,
8081
+ link: linkFor(existing.ref_code)
8082
+ },
8083
+ null,
8084
+ 2
8085
+ )
8086
+ );
8087
+ }
8088
+ for (let attempt = 0; attempt < 5; attempt++) {
8089
+ const code = newRefCode();
8090
+ try {
8091
+ await env.BILLING_DB.prepare("INSERT INTO affiliates (org_id, ref_code, created_at) VALUES (?, ?, ?)").bind(operatorId, code, Date.now()).run();
8092
+ return ok5(
8093
+ JSON.stringify(
8094
+ {
8095
+ ref_code: code,
8096
+ link: linkFor(code)
8097
+ },
8098
+ null,
8099
+ 2
8100
+ )
8101
+ );
8102
+ } catch (errCatch) {
8103
+ const msg = errCatch instanceof Error ? errCatch.message : "";
8104
+ if (!/UNIQUE|constraint/i.test(msg)) throw errCatch;
8105
+ const raced = await env.BILLING_DB.prepare("SELECT ref_code FROM affiliates WHERE org_id = ?").bind(operatorId).first();
8106
+ if (raced) {
8107
+ return ok5(
8108
+ JSON.stringify(
8109
+ {
8110
+ ref_code: raced.ref_code,
8111
+ link: linkFor(raced.ref_code)
8112
+ },
8113
+ null,
8114
+ 2
8115
+ )
8116
+ );
8117
+ }
8118
+ }
8119
+ }
8120
+ return err6("Could not allocate a referral code");
8121
+ } catch (e) {
8122
+ return err6(e instanceof Error ? e.message : "unknown");
8123
+ }
8124
+ }
8125
+ );
8126
+ }
8127
+
7954
8128
  // src/credits.ts
7955
8129
  var TIER_CREDIT_ALLOTMENTS = {
7956
8130
  free: 5e3,
@@ -9327,7 +9501,8 @@ var ALL_TOOL_SCOPES = Object.freeze({
9327
9501
  ...STOREFRONT_DEPLOY_TOOL_SCOPES,
9328
9502
  ...STOREFRONT_PLAN_TOOL_SCOPES,
9329
9503
  ...ATLAS_TOOL_SCOPES,
9330
- ...NETWORK_DEFAULT_TOOL_SCOPES
9504
+ ...NETWORK_DEFAULT_TOOL_SCOPES,
9505
+ ...AFFILIATE_TOOL_SCOPES
9331
9506
  });
9332
9507
  var TOOL_REGISTRY = new Set(Object.keys(ALL_TOOL_SCOPES));
9333
9508
  var UNREGISTERED_TOOLS = /* @__PURE__ */ new Set(["stripe_proxy_call"]);
@@ -9888,16 +10063,16 @@ Pre-resolved context: ${JSON.stringify(definedContext)}` : "";
9888
10063
  ...onUsage ? { onUsage } : {}
9889
10064
  }
9890
10065
  );
9891
- } catch (err8) {
9892
- if (err8 instanceof BedrockRateLimitError) {
9893
- const parsedSeconds = err8.retryAfter ? parseInt(err8.retryAfter, 10) : 60;
10066
+ } catch (err9) {
10067
+ if (err9 instanceof BedrockRateLimitError) {
10068
+ const parsedSeconds = err9.retryAfter ? parseInt(err9.retryAfter, 10) : 60;
9894
10069
  return {
9895
10070
  match: "rate_limited",
9896
10071
  retry_after_seconds: Number.isFinite(parsedSeconds) ? parsedSeconds : 60,
9897
10072
  suggestion: "Bedrock rate limit reached. Please retry after the indicated delay."
9898
10073
  };
9899
10074
  }
9900
- throw err8;
10075
+ throw err9;
9901
10076
  }
9902
10077
  if (!toolCall) {
9903
10078
  return {
@@ -10002,15 +10177,15 @@ function registerAllCarrierAskTools(server2, ctx) {
10002
10177
  title: "Natural Language Carrier Tool Router",
10003
10178
  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.",
10004
10179
  inputSchema: {
10005
- intent: z14.string().min(3).describe(
10180
+ intent: z15.string().min(3).describe(
10006
10181
  "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...')"
10007
10182
  ),
10008
- context: z14.object({
10009
- iccid: z14.string().optional().describe("Subscriber ICCID if already known"),
10010
- account_id: z14.number().optional().describe("Account ID if already known"),
10011
- reseller_id: z14.number().optional().describe("Reseller ID if already known")
10183
+ context: z15.object({
10184
+ iccid: z15.string().optional().describe("Subscriber ICCID if already known"),
10185
+ account_id: z15.number().optional().describe("Account ID if already known"),
10186
+ reseller_id: z15.number().optional().describe("Reseller ID if already known")
10012
10187
  }).optional().describe("Optional pre-resolved context to improve routing accuracy"),
10013
- confirm_token: z14.string().optional().describe(
10188
+ confirm_token: z15.string().optional().describe(
10014
10189
  "One-time token from a previous carrier_ask call with confirm_required=true. Providing this executes the previously staged destructive operation."
10015
10190
  )
10016
10191
  }
@@ -10111,8 +10286,8 @@ function registerAllCarrierAskTools(server2, ctx) {
10111
10286
  modelRan = true;
10112
10287
  }
10113
10288
  });
10114
- } catch (err8) {
10115
- if (err8 instanceof AskRouterNotConfiguredError) {
10289
+ } catch (err9) {
10290
+ if (err9 instanceof AskRouterNotConfiguredError) {
10116
10291
  writeCarrierAskAudit(ctx.env, {
10117
10292
  intent_hash: intentHash,
10118
10293
  match: "error",
@@ -10132,8 +10307,8 @@ function registerAllCarrierAskTools(server2, ctx) {
10132
10307
  type: "text",
10133
10308
  text: JSON.stringify({
10134
10309
  error: "router_not_configured",
10135
- missing: err8.missing,
10136
- message: err8.message
10310
+ missing: err9.missing,
10311
+ message: err9.message
10137
10312
  })
10138
10313
  }
10139
10314
  ],
@@ -10163,7 +10338,7 @@ function registerAllCarrierAskTools(server2, ctx) {
10163
10338
  type: "text",
10164
10339
  text: JSON.stringify({
10165
10340
  error: "routing_error",
10166
- message: err8 instanceof Error ? err8.message : "An unexpected error occurred during routing."
10341
+ message: err9 instanceof Error ? err9.message : "An unexpected error occurred during routing."
10167
10342
  })
10168
10343
  }
10169
10344
  ],
@@ -10214,7 +10389,7 @@ function registerAllCarrierAskTools(server2, ctx) {
10214
10389
  title: "Describe a Carrier Tool",
10215
10390
  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.",
10216
10391
  inputSchema: {
10217
- tool_name: z14.string().describe("The exact MCP tool name to describe (e.g. 'assign_package', 'hlr_set_bitrate')")
10392
+ tool_name: z15.string().describe("The exact MCP tool name to describe (e.g. 'assign_package', 'hlr_set_bitrate')")
10218
10393
  }
10219
10394
  },
10220
10395
  wrapHandler(
@@ -10307,7 +10482,7 @@ function buildExamples(toolName) {
10307
10482
  }
10308
10483
 
10309
10484
  // src/list-recent-ocs-events.ts
10310
- import { z as z15 } from "zod";
10485
+ import { z as z16 } from "zod";
10311
10486
 
10312
10487
  // src/ocs-scoping.ts
10313
10488
  function managedOwnAccountId(auth) {
@@ -10340,10 +10515,10 @@ async function assertManagedIccidAccess(baseUrl2, auth, iccid) {
10340
10515
  return { ok: false, error: "Subscriber not found" };
10341
10516
  }
10342
10517
  return { ok: true };
10343
- } catch (err8) {
10518
+ } catch (err9) {
10344
10519
  return {
10345
10520
  ok: false,
10346
- error: err8 instanceof Error ? err8.message : String(err8)
10521
+ error: err9 instanceof Error ? err9.message : String(err9)
10347
10522
  };
10348
10523
  }
10349
10524
  }
@@ -10475,10 +10650,10 @@ var ALLOWED_EVENT_TYPES = [
10475
10650
  "usage.anomaly"
10476
10651
  ];
10477
10652
  var listRecentOcsEventsSchema = {
10478
- iccid: z15.string().regex(/^\d{19,20}$/).describe("ICCID, 19 or 20 digits, ITU-T E.118 format"),
10479
- limit: z15.number().int().min(1).max(50).default(20).describe("Max events to return, newest first"),
10480
- event_types: z15.array(z15.enum(ALLOWED_EVENT_TYPES)).optional().describe("Filter to specific event types"),
10481
- since: z15.string().datetime().optional().describe("ISO-8601 timestamp; only events after this point")
10653
+ iccid: z16.string().regex(/^\d{19,20}$/).describe("ICCID, 19 or 20 digits, ITU-T E.118 format"),
10654
+ limit: z16.number().int().min(1).max(50).default(20).describe("Max events to return, newest first"),
10655
+ event_types: z16.array(z16.enum(ALLOWED_EVENT_TYPES)).optional().describe("Filter to specific event types"),
10656
+ since: z16.string().datetime().optional().describe("ISO-8601 timestamp; only events after this point")
10482
10657
  };
10483
10658
  async function listRecentOcsEvents2(iccid, limit, eventTypes, since, kv) {
10484
10659
  return listRecentOcsEvents(iccid, limit, eventTypes, since, kv);
@@ -10529,8 +10704,8 @@ function registerListRecentOcsEventsTool(server2, ctx) {
10529
10704
  return {
10530
10705
  content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
10531
10706
  };
10532
- } catch (err8) {
10533
- const message = err8 instanceof Error ? err8.message : String(err8);
10707
+ } catch (err9) {
10708
+ const message = err9 instanceof Error ? err9.message : String(err9);
10534
10709
  return {
10535
10710
  isError: true,
10536
10711
  content: [{ type: "text", text: `Error reading OCS event buffer: ${message}` }]
@@ -10541,7 +10716,7 @@ function registerListRecentOcsEventsTool(server2, ctx) {
10541
10716
  }
10542
10717
 
10543
10718
  // src/tools-rate-governor.ts
10544
- import { z as z16 } from "zod";
10719
+ import { z as z17 } from "zod";
10545
10720
  var MONITORED_ENDPOINTS = [
10546
10721
  "subscriberNetworkEventsOverPeriod",
10547
10722
  // 30/min
@@ -10573,7 +10748,7 @@ function registerRateLimitStatusTool(server2, ctx) {
10573
10748
  title: "API Rate-Limit Governor Status",
10574
10749
  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.",
10575
10750
  inputSchema: {
10576
- endpoint: z16.string().optional().describe(
10751
+ endpoint: z17.string().optional().describe(
10577
10752
  "OCS method name in camelCase (e.g. 'subscriberUsageOverPeriod'). Omit to return status for all monitored endpoints."
10578
10753
  )
10579
10754
  },
@@ -10629,11 +10804,11 @@ function registerRateLimitStatusTool(server2, ctx) {
10629
10804
  }
10630
10805
 
10631
10806
  // src/tools-depletion-events.ts
10632
- import { z as z17 } from "zod";
10633
- function ok5(payload) {
10807
+ import { z as z18 } from "zod";
10808
+ function ok6(payload) {
10634
10809
  return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
10635
10810
  }
10636
- function err6(message) {
10811
+ function err7(message) {
10637
10812
  return { isError: true, content: [{ type: "text", text: message }] };
10638
10813
  }
10639
10814
  async function ownershipToken(ctx) {
@@ -10657,10 +10832,10 @@ function registerDepletionEventsTool(server2, ctx) {
10657
10832
  title: "Subscriber Bundle Depletion Events",
10658
10833
  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.",
10659
10834
  inputSchema: {
10660
- subscriberId: z17.string().optional().describe(
10835
+ subscriberId: z18.string().optional().describe(
10661
10836
  "OCS subscriber ID to look up. Omit to list the 20 most recent fleet-wide depletions."
10662
10837
  ),
10663
- since: z17.string().optional().describe(
10838
+ since: z18.string().optional().describe(
10664
10839
  "ISO-8601 timestamp \u2014 only return events at or after this time (for single-subscriber lookup)."
10665
10840
  )
10666
10841
  },
@@ -10668,13 +10843,13 @@ function registerDepletionEventsTool(server2, ctx) {
10668
10843
  },
10669
10844
  async (args) => {
10670
10845
  if (!ctx.props.scope.includes("read")) {
10671
- return err6(
10846
+ return err7(
10672
10847
  `Scope denied: subscriber_depletion_events requires 'read' scope. Your token has: [${ctx.props.scope.join(", ")}].`
10673
10848
  );
10674
10849
  }
10675
10850
  const kv = ctx.env.RATE_LIMIT_KV;
10676
10851
  if (!kv) {
10677
- return err6(
10852
+ return err7(
10678
10853
  "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."
10679
10854
  );
10680
10855
  }
@@ -10683,14 +10858,14 @@ function registerDepletionEventsTool(server2, ctx) {
10683
10858
  try {
10684
10859
  scopeToken = await ownershipToken(ctx);
10685
10860
  } catch (e) {
10686
- return err6(
10861
+ return err7(
10687
10862
  `Could not resolve the OCS credential needed to scope this read: ${e instanceof Error ? e.message : String(e)}`
10688
10863
  );
10689
10864
  }
10690
10865
  if (subscriberId) {
10691
10866
  const raw = await kv.get(`bundle-depleted:${subscriberId}`, "text");
10692
10867
  if (!raw) {
10693
- return ok5({
10868
+ return ok6({
10694
10869
  subscriber_id: subscriberId,
10695
10870
  depleted: false,
10696
10871
  event: null,
@@ -10701,7 +10876,7 @@ function registerDepletionEventsTool(server2, ctx) {
10701
10876
  try {
10702
10877
  event = JSON.parse(raw);
10703
10878
  } catch {
10704
- return ok5({
10879
+ return ok6({
10705
10880
  subscriber_id: subscriberId,
10706
10881
  depleted: false,
10707
10882
  event: null,
@@ -10709,7 +10884,7 @@ function registerDepletionEventsTool(server2, ctx) {
10709
10884
  });
10710
10885
  }
10711
10886
  if (!await ownsEvent(ctx, scopeToken, event.iccid)) {
10712
- return ok5({
10887
+ return ok6({
10713
10888
  subscriber_id: subscriberId,
10714
10889
  depleted: false,
10715
10890
  event: null,
@@ -10717,14 +10892,14 @@ function registerDepletionEventsTool(server2, ctx) {
10717
10892
  });
10718
10893
  }
10719
10894
  if (since && event.depletedAt < since) {
10720
- return ok5({
10895
+ return ok6({
10721
10896
  subscriber_id: subscriberId,
10722
10897
  depleted: false,
10723
10898
  event: null,
10724
10899
  note: `No bundle depletion after ${since}.`
10725
10900
  });
10726
10901
  }
10727
- return ok5({
10902
+ return ok6({
10728
10903
  subscriber_id: subscriberId,
10729
10904
  depleted: true,
10730
10905
  event: {
@@ -10738,7 +10913,7 @@ function registerDepletionEventsTool(server2, ctx) {
10738
10913
  }
10739
10914
  const listResult = await kv.list({ prefix: "bundle-depleted:", limit: 20 });
10740
10915
  if (listResult.keys.length === 0) {
10741
- return ok5({
10916
+ return ok6({
10742
10917
  depletions: [],
10743
10918
  total: 0,
10744
10919
  note: "No bundle depletion events in the last 7 days."
@@ -10765,7 +10940,7 @@ function registerDepletionEventsTool(server2, ctx) {
10765
10940
  })
10766
10941
  );
10767
10942
  events.sort((a, b) => b.depleted_at.localeCompare(a.depleted_at));
10768
- return ok5({
10943
+ return ok6({
10769
10944
  depletions: events,
10770
10945
  total: events.length,
10771
10946
  list_truncated: !listResult.list_complete
@@ -10775,7 +10950,7 @@ function registerDepletionEventsTool(server2, ctx) {
10775
10950
  }
10776
10951
 
10777
10952
  // src/tools-country-history.ts
10778
- import { z as z18 } from "zod";
10953
+ import { z as z19 } from "zod";
10779
10954
  function isoDate(now, daysAgo = 0) {
10780
10955
  const d = new Date(now.getTime() - daysAgo * 864e5);
10781
10956
  return d.toISOString().slice(0, 10);
@@ -10798,10 +10973,10 @@ function registerCountryHistoryTool(server2, ctx) {
10798
10973
  title: "Subscriber Country History",
10799
10974
  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.",
10800
10975
  inputSchema: {
10801
- 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)"),
10802
- 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)"),
10803
- 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)"),
10804
- 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)")
10976
+ subscriberId: z19.string().regex(/^\d{19,20}$/, "subscriberId must be a 19\u201320 digit ICCID").describe("Subscriber ICCID (19\u201320 digits, ITU-T E.118)"),
10977
+ startDate: z19.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)"),
10978
+ endDate: z19.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)"),
10979
+ limit: z19.number().int().min(1).max(50).default(20).optional().describe("Maximum number of country_events to return, newest first (default 20, max 50)")
10805
10980
  },
10806
10981
  annotations: { readOnlyHint: true, idempotentHint: true }
10807
10982
  },
@@ -10832,8 +11007,8 @@ function registerCountryHistoryTool(server2, ctx) {
10832
11007
  networkEventsOverPeriodParams(subscriberId, start, end)
10833
11008
  );
10834
11009
  gy = gyCountryHistoryFromResponse(events);
10835
- } catch (err8) {
10836
- gyError = err8 instanceof Error ? err8.message : String(err8);
11010
+ } catch (err9) {
11011
+ gyError = err9 instanceof Error ? err9.message : String(err9);
10837
11012
  }
10838
11013
  let syntheticRingIccid = null;
10839
11014
  const msisdnRaw = msisdnFieldFromSubscriberPayload(subscriberPayload);
@@ -10909,7 +11084,7 @@ function registerCountryHistoryTool(server2, ctx) {
10909
11084
  }
10910
11085
 
10911
11086
  // src/apps/fleet-health-app.ts
10912
- import { z as z19 } from "zod";
11087
+ import { z as z20 } from "zod";
10913
11088
  import {
10914
11089
  registerAppTool,
10915
11090
  registerAppResource,
@@ -10960,8 +11135,8 @@ var FLEET_APP_RESOURCE_URI = "ui://fleet-health-dashboard";
10960
11135
  async function safeCall2(client2, method, params) {
10961
11136
  try {
10962
11137
  return { data: await client2.call(method, params), error: null };
10963
- } catch (err8) {
10964
- return { data: null, error: err8 instanceof Error ? err8.message : String(err8) };
11138
+ } catch (err9) {
11139
+ return { data: null, error: err9 instanceof Error ? err9.message : String(err9) };
10965
11140
  }
10966
11141
  }
10967
11142
  async function loadFleet(ctx, accountId) {
@@ -10993,7 +11168,7 @@ function registerFleetHealthApp(server2, ctx) {
10993
11168
  let html;
10994
11169
  try {
10995
11170
  html = renderHtml(fleetScreen(await loadFleet(ctx)));
10996
- } catch (err8) {
11171
+ } catch (err9) {
10997
11172
  html = renderHtml({
10998
11173
  id: "fleet-health",
10999
11174
  title: "Fleet health",
@@ -11001,7 +11176,7 @@ function registerFleetHealthApp(server2, ctx) {
11001
11176
  {
11002
11177
  kind: "note",
11003
11178
  tone: "critical",
11004
- text: `Could not load fleet data: ${err8 instanceof Error ? err8.message : String(err8)}`
11179
+ text: `Could not load fleet data: ${err9 instanceof Error ? err9.message : String(err9)}`
11005
11180
  }
11006
11181
  ]
11007
11182
  });
@@ -11025,7 +11200,7 @@ function registerFleetHealthApp(server2, ctx) {
11025
11200
  title: "Fleet Health Dashboard",
11026
11201
  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.",
11027
11202
  inputSchema: {
11028
- accountId: z19.number().optional().describe("Filter to a specific account (omit for all)")
11203
+ accountId: z20.number().optional().describe("Filter to a specific account (omit for all)")
11029
11204
  },
11030
11205
  annotations: { readOnlyHint: true },
11031
11206
  _meta: {
@@ -11068,7 +11243,7 @@ function registerFleetHealthApp(server2, ctx) {
11068
11243
  }
11069
11244
 
11070
11245
  // src/apps/provisioning-wizard.ts
11071
- import { z as z20 } from "zod";
11246
+ import { z as z21 } from "zod";
11072
11247
  import {
11073
11248
  registerAppTool as registerAppTool2,
11074
11249
  registerAppResource as registerAppResource2,
@@ -11121,10 +11296,10 @@ async function deleteWizardSession(env, sub, wizardId) {
11121
11296
  async function safeCallWithToken(client2, _token, method, params = {}) {
11122
11297
  try {
11123
11298
  return { data: await client2.call(method, params), error: null };
11124
- } catch (err8) {
11299
+ } catch (err9) {
11125
11300
  return {
11126
11301
  data: null,
11127
- error: err8 instanceof Error ? err8.message : String(err8)
11302
+ error: err9 instanceof Error ? err9.message : String(err9)
11128
11303
  };
11129
11304
  }
11130
11305
  }
@@ -11173,10 +11348,10 @@ function registerProvisioningWizard(server2, ctx) {
11173
11348
  title: "eSIM Provisioning Wizard",
11174
11349
  description: "Interactive 3-step wizard to provision an eSIM: select subscriber, choose package template, preview (dry_run) and confirm execution.",
11175
11350
  inputSchema: {
11176
- step: z20.enum(["init", "select-package", "preview", "confirm"]).describe("Current wizard step"),
11177
- wizardId: z20.string().optional().describe("Wizard session ID (absent on init)"),
11178
- subscriber_iccid: z20.string().optional().describe("Subscriber ICCID (required for select-package)"),
11179
- package_template_id: z20.number().optional().describe("Package template ID (required for preview)")
11351
+ step: z21.enum(["init", "select-package", "preview", "confirm"]).describe("Current wizard step"),
11352
+ wizardId: z21.string().optional().describe("Wizard session ID (absent on init)"),
11353
+ subscriber_iccid: z21.string().optional().describe("Subscriber ICCID (required for select-package)"),
11354
+ package_template_id: z21.number().optional().describe("Package template ID (required for preview)")
11180
11355
  },
11181
11356
  _meta: {
11182
11357
  ui: {
@@ -11446,7 +11621,7 @@ function registerProvisioningWizard(server2, ctx) {
11446
11621
  }
11447
11622
 
11448
11623
  // src/apps/balance-topup.ts
11449
- import { z as z21 } from "zod";
11624
+ import { z as z22 } from "zod";
11450
11625
  import {
11451
11626
  registerAppTool as registerAppTool3,
11452
11627
  registerAppResource as registerAppResource3,
@@ -11455,10 +11630,10 @@ import {
11455
11630
  async function safeCallWithToken2(client2, _token, method, params = {}) {
11456
11631
  try {
11457
11632
  return { data: await client2.call(method, params), error: null };
11458
- } catch (err8) {
11633
+ } catch (err9) {
11459
11634
  return {
11460
11635
  data: null,
11461
- error: err8 instanceof Error ? err8.message : String(err8)
11636
+ error: err9 instanceof Error ? err9.message : String(err9)
11462
11637
  };
11463
11638
  }
11464
11639
  }
@@ -11499,9 +11674,9 @@ function registerBalanceTopupApp(server2, ctx) {
11499
11674
  title: "Balance Top-up Form",
11500
11675
  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`.",
11501
11676
  inputSchema: {
11502
- iccid: z21.string().describe("The subscriber ICCID for the account lookup"),
11503
- delta: z21.number().describe("Amount to add (positive) or deduct (negative) from the account balance"),
11504
- preview: z21.boolean().default(true).describe("If true, return preview without writing. If false, execute the balance change.")
11677
+ iccid: z22.string().describe("The subscriber ICCID for the account lookup"),
11678
+ delta: z22.number().describe("Amount to add (positive) or deduct (negative) from the account balance"),
11679
+ preview: z22.boolean().default(true).describe("If true, return preview without writing. If false, execute the balance change.")
11505
11680
  },
11506
11681
  annotations: { destructiveHint: true },
11507
11682
  _meta: {
@@ -11584,7 +11759,7 @@ function registerAllApps(server2, ctx) {
11584
11759
  }
11585
11760
 
11586
11761
  // src/prompts.ts
11587
- import { z as z22 } from "zod";
11762
+ import { z as z23 } from "zod";
11588
11763
  function registerAllPrompts(server2) {
11589
11764
  server2.registerPrompt(
11590
11765
  "fleet_health_report",
@@ -11620,7 +11795,7 @@ Format as a structured report with sections, tables, and actionable recommendati
11620
11795
  {
11621
11796
  title: "Subscriber Deep Dive",
11622
11797
  description: "Comprehensive analysis of a single subscriber: profile, packages, usage patterns, location history, recommendations.",
11623
- argsSchema: { iccid: z22.string().describe("The subscriber ICCID to analyse") }
11798
+ argsSchema: { iccid: z23.string().describe("The subscriber ICCID to analyse") }
11624
11799
  },
11625
11800
  async ({ iccid }) => ({
11626
11801
  messages: [
@@ -11719,7 +11894,7 @@ Analyze and present:
11719
11894
  {
11720
11895
  title: "Bulk Operations Planner",
11721
11896
  description: "Plan bulk operations safely: mass package assignments, account migrations, balance adjustments, or status changes.",
11722
- argsSchema: { operation: z22.string().describe("Describe the bulk operation you want to perform") }
11897
+ argsSchema: { operation: z23.string().describe("Describe the bulk operation you want to perform") }
11723
11898
  },
11724
11899
  async ({ operation }) => ({
11725
11900
  messages: [
@@ -11752,7 +11927,7 @@ NEVER execute destructive operations without confirmation.`
11752
11927
  }
11753
11928
 
11754
11929
  // src/tools-pricing.ts
11755
- import { z as z23 } from "zod";
11930
+ import { z as z24 } from "zod";
11756
11931
  function registerAllPricingTools(server2, ctx) {
11757
11932
  const allTools = [...PRICING_TOOLS, ...PROJECTS_TOOLS];
11758
11933
  for (const tool of allTools) {
@@ -11834,8 +12009,8 @@ function buildPricingHandler(tool, ctx) {
11834
12009
  }
11835
12010
  ]
11836
12011
  };
11837
- } catch (err8) {
11838
- const message = err8 instanceof Error ? err8.message : String(err8);
12012
+ } catch (err9) {
12013
+ const message = err9 instanceof Error ? err9.message : String(err9);
11839
12014
  ctx.audit({
11840
12015
  tool_name: tool.name,
11841
12016
  ocs_method: "billing",
@@ -11859,19 +12034,19 @@ function buildZodSchema(schema) {
11859
12034
  for (const [key, prop] of Object.entries(properties)) {
11860
12035
  let field;
11861
12036
  if (prop.enum) {
11862
- field = z23.enum(prop.enum);
12037
+ field = z24.enum(prop.enum);
11863
12038
  } else if (prop.type === "number") {
11864
- field = z23.number();
12039
+ field = z24.number();
11865
12040
  } else if (prop.type === "boolean") {
11866
- field = z23.boolean();
12041
+ field = z24.boolean();
11867
12042
  } else if (prop.type === "array") {
11868
12043
  if (prop.items?.type === "number") {
11869
- field = z23.array(z23.number());
12044
+ field = z24.array(z24.number());
11870
12045
  } else {
11871
- field = z23.array(z23.string());
12046
+ field = z24.array(z24.string());
11872
12047
  }
11873
12048
  } else {
11874
- field = z23.string();
12049
+ field = z24.string();
11875
12050
  }
11876
12051
  const required = schema.required ?? [];
11877
12052
  if (!required.includes(key)) {
@@ -11886,7 +12061,7 @@ function buildZodSchema(schema) {
11886
12061
  }
11887
12062
 
11888
12063
  // src/tools-greenzone.ts
11889
- import { z as z24 } from "zod";
12064
+ import { z as z25 } from "zod";
11890
12065
 
11891
12066
  // src/greenzone-whitelist.ts
11892
12067
  var STATE_KEY = "greenzone:state";
@@ -12163,10 +12338,10 @@ function resolvePortalBaseUrl(env) {
12163
12338
  function getGreenzoneKv(env) {
12164
12339
  return env.GREENZONE_STATE_KV ?? null;
12165
12340
  }
12166
- function ok6(payload) {
12341
+ function ok7(payload) {
12167
12342
  return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
12168
12343
  }
12169
- function err7(message) {
12344
+ function err8(message) {
12170
12345
  return { isError: true, content: [{ type: "text", text: message }] };
12171
12346
  }
12172
12347
  var KAPTURE_UNAVAILABLE_MSG = "Execute each Kapture step using mcp__kapture__* tools in sequence. Ensure the Kapture extension is active in Chrome before invoking.";
@@ -12177,21 +12352,21 @@ function registerGreenzoneTools(server2, ctx) {
12177
12352
  title: "Greenzone Whitelist \u2014 Add",
12178
12353
  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.",
12179
12354
  inputSchema: {
12180
- host: z24.string().optional().describe("Hostname to whitelist (e.g. 'example.com')"),
12181
- ip: z24.string().optional().describe("IP address or CIDR to whitelist (e.g. '203.0.113.0/24')"),
12182
- dry_run: z24.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
12355
+ host: z25.string().optional().describe("Hostname to whitelist (e.g. 'example.com')"),
12356
+ ip: z25.string().optional().describe("IP address or CIDR to whitelist (e.g. '203.0.113.0/24')"),
12357
+ dry_run: z25.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
12183
12358
  },
12184
12359
  annotations: { destructiveHint: true }
12185
12360
  },
12186
12361
  async (args) => {
12187
12362
  if (!ctx.props.scope.includes("write")) {
12188
- return err7(
12363
+ return err8(
12189
12364
  `Scope denied: greenzone_whitelist_add requires 'write' scope. Your token has: [${ctx.props.scope.join(", ")}].`
12190
12365
  );
12191
12366
  }
12192
12367
  const { host, ip, dry_run } = args;
12193
12368
  if (!host && !ip) {
12194
- return err7("At least one of 'host' or 'ip' must be provided.");
12369
+ return err8("At least one of 'host' or 'ip' must be provided.");
12195
12370
  }
12196
12371
  const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
12197
12372
  const steps = [
@@ -12199,7 +12374,7 @@ function registerGreenzoneTools(server2, ctx) {
12199
12374
  ...ip ? buildAddIpSteps(portalBaseUrl, ip) : []
12200
12375
  ];
12201
12376
  if (dry_run) {
12202
- return ok6({
12377
+ return ok7({
12203
12378
  dry_run: true,
12204
12379
  message: "Would execute the following Kapture steps against the OCS portal:",
12205
12380
  kapture_steps: steps,
@@ -12222,7 +12397,7 @@ function registerGreenzoneTools(server2, ctx) {
12222
12397
  } else {
12223
12398
  kvWarning = "GREENZONE_STATE_KV not bound in stdio mode \u2014 state not persisted.";
12224
12399
  }
12225
- return ok6({
12400
+ return ok7({
12226
12401
  requires_kapture: true,
12227
12402
  message: "Execute the Kapture steps below to apply the change in the OCS portal. " + KAPTURE_UNAVAILABLE_MSG,
12228
12403
  kapture_steps: steps,
@@ -12238,21 +12413,21 @@ function registerGreenzoneTools(server2, ctx) {
12238
12413
  title: "Greenzone Whitelist \u2014 Remove",
12239
12414
  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.",
12240
12415
  inputSchema: {
12241
- host: z24.string().optional().describe("Hostname to remove from whitelist"),
12242
- ip: z24.string().optional().describe("IP address or CIDR to remove from whitelist"),
12243
- dry_run: z24.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
12416
+ host: z25.string().optional().describe("Hostname to remove from whitelist"),
12417
+ ip: z25.string().optional().describe("IP address or CIDR to remove from whitelist"),
12418
+ dry_run: z25.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
12244
12419
  },
12245
12420
  annotations: { destructiveHint: true }
12246
12421
  },
12247
12422
  async (args) => {
12248
12423
  if (!ctx.props.scope.includes("write")) {
12249
- return err7(
12424
+ return err8(
12250
12425
  `Scope denied: greenzone_whitelist_remove requires 'write' scope. Your token has: [${ctx.props.scope.join(", ")}].`
12251
12426
  );
12252
12427
  }
12253
12428
  const { host, ip, dry_run } = args;
12254
12429
  if (!host && !ip) {
12255
- return err7("At least one of 'host' or 'ip' must be provided.");
12430
+ return err8("At least one of 'host' or 'ip' must be provided.");
12256
12431
  }
12257
12432
  const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
12258
12433
  const steps = [
@@ -12260,7 +12435,7 @@ function registerGreenzoneTools(server2, ctx) {
12260
12435
  ...ip ? buildRemoveIpSteps(portalBaseUrl, ip) : []
12261
12436
  ];
12262
12437
  if (dry_run) {
12263
- return ok6({
12438
+ return ok7({
12264
12439
  dry_run: true,
12265
12440
  message: "Would execute the following Kapture steps to remove from OCS portal:",
12266
12441
  kapture_steps: steps,
@@ -12283,7 +12458,7 @@ function registerGreenzoneTools(server2, ctx) {
12283
12458
  } else {
12284
12459
  kvWarning = "GREENZONE_STATE_KV not bound in stdio mode \u2014 state not persisted.";
12285
12460
  }
12286
- return ok6({
12461
+ return ok7({
12287
12462
  requires_kapture: true,
12288
12463
  message: "Execute the Kapture steps below to apply the removal in the OCS portal. " + KAPTURE_UNAVAILABLE_MSG,
12289
12464
  kapture_steps: steps,
@@ -12299,7 +12474,7 @@ function registerGreenzoneTools(server2, ctx) {
12299
12474
  title: "Greenzone Whitelist \u2014 List",
12300
12475
  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.",
12301
12476
  inputSchema: {
12302
- from_portal: z24.boolean().optional().describe(
12477
+ from_portal: z25.boolean().optional().describe(
12303
12478
  "When true, return Kapture steps to read live portal state instead of KV cache"
12304
12479
  )
12305
12480
  },
@@ -12307,14 +12482,14 @@ function registerGreenzoneTools(server2, ctx) {
12307
12482
  },
12308
12483
  async (args) => {
12309
12484
  if (!ctx.props.scope.includes("read")) {
12310
- return err7(
12485
+ return err8(
12311
12486
  `Scope denied: greenzone_whitelist_list requires 'read' scope. Your token has: [${ctx.props.scope.join(", ")}].`
12312
12487
  );
12313
12488
  }
12314
12489
  const { from_portal } = args;
12315
12490
  if (from_portal) {
12316
12491
  const portalBaseUrl = resolvePortalBaseUrl(ctx.env);
12317
- return ok6({
12492
+ return ok7({
12318
12493
  requires_kapture: true,
12319
12494
  message: "Execute the Kapture steps below to read live Greenzone whitelist state from the OCS portal.",
12320
12495
  kapture_steps: buildListSteps(portalBaseUrl),
@@ -12323,7 +12498,7 @@ function registerGreenzoneTools(server2, ctx) {
12323
12498
  }
12324
12499
  const kv = getGreenzoneKv(ctx.env);
12325
12500
  if (!kv) {
12326
- return ok6({
12501
+ return ok7({
12327
12502
  source: "kv_cache",
12328
12503
  hosts: [],
12329
12504
  ips: [],
@@ -12335,7 +12510,7 @@ function registerGreenzoneTools(server2, ctx) {
12335
12510
  }
12336
12511
  try {
12337
12512
  const state = await readState(kv);
12338
- return ok6({
12513
+ return ok7({
12339
12514
  source: "kv_cache",
12340
12515
  hosts: state.hosts,
12341
12516
  ips: state.ips,
@@ -12344,14 +12519,14 @@ function registerGreenzoneTools(server2, ctx) {
12344
12519
  reconcile_drift: state.reconcile_drift ?? false
12345
12520
  });
12346
12521
  } catch (e) {
12347
- return err7(`Failed to read Greenzone state from KV: ${e instanceof Error ? e.message : String(e)}`);
12522
+ return err8(`Failed to read Greenzone state from KV: ${e instanceof Error ? e.message : String(e)}`);
12348
12523
  }
12349
12524
  }
12350
12525
  );
12351
12526
  }
12352
12527
 
12353
12528
  // src/tools-ui-agent-generic.ts
12354
- import { z as z25 } from "zod";
12529
+ import { z as z26 } from "zod";
12355
12530
  var COST_PER_STEP_USD = 0.014;
12356
12531
  var SESSION_OVERHEAD_USD = 5e-3;
12357
12532
  function estimateCostUsd2(steps) {
@@ -12393,13 +12568,13 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12393
12568
  title: "Dispatch Generic Browsing Agent",
12394
12569
  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.",
12395
12570
  inputSchema: {
12396
- prompt: z25.string().min(1).max(4e3).describe(
12571
+ prompt: z26.string().min(1).max(4e3).describe(
12397
12572
  "Natural-language task description for the browsing agent. Be specific: include target URLs, data to extract, or actions to perform."
12398
12573
  ),
12399
- max_steps: z25.number().int().min(1).max(30).optional().describe(
12574
+ max_steps: z26.number().int().min(1).max(30).optional().describe(
12400
12575
  "Advisory step budget (1\u201330, default 15). Prices the dry-run estimate; the hosted agent sets its own depth."
12401
12576
  ),
12402
- dry_run: z25.boolean().optional().describe(
12577
+ dry_run: z26.boolean().optional().describe(
12403
12578
  "Preview cost estimate without dispatching. Returns estimated_cost_usd only \u2014 no browser session is created."
12404
12579
  )
12405
12580
  }
@@ -12442,8 +12617,8 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12442
12617
  }
12443
12618
  try {
12444
12619
  runtime.budget.reserve();
12445
- } catch (err8) {
12446
- if (err8 instanceof SpendLimitError) {
12620
+ } catch (err9) {
12621
+ if (err9 instanceof SpendLimitError) {
12447
12622
  ctx.audit({
12448
12623
  tool_name: toolName,
12449
12624
  ocs_method: "[ui-agent:generic-ask]",
@@ -12462,13 +12637,13 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12462
12637
  type: "text",
12463
12638
  text: JSON.stringify({
12464
12639
  error: "run_limit_reached",
12465
- message: err8.message
12640
+ message: err9.message
12466
12641
  })
12467
12642
  }
12468
12643
  ]
12469
12644
  };
12470
12645
  }
12471
- throw err8;
12646
+ throw err9;
12472
12647
  }
12473
12648
  try {
12474
12649
  const result2 = await runUiAgent(
@@ -12506,7 +12681,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12506
12681
  }
12507
12682
  ]
12508
12683
  };
12509
- } catch (err8) {
12684
+ } catch (err9) {
12510
12685
  ctx.audit({
12511
12686
  tool_name: toolName,
12512
12687
  ocs_method: "[ui-agent:generic-ask]",
@@ -12522,7 +12697,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12522
12697
  type: "text",
12523
12698
  text: JSON.stringify({
12524
12699
  error: "ui_agent_failed",
12525
- message: err8 instanceof Error ? err8.message : String(err8)
12700
+ message: err9 instanceof Error ? err9.message : String(err9)
12526
12701
  })
12527
12702
  }
12528
12703
  ]
@@ -12536,7 +12711,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
12536
12711
  title: "Poll Browsing Agent Task Status",
12537
12712
  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.",
12538
12713
  inputSchema: {
12539
- task_id: z25.string().describe("Task ID returned by ui_agent_ask")
12714
+ task_id: z26.string().describe("Task ID returned by ui_agent_ask")
12540
12715
  }
12541
12716
  },
12542
12717
  async (args) => {
@@ -12679,6 +12854,7 @@ registerScheduleAndUsageTools(server, toolCtx, uiAgentRuntime);
12679
12854
  registerAllUiAgentTools(server, toolCtx, uiAgentRuntime, portalOptIn);
12680
12855
  registerUiAgentAskTools(server, toolCtx, uiAgentRuntime);
12681
12856
  registerStripeConnectTools(server, { env: stdioEnv, props });
12857
+ registerAffiliateTools(server, { env: stdioEnv, props });
12682
12858
  registerWalletTools(server, { env: stdioEnv, props });
12683
12859
  registerGreenzoneTools(server, toolCtx);
12684
12860
  registerUiAgentGenericTools(server, toolCtx, uiAgentRuntime);