@carrierllc/mcp 0.4.1 → 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
@@ -32,7 +32,7 @@ import {
32
32
  subscriberIdParams,
33
33
  usageOverPeriodParams,
34
34
  verifyStorefront
35
- } from "./chunk-6OI56RSR.js";
35
+ } from "./chunk-QZBALDKD.js";
36
36
  import "./chunk-SHKKVIIA.js";
37
37
 
38
38
  // src/index.ts
@@ -8514,6 +8514,14 @@ function writeUsageThresholdAudit(env, severity, remainingCredits, month, taskCo
8514
8514
  }
8515
8515
 
8516
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
+ };
8517
8525
  function noManusKeyError() {
8518
8526
  return {
8519
8527
  isError: true,
@@ -9221,6 +9229,18 @@ Report success or failure with a clear summary.`;
9221
9229
  }
9222
9230
  };
9223
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
+ };
9224
9244
  function registerAllUiAgentTools(server2, ctx) {
9225
9245
  server2.registerTool(
9226
9246
  "ui_create_steering_list",
@@ -9566,6 +9586,10 @@ async function askReply(keys, taskId, reply) {
9566
9586
  }
9567
9587
 
9568
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
+ };
9569
9593
  function computeExpiresAt(askedAt) {
9570
9594
  const asked = new Date(askedAt).getTime();
9571
9595
  if (isNaN(asked)) return "";
@@ -9809,6 +9833,110 @@ function writeAudit(env, row) {
9809
9833
  });
9810
9834
  }
9811
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
+
9812
9940
  // src/stripe-connect-tools.ts
9813
9941
  async function verifyConfirmToken(env, sub, toolName, token2) {
9814
9942
  const key = `confirm:${sub}:${toolName}`;
@@ -9851,6 +9979,18 @@ async function stripePost(stripeKey, path, params, connectedAccountId) {
9851
9979
  const data = await res.json();
9852
9980
  return { ok: res.ok, status: res.status, data };
9853
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
+ };
9854
9994
  function registerStripeConnectTools(server2, ctx) {
9855
9995
  const { env, props: props2 } = ctx;
9856
9996
  const operatorId = props2.org_id ?? props2.sub;
@@ -9858,11 +9998,15 @@ function registerStripeConnectTools(server2, ctx) {
9858
9998
  async function getAccountId() {
9859
9999
  return env.CARRIER_USERS.get(kvKey);
9860
10000
  }
9861
- server2.tool(
10001
+ server2.registerTool(
9862
10002
  "stripe_connect_status",
9863
- "Read-only: returns the Stripe Connect account status, capabilities, and requirements for the authenticated operator.",
9864
10003
  {
9865
- 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")
9866
10010
  },
9867
10011
  async ({ operator_id }) => {
9868
10012
  const opId = operator_id ?? operatorId;
@@ -9906,12 +10050,16 @@ function registerStripeConnectTools(server2, ctx) {
9906
10050
  }
9907
10051
  }
9908
10052
  );
9909
- server2.tool(
10053
+ server2.registerTool(
9910
10054
  "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
10055
  {
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.")
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")
9915
10063
  },
9916
10064
  async ({ limit, status }) => {
9917
10065
  const start = Date.now();
@@ -9940,10 +10088,14 @@ function registerStripeConnectTools(server2, ctx) {
9940
10088
  }
9941
10089
  }
9942
10090
  );
9943
- server2.tool(
10091
+ server2.registerTool(
9944
10092
  "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
- {},
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
+ },
9947
10099
  async () => {
9948
10100
  const start = Date.now();
9949
10101
  try {
@@ -9973,14 +10125,18 @@ function registerStripeConnectTools(server2, ctx) {
9973
10125
  }
9974
10126
  }
9975
10127
  );
9976
- server2.tool(
10128
+ server2.registerTool(
9977
10129
  "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
10130
  {
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.")
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")
9984
10140
  },
9985
10141
  async ({ charge_id, amount_cents, reason, confirm_token }) => {
9986
10142
  const start = Date.now();
@@ -10023,12 +10179,16 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
10023
10179
  }
10024
10180
  }
10025
10181
  );
10026
- server2.tool(
10182
+ server2.registerTool(
10027
10183
  "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
10184
  {
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).")
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")
10032
10192
  },
10033
10193
  async ({ limit, status }) => {
10034
10194
  const start = Date.now();
@@ -10057,12 +10217,16 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
10057
10217
  }
10058
10218
  }
10059
10219
  );
10060
- server2.tool(
10220
+ server2.registerTool(
10061
10221
  "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
10222
  {
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)
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")
10066
10230
  },
10067
10231
  async ({ open_only, limit }) => {
10068
10232
  const start = Date.now();
@@ -10089,12 +10253,16 @@ Call again with confirm_token="${token2}" to execute. Token expires in 5 minutes
10089
10253
  }
10090
10254
  }
10091
10255
  );
10092
- server2.tool(
10256
+ server2.registerTool(
10093
10257
  "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
10258
  {
10096
- review_id: z17.string().min(1).describe("Stripe Radar review ID (prv_...)."),
10097
- 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")
10098
10266
  },
10099
10267
  async ({ review_id, confirm_token }) => {
10100
10268
  const toolName = "radar_review_approve";
@@ -10134,12 +10302,16 @@ Call again with confirm_token="${token2}" to execute. Expires in 5 minutes.`
10134
10302
  }
10135
10303
  }
