@carrierllc/mcp 0.4.1 → 0.6.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
@@ -3,6 +3,8 @@ import {
3
3
  CARRIER_VERSION,
4
4
  TARGET_IDS,
5
5
  acquireEndpointSlot,
6
+ applySubscriberFilters,
7
+ buildListSubscriberParams,
6
8
  buildSite,
7
9
  clerkCliDeps,
8
10
  configureClerkInstance,
@@ -32,7 +34,7 @@ import {
32
34
  subscriberIdParams,
33
35
  usageOverPeriodParams,
34
36
  verifyStorefront
35
- } from "./chunk-6OI56RSR.js";
37
+ } from "./chunk-Y4JEN7M4.js";
36
38
  import "./chunk-SHKKVIIA.js";
37
39
 
38
40
  // src/index.ts
@@ -803,18 +805,10 @@ function registerAllTools(server2, ctx) {
803
805
  TOOL_SCOPES["list_subscribers"],
804
806
  ctx,
805
807
  async (args, token2) => {
806
- const params = {};
807
- if (args.imsi) params.imsi = args.imsi;
808
- if (args.iccid) params.iccid = args.iccid;
809
- if (args.activationCode) params.activationCode = args.activationCode;
810
- if (args.accountId !== void 0) params.accountId = args.accountId;
811
- if (args.msisdn) params.msisdn = args.msisdn;
812
- if (args.status) params.status = args.status;
813
- if (args.offset !== void 0) params.offset = args.offset;
808
+ const params = buildListSubscriberParams(args);
814
809
  const client = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
815
810
  const raw = await client.call("listSubscriber", params);
816
- const limit = args.limit;
817
- const payload = Array.isArray(raw) && typeof limit === "number" && limit >= 0 ? raw.slice(0, limit) : raw;
811
+ const payload = applySubscriberFilters(raw, args);
818
812
  return {
819
813
  content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
820
814
  };
@@ -8514,6 +8508,14 @@ function writeUsageThresholdAudit(env, severity, remainingCredits, month, taskCo
8514
8508
  }
8515
8509
 
8516
8510
  // src/tools-ui-agent-schedule.ts
8511
+ var UI_AGENT_SCHEDULE_TOOL_SCOPES = {
8512
+ ui_agent_schedule_create: "admin",
8513
+ ui_agent_schedule_list: "read",
8514
+ ui_agent_schedule_delete: "admin",
8515
+ ui_agent_schedule_pause: "admin",
8516
+ ui_agent_schedule_resume: "admin",
8517
+ ui_agent_usage: "read"
8518
+ };
8517
8519
  function noManusKeyError() {
8518
8520
  return {
8519
8521
  isError: true,
@@ -9221,6 +9223,18 @@ Report success or failure with a clear summary.`;
9221
9223
  }
9222
9224
  };
9223
9225
  }
9226
+ var UI_AGENT_TOOL_SCOPES = {
9227
+ ui_create_steering_list: "write",
9228
+ ui_build_steering_list: "write",
9229
+ ui_set_account_steering_list: "write",
9230
+ ui_create_account: "admin",
9231
+ ui_create_destination_list: "write",
9232
+ ui_edit_destination_list: "write",
9233
+ ui_delete_destination_list: "admin",
9234
+ ui_delete_package_template: "admin",
9235
+ ui_edit_location_zone: "write",
9236
+ ui_delete_location_zone: "admin"
9237
+ };
9224
9238
  function registerAllUiAgentTools(server2, ctx) {
9225
9239
  server2.registerTool(
9226
9240
  "ui_create_steering_list",
@@ -9566,6 +9580,10 @@ async function askReply(keys, taskId, reply) {
9566
9580
  }
9567
9581
 
9568
9582
  // src/tools-ui-agent-ask.ts
9583
+ var UI_AGENT_ASK_TOOL_SCOPES = {
9584
+ ui_agent_reply: "write",
9585
+ ui_agent_list_pending: "read"
9586
+ };
9569
9587
  function computeExpiresAt(askedAt) {
9570
9588
  const asked = new Date(askedAt).getTime();
9571
9589
  if (isNaN(asked)) return "";
@@ -9809,6 +9827,110 @@ function writeAudit(env, row) {
9809
9827
  });
9810
9828
  }
9811
9829
 
9830
+ // src/tool-annotations.ts
9831
+ function annotationsForScope(scope) {
9832
+ switch (scope) {
9833
+ case "read":
9834
+ return {
9835
+ readOnlyHint: true,
9836
+ destructiveHint: false,
9837
+ idempotentHint: true,
9838
+ openWorldHint: true
9839
+ };
9840
+ case "write":
9841
+ return {
9842
+ readOnlyHint: false,
9843
+ destructiveHint: false,
9844
+ idempotentHint: false,
9845
+ openWorldHint: true
9846
+ };
9847
+ case "admin":
9848
+ return {
9849
+ readOnlyHint: false,
9850
+ destructiveHint: true,
9851
+ idempotentHint: false,
9852
+ openWorldHint: true
9853
+ };
9854
+ }
9855
+ }
9856
+ var ANNOTATION_OVERRIDES = {
9857
+ // --- Destructive despite a non-admin scope -------------------------------
9858
+ /** Removes a package from a live SIM; the subscriber loses service on it. */
9859
+ delete_subscriber_package: { destructiveHint: true },
9860
+ /** Can suspend or terminate a line — service stops until reversed. */
9861
+ modify_subscriber_status: { destructiveHint: true },
9862
+ /** Invalidates the credentials in use; anything holding the old ones breaks. */
9863
+ rotate_credentials: { destructiveHint: true },
9864
+ /** Moves money back to a cardholder. Not reversible from Carrier's side. */
9865
+ stripe_connect_refund: { destructiveHint: true },
9866
+ /** Closes a Radar review against the payment; the decision sticks. */
9867
+ radar_review_decline: { destructiveHint: true },
9868
+ /** Blocks or allows a value fleet-wide; affects future charges, not just one. */
9869
+ radar_value_list_add: { destructiveHint: true },
9870
+ /** Re-parents a whole subscriber range to another account. */
9871
+ move_subscriber_range_to_account: { destructiveHint: true },
9872
+ /** Drops entries off a whitelist, so traffic that worked stops working. */
9873
+ greenzone_whitelist_remove: { destructiveHint: true },
9874
+ /**
9875
+ * Charges the saved card off-session with no browser step and no human
9876
+ * confirmation. Spending real money irreversibly is the thing an agent most
9877
+ * needs warning about, whatever the scope says.
9878
+ */
9879
+ wallet_auto_topup: { destructiveHint: true },
9880
+ // --- Idempotency corrections ---------------------------------------------
9881
+ /**
9882
+ * 'adapt' mode is additive, so calling twice charges twice. Explicitly not
9883
+ * idempotent even though 'set' mode alone would be.
9884
+ */
9885
+ modify_subscriber_balance: { idempotentHint: false },
9886
+ /** Same adapt/set split as the subscriber variant. */
9887
+ modify_account_balance: { idempotentHint: false },
9888
+ /** Starts a checkout session; each call creates a new one. */
9889
+ wallet_topup_checkout: { idempotentHint: false },
9890
+ // --- Read-only tools that are not idempotent -----------------------------
9891
+ /**
9892
+ * Approving a review is an admin state change, but it releases a payment
9893
+ * rather than destroying anything.
9894
+ */
9895
+ radar_review_approve: { destructiveHint: false },
9896
+ /**
9897
+ * Admin-scoped but a documented no-op: Stripe exposes no API for Radar rule
9898
+ * state, so this returns Dashboard instructions and changes nothing. Treated
9899
+ * as read-only so an agent does not gate it behind a confirmation it does
9900
+ * not need — and so it never reports the rule as toggled.
9901
+ */
9902
+ radar_rule_toggle: {
9903
+ readOnlyHint: true,
9904
+ destructiveHint: false,
9905
+ idempotentHint: true
9906
+ }
9907
+ };
9908
+ function annotationsFor(name, scope, overrides = {}) {
9909
+ return {
9910
+ ...annotationsForScope(scope),
9911
+ ...ANNOTATION_OVERRIDES[name] ?? {},
9912
+ ...overrides
9913
+ };
9914
+ }
9915
+ function installAnnotationDefaults(server2, scopes) {
9916
+ const target = server2;
9917
+ const original = target.registerTool.bind(target);
9918
+ let wrapped = false;
9919
+ target.registerTool = (name, config, ...rest) => {
9920
+ wrapped = true;
9921
+ const scope = scopes[name] ?? UNKNOWN_TOOL_SCOPE;
9922
+ const declared = config?.annotations ?? {};
9923
+ const merged = { ...annotationsFor(name, scope), ...declared };
9924
+ return original(name, { ...config, annotations: merged }, ...rest);
9925
+ };
9926
+ return {
9927
+ get wrapped() {
9928
+ return wrapped;
9929
+ }
9930
+ };
9931
+ }
9932
+ var UNKNOWN_TOOL_SCOPE = "admin";
9933
+
9812
9934
  // src/stripe-connect-tools.ts
9813
9935
  async function verifyConfirmToken(env, sub, toolName, token2) {
9814
9936
  const key = `confirm:${sub}:${toolName}`;
@@ -9851,6 +9973,18 @@ async function stripePost(stripeKey, path, params, connectedAccountId) {
9851
9973
  const data = await res.json();
9852
9974
  return { ok: res.ok, status: res.status, data };
9853
9975
  }
9976
+ var STRIPE_CONNECT_TOOL_SCOPES = {
9977
+ stripe_connect_status: "read",
9978
+ stripe_connect_payouts: "read",
9979
+ stripe_connect_balance: "read",
9980
+ stripe_connect_refund: "write",
9981
+ stripe_connect_dispute_list: "read",
9982
+ radar_review_list: "read",
9983
+ radar_review_approve: "admin",
9984
+ radar_review_decline: "admin",
9985
+ radar_value_list_add: "admin",
9986
+ radar_rule_toggle: "admin"
9987
+ };
9854
9988
  function registerStripeConnectTools(server2, ctx) {
9855
9989
  const { env, props: props2 } = ctx;
9856
9990
  const operatorId = props2.org_id ?? props2.sub;
@@ -9858,11 +9992,15 @@ function registerStripeConnectTools(server2, ctx) {
9858
9992
  async function getAccountId() {
9859
9993
  return env.CARRIER_USERS.get(kvKey);
9860
9994
  }
9861
- server2.tool(
9995
+ server2.registerTool(
9862
9996
  "stripe_connect_status",
9863
- "Read-only: returns the Stripe Connect account status, capabilities, and requirements for the authenticated operator.",
9864
9997
  {
9865
- operator_id: z17.string().optional().describe("Override operator_id (admin use). Defaults to caller's org/user.")
9998
+ title: "Stripe Connect Status",
9999
+ description: "Read-only: returns the Stripe Connect account status, capabilities, and requirements for the authenticated operator.",
10000
+ inputSchema: {
10001
+ operator_id: z17.string().optional().describe("Override operator_id (admin use). Defaults to caller's org/user.")
10002
+ },
10003
+ annotations: annotationsFor("stripe_connect_status", "read")
9866
10004
  },
9867
10005
  async ({ operator_id }) => {
9868
10006
  const opId = operator_id ?? operatorId;
@@ -9906,12 +10044,16 @@ function registerStripeConnectTools(server2, ctx) {
9906
10044
  }
9907
10045
  }
9908
10046
  );
9909
- server2.tool(
10047
+ server2.registerTool(
9910
10048
  "stripe_connect_payouts",
9911
- "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`.",
9912
10049
  {
9913
- limit: z17.number().int().min(1).max(50).default(10).describe("Number of payouts to return."),
9914
- status: z17.enum(["pending", "paid", "failed", "canceled", "in_transit"]).optional().describe("Filter by payout status.")
10050
+ title: "List Stripe Payouts",
10051
+ 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`.",
10052
+ inputSchema: {
10053
+ limit: z17.number().int().min(1).max(50).default(10).describe("Number of payouts to return."),
10054
+ status: z17.enum(["pending", "paid", "failed", "canceled", "in_transit"]).optional().describe("Filter by payout status.")
10055
+ },
10056
+ annotations: annotationsFor("stripe_connect_payouts", "read")
9915
10057
  },
9916
10058
  async ({ limit, status }) => {
9917
10059
  const start = Date.now();
@@ -9940,10 +10082,14 @@ function registerStripeConnectTools(server2, ctx) {
9940
10082
  }
9941
10083
  }
9942
10084
  );
9943
- server2.tool(
10085
+ server2.registerTool(
9944
10086
  "stripe_connect_balance",
9945
- "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`.",
9946
- {},
10087
+ {
10088
+ title: "Stripe Connect Balance",
10089
+ 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`.",
10090
+ inputSchema: {},
10091
+ annotations: annotationsFor("stripe_connect_balance", "read")
10092
+ },
9947
10093
  async () => {
9948
10094
  const start = Date.now();
9949
10095
  try {
@@ -9973,14 +10119,18 @@ function registerStripeConnectTools(server2, ctx) {
9973
10119
  }
9974
10120
  }
9975
10121
  );
9976
- server2.tool(
10122
+ server2.registerTool(
9977
10123
  "stripe_connect_refund",
9978
- "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.",
9979
10124
  {
9980
- charge_id: z17.string().min(1).describe("Stripe charge ID (ch_...)."),
9981
- amount_cents: z17.number().int().min(1).optional().describe("Partial refund amount in cents. Omit for full refund."),
9982
- reason: z17.enum(["duplicate", "fraudulent", "requested_by_customer"]).optional(),
9983
- confirm_token: z17.string().optional().describe("Confirmation token from previous call. Required to execute.")
10125
+ title: "Refund a Charge (moves real money)",
10126
+ 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.",
10127
+ inputSchema: {
10128
+ charge_id: z17.string().min(1).describe("Stripe charge ID (ch_...)."),
10129
+ amount_cents: z17.number().int().min(1).optional().describe("Partial refund amount in cents. Omit for full refund."),
10130
+ reason: z17.enum(["duplicate", "fraudulent", "requested_by_customer"]).optional(),
10131
+ confirm_token: z17.string().optional().describe("Confirmation token from previous call. Required to execute.")
10132
+ },
10133
+ annotations: annotationsFor("stripe_connect_refund", "write")
9984
10134
  },
9985
10135
  async ({ charge_id, amount_cents, reason, confirm_token }) => {
9986
10136
  const start = Date.now();
@@ -10023,12 +10173,16 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
10023
10173
  }
10024
10174
  }
10025
10175
  );
10026
- server2.tool(
10176
+ server2.registerTool(
10027
10177
  "stripe_connect_dispute_list",
10028
- "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.",
10029
10178
  {
10030
- limit: z17.number().int().min(1).max(50).default(10),
10031
- status: z17.string().optional().describe("Filter by dispute status (e.g. needs_response, under_review).")
10179
+ title: "List Chargeback Disputes",
10180
+ 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.",
10181
+ inputSchema: {
10182
+ limit: z17.number().int().min(1).max(50).default(10),
10183
+ status: z17.string().optional().describe("Filter by dispute status (e.g. needs_response, under_review).")
10184
+ },
10185
+ annotations: annotationsFor("stripe_connect_dispute_list", "read")
10032
10186
  },
10033
10187
  async ({ limit, status }) => {
10034
10188
  const start = Date.now();
@@ -10057,12 +10211,16 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
10057
10211
  }
10058
10212
  }
10059
10213
  );
10060
- server2.tool(
10214
+ server2.registerTool(
10061
10215
  "radar_review_list",
10062
- "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`.",
10063
10216
  {
10064
- open_only: z17.boolean().default(true).describe("If true, only returns open (undecided) reviews."),
10065
- limit: z17.number().int().min(1).max(50).default(10)
10217
+ title: "List Radar Fraud Reviews",
10218
+ 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`.",
10219
+ inputSchema: {
10220
+ open_only: z17.boolean().default(true).describe("If true, only returns open (undecided) reviews."),
10221
+ limit: z17.number().int().min(1).max(50).default(10)
10222
+ },
10223
+ annotations: annotationsFor("radar_review_list", "read")
10066
10224
  },
10067
10225
  async ({ open_only, limit }) => {
10068
10226
  const start = Date.now();
@@ -10089,12 +10247,16 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
10089
10247
  }
10090
10248
  }
10091
10249
  );
10092
- server2.tool(
10250
+ server2.registerTool(
10093
10251
  "radar_review_approve",
10094
- "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`.",
10095
10252
  {
10096
- review_id: z17.string().min(1).describe("Stripe Radar review ID (prv_...)."),
10097
- confirm_token: z17.string().optional()
10253
+ title: "Approve Radar Review (captures charge)",
10254
+ 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`.",
10255
+ inputSchema: {
10256
+ review_id: z17.string().min(1).describe("Stripe Radar review ID (prv_...)."),
10257
+ confirm_token: z17.string().optional()
10258
+ },
10259
+ annotations: annotationsFor("radar_review_approve", "admin")
10098
10260
  },
10099
10261
  async ({ review_id, confirm_token }) => {
10100
10262
  const toolName = "radar_review_approve";
@@ -10134,12 +10296,16 @@ Call again with confirm_token="${token2}" to execute. Expires in 5 minutes.`
10134
10296
  }
10135
10297
  }
10136
10298
  );
10137
- server2.tool(
10299
+ server2.registerTool(
10138
10300
  "radar_review_decline",
10139
- "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`.",
10140
10301
  {
10141
- review_id: z17.string().min(1),
10142
- confirm_token: z17.string().optional()
10302
+ title: "Decline Radar Review (blocks charge)",
10303
+ 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`.",
10304
+ inputSchema: {
10305
+ review_id: z17.string().min(1),
10306
+ confirm_token: z17.string().optional()
10307
+ },
10308
+ annotations: annotationsFor("radar_review_decline", "admin")
10143
10309
  },
10144
10310
  async ({ review_id, confirm_token }) => {
10145
10311
  const toolName = "radar_review_decline";
@@ -10179,13 +10345,17 @@ Expires in 5 minutes.`
10179
10345
  }
10180
10346
  }
10181
10347
  );
10182
- server2.tool(
10348
+ server2.registerTool(
10183
10349
  "radar_value_list_add",
10184
- "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`).",
10185
10350
  {
10186
- value_list_id: z17.string().min(1).describe("Stripe Radar value list ID (rsl_...)."),
10187
- value: z17.string().min(1).describe("The value to add (email, IP address, country code, etc.)."),
10188
- confirm_token: z17.string().optional()
10351
+ title: "Add Value to Radar List",
10352
+ 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`).",
10353
+ inputSchema: {
10354
+ value_list_id: z17.string().min(1).describe("Stripe Radar value list ID (rsl_...)."),
10355
+ value: z17.string().min(1).describe("The value to add (email, IP address, country code, etc.)."),
10356
+ confirm_token: z17.string().optional()
10357
+ },
10358
+ annotations: annotationsFor("radar_value_list_add", "admin")
10189
10359
  },
10190
10360
  async ({ value_list_id, value, confirm_token }) => {
10191
10361
  const toolName = "radar_value_list_add";
@@ -10225,12 +10395,16 @@ Expires in 5 minutes.`
10225
10395
  }
10226
10396
  }
10227
10397
  );
10228
- server2.tool(
10398
+ server2.registerTool(
10229
10399
  "radar_rule_toggle",
10230
- "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`.",
10231
10400
  {
10232
- rule_id: z17.string().min(1).describe("Stripe Radar rule ID."),
10233
- enabled: z17.boolean().describe("true = enable, false = disable.")
10401
+ title: "Radar Rule Toggle (instructions only)",
10402
+ 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`.",
10403
+ inputSchema: {
10404
+ rule_id: z17.string().min(1).describe("Stripe Radar rule ID."),
10405
+ enabled: z17.boolean().describe("true = enable, false = disable.")
10406
+ },
10407
+ annotations: annotationsFor("radar_rule_toggle", "admin")
10234
10408
  },
10235
10409
  async ({ rule_id, enabled }) => {
10236
10410
  return ok2(
@@ -10501,6 +10675,11 @@ async function runAutoTopup(env, args) {
10501
10675
  };
10502
10676
  }
10503
10677
  }
10678
+ var WALLET_TOOL_SCOPES = {
10679
+ wallet_balance: "read",
10680
+ wallet_topup_checkout: "write",
10681
+ wallet_auto_topup: "write"
10682
+ };
10504
10683
  function ok3(text) {
10505
10684
  return { content: [{ type: "text", text }] };
10506
10685
  }
@@ -10510,10 +10689,14 @@ function err3(text) {
10510
10689
  function registerWalletTools(server2, ctx) {
10511
10690
  const { env, props: props2 } = ctx;
10512
10691
  const defaultOrgId = props2.org_id ?? props2.sub;
10513
- server2.tool(
10692
+ server2.registerTool(
10514
10693
  "wallet_balance",
10515
- "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`.",
10516
- {},
10694
+ {
10695
+ title: "Wallet Balance",
10696
+ 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`.",
10697
+ inputSchema: {},
10698
+ annotations: annotationsFor("wallet_balance", "read")
10699
+ },
10517
10700
  async () => {
10518
10701
  const orgId = defaultOrgId;
10519
10702
  const start = Date.now();
@@ -10544,13 +10727,17 @@ function registerWalletTools(server2, ctx) {
10544
10727
  }
10545
10728
  }
10546
10729
  );
10547
- server2.tool(
10730
+ server2.registerTool(
10548
10731
  "wallet_topup_checkout",
10549
- "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.",
10550
10732
  {
10551
- pack: z18.string().optional().describe(
10552
- "Pack id (pack_500/pack_1000/pack_2500/pack_5000) or exact EUR-cents amount. Omit to list packs."
10553
- )
10733
+ title: "Wallet Top-up Checkout",
10734
+ 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.",
10735
+ inputSchema: {
10736
+ pack: z18.string().optional().describe(
10737
+ "Pack id (pack_500/pack_1000/pack_2500/pack_5000) or exact EUR-cents amount. Omit to list packs."
10738
+ )
10739
+ },
10740
+ annotations: annotationsFor("wallet_topup_checkout", "write")
10554
10741
  },
10555
10742
  async ({ pack }) => {
10556
10743
  const orgId = defaultOrgId;
@@ -10601,11 +10788,15 @@ function registerWalletTools(server2, ctx) {
10601
10788
  }
10602
10789
  }
10603
10790
  );
10604
- server2.tool(
10791
+ server2.registerTool(
10605
10792
  "wallet_auto_topup",
10606
- "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`.",
10607
10793
  {
10608
- pack_cents: z18.number().int().positive().optional().describe("Override pack amount in EUR cents (must match a catalog pack).")
10794
+ title: "Wallet Auto Top-up (charges card now)",
10795
+ 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`.",
10796
+ inputSchema: {
10797
+ pack_cents: z18.number().int().positive().optional().describe("Override pack amount in EUR cents (must match a catalog pack).")
10798
+ },
10799
+ annotations: annotationsFor("wallet_auto_topup", "write")
10609
10800
  },
10610
10801
  async ({ pack_cents }) => {
10611
10802
  const orgId = defaultOrgId;
@@ -11153,16 +11344,23 @@ function registerUiAgentGenericTools(server2, _ctx) {
11153
11344
 
11154
11345
  // src/tools-storefront-logo.ts
11155
11346
  import { z as z21 } from "zod";
11347
+ var STOREFRONT_LOGO_TOOL_SCOPES = {
11348
+ generate_storefront_logo: "read"
11349
+ };
11156
11350
  var ok5 = (text) => ({ content: [{ type: "text", text }] });
11157
11351
  var err5 = (text) => ({ isError: true, content: [{ type: "text", text }] });
11158
11352
  function registerStorefrontLogoTools(server2, env = {}) {
11159
- server2.tool(
11353
+ server2.registerTool(
11160
11354
  "generate_storefront_logo",
11161
- "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.",
11162
11355
  {
11163
- name: z21.string().min(1).describe("Brand / shop name, e.g. Bananas"),
11164
- accent: z21.string().regex(/^#?[0-9A-Fa-f]{6}$/).optional().describe("Accent hex, e.g. #FF8A00"),
11165
- tagline: z21.string().optional().describe("Optional tagline for AI-enhanced marks")
11356
+ title: "Generate Storefront Logo",
11357
+ 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.",
11358
+ inputSchema: {
11359
+ name: z21.string().min(1).describe("Brand / shop name, e.g. Bananas"),
11360
+ accent: z21.string().regex(/^#?[0-9A-Fa-f]{6}$/).optional().describe("Accent hex, e.g. #FF8A00"),
11361
+ tagline: z21.string().optional().describe("Optional tagline for AI-enhanced marks")
11362
+ },
11363
+ annotations: annotationsFor("generate_storefront_logo", "read")
11166
11364
  },
11167
11365
  async ({ name, accent, tagline }) => {
11168
11366
  try {
@@ -11193,16 +11391,25 @@ function registerStorefrontLogoTools(server2, env = {}) {
11193
11391
 
11194
11392
  // src/tools-storefront-deploy.ts
11195
11393
  import { z as z22 } from "zod";
11394
+ var STOREFRONT_DEPLOY_TOOL_SCOPES = {
11395
+ list_deploy_targets: "read",
11396
+ deploy_storefront: "write",
11397
+ provision_storefront_clerk: "write"
11398
+ };
11196
11399
  var ok6 = (value) => ({
11197
11400
  content: [{ type: "text", text: JSON.stringify(value) }]
11198
11401
  });
11199
11402
  var err6 = (text) => ({ isError: true, content: [{ type: "text", text }] });
11200
11403
  function registerStorefrontDeployTools(server2) {
11201
- server2.tool(
11404
+ server2.registerTool(
11202
11405
  "list_deploy_targets",
11203
- "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.",
11204
11406
  {
11205
- dir: z22.string().min(1).describe("Path to the scaffolded storefront directory")
11407
+ title: "List Deploy Targets",
11408
+ 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.",
11409
+ inputSchema: {
11410
+ dir: z22.string().min(1).describe("Path to the scaffolded storefront directory")
11411
+ },
11412
+ annotations: annotationsFor("list_deploy_targets", "read")
11206
11413
  },
11207
11414
  async ({ dir }) => {
11208
11415
  try {
@@ -11218,17 +11425,21 @@ function registerStorefrontDeployTools(server2) {
11218
11425
  }
11219
11426
  }
11220
11427
  );
11221
- server2.tool(
11428
+ server2.registerTool(
11222
11429
  "deploy_storefront",
11223
- "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.",
11224
11430
  {
11225
- dir: z22.string().min(1).describe("Path to the scaffolded storefront directory"),
11226
- target: z22.enum(TARGET_IDS).optional().describe("Force a host instead of auto-detecting"),
11227
- name: z22.string().optional().describe("Project/worker name (defaults from brand)"),
11228
- skip_build: z22.boolean().optional().describe("Deploy the existing build without rebuilding"),
11229
- push_secrets: z22.boolean().optional().describe("Stage runtime secrets on the host before deploy (default true)"),
11230
- verify: z22.boolean().optional().describe("Probe the deployed URL and report whether it actually serves (default true)"),
11231
- domain: z22.boolean().optional().describe("Route the brand domain to the deployment (Cloudflare provisions DNS + cert)")
11431
+ title: "Deploy Storefront",
11432
+ 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.",
11433
+ inputSchema: {
11434
+ dir: z22.string().min(1).describe("Path to the scaffolded storefront directory"),
11435
+ target: z22.enum(TARGET_IDS).optional().describe("Force a host instead of auto-detecting"),
11436
+ name: z22.string().optional().describe("Project/worker name (defaults from brand)"),
11437
+ skip_build: z22.boolean().optional().describe("Deploy the existing build without rebuilding"),
11438
+ push_secrets: z22.boolean().optional().describe("Stage runtime secrets on the host before deploy (default true)"),
11439
+ verify: z22.boolean().optional().describe("Probe the deployed URL and report whether it actually serves (default true)"),
11440
+ domain: z22.boolean().optional().describe("Route the brand domain to the deployment (Cloudflare provisions DNS + cert)")
11441
+ },
11442
+ annotations: annotationsFor("deploy_storefront", "write")
11232
11443
  },
11233
11444
  async ({ dir, target, name, skip_build, push_secrets, verify, domain }) => {
11234
11445
  try {
@@ -11268,15 +11479,19 @@ function registerStorefrontDeployTools(server2) {
11268
11479
  }
11269
11480
  }
11270
11481
  );
11271
- server2.tool(
11482
+ server2.registerTool(
11272
11483
  "provision_storefront_clerk",
11273
- "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.",
11274
11484
  {
11275
- dir: z22.string().min(1).describe("Path to the scaffolded storefront directory"),
11276
- name: z22.string().optional().describe("Clerk application name (defaults from brand)"),
11277
- production: z22.boolean().optional().describe("Also create a production instance (Platform API only)"),
11278
- no_create: z22.boolean().optional().describe("Never create a new application; only reuse existing keys"),
11279
- url: z22.string().optional().describe("Deployed URL to whitelist as origin and redirect")
11485
+ title: "Provision Storefront Clerk App",
11486
+ 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.",
11487
+ inputSchema: {
11488
+ dir: z22.string().min(1).describe("Path to the scaffolded storefront directory"),
11489
+ name: z22.string().optional().describe("Clerk application name (defaults from brand)"),
11490
+ production: z22.boolean().optional().describe("Also create a production instance (Platform API only)"),
11491
+ no_create: z22.boolean().optional().describe("Never create a new application; only reuse existing keys"),
11492
+ url: z22.string().optional().describe("Deployed URL to whitelist as origin and redirect")
11493
+ },
11494
+ annotations: annotationsFor("provision_storefront_clerk", "write")
11280
11495
  },
11281
11496
  async ({ dir, name, production, no_create, url }) => {
11282
11497
  try {
@@ -11323,6 +11538,76 @@ function registerStorefrontDeployTools(server2) {
11323
11538
  );
11324
11539
  }
11325
11540
 
11541
+ // src/tool-registry.ts
11542
+ var INTELLIGENCE_TOOL_SCOPES = {
11543
+ diagnose_subscriber: "read",
11544
+ fleet_health: "read",
11545
+ detect_usage_anomalies: "read",
11546
+ optimize_package: "read",
11547
+ churn_risk: "read",
11548
+ audit_network_coverage: "read",
11549
+ marketing_intelligence: "read",
11550
+ high_cost_subscribers: "read"
11551
+ };
11552
+ var UI_AGENT_GENERIC_TOOL_SCOPES = {
11553
+ ui_agent_ask: "write",
11554
+ ui_agent_status: "read"
11555
+ };
11556
+ var GREENZONE_TOOL_SCOPES = {
11557
+ greenzone_whitelist_add: "write",
11558
+ greenzone_whitelist_remove: "write",
11559
+ greenzone_whitelist_list: "read"
11560
+ };
11561
+ var OPS_TOOL_SCOPES = {
11562
+ list_recent_ocs_events: "read",
11563
+ rate_limit_status: "read",
11564
+ subscriber_country_history: "read",
11565
+ subscriber_depletion_events: "read"
11566
+ };
11567
+ var APP_TOOL_SCOPES = {
11568
+ fleet_health_app: "read",
11569
+ provision_esim_wizard: "write",
11570
+ balance_topup_form: "admin"
11571
+ };
11572
+ var CARRIER_ASK_TOOL_SCOPES = {
11573
+ carrier_ask: "read",
11574
+ carrier_ask_describe: "read"
11575
+ };
11576
+ var STRIPE_PROXY_TOOL_SCOPES = {
11577
+ stripe_proxy_call: "write"
11578
+ };
11579
+ function scopesFromToolDefs(tools) {
11580
+ return Object.fromEntries(tools.map((t) => [t.name, t.scope]));
11581
+ }
11582
+ var PRICING_TOOL_SCOPES = scopesFromToolDefs(PRICING_TOOLS);
11583
+ var PROJECTS_TOOL_SCOPES = scopesFromToolDefs(PROJECTS_TOOLS);
11584
+ var ALL_TOOL_SCOPES = Object.freeze({
11585
+ ...TOOL_SCOPES,
11586
+ ...BACKLOG_TOOL_SCOPES,
11587
+ ...INTELLIGENCE_TOOL_SCOPES,
11588
+ ...OPS_TOOL_SCOPES,
11589
+ ...PRICING_TOOL_SCOPES,
11590
+ ...PROJECTS_TOOL_SCOPES,
11591
+ ...APP_TOOL_SCOPES,
11592
+ ...UI_AGENT_TOOL_SCOPES,
11593
+ ...UI_AGENT_ASK_TOOL_SCOPES,
11594
+ ...UI_AGENT_SCHEDULE_TOOL_SCOPES,
11595
+ ...UI_AGENT_GENERIC_TOOL_SCOPES,
11596
+ ...WALLET_TOOL_SCOPES,
11597
+ ...STRIPE_CONNECT_TOOL_SCOPES,
11598
+ ...GREENZONE_TOOL_SCOPES,
11599
+ ...CARRIER_ASK_TOOL_SCOPES,
11600
+ ...STRIPE_PROXY_TOOL_SCOPES,
11601
+ ...STOREFRONT_LOGO_TOOL_SCOPES,
11602
+ // Storefront deploy tools exist only in this package, not in the Worker.
11603
+ ...STOREFRONT_DEPLOY_TOOL_SCOPES
11604
+ });
11605
+ var TOOL_REGISTRY2 = new Set(Object.keys(ALL_TOOL_SCOPES));
11606
+ var UNREGISTERED_TOOLS = /* @__PURE__ */ new Set(["stripe_proxy_call"]);
11607
+ var REGISTERED_TOOL_COUNT = Object.keys(ALL_TOOL_SCOPES).filter(
11608
+ (name) => !UNREGISTERED_TOOLS.has(name)
11609
+ ).length;
11610
+
11326
11611
  // src/index.ts
11327
11612
  var baseUrl = process.env.CARRIER_OCS_BASE_URL ?? process.env.ESIMVAULT_BASE_URL ?? "https://ocs.esimvault.cloud";
11328
11613
  var token = process.env.CARRIER_OCS_API_TOKEN ?? process.env.ESIMVAULT_API_TOKEN;
@@ -11373,6 +11658,7 @@ var server = new McpServer(
11373
11658
  instructions: "Carrier MCP \u2014 single-user stdio mode. Full tool registry (mirrors the deployed Worker)."
11374
11659
  }
11375
11660
  );
11661
+ installAnnotationDefaults(server, ALL_TOOL_SCOPES);
11376
11662
  registerAllTools(server, toolCtx);
11377
11663
  registerIntelligenceTools(server, toolCtx);
11378
11664
  registerAllBacklogTools(server, toolCtx);