@carrierllc/mcp 0.13.11 → 0.13.13

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
@@ -17,6 +17,7 @@ import {
17
17
  deploySite,
18
18
  esimStatusPerAccountParams,
19
19
  extractDailyUsage,
20
+ extractEsimStatusByAccount,
20
21
  extractEsimStatusCounts,
21
22
  extractResellerBalance,
22
23
  extractSimProviderStatus,
@@ -62,6 +63,7 @@ import {
62
63
  recurringPackageParams,
63
64
  renderHtml,
64
65
  repairPlanFor,
66
+ scopeListSubscriberParams,
65
67
  staleActivePackages,
66
68
  storefrontClerkUrls,
67
69
  subscriberIdParams,
@@ -70,8 +72,9 @@ import {
70
72
  summarizeNetworkEvents,
71
73
  usageOverPeriodParams,
72
74
  verifyStorefront,
73
- withOcsListSummary
74
- } from "./chunk-ZLPXWOYO.js";
75
+ withOcsListSummary,
76
+ withTemplateResellerId
77
+ } from "./chunk-OJITGTFO.js";
75
78
  import "./chunk-SHKKVIIA.js";
76
79
 
77
80
  // src/index.ts
@@ -1510,7 +1513,7 @@ function registerAllTools(server2, ctx) {
1510
1513
  "list_subscribers",
1511
1514
  {
1512
1515
  title: "List Subscribers",
1513
- description: "Use this to list subscribers with optional filters and pagination. Good for fleet enumeration, bulk status checks, and finding subscribers by account or status. OCS REQUIRES at least one search key \u2014 provide exactly one of: `imsi`, `iccid`, `activationCode`, `accountId`, or `msisdn`. Calling with no key will be rejected before reaching OCS. Params: `imsi` (string), `iccid` (string), `activationCode` (string), `accountId` (integer), `msisdn` (string), `status` (string, e.g. 'ACTIVE'/'SUSPENDED'), `offset` (integer, pagination \u2014 default 0), `limit` (integer, max results \u2014 always set to avoid unbounded fetches; recommended max 100 per call). Returns: `{ summary, subscriberList: [...] }` \u2014 subscriber summary records with ICCID, status and account, behind a server-counted `summary.total` (rows returned) and `summary.breakdown.status`. Answer 'how many' from `summary`, not by counting rows \u2014 but when `summary.moreAvailable` is set this is one page, so report `summary.upstreamTotal` for the fleet-wide figure, never `total`. Do NOT use this to fetch full details for a specific subscriber \u2014 use `get_subscriber` for that.",
1516
+ description: "Use this to list subscribers with optional filters and pagination. Good for fleet enumeration, bulk status checks, and finding subscribers by account or status. OCS needs a search key: `imsi`, `iccid`, `activationCode`, `accountId` or `msisdn`. With none, Carrier scopes the call to this organization's default account (its preferred_account_id, else the lowest-id account under its reseller) and says so in `default_account`. If no account can be found the call fails with an error \u2014 it never returns an empty list. Params: `imsi` (string), `iccid` (string), `activationCode` (string), `accountId` (integer), `msisdn` (string), `status` (string, e.g. 'ACTIVE'/'SUSPENDED'), `offset` (integer, pagination \u2014 default 0), `limit` (integer, max results \u2014 always set to avoid unbounded fetches; recommended max 100 per call). Returns: `{ summary, subscriberList: [...] }` \u2014 subscriber summary records with ICCID, status and account, behind a server-counted `summary.total` (rows returned) and `summary.breakdown.status`. Answer 'how many' from `summary`, not by counting rows \u2014 but when `summary.moreAvailable` is set this is one page, so report `summary.upstreamTotal` for the fleet-wide figure, never `total`. Do NOT use this to fetch full details for a specific subscriber \u2014 use `get_subscriber` for that.",
1514
1517
  inputSchema: z.object({
1515
1518
  imsi: z.string().optional().describe("Filter by IMSI"),
1516
1519
  iccid: z.string().optional().describe("Filter by ICCID"),
@@ -1520,12 +1523,7 @@ function registerAllTools(server2, ctx) {
1520
1523
  status: z.string().optional().describe("Filter by status"),
1521
1524
  offset: z.number().optional().describe("Pagination offset"),
1522
1525
  limit: z.number().optional().describe("Max results to return")
1523
- }).refine(
1524
- (d) => d.imsi !== void 0 || d.iccid !== void 0 || d.activationCode !== void 0 || d.accountId !== void 0 || d.msisdn !== void 0,
1525
- {
1526
- message: "Provide at least one of: imsi, iccid, activationCode, accountId, msisdn"
1527
- }
1528
- ),
1526
+ }),
1529
1527
  annotations: { readOnlyHint: true }
1530
1528
  },
1531
1529
  wrapHandler(
@@ -1536,13 +1534,25 @@ function registerAllTools(server2, ctx) {
1536
1534
  async (args, token2) => {
1537
1535
  const params = buildListSubscriberParams(args);
1538
1536
  const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
1539
- const raw = await client2.call("listSubscriber", params);
1537
+ const scope = await scopeListSubscriberParams({
1538
+ params,
1539
+ ...ctx.managedAuth.managed ? { managed: true } : {},
1540
+ ...ctx.managedAuth.preferredAccountId ? { preferredAccountId: ctx.managedAuth.preferredAccountId } : {},
1541
+ call: (method, callParams) => client2.call(method, callParams)
1542
+ });
1543
+ if (!scope.ok) throw new Error(scope.message);
1544
+ const raw = await client2.call("listSubscriber", scope.params);
1540
1545
  const payload = applySubscriberFilters(raw, args);
1546
+ const summarised = withOcsListSummary(payload, "subscribers");
1541
1547
  return {
1542
1548
  content: [
1543
1549
  {
1544
1550
  type: "text",
1545
- text: JSON.stringify(withOcsListSummary(payload, "subscribers"), null, 2)
1551
+ text: JSON.stringify(
1552
+ scope.defaultAccount ? { ...summarised, default_account: scope.defaultAccount } : summarised,
1553
+ null,
1554
+ 2
1555
+ )
1546
1556
  }
1547
1557
  ]
1548
1558
  };
@@ -2213,7 +2223,11 @@ function registerAllTools(server2, ctx) {
2213
2223
  content: [{ type: "text", text: `Error: ${params.error}` }]
2214
2224
  };
2215
2225
  }
2216
- return ocsCall(ctx.env, token2, "createPrepaidPackageTemplate", params);
2226
+ const withReseller = await withTemplateResellerId(
2227
+ params,
2228
+ () => getDefaultResellerId(ctx.env, token2)
2229
+ );
2230
+ return ocsCall(ctx.env, token2, "createPrepaidPackageTemplate", withReseller);
2217
2231
  }
2218
2232
  )
2219
2233
  );
