@carrierllc/mcp 0.4.0 → 0.5.0

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
@@ -25,13 +25,14 @@ import {
25
25
  probeAll,
26
26
  provisionClerk,
27
27
  rankTargets,
28
+ recurringPackageParams,
28
29
  renderHtml,
29
30
  repairPlanFor,
30
31
  storefrontClerkUrls,
31
32
  subscriberIdParams,
32
33
  usageOverPeriodParams,
33
34
  verifyStorefront
34
- } from "./chunk-RBG4MKMW.js";
35
+ } from "./chunk-QZBALDKD.js";
35
36
  import "./chunk-SHKKVIIA.js";
36
37
 
37
38
  // src/index.ts
@@ -1217,21 +1218,10 @@ function registerAllTools(server2, ctx) {
1217
1218
  TOOL_SCOPES["assign_recurring_package"],
1218
1219
  ctx,
1219
1220
  async ({ iccid, packageTemplateId, activation_at_first_use, start_time_utc }, token2) => {
1220
- const cache = /* @__PURE__ */ new Map();
1221
- const sub = await resolveSubscriberByIccid(ctx.env, token2, iccid, cache);
1222
- const subscriberId = sub.id ?? sub.subscriberId;
1223
- if (subscriberId === void 0) {
1224
- return {
1225
- isError: true,
1226
- content: [{ type: "text", text: `Error: Could not resolve subscriberId for ICCID ${iccid}` }]
1227
- };
1228
- }
1229
- const params = {
1230
- subscriber: Number(subscriberId),
1231
- packageTemplateId
1232
- };
1233
- if (activation_at_first_use === true) params.activationAtFirstUse = true;
1234
- if (start_time_utc !== void 0) params.startTimeUTC = start_time_utc;
1221
+ const params = recurringPackageParams(iccid, packageTemplateId, {
1222
+ activationAtFirstUse: activation_at_first_use,
1223
+ startTimeUTC: start_time_utc
1224
+ });
1235
1225
  return ocsCall(ctx.env, token2, "affectRecurringPackageToSubscriber", params);
1236
1226
  }
1237
1227
  )
@@ -3544,7 +3534,10 @@ High suspension rate (${totalSuspended} suspended vs ${totalActive} active)`);
3544
3534
  (a) => Number(a.balance ?? 0) < 10
3545
3535
  );
3546
3536
  const packageOnlyZero = accounts.filter(
3547
- (a) => Boolean(a.packageOnly) && Number(a.balance ?? 0) <= 0
3537
+ (a) => a.packageOnly === true && Number(a.balance ?? 0) === 0
3538
+ );
3539
+ const criticalZero = accounts.filter(
3540
+ (a) => a.packageOnly === false && Number(a.balance ?? 0) <= 0
3548
3541
  );
3549
3542
  sections.push(`
3550
3543
  ## Account Summary`);
@@ -3560,23 +3553,28 @@ High suspension rate (${totalSuspended} suspended vs ${totalActive} active)`);
3560
3553
  sections.push(`| ${a.name ?? a.accountId ?? "?"} | ${Number(a.balance ?? 0).toFixed(2)} | ${a.packageOnly ? "yes" : "no"} |`);
3561
3554
  }
3562
3555
  }
3563
- const critical = packageOnlyZero.length;
3564
- const warning = lowBalance.length - critical;
3565
- const healthy = accounts.length - lowBalance.length;
3556
+ const informational = packageOnlyZero.length;
3557
+ const critical = criticalZero.length;
3558
+ const warning = lowBalance.length - informational - critical;
3559
+ const healthy = accounts.length - informational - critical - Math.max(warning, 0);
3566
3560
  sections.push(`
3567
3561
  ## Fleet Health Verdict`);
3568
3562
  sections.push(`- Healthy: ${healthy}`);
