@carrierllc/mcp 0.9.2 → 0.9.3

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/README.md CHANGED
@@ -12,7 +12,7 @@ Natural language control of your MVNO/eSIM fleet. 43 OCS tools + 8 intelligence
12
12
  ### Interactive CLI (non-developers)
13
13
 
14
14
  ```bash
15
- npx -y @carrierllc/mcp
15
+ npx -y -p @carrierllc/mcp carrier-mcp
16
16
  # opens the Carrier TUI: status, sign-up/sign-in, install MCP, fleet NL examples
17
17
  ```
18
18
 
@@ -51,7 +51,7 @@ flags. Two rules apply everywhere:
51
51
  ### Stdio server (token env)
52
52
 
53
53
  ```bash
54
- ESIMVAULT_API_TOKEN=your_key npx -y @carrierllc/mcp
54
+ ESIMVAULT_API_TOKEN=your_key npx -y -p @carrierllc/mcp carrier-mcp
55
55
  ```
56
56
 
57
57
  Get a key at [mcp.carrier.llc](https://mcp.carrier.llc) or an org API key from the console.
@@ -67,7 +67,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
67
67
  "mcpServers": {
68
68
  "carrier": {
69
69
  "command": "npx",
70
- "args": ["-y", "@carrierllc/mcp"],
70
+ "args": ["-y", "-p", "@carrierllc/mcp", "carrier-mcp"],
71
71
  "env": {
72
72
  "ESIMVAULT_API_TOKEN": "YOUR_KEY"
73
73
  }
@@ -85,7 +85,7 @@ Add to `~/.cursor/mcp.json`:
85
85
  "mcpServers": {
86
86
  "carrier": {
87
87
  "command": "npx",
88
- "args": ["-y", "@carrierllc/mcp"],
88
+ "args": ["-y", "-p", "@carrierllc/mcp", "carrier-mcp"],
89
89
  "env": {
90
90
  "ESIMVAULT_API_TOKEN": "YOUR_KEY"
91
91
  }
@@ -103,7 +103,7 @@ Add to `~/.codeium/windsurf/mcp_config.json`:
103
103
  "mcpServers": {
104
104
  "carrier": {
105
105
  "command": "npx",
106
- "args": ["-y", "@carrierllc/mcp"],
106
+ "args": ["-y", "-p", "@carrierllc/mcp", "carrier-mcp"],
107
107
  "env": {
108
108
  "ESIMVAULT_API_TOKEN": "YOUR_KEY"
109
109
  }
@@ -1792,7 +1792,7 @@ function storefrontScreen(input) {
1792
1792
  // package.json
1793
1793
  var package_default = {
1794
1794
  name: "@carrierllc/mcp",
1795
- version: "0.9.2",
1795
+ version: "0.9.3",
1796
1796
  description: "Carrier MCP \u2014 natural-language control of MVNO/eSIM fleets via eSIMVault OCS. Stdio mode for direct integration with Claude Desktop, Cursor, Windsurf, and MCP-compatible clients. Ships the `carrier` CLI (plugin install + white-label eSIM storefront scaffold).",
1797
1797
  license: "MIT",
1798
1798
  author: "Carrier (Lifecycle Innovations Limited)",
@@ -1840,7 +1840,7 @@ var package_default = {
1840
1840
  zod: "^4.4.3"
1841
1841
  },
1842
1842
  devDependencies: {
1843
- "@cloudflare/workers-types": "^4.20260521.1",
1843
+ "@cloudflare/workers-types": "^5.20260819.1",
1844
1844
  "@types/node": "^26.1.2",
1845
1845
  "@typescript-eslint/eslint-plugin": "^8.59.4",
1846
1846
  "@typescript-eslint/parser": "^8.67.0",
@@ -1876,7 +1876,113 @@ var package_default = {
1876
1876
  // src/version.ts
1877
1877
  var CARRIER_VERSION = package_default.version;
1878
1878
 
1879
- // ../../packages/ocs-client/dist/chunk-EUYDFZ33.js
1879
+ // ../../packages/ocs-client/dist/chunk-CGMK4FTD.js
1880
+ var DEFAULT_OCS_BUDGET = {
1881
+ attemptMs: 3e4,
1882
+ maxAttempts: 3,
1883
+ totalMs: 75e3
1884
+ };
1885
+ var OCS_METHOD_BUDGETS = {
1886
+ // 3.4 MB, 88-128 s measured. 180 s is 1.4x the slowest observation. One
1887
+ // attempt: payload size does not vary, so a retry only doubles the wait.
1888
+ getCustomerTariff: { attemptMs: 18e4, maxAttempts: 1, totalMs: 18e4 }
1889
+ };
1890
+ var MIN_ATTEMPT_BUDGET_MS = 1e3;
1891
+ var BACKOFF_BASE_MS = 500;
1892
+ var OcsTimeoutError = class extends Error {
1893
+ constructor(method, timeoutMs, budgetMs = timeoutMs, attempts = 1) {
1894
+ super(
1895
+ `[${method}] OCS request timed out after ${timeoutMs}ms` + (attempts > 1 ? ` on each of ${attempts} attempts` : "") + (budgetMs > timeoutMs ? ` (budget ${budgetMs}ms)` : "") + `. The upstream did not deliver a complete response in time \u2014 usually an oversized payload or a wedged upstream. Narrow the request, or raise OCS_TIMEOUT_MS / OCS_TOTAL_TIMEOUT_MS if this method is legitimately slower here.`
1896
+ );
1897
+ this.method = method;
1898
+ this.timeoutMs = timeoutMs;
1899
+ this.budgetMs = budgetMs;
1900
+ this.attempts = attempts;
1901
+ this.name = "OcsTimeoutError";
1902
+ }
1903
+ method;
1904
+ timeoutMs;
1905
+ budgetMs;
1906
+ attempts;
1907
+ };
1908
+ function envInt(name) {
1909
+ const proc = globalThis["process"];
1910
+ const parsed = Number(proc?.env?.[name]);
1911
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
1912
+ }
1913
+ function ocsBudgetFor(method) {
1914
+ const base = OCS_METHOD_BUDGETS[method] ?? DEFAULT_OCS_BUDGET;
1915
+ const attemptMs = envInt("OCS_TIMEOUT_MS") ?? base.attemptMs;
1916
+ const totalMs = Math.max(envInt("OCS_TOTAL_TIMEOUT_MS") ?? base.totalMs, attemptMs);
1917
+ return { attemptMs, maxAttempts: base.maxAttempts, totalMs };
1918
+ }
1919
+ function sleep2(ms, signal) {
1920
+ return new Promise((resolve) => {
1921
+ const timer = setTimeout(finish, ms);
1922
+ function finish() {
1923
+ clearTimeout(timer);
1924
+ signal.removeEventListener("abort", finish);
1925
+ resolve();
1926
+ }
1927
+ signal.addEventListener("abort", finish, { once: true });
1928
+ });
1929
+ }
1930
+ async function withDeadline(work, deadline, ms) {
1931
+ let timer;
1932
+ try {
1933
+ return await Promise.race([
1934
+ work,
1935
+ new Promise((_resolve, reject) => {
1936
+ timer = setTimeout(() => reject(deadline), ms);
1937
+ })
1938
+ ]);
1939
+ } finally {
1940
+ if (timer !== void 0) clearTimeout(timer);
1941
+ }
1942
+ }
1943
+ async function runWithBudget(method, attempt, budget = ocsBudgetFor(method)) {
1944
+ const deadlineAt = Date.now() + budget.totalMs;
1945
+ const budgetController = new AbortController();
1946
+ const budgetTimer = setTimeout(() => budgetController.abort(), budget.totalMs);
1947
+ let attempts = 0;
1948
+ let lastTimeout;
1949
+ try {
1950
+ for (let i = 0; i < budget.maxAttempts; i++) {
1951
+ const remaining = deadlineAt - Date.now();
1952
+ if (remaining < MIN_ATTEMPT_BUDGET_MS) break;
1953
+ const attemptMs = Math.min(budget.attemptMs, remaining);
1954
+ attempts++;
1955
+ const attemptController = new AbortController();
1956
+ const onBudgetAbort = () => attemptController.abort();
1957
+ budgetController.signal.addEventListener("abort", onBudgetAbort, { once: true });
1958
+ const attemptTimer = setTimeout(() => attemptController.abort(), attemptMs);
1959
+ const breach = new OcsTimeoutError(method, attemptMs, budget.totalMs, attempts);
1960
+ try {
1961
+ return await withDeadline(
1962
+ attempt(attemptController.signal, attemptMs),
1963
+ breach,
1964
+ attemptMs
1965
+ );
1966
+ } catch (err) {
1967
+ if (!(err instanceof OcsTimeoutError)) throw err;
1968
+ lastTimeout = new OcsTimeoutError(method, attemptMs, budget.totalMs, attempts);
1969
+ } finally {
1970
+ clearTimeout(attemptTimer);
1971
+ budgetController.signal.removeEventListener("abort", onBudgetAbort);
1972
+ attemptController.abort();
1973
+ }
1974
+ if (i === budget.maxAttempts - 1) break;
1975
+ const backoff = Math.random() * BACKOFF_BASE_MS * 2 ** i;
1976
+ if (Date.now() + backoff + MIN_ATTEMPT_BUDGET_MS > deadlineAt) break;
1977
+ await sleep2(backoff, budgetController.signal);
1978
+ }
1979
+ } finally {
1980
+ clearTimeout(budgetTimer);
1981
+ }
1982
+ throw lastTimeout ?? new OcsTimeoutError(method, budget.attemptMs, budget.totalMs, Math.max(attempts, 1));
1983
+ }
1984
+
1985
+ // ../../packages/ocs-client/dist/chunk-OPUTHPZF.js
1880
1986
  var ENDPOINT_LIMITS_PER_MIN = {
1881
1987
  // Global sentinel (keyed as "__global__")
1882
1988
  __global__: 600,
@@ -2036,6 +2142,24 @@ function getRateLimitWindowCounts(resellerKey, endpoint) {
2036
2142
  }
2037
2143
  return { calls_in_window, batch_calls_in_window };
2038
2144
  }
2145
+ var OCS_PARAM_RENAMES = {
2146
+ getResellerInfo: { resellerId: "id" }
2147
+ };
2148
+ function applyParamRenames(method, params) {
2149
+ const renames = OCS_PARAM_RENAMES[method];
2150
+ if (!renames || !params || typeof params !== "object" || Array.isArray(params)) {
2151
+ return params;
2152
+ }
2153
+ const record = params;
2154
+ let out;
2155
+ for (const [from, to] of Object.entries(renames)) {
2156
+ if (!Object.prototype.hasOwnProperty.call(record, from)) continue;
2157
+ out ??= { ...record };
2158
+ if (!Object.prototype.hasOwnProperty.call(record, to)) out[to] = record[from];
2159
+ delete out[from];
2160
+ }
2161
+ return out ?? params;
2162
+ }
2039
2163
 
2040
2164
  // ../../packages/ocs-client/dist/chunk-JWSVJVAF.js
2041
2165
  var OCS_MAX_USAGE_WINDOW_DAYS = 7;
@@ -2388,6 +2512,195 @@ var ROUTER_RULES = `Rules:
2388
2512
  7. A tool answers the question only if its description says it does. Do not infer capability from the tool's NAME: several names share words with unrelated questions, and picking on the name alone has produced confidently wrong answers.
2389
2513
  8. Read the "Do NOT use this to \u2026" steers in a description as hard exclusions. They exist because that tool is the common wrong answer for a neighbouring question, and they name the tool to use instead.
2390
2514
  9. Prefer carrier_clarify over a tool that would return real data about a different question. Answering "which subscribers erode margin" when the user asked "what do my users' megabytes cost in total" is worse than admitting the gap, because the output looks like an answer.`;
2515
+ var OCS_LIST_KEYS = {
2516
+ subscribers: ["subscriberList"],
2517
+ templates: ["template", "prepaidPackageTemplate"],
2518
+ zones: ["listDetailedLocationZone", "locationZone", "zone", "zones"],
2519
+ resellers: ["reseller"],
2520
+ packages: ["prepaidPackage", "package"]
2521
+ };
2522
+ function ocsArray(data, keys) {
2523
+ if (Array.isArray(data)) return data;
2524
+ if (data === null || typeof data !== "object") return [];
2525
+ const candidates = typeof keys === "string" ? OCS_LIST_KEYS[keys] : keys;
2526
+ const obj = data;
2527
+ for (const key of candidates) {
2528
+ const v = obj[key];
2529
+ if (Array.isArray(v)) return v;
2530
+ }
2531
+ return [];
2532
+ }
2533
+ var FACET_TALLY_MAX = 25;
2534
+ var OCS_LIST_FACETS = {
2535
+ templates: [
2536
+ { label: "recurring", paths: ["recurring"] },
2537
+ {
2538
+ label: "locationZone",
2539
+ paths: [
2540
+ "rdbLocationZones.locationzonename",
2541
+ "locationzonename",
2542
+ "locationzoneid"
2543
+ ]
2544
+ }
2545
+ ],
2546
+ subscribers: [{ label: "status", paths: ["status"] }],
2547
+ packages: [
2548
+ { label: "status", paths: ["status"] },
2549
+ { label: "recurring", paths: ["recurring"] }
2550
+ ],
2551
+ zones: [],
2552
+ resellers: []
2553
+ };
2554
+ var OCS_LIST_NESTED = {
2555
+ resellers: { label: "accounts", path: "account" }
2556
+ };
2557
+ function pagingHints(data) {
2558
+ if (data === null || typeof data !== "object" || Array.isArray(data)) {
2559
+ return { hasMore: false, nbFound: void 0 };
2560
+ }
2561
+ const obj = data;
2562
+ return {
2563
+ hasMore: obj.hasMore === true,
2564
+ nbFound: typeof obj.nbFound === "number" ? obj.nbFound : void 0
2565
+ };
2566
+ }
2567
+ function readPath(row, path) {
2568
+ let cur = row;
2569
+ for (const seg of path.split(".")) {
2570
+ if (cur === null || typeof cur !== "object") return void 0;
2571
+ cur = cur[seg];
2572
+ }
2573
+ return cur;
2574
+ }
2575
+ function facetKey(v) {
2576
+ if (v === null || v === void 0) return null;
2577
+ if (typeof v === "boolean" || typeof v === "number") return String(v);
2578
+ if (typeof v === "string") return v;
2579
+ return null;
2580
+ }
2581
+ function ocsListSummary(data, kind, facets = OCS_LIST_FACETS[kind]) {
2582
+ const rows = ocsArray(data, kind);
2583
+ const breakdown = {};
2584
+ for (const facet of facets) {
2585
+ const counts = /* @__PURE__ */ new Map();
2586
+ let missing = 0;
2587
+ for (const row of rows) {
2588
+ if (row === null || typeof row !== "object") {
2589
+ missing += 1;
2590
+ continue;
2591
+ }
2592
+ let key = null;
2593
+ for (const path of facet.paths) {
2594
+ key = facetKey(readPath(row, path));
2595
+ if (key !== null) break;
2596
+ }
2597
+ if (key === null) {
2598
+ missing += 1;
2599
+ continue;
2600
+ }
2601
+ counts.set(key, (counts.get(key) ?? 0) + 1);
2602
+ }
2603
+ if (counts.size === 0 && missing === 0) continue;
2604
+ const entry = { distinct: counts.size };
2605
+ if (counts.size > 0 && counts.size <= FACET_TALLY_MAX) {
2606
+ entry.counts = Object.fromEntries(
2607
+ [...counts].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
2608
+ );
2609
+ }
2610
+ if (missing > 0) entry.missing = missing;
2611
+ breakdown[facet.label] = entry;
2612
+ }
2613
+ const { hasMore, nbFound } = pagingHints(data);
2614
+ const more = hasMore || nbFound !== void 0 && nbFound > rows.length;
2615
+ const summary = {
2616
+ total: rows.length,
2617
+ note: `Counted server-side: ${rows.length} row${rows.length === 1 ? "" : "s"} in this result. Use these figures directly rather than recounting the rows below. ` + (more ? "This is ONE PAGE \u2014 OCS holds more rows than are returned here" + (nbFound !== void 0 ? ` (${nbFound} match upstream)` : "") + ", so do not report `total` as a fleet-wide figure; narrow the filters or page for the rest." : "Nothing was truncated: these are all the rows OCS returned.")
2618
+ };
2619
+ if (more) {
2620
+ summary.moreAvailable = true;
2621
+ if (nbFound !== void 0) summary.upstreamTotal = nbFound;
2622
+ }
2623
+ const nest = OCS_LIST_NESTED[kind];
2624
+ if (nest) {
2625
+ const n = rows.reduce((acc, row) => {
2626
+ const child = row && typeof row === "object" ? readPath(row, nest.path) : void 0;
2627
+ return acc + (Array.isArray(child) ? child.length : 0);
2628
+ }, 0);
2629
+ summary.nested = { [nest.label]: n };
2630
+ summary.note += ` This envelope NESTS: \`total\` counts the outer rows, while there are ${n} ${nest.label} inside them under \`${nest.path}\`. A question about ${nest.label} is answered by that figure, not by \`total\`.`;
2631
+ }
2632
+ if (Object.keys(breakdown).length > 0) {
2633
+ summary.breakdown = breakdown;
2634
+ summary.note += " `breakdown` gives, per field, how many distinct values occur and how many rows carry each (`missing` counts rows where the field is absent).";
2635
+ }
2636
+ return summary;
2637
+ }
2638
+ function withOcsListSummary(data, kind, facets) {
2639
+ const summary = ocsListSummary(data, kind, facets);
2640
+ if (data !== null && typeof data === "object" && !Array.isArray(data)) {
2641
+ return { summary, ...data };
2642
+ }
2643
+ return { summary, [OCS_LIST_KEYS[kind][0]]: data };
2644
+ }
2645
+ var OCS_DATA_QUANTITY_TYPE = "33";
2646
+ function readBytes(entry) {
2647
+ const total = entry.total;
2648
+ if (total !== null && typeof total === "object") {
2649
+ const perType = total.quantityPerType;
2650
+ if (perType !== null && typeof perType === "object") {
2651
+ const v = perType[OCS_DATA_QUANTITY_TYPE];
2652
+ if (v !== void 0) return Number(v) || 0;
2653
+ }
2654
+ }
2655
+ return Number(entry.dataBytes ?? entry.dataVolume ?? entry.totalData ?? 0) || 0;
2656
+ }
2657
+ function extractDailyUsage(data) {
2658
+ const out = [];
2659
+ if (data === null || typeof data !== "object") return out;
2660
+ if (Array.isArray(data)) {
2661
+ for (const entry of data) {
2662
+ if (entry === null || typeof entry !== "object") continue;
2663
+ const e = entry;
2664
+ out.push({ date: String(e.date ?? e.day ?? "?"), bytes: readBytes(e) });
2665
+ }
2666
+ return out;
2667
+ }
2668
+ const usages = data.usages;
2669
+ if (!Array.isArray(usages)) return out;
2670
+ for (const usage of usages) {
2671
+ if (usage === null || typeof usage !== "object") continue;
2672
+ const periods = usage.subsPeriodUsages;
2673
+ if (!Array.isArray(periods)) continue;
2674
+ for (const period of periods) {
2675
+ if (period === null || typeof period !== "object") continue;
2676
+ const p = period;
2677
+ out.push({ date: String(p.day ?? p.date ?? "?"), bytes: readBytes(p) });
2678
+ }
2679
+ }
2680
+ return out;
2681
+ }
2682
+ function daysUntil(dateStr) {
2683
+ const now = /* @__PURE__ */ new Date();
2684
+ const target = new Date(dateStr);
2685
+ return Math.ceil((target.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24));
2686
+ }
2687
+ function formatBytes(bytes) {
2688
+ if (bytes === 0) return "0 B";
2689
+ const units = ["B", "KB", "MB", "GB", "TB"];
2690
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
2691
+ return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${units[i]}`;
2692
+ }
2693
+ function isActiveSubscriber(row) {
2694
+ return (extractSubscriberStatus(row) ?? "").toUpperCase() === "ACTIVE";
2695
+ }
2696
+ function filterActiveSubscribers(rows) {
2697
+ return rows.filter((row) => isActiveSubscriber(row));
2698
+ }
2699
+ function subscriberRows(raw) {
2700
+ if (Array.isArray(raw)) return raw;
2701
+ const list = raw?.subscriberList;
2702
+ return Array.isArray(list) ? list : [];
2703
+ }
2391
2704
 
2392
2705
  // src/lib/storefront-logo.ts
2393
2706
  var OPENAI_KEY_NAMES = [
@@ -2510,9 +2823,11 @@ async function generateStorefrontLogo(input) {
2510
2823
  }
2511
2824
 
2512
2825
  export {
2826
+ runWithBudget,
2513
2827
  acquireEndpointSlot,
2514
2828
  getLimitForEndpoint,
2515
2829
  getRateLimitWindowCounts,
2830
+ applyParamRenames,
2516
2831
  OCS_MAX_USAGE_WINDOW_DAYS,
2517
2832
  clampUsagePeriod,
2518
2833
  lastNDaysPeriod,
@@ -2531,6 +2846,12 @@ export {
2531
2846
  recordToolDescription,
2532
2847
  buildRouterCatalog,
2533
2848
  ROUTER_RULES,
2849
+ withOcsListSummary,
2850
+ extractDailyUsage,
2851
+ daysUntil,
2852
+ formatBytes,
2853
+ filterActiveSubscribers,
2854
+ subscriberRows,
2534
2855
  generateStorefrontLogo,
2535
2856
  run,
2536
2857
  which,
@@ -2569,4 +2890,4 @@ export {
2569
2890
  storefrontScreen,
2570
2891
  CARRIER_VERSION
2571
2892
  };
2572
- //# sourceMappingURL=chunk-4XHSOF62.js.map
2893
+ //# sourceMappingURL=chunk-KHNJNJX3.js.map