@@ -10672,19 +10686,23 @@ function extractAccounts(json) {
10672
10686
  function toFleetInput(raw) {
10673
10687
  const counts = extractEsimStatusCounts(raw.status);
10674
10688
  const accountRecords = extractAccounts(raw.accounts);
10675
- const accounts = accountRecords.map((a) => ({
10676
- name: String(a.name ?? a.accountId ?? "?"),
10677
- balance: Number(a.balance ?? 0),
10678
- // Per-account status breakdown is not in listResellerAccount; the fleet
10679
- // totals come from the status call. Showing 0 here would read as "this
10680
- // account has no eSIMs", so the per-account columns stay at the balance
10681
- // and flags this call actually carries.
10682
- active: 0,
10683
- suspended: 0,
10684
- inventory: 0,
10685
- other: 0,
10686
- packageOnly: Boolean(a.packageOnly)
10687
- }));
10689
+ const byId = /* @__PURE__ */ new Map();
10690
+ for (const row of extractEsimStatusByAccount(raw.status)) {
10691
+ if (row.id !== void 0) byId.set(row.id, row);
10692
+ }
10693
+ const accounts = accountRecords.map((a) => {
10694
+ const id = Number(a.id ?? a.accountId);
10695
+ const status = Number.isFinite(id) ? byId.get(id) : void 0;
10696
+ return {
10697
+ name: String(a.name ?? a.accountId ?? "?"),
10698
+ balance: Number(a.balance ?? 0),
10699
+ active: status?.active ?? 0,
10700
+ suspended: status?.suspended ?? 0,
10701
+ inventory: status?.inventory ?? 0,
10702
+ other: status?.other ?? 0,
10703
+ packageOnly: Boolean(a.packageOnly)
10704
+ };
10705
+ });
10688
10706
  const unavailable = [];
10689
10707
  if (raw.statusError) unavailable.push(`esimStatusPerAccount: ${raw.statusError}`);
10690
10708
  if (raw.accountsError) unavailable.push(`listResellerAccount: ${raw.accountsError}`);