3569
- sections.push(`- Warning (low balance, not package-only-zero): ${Math.max(warning, 0)}`);
3570
- sections.push(`- Critical (package-only and 0 balance): ${critical}`);
3563
+ sections.push(`- Informational (package-only at 0 balance): ${informational}`);
3564
+ sections.push(`- Warning (low balance): ${Math.max(warning, 0)}`);
3565
+ sections.push(`- Critical (non-package-only at 0 balance): ${critical}`);
3571
3566
  if (accounts.length === 0) {
3572
3567
  sections.push(`
3573
3568
  No accounts found under this reseller.`);
3574
3569
  } else if (critical > 0) {
3575
3570
  sections.push(`
3576
- Action: top up package-only accounts at 0 balance to keep packages assignable.`);
3571
+ Action: top up non-package-only accounts at 0 balance to restore rated traffic.`);
3577
3572
  } else if (lowBalance.length === 0) {
3578
3573
  sections.push(`
3579
3574
  All accounts healthy.`);
3575
+ } else if (informational > 0 && warning <= 0) {
3576
+ sections.push(`
3577
+ Package-only accounts at 0 balance require no action.`);
3580
3578
  }
3581
3579
  } else {
3582
3580
  sections.push(`
@@ -8516,6 +8514,14 @@ function writeUsageThresholdAudit(env, severity, remainingCredits, month, taskCo
8516
8514
  }
8517
8515
 
8518
8516
  // src/tools-ui-agent-schedule.ts
8517
+ var UI_AGENT_SCHEDULE_TOOL_SCOPES = {
8518
+ ui_agent_schedule_create: "admin",
8519
+ ui_agent_schedule_list: "read",
8520
+ ui_agent_schedule_delete: "admin",
8521
+ ui_agent_schedule_pause: "admin",
8522
+ ui_agent_schedule_resume: "admin",
8523
+ ui_agent_usage: "read"
8524
+ };
8519
8525
  function noManusKeyError() {
8520
8526
  return {
8521
8527
  isError: true,
@@ -9223,6 +9229,18 @@ Report success or failure with a clear summary.`;
9223
9229
  }
9224
9230
  };
9225
9231
  }
9232
+ var UI_AGENT_TOOL_SCOPES = {
9233
+ ui_create_steering_list: "write",
9234
+ ui_build_steering_list: "write",
9235
+ ui_set_account_steering_list: "write",
9236
+ ui_create_account: "admin",
9237
+ ui_create_destination_list: "write",
9238
+ ui_edit_destination_list: "write",
9239
+ ui_delete_destination_list: "admin",
9240
+ ui_delete_package_template: "admin",
9241
+ ui_edit_location_zone: "write",
9242
+ ui_delete_location_zone: "admin"
9243
+ };
9226
9244
  function registerAllUiAgentTools(server2, ctx) {
9227
9245
  server2.registerTool(
9228
9246
  "ui_create_steering_list",
@@ -9568,6 +9586,10 @@ async function askReply(keys, taskId, reply) {
9568
9586
  }
9569
9587
 
9570
9588
  // src/tools-ui-agent-ask.ts
9589
+ var UI_AGENT_ASK_TOOL_SCOPES = {
9590
+ ui_agent_reply: "write",
9591
+ ui_agent_list_pending: "read"
9592
+ };
9571
9593
  function computeExpiresAt(askedAt) {
9572
9594
  const asked = new Date(askedAt).getTime();
9573
9595
  if (isNaN(asked)) return "";
@@ -9811,6 +9833,110 @@ function writeAudit(env, row) {
9811
9833
  });
9812
9834
  }
9813
9835
 
9836
+ // src/tool-annotations.ts
9837
+ function annotationsForScope(scope) {
9838
+ switch (scope) {
9839
+ case "read":
9840
+ return {
9841
+ readOnlyHint: true,
9842
+ destructiveHint: false,
9843
+ idempotentHint: true,
9844
+ openWorldHint: true
9845
+ };
9846
+ case "write":
9847
+ return {
9848
+ readOnlyHint: false,
9849
+ destructiveHint: false,
9850
+ idempotentHint: false,
9851
+ openWorldHint: true
9852
+ };
9853
+ case "admin":
9854
+ return {
9855
+ readOnlyHint: false,
9856
+ destructiveHint: true,
9857
+ idempotentHint: false,
9858
+ openWorldHint: true
9859
+ };
9860
+ }
9861
+ }
9862
+ var ANNOTATION_OVERRIDES = {
9863
+ // --- Destructive despite a non-admin scope -------------------------------
9864
+ /** Removes a package from a live SIM; the subscriber loses service on it. */
9865
+ delete_subscriber_package: { destructiveHint: true },
9866
+ /** Can suspend or terminate a line — service stops until reversed. */
9867
+ modify_subscriber_status: { destructiveHint: true },
9868
+ /** Invalidates the credentials in use; anything holding the old ones breaks. */
9869
+ rotate_credentials: { destructiveHint: true },
9870
+ /** Moves money back to a cardholder. Not reversible from Carrier's side. */
9871
+ stripe_connect_refund: { destructiveHint: true },
9872
+ /** Closes a Radar review against the payment; the decision sticks. */
9873
+ radar_review_decline: { destructiveHint: true },
9874
+ /** Blocks or allows a value fleet-wide; affects future charges, not just one. */
9875
+ radar_value_list_add: { destructiveHint: true },
9876
+ /** Re-parents a whole subscriber range to another account. */
9877
+ move_subscriber_range_to_account: { destructiveHint: true },
9878
+ /** Drops entries off a whitelist, so traffic that worked stops working. */
9879
+ greenzone_whitelist_remove: { destructiveHint: true },
9880
+ /**
9881
+ * Charges the saved card off-session with no browser step and no human
9882
+ * confirmation. Spending real money irreversibly is the thing an agent most
9883
+ * needs warning about, whatever the scope says.
9884
+ */
9885
+ wallet_auto_topup: { destructiveHint: true },
9886
+ // --- Idempotency corrections ---------------------------------------------
9887
+ /**
9888
+ * 'adapt' mode is additive, so calling twice charges twice. Explicitly not
9889
+ * idempotent even though 'set' mode alone would be.
9890
+ */
9891
+ modify_subscriber_balance: { idempotentHint: false },
9892
+ /** Same adapt/set split as the subscriber variant. */
9893
+ modify_account_balance: { idempotentHint: false },
9894
+ /** Starts a checkout session; each call creates a new one. */
9895
+ wallet_topup_checkout: { idempotentHint: false },
9896
+ // --- Read-only tools that are not idempotent -----------------------------
9897
+ /**
9898
+ * Approving a review is an admin state change, but it releases a payment
9899
+ * rather than destroying anything.
9900
+ */
9901
+ radar_review_approve: { destructiveHint: false },
9902
+ /**
9903
+ * Admin-scoped but a documented no-op: Stripe exposes no API for Radar rule
9904
+ * state, so this returns Dashboard instructions and changes nothing. Treated
9905
+ * as read-only so an agent does not gate it behind a confirmation it does
9906
+ * not need — and so it never reports the rule as toggled.
9907
+ */
9908
+ radar_rule_toggle: {
9909
+ readOnlyHint: true,
9910
+ destructiveHint: false,
9911
+ idempotentHint: true
9912
+ }
9913
+ };
9914
+ function annotationsFor(name, scope, overrides = {}) {
9915
+ return {
9916
+ ...annotationsForScope(scope),
9917
+ ...ANNOTATION_OVERRIDES[name] ?? {},
9918
+ ...overrides
9919
+ };
9920
+ }
9921
+ function installAnnotationDefaults(server2, scopes) {
9922
+ const target = server2;
9923
+ const original = target.registerTool.bind(target);
9924
+ let wrapped = false;
9925
+ target.registerTool = (name, config, ...rest) => {
9926
+ wrapped = true;
9927
+ const scope = scopes[name] ?? UNKNOWN_TOOL_SCOPE;
9928
+ const declared = config?.annotations ?? {};
9929
+ const merged = { ...annotationsFor(name, scope), ...declared };
9930
+ return original(name, { ...config, annotations: merged }, ...rest);
9931
+ };
9932
+ return {
9933
+ get wrapped() {
9934
+ return wrapped;
9935
+ }
9936
+ };
9937
+ }
9938
+ var UNKNOWN_TOOL_SCOPE = "admin";
9939
+
9814
9940
  // src/stripe-connect-tools.ts
9815
9941
  async function verifyConfirmToken(env, sub, toolName, token2) {
9816
9942
  const key = `confirm:${sub}:${toolName}`;
@@ -9853,6 +9979,18 @@ async function stripePost(stripeKey, path, params, connectedAccountId) {
9853
9979
  const data = await res.json();
9854
9980
  return { ok: res.ok, status: res.status, data };
9855
9981
  }
9982
+ var STRIPE_CONNECT_TOOL_SCOPES = {
9983
+ stripe_connect_status: "read",
9984
+ stripe_connect_payouts: "read",
9985
+ stripe_connect_balance: "read",
9986
+ stripe_connect_refund: "write",
9987
+ stripe_connect_dispute_list: "read",
9988
+ radar_review_list: "read",
9989
+ radar_review_approve: "admin",
9990
+ radar_review_decline: "admin",
9991
+ radar_value_list_add: "admin",
9992
+ radar_rule_toggle: "admin"
9993
+ };
9856
9994
  function registerStripeConnectTools(server2, ctx) {
9857
9995
  const { env, props: props2 } = ctx;
9858
9996
  const operatorId = props2.org_id ?? props2.sub;
@@ -9860,11 +9998,15 @@ function registerStripeConnectTools(server2, ctx) {
9860
9998
  async function getAccountId() {
9861
9999
  return env.CARRIER_USERS.get(kvKey);
9862
10000
  }
9863
- server2.tool(
10001
+ server2.registerTool(
9864
10002
  "stripe_connect_status",
9865
- "Read-only: returns the Stripe Connect account status, capabilities, and requirements for the authenticated operator.",
9866
10003
  {
9867
- operator_id: z17.string().optional().describe("Override operator_id (admin use). Defaults to caller's org/user.")
10004
+ title: "Stripe Connect Status",
10005
+ description: "Read-only: returns the Stripe Connect account status, capabilities, and requirements for the authenticated operator.",
10006
+ inputSchema: {
10007
+ operator_id: z17.string().optional().describe("Override operator_id (admin use). Defaults to caller's org/user.")
10008
+ },
10009
+ annotations: annotationsFor("stripe_connect_status", "read")
9868
10010
  },
9869
10011
  async ({ operator_id }) => {
9870
10012
  const opId = operator_id ?? operatorId;
@@ -9908,12 +10050,16 @@ function registerStripeConnectTools(server2, ctx) {
9908
10050
  }
9909
10051
  }
9910
10052
  );
9911
- server2.tool(
10053
+ server2.registerTool(
9912
10054
  "stripe_connect_payouts",
9913
- "List money Stripe has already sent from the operator's connected account to their bank, newest first. Read-only. Params: `limit` (1-50, default 10), `status` (optional Stripe payout status, e.g. 'paid', 'pending', 'failed'). Returns: { payouts: [{ id, amount, currency, status, arrival_date, automatic, created }], has_more } \u2014 amount in the currency's smallest unit. Do NOT use this for money still sitting at Stripe and not yet paid out \u2014 use `stripe_connect_balance`.",
9914
10055
  {
9915
- limit: z17.number().int().min(1).max(50).default(10).describe("Number of payouts to return."),
9916
- status: z17.enum(["pending", "paid", "failed", "canceled", "in_transit"]).optional().describe("Filter by payout status.")
10056
+ title: "List Stripe Payouts",
10057
+ description: "List money Stripe has already sent from the operator's connected account to their bank, newest first. Read-only. Params: `limit` (1-50, default 10), `status` (optional Stripe payout status, e.g. 'paid', 'pending', 'failed'). Returns: { payouts: [{ id, amount, currency, status, arrival_date, automatic, created }], has_more } \u2014 amount in the currency's smallest unit. Do NOT use this for money still sitting at Stripe and not yet paid out \u2014 use `stripe_connect_balance`.",
10058
+ inputSchema: {
10059
+ limit: z17.number().int().min(1).max(50).default(10).describe("Number of payouts to return."),
10060
+ status: z17.enum(["pending", "paid", "failed", "canceled", "in_transit"]).optional().describe("Filter by payout status.")
10061
+ },
10062
+ annotations: annotationsFor("stripe_connect_payouts", "read")
9917
10063
  },
9918
10064
  async ({ limit, status }) => {
9919
10065
  const start = Date.now();
@@ -9942,10 +10088,14 @@ function registerStripeConnectTools(server2, ctx) {
9942
10088
  }
9943
10089
  }
9944
10090
  );
9945
- server2.tool(
10091
+ server2.registerTool(
9946
10092
  "stripe_connect_balance",
9947
- "Read the money sitting in the operator's connected Stripe account: available and pending, split per currency. Params: none (the connected account is resolved from the session). Returns: { account_id, available: [{ amount, currency }], pending: [{ amount, currency }] } \u2014 amounts in the currency's smallest unit (cents for EUR/USD). Do NOT use this for the Carrier prepaid wallet \u2014 use `wallet_balance`. Do NOT use this for money already paid out to the bank \u2014 use `stripe_connect_payouts`.",
9948
- {},
10093
+ {
10094
+ title: "Stripe Connect Balance",
10095
+ description: "Read the money sitting in the operator's connected Stripe account: available and pending, split per currency. Params: none (the connected account is resolved from the session). Returns: { account_id, available: [{ amount, currency }], pending: [{ amount, currency }] } \u2014 amounts in the currency's smallest unit (cents for EUR/USD). Do NOT use this for the Carrier prepaid wallet \u2014 use `wallet_balance`. Do NOT use this for money already paid out to the bank \u2014 use `stripe_connect_payouts`.",
10096
+ inputSchema: {},
10097
+ annotations: annotationsFor("stripe_connect_balance", "read")
10098
+ },
9949
10099
  async () => {
9950
10100
  const start = Date.now();
9951
10101
  try {
@@ -9975,14 +10125,18 @@ function registerStripeConnectTools(server2, ctx) {
9975
10125
  }
9976
10126
  }
9977
10127
  );
9978
- server2.tool(
10128
+ server2.registerTool(
9979
10129
  "stripe_connect_refund",
9980
- "Refund money to a customer on one charge in the operator's connected Stripe account. This returns real funds and Stripe cannot reverse a refund once issued. Two-step by design: call without `confirm_token` to get a token and a summary of what would be refunded, then call again with that token to execute. The token expires in 5 minutes. Params: `charge_id` (Stripe charge id, `ch_...`), `amount_cents` (optional integer in the charge's smallest currency unit \u2014 omit for a full refund), `reason` ('duplicate' | 'fraudulent' | 'requested_by_customer', optional), `confirm_token`. Returns: the refund id and status. Requires `write` scope. Do NOT use this to stop a charge that has not settled yet \u2014 decline it with `radar_review_decline`. Do NOT use this to contest a chargeback \u2014 read the case with `stripe_connect_dispute_list` first, since refunding a disputed charge can cost the operator twice.",
9981
10130
  {
9982
- charge_id: z17.string().min(1).describe("Stripe charge ID (ch_...)."),
9983
- amount_cents: z17.number().int().min(1).optional().describe("Partial refund amount in cents. Omit for full refund."),
9984
- reason: z17.enum(["duplicate", "fraudulent", "requested_by_customer"]).optional(),
9985
- confirm_token: z17.string().optional().describe("Confirmation token from previous call. Required to execute.")
10131
+ title: "Refund a Charge (moves real money)",
10132
+ description: "Refund money to a customer on one charge in the operator's connected Stripe account. This returns real funds and Stripe cannot reverse a refund once issued. Two-step by design: call without `confirm_token` to get a token and a summary of what would be refunded, then call again with that token to execute. The token expires in 5 minutes. Params: `charge_id` (Stripe charge id, `ch_...`), `amount_cents` (optional integer in the charge's smallest currency unit \u2014 omit for a full refund), `reason` ('duplicate' | 'fraudulent' | 'requested_by_customer', optional), `confirm_token`. Returns: the refund id and status. Requires `write` scope. Do NOT use this to stop a charge that has not settled yet \u2014 decline it with `radar_review_decline`. Do NOT use this to contest a chargeback \u2014 read the case with `stripe_connect_dispute_list` first, since refunding a disputed charge can cost the operator twice.",
10133
+ inputSchema: {
10134
+ charge_id: z17.string().min(1).describe("Stripe charge ID (ch_...)."),
10135
+ amount_cents: z17.number().int().min(1).optional().describe("Partial refund amount in cents. Omit for full refund."),
10136
+ reason: z17.enum(["duplicate", "fraudulent", "requested_by_customer"]).optional(),
10137
+ confirm_token: z17.string().optional().describe("Confirmation token from previous call. Required to execute.")
10138
+ },
10139
+ annotations: annotationsFor("stripe_connect_refund", "write")
9986
10140
  },
9987
10141
  async ({ charge_id, amount_cents, reason, confirm_token }) => {
9988
10142
  const start = Date.now();
@@ -10025,12 +10179,16 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
10025
10179
  }
10026
10180
  }
10027
10181
  );
10028
- server2.tool(
10182
+ server2.registerTool(
10029
10183
  "stripe_connect_dispute_list",
10030
- "List chargebacks customers have raised with their bank against the operator's connected Stripe account. Read-only \u2014 responding to one is Dashboard work. Params: `limit` (1-50, default 10), `status` (optional Stripe dispute status, e.g. 'needs_response', 'under_review'). Returns: { disputes: [{ id, charge, amount, currency, status, reason, evidence_details.due_by }], has_more } \u2014 amount in the currency's smallest unit, `due_by` a Unix timestamp or null. Do NOT use this for charges Stripe is holding for fraud review before they settle \u2014 those are `radar_review_list`. Check this before refunding: refunding a disputed charge can cost the operator the amount twice.",
10031
10184
  {
10032
- limit: z17.number().int().min(1).max(50).default(10),
10033
- status: z17.string().optional().describe("Filter by dispute status (e.g. needs_response, under_review).")
10185
+ title: "List Chargeback Disputes",
10186
+ description: "List chargebacks customers have raised with their bank against the operator's connected Stripe account. Read-only \u2014 responding to one is Dashboard work. Params: `limit` (1-50, default 10), `status` (optional Stripe dispute status, e.g. 'needs_response', 'under_review'). Returns: { disputes: [{ id, charge, amount, currency, status, reason, evidence_details.due_by }], has_more } \u2014 amount in the currency's smallest unit, `due_by` a Unix timestamp or null. Do NOT use this for charges Stripe is holding for fraud review before they settle \u2014 those are `radar_review_list`. Check this before refunding: refunding a disputed charge can cost the operator the amount twice.",
10187
+ inputSchema: {
10188
+ limit: z17.number().int().min(1).max(50).default(10),
10189
+ status: z17.string().optional().describe("Filter by dispute status (e.g. needs_response, under_review).")
10190
+ },
10191
+ annotations: annotationsFor("stripe_connect_dispute_list", "read")
10034
10192
  },
10035
10193
  async ({ limit, status }) => {
10036
10194
  const start = Date.now();
@@ -10059,12 +10217,16 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
10059
10217
  }
10060
10218
  }
10061
10219
  );
10062
- server2.tool(
10220
+ server2.registerTool(
10063
10221
  "radar_review_list",
10064
- "List the Stripe Radar fraud reviews waiting on a manual approve/decline decision, newest first. Reads only \u2014 deciding a review is a separate call. Params: `open_only` (boolean, default true \u2014 false also returns already-decided reviews), `limit` (1-50, default 10). Returns: { reviews: [{ id (`prv_...`), charge, reason, opened_reason, closed_reason, created, closed }], has_more }. Call this first to get the `review_id` that `radar_review_approve` and `radar_review_decline` require. Do NOT use this for chargebacks the customer has already raised with their bank \u2014 use `stripe_connect_dispute_list`.",
10065
10222
  {
10066
- open_only: z17.boolean().default(true).describe("If true, only returns open (undecided) reviews."),
10067
- limit: z17.number().int().min(1).max(50).default(10)
10223
+ title: "List Radar Fraud Reviews",
10224
+ description: "List the Stripe Radar fraud reviews waiting on a manual approve/decline decision, newest first. Reads only \u2014 deciding a review is a separate call. Params: `open_only` (boolean, default true \u2014 false also returns already-decided reviews), `limit` (1-50, default 10). Returns: { reviews: [{ id (`prv_...`), charge, reason, opened_reason, closed_reason, created, closed }], has_more }. Call this first to get the `review_id` that `radar_review_approve` and `radar_review_decline` require. Do NOT use this for chargebacks the customer has already raised with their bank \u2014 use `stripe_connect_dispute_list`.",
10225
+ inputSchema: {
10226
+ open_only: z17.boolean().default(true).describe("If true, only returns open (undecided) reviews."),
10227
+ limit: z17.number().int().min(1).max(50).default(10)
10228
+ },
10229
+ annotations: annotationsFor("radar_review_list", "read")
10068
10230
  },
10069
10231
  async ({ open_only, limit }) => {
10070
10232
  const start = Date.now();
@@ -10091,12 +10253,16 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
10091
10253
  }
10092
10254
  }
10093
10255
  );
10094
- server2.tool(
10256
+ server2.registerTool(
10095
10257
  "radar_review_approve",
10096
- "Clear a Stripe Radar fraud review so the held charge is captured and the money is taken. Approving overrides Radar's fraud signal, so a genuinely fraudulent charge approved here usually returns later as a chargeback plus a fee. Two-step by design: call without `confirm_token` to get a token, then call again with it to execute. Params: `review_id` (`prv_...`, from `radar_review_list`), `confirm_token`. Requires `admin` scope. Do NOT call this to block a suspicious charge \u2014 that is `radar_review_decline`, the opposite outcome. Do NOT call this to return money on a charge that already went through \u2014 that is `stripe_connect_refund`.",
10097
10258
  {
10098
- review_id: z17.string().min(1).describe("Stripe Radar review ID (prv_...)."),
10099
- confirm_token: z17.string().optional()
10259
+ title: "Approve Radar Review (captures charge)",
10260
+ description: "Clear a Stripe Radar fraud review so the held charge is captured and the money is taken. Approving overrides Radar's fraud signal, so a genuinely fraudulent charge approved here usually returns later as a chargeback plus a fee. Two-step by design: call without `confirm_token` to get a token, then call again with it to execute. Params: `review_id` (`prv_...`, from `radar_review_list`), `confirm_token`. Requires `admin` scope. Do NOT call this to block a suspicious charge \u2014 that is `radar_review_decline`, the opposite outcome. Do NOT call this to return money on a charge that already went through \u2014 that is `stripe_connect_refund`.",
10261
+ inputSchema: {
10262
+ review_id: z17.string().min(1).describe("Stripe Radar review ID (prv_...)."),
10263
+ confirm_token: z17.string().optional()
10264
+ },
10265
+ annotations: annotationsFor("radar_review_approve", "admin")
10100
10266
  },
10101
10267
  async ({ review_id, confirm_token }) => {
10102
10268
  const toolName = "radar_review_approve";
@@ -10136,12 +10302,16 @@ Call again with confirm_token="${token2}" to execute. Expires in 5 minutes.`
10136
10302
  }
10137
10303
  }
10138
10304
  );