10136
10304
  );
10137
- server2.tool(
10305
+ server2.registerTool(
10138
10306
  "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
10307
  {
10141
- review_id: z17.string().min(1),
10142
- 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")
10143
10315
  },
10144
10316
  async ({ review_id, confirm_token }) => {
10145
10317
  const toolName = "radar_review_decline";
@@ -10179,13 +10351,17 @@ Expires in 5 minutes.`
10179
10351
  }
10180
10352
  }
10181
10353
  );
10182
- server2.tool(
10354
+ server2.registerTool(
10183
10355
  "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
10356
  {
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()
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")
10189
10365
  },
10190
10366
  async ({ value_list_id, value, confirm_token }) => {
10191
10367
  const toolName = "radar_value_list_add";
@@ -10225,12 +10401,16 @@ Expires in 5 minutes.`
10225
10401
  }
10226
10402
  }
10227
10403
  );
10228
- server2.tool(
10404
+ server2.registerTool(
10229
10405
  "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
10406
  {
10232
- rule_id: z17.string().min(1).describe("Stripe Radar rule ID."),
10233
- 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")
10234
10414
  },
10235
10415
  async ({ rule_id, enabled }) => {
10236
10416
  return ok2(
@@ -10501,6 +10681,11 @@ async function runAutoTopup(env, args) {
10501
10681
  };
10502
10682
  }
10503
10683
  }
10684
+ var WALLET_TOOL_SCOPES = {
10685
+ wallet_balance: "read",
10686
+ wallet_topup_checkout: "write",
10687
+ wallet_auto_topup: "write"
10688
+ };
10504
10689
  function ok3(text) {
10505
10690
  return { content: [{ type: "text", text }] };
10506
10691
  }
@@ -10510,10 +10695,14 @@ function err3(text) {
10510
10695
  function registerWalletTools(server2, ctx) {
10511
10696
  const { env, props: props2 } = ctx;
10512
10697
  const defaultOrgId = props2.org_id ?? props2.sub;
10513
- server2.tool(
10698
+ server2.registerTool(
10514
10699
  "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
- {},
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
+ },
10517
10706
  async () => {
10518
10707
  const orgId = defaultOrgId;
10519
10708
  const start = Date.now();
@@ -10544,13 +10733,17 @@ function registerWalletTools(server2, ctx) {
10544
10733
  }
10545
10734
  }
10546
10735
  );
10547
- server2.tool(
10736
+ server2.registerTool(
10548
10737
  "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
10738
  {
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
- )
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")
10554
10747
  },
10555
10748
  async ({ pack }) => {
10556
10749
  const orgId = defaultOrgId;
@@ -10601,11 +10794,15 @@ function registerWalletTools(server2, ctx) {
10601
10794
  }
10602
10795
  }
10603
10796
  );
10604
- server2.tool(
10797
+ server2.registerTool(
10605
10798
  "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
10799
  {
10608
- 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")
10609
10806
  },
10610
10807
  async ({ pack_cents }) => {
10611
10808
  const orgId = defaultOrgId;
@@ -11153,16 +11350,23 @@ function registerUiAgentGenericTools(server2, _ctx) {
11153
11350
 
11154
11351
  // src/tools-storefront-logo.ts
11155
11352
  import { z as z21 } from "zod";
11353
+ var STOREFRONT_LOGO_TOOL_SCOPES = {
11354
+ generate_storefront_logo: "read"
11355
+ };
11156
11356
  var ok5 = (text) => ({ content: [{ type: "text", text }] });
11157
11357
  var err5 = (text) => ({ isError: true, content: [{ type: "text", text }] });
11158
11358
  function registerStorefrontLogoTools(server2, env = {}) {
11159
- server2.tool(
11359
+ server2.registerTool(
11160
11360
  "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
11361
  {
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")
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")
11166
11370
  },
11167
11371
  async ({ name, accent, tagline }) => {
11168
11372
  try {
@@ -11193,16 +11397,25 @@ function registerStorefrontLogoTools(server2, env = {}) {
11193
11397
 
11194
11398
  // src/tools-storefront-deploy.ts
11195
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
+ };
11196
11405
  var ok6 = (value) => ({
11197
11406
  content: [{ type: "text", text: JSON.stringify(value) }]
11198
11407
  });
11199
11408
  var err6 = (text) => ({ isError: true, content: [{ type: "text", text }] });
11200
11409
  function registerStorefrontDeployTools(server2) {
11201
- server2.tool(
11410
+ server2.registerTool(
11202
11411
  "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
11412
  {
11205
- 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")
11206
11419
  },
11207
11420
  async ({ dir }) => {
11208
11421
  try {
@@ -11218,17 +11431,21 @@ function registerStorefrontDeployTools(server2) {
11218
11431
  }
11219
11432
  }
11220
11433
  );
11221
- server2.tool(
11434
+ server2.registerTool(
11222
11435
  "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
11436
  {
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)")
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")
11232
11449
  },
11233
11450
  async ({ dir, target, name, skip_build, push_secrets, verify, domain }) => {
11234
11451
  try {
@@ -11268,15 +11485,19 @@ function registerStorefrontDeployTools(server2) {
11268
11485
  }
11269
11486
  }
11270
11487
  );
11271
- server2.tool(
11488
+ server2.registerTool(
11272
11489
  "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
11490
  {
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")
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")
11280
11501
  },
11281
11502
  async ({ dir, name, production, no_create, url }) => {
11282
11503
  try {
@@ -11323,6 +11544,76 @@ function registerStorefrontDeployTools(server2) {
11323
11544
  );
11324
11545
  }
11325
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
+
11326
11617
  // src/index.ts
11327
11618
  var baseUrl = process.env.CARRIER_OCS_BASE_URL ?? process.env.ESIMVAULT_BASE_URL ?? "https://ocs.esimvault.cloud";
11328
11619
  var token = process.env.CARRIER_OCS_API_TOKEN ?? process.env.ESIMVAULT_API_TOKEN;
@@ -11373,6 +11664,7 @@ var server = new McpServer(
11373
11664
  instructions: "Carrier MCP \u2014 single-user stdio mode. Full tool registry (mirrors the deployed Worker)."
11374
11665
  }
11375
11666
  );
11667
+ installAnnotationDefaults(server, ALL_TOOL_SCOPES);
11376
11668
  registerAllTools(server, toolCtx);
11377
11669
  registerIntelligenceTools(server, toolCtx);
11378
11670
  registerAllBacklogTools(server, toolCtx);