10139
- server2.tool(
10305
+ server2.registerTool(
10140
10306
  "radar_review_decline",
10141
- "Close a Stripe Radar fraud review as fraudulent, so the held charge is blocked and the customer is never billed. The customer sees a failed payment; if they were legitimate, they have to pay again. Two-step by design: call without `confirm_token` to get a token, then call again with it to execute. Params: `review_id` (`prv_...`, from `radar_review_list`), `confirm_token`. Requires `admin` scope. Do NOT call this to let a charge through \u2014 that is `radar_review_approve`, the opposite outcome. Do NOT call this on a charge that already succeeded; a review only exists while the charge is held, and returning settled money is `stripe_connect_refund`.",
10142
10307
  {
10143
- review_id: z17.string().min(1),
10144
- confirm_token: z17.string().optional()
10308
+ title: "Decline Radar Review (blocks charge)",
10309
+ description: "Close a Stripe Radar fraud review as fraudulent, so the held charge is blocked and the customer is never billed. The customer sees a failed payment; if they were legitimate, they have to pay again. Two-step by design: call without `confirm_token` to get a token, then call again with it to execute. Params: `review_id` (`prv_...`, from `radar_review_list`), `confirm_token`. Requires `admin` scope. Do NOT call this to let a charge through \u2014 that is `radar_review_approve`, the opposite outcome. Do NOT call this on a charge that already succeeded; a review only exists while the charge is held, and returning settled money is `stripe_connect_refund`.",
10310
+ inputSchema: {
10311
+ review_id: z17.string().min(1),
10312
+ confirm_token: z17.string().optional()
10313
+ },
10314
+ annotations: annotationsFor("radar_review_decline", "admin")
10145
10315
  },
10146
10316
  async ({ review_id, confirm_token }) => {
10147
10317
  const toolName = "radar_review_decline";
@@ -10181,13 +10351,17 @@ Expires in 5 minutes.`
10181
10351
  }
10182
10352
  }
10183
10353
  );
10184
- server2.tool(
10354
+ server2.registerTool(
10185
10355
  "radar_value_list_add",
10186
- "Add one value to an existing Stripe Radar value list, changing how every future charge is scored. Whether the value ends up blocked or allowed depends entirely on which list you name in `value_list_id` \u2014 this tool does not choose, and putting a value on the allow list when you meant the block list silently lets fraud through. Confirm the list's purpose before calling. Two-step by design: call without `confirm_token` to get a token, then call again with it to execute. Params: `value_list_id` (`rsl_...`, an existing list), `value` (one email, IP address, card fingerprint, or ISO country code \u2014 one item per call), `confirm_token`. Requires `admin` scope. Do NOT use this to decide a single charge that is already under review \u2014 use `radar_review_approve` or `radar_review_decline`. Do NOT use this to create a list or a rule: only `value` is added, and rules cannot be edited through this API at all (see `radar_rule_toggle`).",
10187
10356
  {
10188
- value_list_id: z17.string().min(1).describe("Stripe Radar value list ID (rsl_...)."),
10189
- value: z17.string().min(1).describe("The value to add (email, IP address, country code, etc.)."),
10190
- confirm_token: z17.string().optional()
10357
+ title: "Add Value to Radar List",
10358
+ description: "Add one value to an existing Stripe Radar value list, changing how every future charge is scored. Whether the value ends up blocked or allowed depends entirely on which list you name in `value_list_id` \u2014 this tool does not choose, and putting a value on the allow list when you meant the block list silently lets fraud through. Confirm the list's purpose before calling. Two-step by design: call without `confirm_token` to get a token, then call again with it to execute. Params: `value_list_id` (`rsl_...`, an existing list), `value` (one email, IP address, card fingerprint, or ISO country code \u2014 one item per call), `confirm_token`. Requires `admin` scope. Do NOT use this to decide a single charge that is already under review \u2014 use `radar_review_approve` or `radar_review_decline`. Do NOT use this to create a list or a rule: only `value` is added, and rules cannot be edited through this API at all (see `radar_rule_toggle`).",
10359
+ inputSchema: {
10360
+ value_list_id: z17.string().min(1).describe("Stripe Radar value list ID (rsl_...)."),
10361
+ value: z17.string().min(1).describe("The value to add (email, IP address, country code, etc.)."),
10362
+ confirm_token: z17.string().optional()
10363
+ },
10364
+ annotations: annotationsFor("radar_value_list_add", "admin")
10191
10365
  },
10192
10366
  async ({ value_list_id, value, confirm_token }) => {
10193
10367
  const toolName = "radar_value_list_add";
@@ -10227,12 +10401,16 @@ Expires in 5 minutes.`
10227
10401
  }
10228
10402
  }
10229
10403
  );
10230
- server2.tool(
10404
+ server2.registerTool(
10231
10405
  "radar_rule_toggle",
10232
- "Return the Stripe Dashboard steps for enabling or disabling a Radar rule. This tool changes nothing: Stripe has no public API for rule state, so the call always succeeds and the rule stays exactly as it was. Never report the rule as toggled. Params: `rule_id` (Stripe Radar rule id), `enabled` (true for the enable instructions, false for disable). Returns: the Dashboard URL and steps for a human to follow. Requires `admin` scope. Do NOT use this when a change must actually take effect from an agent \u2014 the only Radar state reachable through this API is value-list membership, via `radar_value_list_add`.",
10233
10406
  {
10234
- rule_id: z17.string().min(1).describe("Stripe Radar rule ID."),
10235
- enabled: z17.boolean().describe("true = enable, false = disable.")
10407
+ title: "Radar Rule Toggle (instructions only)",
10408
+ description: "Return the Stripe Dashboard steps for enabling or disabling a Radar rule. This tool changes nothing: Stripe has no public API for rule state, so the call always succeeds and the rule stays exactly as it was. Never report the rule as toggled. Params: `rule_id` (Stripe Radar rule id), `enabled` (true for the enable instructions, false for disable). Returns: the Dashboard URL and steps for a human to follow. Requires `admin` scope. Do NOT use this when a change must actually take effect from an agent \u2014 the only Radar state reachable through this API is value-list membership, via `radar_value_list_add`.",
10409
+ inputSchema: {
10410
+ rule_id: z17.string().min(1).describe("Stripe Radar rule ID."),
10411
+ enabled: z17.boolean().describe("true = enable, false = disable.")
10412
+ },
10413
+ annotations: annotationsFor("radar_rule_toggle", "admin")
10236
10414
  },
10237
10415
  async ({ rule_id, enabled }) => {
10238
10416
  return ok2(
@@ -10503,6 +10681,11 @@ async function runAutoTopup(env, args) {
10503
10681
  };
10504
10682
  }
10505
10683
  }
10684
+ var WALLET_TOOL_SCOPES = {
10685
+ wallet_balance: "read",
10686
+ wallet_topup_checkout: "write",
10687
+ wallet_auto_topup: "write"
10688
+ };
10506
10689
  function ok3(text) {
10507
10690
  return { content: [{ type: "text", text }] };
10508
10691
  }
@@ -10512,10 +10695,14 @@ function err3(text) {
10512
10695
  function registerWalletTools(server2, ctx) {
10513
10696
  const { env, props: props2 } = ctx;
10514
10697
  const defaultOrgId = props2.org_id ?? props2.sub;
10515
- server2.tool(
10698
+ server2.registerTool(
10516
10699
  "wallet_balance",
10517
- "Read the caller organisation's Carrier prepaid wallet: current balance and auto-top-up settings. The wallet is what Carrier bills your own eSIM spend against \u2014 it is not an OCS balance and not your MCP plan credits. Params: none (the organisation is taken from the session). Returns: { org_id, balance_eur_cents, auto_topup_enabled, auto_topup_threshold_cents, auto_topup_pack_cents } \u2014 all amounts in EUR cents, not euros. Do NOT use this for a reseller account balance in OCS \u2014 use `get_reseller_info` or `list_reseller_accounts`. Do NOT use this for a subscriber's OCS balance \u2014 use `get_subscriber`. Do NOT use this for MCP plan credits \u2014 use `credit_balance`. Do NOT use this for money held at Stripe \u2014 use `stripe_connect_balance`.",
10518
- {},
10700
+ {
10701
+ title: "Wallet Balance",
10702
+ description: "Read the caller organisation's Carrier prepaid wallet: current balance and auto-top-up settings. The wallet is what Carrier bills your own eSIM spend against \u2014 it is not an OCS balance and not your MCP plan credits. Params: none (the organisation is taken from the session). Returns: { org_id, balance_eur_cents, auto_topup_enabled, auto_topup_threshold_cents, auto_topup_pack_cents } \u2014 all amounts in EUR cents, not euros. Do NOT use this for a reseller account balance in OCS \u2014 use `get_reseller_info` or `list_reseller_accounts`. Do NOT use this for a subscriber's OCS balance \u2014 use `get_subscriber`. Do NOT use this for MCP plan credits \u2014 use `credit_balance`. Do NOT use this for money held at Stripe \u2014 use `stripe_connect_balance`.",
10703
+ inputSchema: {},
10704
+ annotations: annotationsFor("wallet_balance", "read")
10705
+ },
10519
10706
  async () => {
10520
10707
  const orgId = defaultOrgId;
10521
10708
  const start = Date.now();
@@ -10546,13 +10733,17 @@ function registerWalletTools(server2, ctx) {
10546
10733
  }
10547
10734
  }
10548
10735
  );
10549
- server2.tool(
10736
+ server2.registerTool(
10550
10737
  "wallet_topup_checkout",
10551
- "Create a Stripe Checkout link so a human can top up the Carrier prepaid wallet by card. This tool charges nothing itself: it returns a hosted URL, and the wallet is credited (pack amount + bonus) only after someone completes the payment in a browser. Params: `pack` (optional \u2014 pack id `pack_500`/`pack_1000`/`pack_2500`/`pack_5000`, or an exact EUR-cents amount matching a catalog pack; omit to list the packs and their bonuses without creating a session). Returns: { pack_id, pack_eur_cents, bonus_eur_cents, checkout_session_id, checkout_url } \u2014 amounts in EUR cents. Do NOT use this when the wallet must be funded now with no human in the loop \u2014 use `wallet_auto_topup`, which charges the saved card immediately. Do NOT use this to move money in OCS \u2014 `modify_account_balance` and `modify_subscriber_balance` are unrelated ledgers.",
10552
10738
  {
10553
- pack: z18.string().optional().describe(
10554
- "Pack id (pack_500/pack_1000/pack_2500/pack_5000) or exact EUR-cents amount. Omit to list packs."
10555
- )
10739
+ title: "Wallet Top-up Checkout",
10740
+ description: "Create a Stripe Checkout link so a human can top up the Carrier prepaid wallet by card. This tool charges nothing itself: it returns a hosted URL, and the wallet is credited (pack amount + bonus) only after someone completes the payment in a browser. Params: `pack` (optional \u2014 pack id `pack_500`/`pack_1000`/`pack_2500`/`pack_5000`, or an exact EUR-cents amount matching a catalog pack; omit to list the packs and their bonuses without creating a session). Returns: { pack_id, pack_eur_cents, bonus_eur_cents, checkout_session_id, checkout_url } \u2014 amounts in EUR cents. Do NOT use this when the wallet must be funded now with no human in the loop \u2014 use `wallet_auto_topup`, which charges the saved card immediately. Do NOT use this to move money in OCS \u2014 `modify_account_balance` and `modify_subscriber_balance` are unrelated ledgers.",
10741
+ inputSchema: {
10742
+ pack: z18.string().optional().describe(
10743
+ "Pack id (pack_500/pack_1000/pack_2500/pack_5000) or exact EUR-cents amount. Omit to list packs."
10744
+ )
10745
+ },
10746
+ annotations: annotationsFor("wallet_topup_checkout", "write")
10556
10747
  },
10557
10748
  async ({ pack }) => {
10558
10749
  const orgId = defaultOrgId;
@@ -10603,11 +10794,15 @@ function registerWalletTools(server2, ctx) {
10603
10794
  }
10604
10795
  }
10605
10796
  );
10606
- server2.tool(
10797
+ server2.registerTool(
10607
10798
  "wallet_auto_topup",
10608
- "Charge the organisation's saved card off-session, right now, and credit the Carrier prepaid wallet with the pack. This spends real money without any browser step or human confirmation, so only call it when the user has asked to be charged. Params: `pack_cents` (optional integer, EUR cents \u2014 must match a catalog pack; omit to use the organisation's configured auto-top-up pack). Returns on success: { org_id, charged: true, credited_eur_cents, balance_after_cents, payment_intent_id } \u2014 amounts in EUR cents. Returns { charged: false, reason } as an error when the card declines or no payment method is saved. Do NOT use this to let a human pay in a browser \u2014 use `wallet_topup_checkout`, which only returns a link. Do NOT use this to change the threshold that triggers automatic top-ups; this tool performs one charge and does not edit the configuration. Do NOT use this to credit an OCS reseller account or subscriber \u2014 see `modify_account_balance` and `modify_subscriber_balance`.",
10609
10799
  {
10610
- pack_cents: z18.number().int().positive().optional().describe("Override pack amount in EUR cents (must match a catalog pack).")
10800
+ title: "Wallet Auto Top-up (charges card now)",
10801
+ description: "Charge the organisation's saved card off-session, right now, and credit the Carrier prepaid wallet with the pack. This spends real money without any browser step or human confirmation, so only call it when the user has asked to be charged. Params: `pack_cents` (optional integer, EUR cents \u2014 must match a catalog pack; omit to use the organisation's configured auto-top-up pack). Returns on success: { org_id, charged: true, credited_eur_cents, balance_after_cents, payment_intent_id } \u2014 amounts in EUR cents. Returns { charged: false, reason } as an error when the card declines or no payment method is saved. Do NOT use this to let a human pay in a browser \u2014 use `wallet_topup_checkout`, which only returns a link. Do NOT use this to change the threshold that triggers automatic top-ups; this tool performs one charge and does not edit the configuration. Do NOT use this to credit an OCS reseller account or subscriber \u2014 see `modify_account_balance` and `modify_subscriber_balance`.",
10802
+ inputSchema: {
10803
+ pack_cents: z18.number().int().positive().optional().describe("Override pack amount in EUR cents (must match a catalog pack).")
10804
+ },
10805
+ annotations: annotationsFor("wallet_auto_topup", "write")
10611
10806
  },
10612
10807
  async ({ pack_cents }) => {
10613
10808
  const orgId = defaultOrgId;
@@ -11155,16 +11350,23 @@ function registerUiAgentGenericTools(server2, _ctx) {
11155
11350
 
11156
11351
  // src/tools-storefront-logo.ts
11157
11352
  import { z as z21 } from "zod";
11353
+ var STOREFRONT_LOGO_TOOL_SCOPES = {
11354
+ generate_storefront_logo: "read"
11355
+ };
11158
11356
  var ok5 = (text) => ({ content: [{ type: "text", text }] });
11159
11357
  var err5 = (text) => ({ isError: true, content: [{ type: "text", text }] });
11160
11358
  function registerStorefrontLogoTools(server2, env = {}) {
11161
- server2.tool(
11359
+ server2.registerTool(
11162
11360
  "generate_storefront_logo",
11163
- "Generate a storefront logo (SVG always; PNG when an image key is configured on the Carrier worker). Works for any authenticated Carrier MCP user \u2014 no local image-API key required. Write the SVG to public/brand/logo.svg in the storefront.",
11164
11361
  {
11165
- name: z21.string().min(1).describe("Brand / shop name, e.g. Bananas"),
11166
- accent: z21.string().regex(/^#?[0-9A-Fa-f]{6}$/).optional().describe("Accent hex, e.g. #FF8A00"),
11167
- tagline: z21.string().optional().describe("Optional tagline for AI-enhanced marks")
11362
+ title: "Generate Storefront Logo",
11363
+ description: "Generate a storefront logo (SVG always; PNG when an image key is configured on the Carrier worker). Works for any authenticated Carrier MCP user \u2014 no local image-API key required. Write the SVG to public/brand/logo.svg in the storefront.",
11364
+ inputSchema: {
11365
+ name: z21.string().min(1).describe("Brand / shop name, e.g. Bananas"),
11366
+ accent: z21.string().regex(/^#?[0-9A-Fa-f]{6}$/).optional().describe("Accent hex, e.g. #FF8A00"),
11367
+ tagline: z21.string().optional().describe("Optional tagline for AI-enhanced marks")
11368
+ },
11369
+ annotations: annotationsFor("generate_storefront_logo", "read")
11168
11370
  },
11169
11371
  async ({ name, accent, tagline }) => {
11170
11372
  try {
@@ -11195,16 +11397,25 @@ function registerStorefrontLogoTools(server2, env = {}) {
11195
11397
 
11196
11398
  // src/tools-storefront-deploy.ts
11197
11399
  import { z as z22 } from "zod";
11400
+ var STOREFRONT_DEPLOY_TOOL_SCOPES = {
11401
+ list_deploy_targets: "read",
11402
+ deploy_storefront: "write",
11403
+ provision_storefront_clerk: "write"
11404
+ };
11198
11405
  var ok6 = (value) => ({
11199
11406
  content: [{ type: "text", text: JSON.stringify(value) }]
11200
11407
  });
11201
11408
  var err6 = (text) => ({ isError: true, content: [{ type: "text", text }] });
11202
11409
  function registerStorefrontDeployTools(server2) {
11203
- server2.tool(
11410
+ server2.registerTool(
11204
11411
  "list_deploy_targets",
11205
- "Report which storefront deploy hosts (Cloudflare, Vercel, Netlify, Fly) are installed, logged in, and ready on this machine, and which one a deploy would pick.",
11206
11412
  {
11207
- dir: z22.string().min(1).describe("Path to the scaffolded storefront directory")
11413
+ title: "List Deploy Targets",
11414
+ description: "Report which storefront deploy hosts (Cloudflare, Vercel, Netlify, Fly) are installed, logged in, and ready on this machine, and which one a deploy would pick.",
11415
+ inputSchema: {
11416
+ dir: z22.string().min(1).describe("Path to the scaffolded storefront directory")
11417
+ },
11418
+ annotations: annotationsFor("list_deploy_targets", "read")
11208
11419
  },
11209
11420
  async ({ dir }) => {
11210
11421
  try {
@@ -11220,17 +11431,21 @@ function registerStorefrontDeployTools(server2) {
11220
11431
  }
11221
11432
  }
11222
11433
  );
11223
- server2.tool(
11434
+ server2.registerTool(
11224
11435
  "deploy_storefront",
11225
- "Build and deploy a scaffolded storefront to whichever host is available on this machine, then push its runtime secrets (CARRIER_API_KEY, CLERK_SECRET_KEY) to that host.",
11226
11436
  {
11227
- dir: z22.string().min(1).describe("Path to the scaffolded storefront directory"),
11228
- target: z22.enum(TARGET_IDS).optional().describe("Force a host instead of auto-detecting"),
11229
- name: z22.string().optional().describe("Project/worker name (defaults from brand)"),
11230
- skip_build: z22.boolean().optional().describe("Deploy the existing build without rebuilding"),
11231
- push_secrets: z22.boolean().optional().describe("Stage runtime secrets on the host before deploy (default true)"),
11232
- verify: z22.boolean().optional().describe("Probe the deployed URL and report whether it actually serves (default true)"),
11233
- domain: z22.boolean().optional().describe("Route the brand domain to the deployment (Cloudflare provisions DNS + cert)")
11437
+ title: "Deploy Storefront",
11438
+ description: "Build and deploy a scaffolded storefront to whichever host is available on this machine, then push its runtime secrets (CARRIER_API_KEY, CLERK_SECRET_KEY) to that host.",
11439
+ inputSchema: {
11440
+ dir: z22.string().min(1).describe("Path to the scaffolded storefront directory"),
11441
+ target: z22.enum(TARGET_IDS).optional().describe("Force a host instead of auto-detecting"),
11442
+ name: z22.string().optional().describe("Project/worker name (defaults from brand)"),
11443
+ skip_build: z22.boolean().optional().describe("Deploy the existing build without rebuilding"),
11444
+ push_secrets: z22.boolean().optional().describe("Stage runtime secrets on the host before deploy (default true)"),
11445
+ verify: z22.boolean().optional().describe("Probe the deployed URL and report whether it actually serves (default true)"),
11446
+ domain: z22.boolean().optional().describe("Route the brand domain to the deployment (Cloudflare provisions DNS + cert)")
11447
+ },
11448
+ annotations: annotationsFor("deploy_storefront", "write")
11234
11449
  },
11235
11450
  async ({ dir, target, name, skip_build, push_secrets, verify, domain }) => {
11236
11451
  try {
@@ -11270,15 +11485,19 @@ function registerStorefrontDeployTools(server2) {
11270
11485
  }
11271
11486
  }
11272
11487
  );
11273
- server2.tool(
11488
+ server2.registerTool(
11274
11489
  "provision_storefront_clerk",
11275
- "Create a Clerk application (Platform API) or reuse Clerk keys already on this machine, write them into the storefront .env.local, and configure allowed origins and redirect URLs on the instance.",
11276
11490
  {
11277
- dir: z22.string().min(1).describe("Path to the scaffolded storefront directory"),
11278
- name: z22.string().optional().describe("Clerk application name (defaults from brand)"),
11279
- production: z22.boolean().optional().describe("Also create a production instance (Platform API only)"),
11280
- no_create: z22.boolean().optional().describe("Never create a new application; only reuse existing keys"),
11281
- url: z22.string().optional().describe("Deployed URL to whitelist as origin and redirect")
11491
+ title: "Provision Storefront Clerk App",
11492
+ description: "Create a Clerk application (Platform API) or reuse Clerk keys already on this machine, write them into the storefront .env.local, and configure allowed origins and redirect URLs on the instance.",
11493
+ inputSchema: {
11494
+ dir: z22.string().min(1).describe("Path to the scaffolded storefront directory"),
11495
+ name: z22.string().optional().describe("Clerk application name (defaults from brand)"),
11496
+ production: z22.boolean().optional().describe("Also create a production instance (Platform API only)"),
11497
+ no_create: z22.boolean().optional().describe("Never create a new application; only reuse existing keys"),
11498
+ url: z22.string().optional().describe("Deployed URL to whitelist as origin and redirect")
11499
+ },
11500
+ annotations: annotationsFor("provision_storefront_clerk", "write")
11282
11501
  },
11283
11502
  async ({ dir, name, production, no_create, url }) => {
11284
11503
  try {
@@ -11325,6 +11544,76 @@ function registerStorefrontDeployTools(server2) {
11325
11544
  );
11326
11545
  }
11327
11546
 
11547
+ // src/tool-registry.ts
11548
+ var INTELLIGENCE_TOOL_SCOPES = {
11549
+ diagnose_subscriber: "read",
11550
+ fleet_health: "read",
11551
+ detect_usage_anomalies: "read",
11552
+ optimize_package: "read",
11553
+ churn_risk: "read",
11554
+ audit_network_coverage: "read",
11555
+ marketing_intelligence: "read",
11556
+ high_cost_subscribers: "read"
11557
+ };
11558
+ var UI_AGENT_GENERIC_TOOL_SCOPES = {
11559
+ ui_agent_ask: "write",
11560
+ ui_agent_status: "read"
11561
+ };
11562
+ var GREENZONE_TOOL_SCOPES = {
11563
+ greenzone_whitelist_add: "write",
11564
+ greenzone_whitelist_remove: "write",
11565
+ greenzone_whitelist_list: "read"
11566
+ };
11567
+ var OPS_TOOL_SCOPES = {
11568
+ list_recent_ocs_events: "read",
11569
+ rate_limit_status: "read",
11570
+ subscriber_country_history: "read",
11571
+ subscriber_depletion_events: "read"
11572
+ };
11573
+ var APP_TOOL_SCOPES = {
11574
+ fleet_health_app: "read",
11575
+ provision_esim_wizard: "write",
11576
+ balance_topup_form: "admin"
11577
+ };
11578
+ var CARRIER_ASK_TOOL_SCOPES = {
11579
+ carrier_ask: "read",
11580
+ carrier_ask_describe: "read"
11581
+ };
11582
+ var STRIPE_PROXY_TOOL_SCOPES = {
11583
+ stripe_proxy_call: "write"
11584
+ };
11585
+ function scopesFromToolDefs(tools) {
11586
+ return Object.fromEntries(tools.map((t) => [t.name, t.scope]));
11587
+ }
11588
+ var PRICING_TOOL_SCOPES = scopesFromToolDefs(PRICING_TOOLS);
11589
+ var PROJECTS_TOOL_SCOPES = scopesFromToolDefs(PROJECTS_TOOLS);
11590
+ var ALL_TOOL_SCOPES = Object.freeze({
11591
+ ...TOOL_SCOPES,
11592
+ ...BACKLOG_TOOL_SCOPES,
11593
+ ...INTELLIGENCE_TOOL_SCOPES,
11594
+ ...OPS_TOOL_SCOPES,
11595
+ ...PRICING_TOOL_SCOPES,
11596
+ ...PROJECTS_TOOL_SCOPES,
11597
+ ...APP_TOOL_SCOPES,
11598
+ ...UI_AGENT_TOOL_SCOPES,
11599
+ ...UI_AGENT_ASK_TOOL_SCOPES,
11600
+ ...UI_AGENT_SCHEDULE_TOOL_SCOPES,
11601
+ ...UI_AGENT_GENERIC_TOOL_SCOPES,
11602
+ ...WALLET_TOOL_SCOPES,
11603
+ ...STRIPE_CONNECT_TOOL_SCOPES,
11604
+ ...GREENZONE_TOOL_SCOPES,
11605
+ ...CARRIER_ASK_TOOL_SCOPES,
11606
+ ...STRIPE_PROXY_TOOL_SCOPES,
11607
+ ...STOREFRONT_LOGO_TOOL_SCOPES,
11608
+ // Storefront deploy tools exist only in this package, not in the Worker.
11609
+ ...STOREFRONT_DEPLOY_TOOL_SCOPES
11610
+ });
11611
+ var TOOL_REGISTRY2 = new Set(Object.keys(ALL_TOOL_SCOPES));
11612
+ var UNREGISTERED_TOOLS = /* @__PURE__ */ new Set(["stripe_proxy_call"]);
11613
+ var REGISTERED_TOOL_COUNT = Object.keys(ALL_TOOL_SCOPES).filter(
11614
+ (name) => !UNREGISTERED_TOOLS.has(name)
11615
+ ).length;
11616
+
11328
11617
  // src/index.ts
11329
11618
  var baseUrl = process.env.CARRIER_OCS_BASE_URL ?? process.env.ESIMVAULT_BASE_URL ?? "https://ocs.esimvault.cloud";
11330
11619
  var token = process.env.CARRIER_OCS_API_TOKEN ?? process.env.ESIMVAULT_API_TOKEN;
@@ -11375,6 +11664,7 @@ var server = new McpServer(
11375
11664
  instructions: "Carrier MCP \u2014 single-user stdio mode. Full tool registry (mirrors the deployed Worker)."
11376
11665
  }
11377
11666
  );
11667
+ installAnnotationDefaults(server, ALL_TOOL_SCOPES);
11378
11668
  registerAllTools(server, toolCtx);
11379
11669
  registerIntelligenceTools(server, toolCtx);
11380
11670
  registerAllBacklogTools(server, toolCtx);