@carrierllc/mcp 0.13.12 → 0.13.14
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/{chunk-IQDAU7WA.js → chunk-DIHA6TVT.js} +602 -5
- package/dist/chunk-DIHA6TVT.js.map +1 -0
- package/dist/cli.js +97 -10
- package/dist/cli.js.map +1 -1
- package/dist/index.js +202 -74
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/plugin/carrier/.claude-plugin/plugin.json +1 -1
- package/plugin/carrier/skills/publish-storefront/SKILL.md +2 -2
- package/dist/chunk-IQDAU7WA.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
CARRIER_VERSION,
|
|
4
|
+
NETWORK_DEFAULT_MODES,
|
|
4
5
|
OCS_MAX_USAGE_WINDOW_DAYS,
|
|
5
6
|
OcsApiError,
|
|
6
7
|
OcsClient,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
formatProbes,
|
|
29
30
|
generateStorefrontLogo,
|
|
30
31
|
getLimitForEndpoint,
|
|
32
|
+
getNetworkDefault,
|
|
31
33
|
getRateLimitWindowCounts,
|
|
32
34
|
gyCountryHistoryFromResponse,
|
|
33
35
|
imsiFromSubscriberRecord,
|
|
@@ -40,6 +42,8 @@ import {
|
|
|
40
42
|
mccToIso,
|
|
41
43
|
mergeEnvLocal,
|
|
42
44
|
modifySubscriberStatusParams,
|
|
45
|
+
networkDefaultKey,
|
|
46
|
+
networkDefaultPartition,
|
|
43
47
|
networkEventRows,
|
|
44
48
|
networkEventsOverPeriodParams,
|
|
45
49
|
ocsHlrBitrate,
|
|
@@ -53,6 +57,7 @@ import {
|
|
|
53
57
|
packageName,
|
|
54
58
|
packageTemplateCreateFromToolJson,
|
|
55
59
|
packageTemplateModifyFromToolJson,
|
|
60
|
+
planNetworkDefault,
|
|
56
61
|
prepaidPackageLimits,
|
|
57
62
|
prepaidPackageRows,
|
|
58
63
|
probeAll,
|
|
@@ -63,6 +68,8 @@ import {
|
|
|
63
68
|
recurringPackageParams,
|
|
64
69
|
renderHtml,
|
|
65
70
|
repairPlanFor,
|
|
71
|
+
scopeListSubscriberParams,
|
|
72
|
+
setNetworkDefault,
|
|
66
73
|
staleActivePackages,
|
|
67
74
|
storefrontClerkUrls,
|
|
68
75
|
subscriberIdParams,
|
|
@@ -73,7 +80,7 @@ import {
|
|
|
73
80
|
verifyStorefront,
|
|
74
81
|
withOcsListSummary,
|
|
75
82
|
withTemplateResellerId
|
|
76
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-DIHA6TVT.js";
|
|
77
84
|
import "./chunk-SHKKVIIA.js";
|
|
78
85
|
|
|
79
86
|
// src/index.ts
|
|
@@ -1066,22 +1073,22 @@ function boundZonesResult(raw, opts = {}) {
|
|
|
1066
1073
|
const { verbose = false, resellerId } = opts;
|
|
1067
1074
|
const maxZones = opts.maxZones ?? ZONES_MAX_ZONES;
|
|
1068
1075
|
const maxOperators = opts.maxOperators ?? (verbose ? ZONES_VERBOSE_MAX_OPERATORS : ZONES_MAX_OPERATORS);
|
|
1069
|
-
const totalOperators = all.reduce((n,
|
|
1076
|
+
const totalOperators = all.reduce((n, z25) => n + (z25.operators?.length ?? 0), 0);
|
|
1070
1077
|
const keptZones = all.slice(0, maxZones);
|
|
1071
1078
|
let budget = maxOperators;
|
|
1072
1079
|
let returnedOperators = 0;
|
|
1073
|
-
const zones = keptZones.map((
|
|
1074
|
-
const ops =
|
|
1080
|
+
const zones = keptZones.map((z25) => {
|
|
1081
|
+
const ops = z25.operators ?? [];
|
|
1075
1082
|
const take = Math.max(0, Math.min(ops.length, budget));
|
|
1076
1083
|
budget -= take;
|
|
1077
1084
|
returnedOperators += take;
|
|
1078
1085
|
const sliced = ops.slice(0, take);
|
|
1079
1086
|
if (verbose) {
|
|
1080
|
-
return { ...
|
|
1087
|
+
return { ...z25, operators: sliced, operatorCount: ops.length };
|
|
1081
1088
|
}
|
|
1082
1089
|
return {
|
|
1083
|
-
zoneId:
|
|
1084
|
-
zoneName:
|
|
1090
|
+
zoneId: z25.zoneId,
|
|
1091
|
+
zoneName: z25.zoneName,
|
|
1085
1092
|
operatorCount: ops.length,
|
|
1086
1093
|
operators: sliced.map((o) => ({
|
|
1087
1094
|
net: o.networkId,
|
|
@@ -1512,7 +1519,7 @@ function registerAllTools(server2, ctx) {
|
|
|
1512
1519
|
"list_subscribers",
|
|
1513
1520
|
{
|
|
1514
1521
|
title: "List Subscribers",
|
|
1515
|
-
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
|
|
1522
|
+
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.",
|
|
1516
1523
|
inputSchema: z.object({
|
|
1517
1524
|
imsi: z.string().optional().describe("Filter by IMSI"),
|
|
1518
1525
|
iccid: z.string().optional().describe("Filter by ICCID"),
|
|
@@ -1522,12 +1529,7 @@ function registerAllTools(server2, ctx) {
|
|
|
1522
1529
|
status: z.string().optional().describe("Filter by status"),
|
|
1523
1530
|
offset: z.number().optional().describe("Pagination offset"),
|
|
1524
1531
|
limit: z.number().optional().describe("Max results to return")
|
|
1525
|
-
})
|
|
1526
|
-
(d) => d.imsi !== void 0 || d.iccid !== void 0 || d.activationCode !== void 0 || d.accountId !== void 0 || d.msisdn !== void 0,
|
|
1527
|
-
{
|
|
1528
|
-
message: "Provide at least one of: imsi, iccid, activationCode, accountId, msisdn"
|
|
1529
|
-
}
|
|
1530
|
-
),
|
|
1532
|
+
}),
|
|
1531
1533
|
annotations: { readOnlyHint: true }
|
|
1532
1534
|
},
|
|
1533
1535
|
wrapHandler(
|
|
@@ -1538,13 +1540,25 @@ function registerAllTools(server2, ctx) {
|
|
|
1538
1540
|
async (args, token2) => {
|
|
1539
1541
|
const params = buildListSubscriberParams(args);
|
|
1540
1542
|
const client2 = new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2);
|
|
1541
|
-
const
|
|
1543
|
+
const scope = await scopeListSubscriberParams({
|
|
1544
|
+
params,
|
|
1545
|
+
...ctx.managedAuth.managed ? { managed: true } : {},
|
|
1546
|
+
...ctx.managedAuth.preferredAccountId ? { preferredAccountId: ctx.managedAuth.preferredAccountId } : {},
|
|
1547
|
+
call: (method, callParams) => client2.call(method, callParams)
|
|
1548
|
+
});
|
|
1549
|
+
if (!scope.ok) throw new Error(scope.message);
|
|
1550
|
+
const raw = await client2.call("listSubscriber", scope.params);
|
|
1542
1551
|
const payload = applySubscriberFilters(raw, args);
|
|
1552
|
+
const summarised = withOcsListSummary(payload, "subscribers");
|
|
1543
1553
|
return {
|
|
1544
1554
|
content: [
|
|
1545
1555
|
{
|
|
1546
1556
|
type: "text",
|
|
1547
|
-
text: JSON.stringify(
|
|
1557
|
+
text: JSON.stringify(
|
|
1558
|
+
scope.defaultAccount ? { ...summarised, default_account: scope.defaultAccount } : summarised,
|
|
1559
|
+
null,
|
|
1560
|
+
2
|
|
1561
|
+
)
|
|
1548
1562
|
}
|
|
1549
1563
|
]
|
|
1550
1564
|
};
|
|
@@ -5069,7 +5083,7 @@ function registerAllBacklogTools(server2, ctx) {
|
|
|
5069
5083
|
}
|
|
5070
5084
|
|
|
5071
5085
|
// src/tools-carrier-ask.ts
|
|
5072
|
-
import { z as
|
|
5086
|
+
import { z as z13 } from "zod";
|
|
5073
5087
|
|
|
5074
5088
|
// src/tools-ui-agent.ts
|
|
5075
5089
|
import { z as z4 } from "zod";
|
|
@@ -7706,6 +7720,118 @@ function registerAtlasTools(server2, ctx) {
|
|
|
7706
7720
|
);
|
|
7707
7721
|
}
|
|
7708
7722
|
|
|
7723
|
+
// src/tools-network-default.ts
|
|
7724
|
+
import { z as z12 } from "zod";
|
|
7725
|
+
var NETWORK_DEFAULT_TOOL_SCOPES = {
|
|
7726
|
+
get_network_default: "read",
|
|
7727
|
+
set_network_default: "write",
|
|
7728
|
+
plan_network_default: "read"
|
|
7729
|
+
};
|
|
7730
|
+
var choiceShape = z12.object({
|
|
7731
|
+
mode: z12.enum(NETWORK_DEFAULT_MODES),
|
|
7732
|
+
steeringListId: z12.number().int().positive().optional()
|
|
7733
|
+
}).describe("`cheapest_available`, `all`, or `custom` with a steeringListId from list_steering_lists");
|
|
7734
|
+
function ioFor(ctx, token2) {
|
|
7735
|
+
const kv = ctx.env.CARRIER_USERS;
|
|
7736
|
+
const key = networkDefaultKey(
|
|
7737
|
+
networkDefaultPartition({
|
|
7738
|
+
...ctx.props.org_id ? { orgId: ctx.props.org_id } : {},
|
|
7739
|
+
sub: ctx.props.sub
|
|
7740
|
+
})
|
|
7741
|
+
);
|
|
7742
|
+
const io = {
|
|
7743
|
+
resolveResellerId: () => getDefaultResellerId(ctx.env, token2),
|
|
7744
|
+
// Single operator, own OCS token: the token is the tenant boundary (see
|
|
7745
|
+
// docs/single-tenant-model.md), so no managed scoping is needed here.
|
|
7746
|
+
callOcs: (method, resellerId) => new OcsClient(ctx.env.CARRIER_OCS_BASE_URL, token2).call(method, resellerId),
|
|
7747
|
+
// No binding means no store: say so (undefined), never pretend "nothing stored".
|
|
7748
|
+
readStored: () => kv ? kv.get(key, "json").catch(() => null) : void 0
|
|
7749
|
+
};
|
|
7750
|
+
if (kv) {
|
|
7751
|
+
io.writeStored = async (record) => {
|
|
7752
|
+
await kv.put(key, JSON.stringify(record));
|
|
7753
|
+
};
|
|
7754
|
+
}
|
|
7755
|
+
return io;
|
|
7756
|
+
}
|
|
7757
|
+
function asResult(r) {
|
|
7758
|
+
if (r.ok) return { content: [{ type: "text", text: JSON.stringify(r.data, null, 2) }] };
|
|
7759
|
+
return {
|
|
7760
|
+
isError: true,
|
|
7761
|
+
content: [{ type: "text", text: JSON.stringify({ error: r.error, code: r.code, status: r.status }) }]
|
|
7762
|
+
};
|
|
7763
|
+
}
|
|
7764
|
+
function registerNetworkDefaultTools(server2, ctx) {
|
|
7765
|
+
server2.registerTool(
|
|
7766
|
+
"get_network_default",
|
|
7767
|
+
{
|
|
7768
|
+
title: "Get Network Default",
|
|
7769
|
+
description: "Use this to read the network default for this organisation: which networks a SIM is given in a country. The system default is `cheapest_available` (the lowest data rate among networks really available in the country). The org can choose `all` or a `custom` steering list instead. Returns: `systemDefault`, `orgDefault` (null when none stored), `effectiveMode`, `warnings`, and `appliesTo` (always `new_assignments_only`: existing SIMs are never changed by this setting). Do NOT use this to see which list a specific SIM has \u2014 use `get_subscriber`.",
|
|
7770
|
+
inputSchema: {},
|
|
7771
|
+
annotations: { readOnlyHint: true }
|
|
7772
|
+
},
|
|
7773
|
+
wrapHandler(
|
|
7774
|
+
"get_network_default",
|
|
7775
|
+
"getCustomerTariff",
|
|
7776
|
+
NETWORK_DEFAULT_TOOL_SCOPES["get_network_default"],
|
|
7777
|
+
ctx,
|
|
7778
|
+
async (_args, token2) => asResult(await getNetworkDefault(ioFor(ctx, token2)))
|
|
7779
|
+
)
|
|
7780
|
+
);
|
|
7781
|
+
server2.registerTool(
|
|
7782
|
+
"set_network_default",
|
|
7783
|
+
{
|
|
7784
|
+
title: "Set Network Default",
|
|
7785
|
+
description: "Use this to set the organisation's network default to `cheapest_available`, `all`, or `custom` (a steering list id from `list_steering_lists`, which must exist). This only stores the setting. It changes no SIM, writes nothing to OCS and does not migrate existing customers: it applies to NEW assignments. A single assignment can still override it with `plan_network_default`'s `simOverride`. Params: `mode` (required), `steeringListId` (required when mode is `custom`), `dry_run` (preview only). Do NOT use this to assign a list to one SIM \u2014 use `modify_subscriber_steering_list`.",
|
|
7786
|
+
inputSchema: {
|
|
7787
|
+
mode: z12.enum(NETWORK_DEFAULT_MODES).describe("cheapest_available | all | custom"),
|
|
7788
|
+
steeringListId: z12.number().int().positive().optional().describe("Required when mode is custom"),
|
|
7789
|
+
dry_run: z12.boolean().optional().describe("If true, validate and return what would be stored; store nothing")
|
|
7790
|
+
},
|
|
7791
|
+
annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false }
|
|
7792
|
+
},
|
|
7793
|
+
wrapHandler(
|
|
7794
|
+
"set_network_default",
|
|
7795
|
+
"listSteeringList",
|
|
7796
|
+
NETWORK_DEFAULT_TOOL_SCOPES["set_network_default"],
|
|
7797
|
+
ctx,
|
|
7798
|
+
async (args, token2) => {
|
|
7799
|
+
const body2 = args.mode === "custom" ? { mode: args.mode, steeringListId: args.steeringListId } : { mode: args.mode };
|
|
7800
|
+
const io = ioFor(ctx, token2);
|
|
7801
|
+
if (args.dry_run === true) {
|
|
7802
|
+
if (!io.writeStored) return asResult(await setNetworkDefault(io, body2, ctx.props.sub, /* @__PURE__ */ new Date()));
|
|
7803
|
+
const probe = await setNetworkDefault({ ...io, writeStored: async () => {
|
|
7804
|
+
} }, body2, ctx.props.sub, /* @__PURE__ */ new Date());
|
|
7805
|
+
if (!probe.ok) return asResult(probe);
|
|
7806
|
+
return asResult({ ok: true, data: { dry_run: true, wouldStore: probe.data.orgDefault, appliesTo: probe.data.appliesTo } });
|
|
7807
|
+
}
|
|
7808
|
+
return asResult(await setNetworkDefault(io, body2, ctx.props.sub, /* @__PURE__ */ new Date()));
|
|
7809
|
+
}
|
|
7810
|
+
)
|
|
7811
|
+
);
|
|
7812
|
+
server2.registerTool(
|
|
7813
|
+
"plan_network_default",
|
|
7814
|
+
{
|
|
7815
|
+
title: "Plan Network Default",
|
|
7816
|
+
description: "Use this to see which networks a SIM would be given in one or more countries, before anything is assigned. By default that is the cheapest available network per country (lowest data rate above zero, among rules really in force). Precedence: `simOverride`, then `packageOverride`, then the org default, then `cheapest_available`. Read only: `applied` is always false. If the tariff is missing or cannot be compared it falls back to ALL networks and says so in `warnings` and `summary` (`fellBackToAll: true`); never present that as a cheapest choice. The price basis is the wholesale data rate per MB, not the end price. Creating a steering list has no OCS REST method, so when no existing list matches the result is `needs_list`, not a created list. Params: `countries` (ISO-3166 alpha-2 codes, required), `simOverride`, `packageOverride`, `resellerId` (optional).",
|
|
7817
|
+
inputSchema: {
|
|
7818
|
+
countries: z12.array(z12.string().length(2)).min(1).describe('ISO-3166 alpha-2 codes, e.g. ["CA"]'),
|
|
7819
|
+
simOverride: choiceShape.optional(),
|
|
7820
|
+
packageOverride: choiceShape.optional(),
|
|
7821
|
+
resellerId: z12.number().int().positive().optional().describe("Omit to use the token owner's reseller")
|
|
7822
|
+
},
|
|
7823
|
+
annotations: { readOnlyHint: true }
|
|
7824
|
+
},
|
|
7825
|
+
wrapHandler(
|
|
7826
|
+
"plan_network_default",
|
|
7827
|
+
"getCustomerTariff",
|
|
7828
|
+
NETWORK_DEFAULT_TOOL_SCOPES["plan_network_default"],
|
|
7829
|
+
ctx,
|
|
7830
|
+
async (args, token2) => asResult(await planNetworkDefault(ioFor(ctx, token2), args, /* @__PURE__ */ new Date()))
|
|
7831
|
+
)
|
|
7832
|
+
);
|
|
7833
|
+
}
|
|
7834
|
+
|
|
7709
7835
|
// src/credits.ts
|
|
7710
7836
|
var TIER_CREDIT_ALLOTMENTS = {
|
|
7711
7837
|
free: 5e3,
|
|
@@ -9080,7 +9206,8 @@ var ALL_TOOL_SCOPES = Object.freeze({
|
|
|
9080
9206
|
...STOREFRONT_LOGO_TOOL_SCOPES,
|
|
9081
9207
|
// Storefront deploy tools exist only in this package, not in the Worker.
|
|
9082
9208
|
...STOREFRONT_DEPLOY_TOOL_SCOPES,
|
|
9083
|
-
...ATLAS_TOOL_SCOPES
|
|
9209
|
+
...ATLAS_TOOL_SCOPES,
|
|
9210
|
+
...NETWORK_DEFAULT_TOOL_SCOPES
|
|
9084
9211
|
});
|
|
9085
9212
|
var TOOL_REGISTRY = new Set(Object.keys(ALL_TOOL_SCOPES));
|
|
9086
9213
|
var UNREGISTERED_TOOLS = /* @__PURE__ */ new Set(["stripe_proxy_call"]);
|
|
@@ -9755,15 +9882,15 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
9755
9882
|
title: "Natural Language Carrier Tool Router",
|
|
9756
9883
|
description: "Use this when you want to perform a carrier operation but don't know which specific tool to call. Describe your intent in plain language and carrier_ask will identify the correct tool(s) and suggest the required parameters. For simple read intents, it executes directly and returns results. For destructive operations it returns a confirm_token that you must pass in a second call. Params: `intent` (string \u2014 natural language description of what you want to do), `context` (optional: iccid, account_id, reseller_id if already known). Returns: RouteResult \u2014 confirmed | pending_confirm | ambiguous | none. Do NOT use this when you know the right tool \u2014 direct calls are faster and cheaper.",
|
|
9757
9884
|
inputSchema: {
|
|
9758
|
-
intent:
|
|
9885
|
+
intent: z13.string().min(3).describe(
|
|
9759
9886
|
"Natural-language description of the operation to perform (e.g. 'suspend ICCID 89316 until next month', 'show fleet health', 'assign Europe 5GB package to subscriber 89316...')"
|
|
9760
9887
|
),
|
|
9761
|
-
context:
|
|
9762
|
-
iccid:
|
|
9763
|
-
account_id:
|
|
9764
|
-
reseller_id:
|
|
9888
|
+
context: z13.object({
|
|
9889
|
+
iccid: z13.string().optional().describe("Subscriber ICCID if already known"),
|
|
9890
|
+
account_id: z13.number().optional().describe("Account ID if already known"),
|
|
9891
|
+
reseller_id: z13.number().optional().describe("Reseller ID if already known")
|
|
9765
9892
|
}).optional().describe("Optional pre-resolved context to improve routing accuracy"),
|
|
9766
|
-
confirm_token:
|
|
9893
|
+
confirm_token: z13.string().optional().describe(
|
|
9767
9894
|
"One-time token from a previous carrier_ask call with confirm_required=true. Providing this executes the previously staged destructive operation."
|
|
9768
9895
|
)
|
|
9769
9896
|
}
|
|
@@ -9967,7 +10094,7 @@ function registerAllCarrierAskTools(server2, ctx) {
|
|
|
9967
10094
|
title: "Describe a Carrier Tool",
|
|
9968
10095
|
description: "Get full documentation for any registered Carrier MCP tool: description, parameters, 2-3 example invocations, required scope, destructive flag, and guidance. Params: `tool_name` (exact MCP tool name, e.g. 'assign_package'). Returns: structured tool metadata. Does NOT execute the tool.",
|
|
9969
10096
|
inputSchema: {
|
|
9970
|
-
tool_name:
|
|
10097
|
+
tool_name: z13.string().describe("The exact MCP tool name to describe (e.g. 'assign_package', 'hlr_set_bitrate')")
|
|
9971
10098
|
}
|
|
9972
10099
|
},
|
|
9973
10100
|
wrapHandler(
|
|
@@ -10060,7 +10187,7 @@ function buildExamples(toolName) {
|
|
|
10060
10187
|
}
|
|
10061
10188
|
|
|
10062
10189
|
// src/list-recent-ocs-events.ts
|
|
10063
|
-
import { z as
|
|
10190
|
+
import { z as z14 } from "zod";
|
|
10064
10191
|
|
|
10065
10192
|
// src/ocs-scoping.ts
|
|
10066
10193
|
function managedOwnAccountId(auth) {
|
|
@@ -10228,10 +10355,10 @@ var ALLOWED_EVENT_TYPES = [
|
|
|
10228
10355
|
"usage.anomaly"
|
|
10229
10356
|
];
|
|
10230
10357
|
var listRecentOcsEventsSchema = {
|
|
10231
|
-
iccid:
|
|
10232
|
-
limit:
|
|
10233
|
-
event_types:
|
|
10234
|
-
since:
|
|
10358
|
+
iccid: z14.string().regex(/^\d{19,20}$/).describe("ICCID, 19 or 20 digits, ITU-T E.118 format"),
|
|
10359
|
+
limit: z14.number().int().min(1).max(50).default(20).describe("Max events to return, newest first"),
|
|
10360
|
+
event_types: z14.array(z14.enum(ALLOWED_EVENT_TYPES)).optional().describe("Filter to specific event types"),
|
|
10361
|
+
since: z14.string().datetime().optional().describe("ISO-8601 timestamp; only events after this point")
|
|
10235
10362
|
};
|
|
10236
10363
|
async function listRecentOcsEvents2(iccid, limit, eventTypes, since, kv) {
|
|
10237
10364
|
return listRecentOcsEvents(iccid, limit, eventTypes, since, kv);
|
|
@@ -10294,7 +10421,7 @@ function registerListRecentOcsEventsTool(server2, ctx) {
|
|
|
10294
10421
|
}
|
|
10295
10422
|
|
|
10296
10423
|
// src/tools-rate-governor.ts
|
|
10297
|
-
import { z as
|
|
10424
|
+
import { z as z15 } from "zod";
|
|
10298
10425
|
var MONITORED_ENDPOINTS = [
|
|
10299
10426
|
"subscriberNetworkEventsOverPeriod",
|
|
10300
10427
|
// 30/min
|
|
@@ -10326,7 +10453,7 @@ function registerRateLimitStatusTool(server2, ctx) {
|
|
|
10326
10453
|
title: "API Rate-Limit Governor Status",
|
|
10327
10454
|
description: "Inspect Bridge4IP OCS rate-limit governor state \u2014 bucket fill levels, per-endpoint limits, and 80% alert thresholds. Use this to answer: 'are we rate limited', 'check ocs quota', 'how full is the rate limit bucket', 'rate limit status'. Params: `endpoint` (optional \u2014 OCS camelCase method name to inspect a single bucket, e.g. 'subscriberUsageOverPeriod'; omit for all monitored endpoints). Returns: per-endpoint { limit_per_min, batch_cap_per_min, calls_in_window, batch_calls_in_window, utilisation_pct, alert (true when \u226580% of limit) }. Note: counts come from the in-process sliding-window log (same key as OCS calls: API token + endpoint) \u2014 they reset when the Worker isolate recycles. Use as a relative signal, not an exact counter. Do NOT use this to adjust limits \u2014 limits are set by Bridge4IP contract and cannot be changed via MCP.",
|
|
10328
10455
|
inputSchema: {
|
|
10329
|
-
endpoint:
|
|
10456
|
+
endpoint: z15.string().optional().describe(
|
|
10330
10457
|
"OCS method name in camelCase (e.g. 'subscriberUsageOverPeriod'). Omit to return status for all monitored endpoints."
|
|
10331
10458
|
)
|
|
10332
10459
|
},
|
|
@@ -10382,7 +10509,7 @@ function registerRateLimitStatusTool(server2, ctx) {
|
|
|
10382
10509
|
}
|
|
10383
10510
|
|
|
10384
10511
|
// src/tools-depletion-events.ts
|
|
10385
|
-
import { z as
|
|
10512
|
+
import { z as z16 } from "zod";
|
|
10386
10513
|
function ok4(payload) {
|
|
10387
10514
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
10388
10515
|
}
|
|
@@ -10410,10 +10537,10 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10410
10537
|
title: "Subscriber Bundle Depletion Events",
|
|
10411
10538
|
description: "Look up bundle depletion events for a subscriber (or list recent fleet-wide depletions). Returns the most recent bundle.depleted event stored when the prepaid usage webhook received a last_slice notification. Events are retained for 7 days. Use subscriberId to check a specific subscriber, or omit to list the 20 most recent fleet-wide depletions. Requires 'read' scope.",
|
|
10412
10539
|
inputSchema: {
|
|
10413
|
-
subscriberId:
|
|
10540
|
+
subscriberId: z16.string().optional().describe(
|
|
10414
10541
|
"OCS subscriber ID to look up. Omit to list the 20 most recent fleet-wide depletions."
|
|
10415
10542
|
),
|
|
10416
|
-
since:
|
|
10543
|
+
since: z16.string().optional().describe(
|
|
10417
10544
|
"ISO-8601 timestamp \u2014 only return events at or after this time (for single-subscriber lookup)."
|
|
10418
10545
|
)
|
|
10419
10546
|
},
|
|
@@ -10528,7 +10655,7 @@ function registerDepletionEventsTool(server2, ctx) {
|
|
|
10528
10655
|
}
|
|
10529
10656
|
|
|
10530
10657
|
// src/tools-country-history.ts
|
|
10531
|
-
import { z as
|
|
10658
|
+
import { z as z17 } from "zod";
|
|
10532
10659
|
function isoDate(now, daysAgo = 0) {
|
|
10533
10660
|
const d = new Date(now.getTime() - daysAgo * 864e5);
|
|
10534
10661
|
return d.toISOString().slice(0, 10);
|
|
@@ -10551,10 +10678,10 @@ function registerCountryHistoryTool(server2, ctx) {
|
|
|
10551
10678
|
title: "Subscriber Country History",
|
|
10552
10679
|
description: "Return cross-border movement history for a subscriber, reconstructed from OCS Gy charging events. Reads `gyAnswer.events` from `subscriberNetworkEventsOverPeriod` \u2014 every Gy record carries its own country, operator and MCC \u2014 and collapses consecutive same-country records into stays with first-seen and last-seen timestamps. This needs no webhook: it works with Relay LU and Relay Gy off. Relay `country.entered` events from the 24h ring-buffer are merged in as a supplement when present. Use this to answer: 'where has subscriber X traveled', 'show country history for ICCID', 'border crossings for this SIM'. Params: `subscriberId` (ICCID, 19\u201320 digits), `startDate`/`endDate` (YYYY-MM-DD, max 7 days apart, default the last 7 days), `limit` (1\u201350, default 20, newest first). Returns: `stays` ({ country, country_name, first_seen, last_seen, operators, mccs, event_count, used_bytes }), `transitions` ({ from, to, at }), and `country_events` in the legacy flat shape. `used_bytes` already handles the string-typed `usedUnits` and its 'N/A' values. Do NOT use this for raw per-session records \u2014 use `subscriber_network_events` for those.",
|
|
10553
10680
|
inputSchema: {
|
|
10554
|
-
subscriberId:
|
|
10555
|
-
startDate:
|
|
10556
|
-
endDate:
|
|
10557
|
-
limit:
|
|
10681
|
+
subscriberId: z17.string().regex(/^\d{19,20}$/, "subscriberId must be a 19\u201320 digit ICCID").describe("Subscriber ICCID (19\u201320 digits, ITU-T E.118)"),
|
|
10682
|
+
startDate: z17.string().regex(/^\d{4}-\d{2}-\d{2}$/, "startDate must be YYYY-MM-DD").optional().describe("Start of the period, YYYY-MM-DD inclusive (default: 7 days ago)"),
|
|
10683
|
+
endDate: z17.string().regex(/^\d{4}-\d{2}-\d{2}$/, "endDate must be YYYY-MM-DD").optional().describe("End of the period, YYYY-MM-DD inclusive, max 7 days from start (default: today)"),
|
|
10684
|
+
limit: z17.number().int().min(1).max(50).default(20).optional().describe("Maximum number of country_events to return, newest first (default 20, max 50)")
|
|
10558
10685
|
},
|
|
10559
10686
|
annotations: { readOnlyHint: true, idempotentHint: true }
|
|
10560
10687
|
},
|
|
@@ -10662,7 +10789,7 @@ function registerCountryHistoryTool(server2, ctx) {
|
|
|
10662
10789
|
}
|
|
10663
10790
|
|
|
10664
10791
|
// src/apps/fleet-health-app.ts
|
|
10665
|
-
import { z as
|
|
10792
|
+
import { z as z18 } from "zod";
|
|
10666
10793
|
import {
|
|
10667
10794
|
registerAppTool,
|
|
10668
10795
|
registerAppResource,
|
|
@@ -10778,7 +10905,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
10778
10905
|
title: "Fleet Health Dashboard",
|
|
10779
10906
|
description: "Draw the fleet overview as an interactive panel: eSIM status, per-account breakdown and low-balance alerts. Returns structuredContent for the panel, so it only helps a client that can render MCP UI resources. Do NOT use this when the answer has to be readable text, or in any headless or scripted context \u2014 `fleet_health` returns the same data as prose.",
|
|
10780
10907
|
inputSchema: {
|
|
10781
|
-
accountId:
|
|
10908
|
+
accountId: z18.number().optional().describe("Filter to a specific account (omit for all)")
|
|
10782
10909
|
},
|
|
10783
10910
|
annotations: { readOnlyHint: true },
|
|
10784
10911
|
_meta: {
|
|
@@ -10821,7 +10948,7 @@ function registerFleetHealthApp(server2, ctx) {
|
|
|
10821
10948
|
}
|
|
10822
10949
|
|
|
10823
10950
|
// src/apps/provisioning-wizard.ts
|
|
10824
|
-
import { z as
|
|
10951
|
+
import { z as z19 } from "zod";
|
|
10825
10952
|
import {
|
|
10826
10953
|
registerAppTool as registerAppTool2,
|
|
10827
10954
|
registerAppResource as registerAppResource2,
|
|
@@ -10926,10 +11053,10 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
10926
11053
|
title: "eSIM Provisioning Wizard",
|
|
10927
11054
|
description: "Interactive 3-step wizard to provision an eSIM: select subscriber, choose package template, preview (dry_run) and confirm execution.",
|
|
10928
11055
|
inputSchema: {
|
|
10929
|
-
step:
|
|
10930
|
-
wizardId:
|
|
10931
|
-
subscriber_iccid:
|
|
10932
|
-
package_template_id:
|
|
11056
|
+
step: z19.enum(["init", "select-package", "preview", "confirm"]).describe("Current wizard step"),
|
|
11057
|
+
wizardId: z19.string().optional().describe("Wizard session ID (absent on init)"),
|
|
11058
|
+
subscriber_iccid: z19.string().optional().describe("Subscriber ICCID (required for select-package)"),
|
|
11059
|
+
package_template_id: z19.number().optional().describe("Package template ID (required for preview)")
|
|
10933
11060
|
},
|
|
10934
11061
|
_meta: {
|
|
10935
11062
|
ui: {
|
|
@@ -11199,7 +11326,7 @@ function registerProvisioningWizard(server2, ctx) {
|
|
|
11199
11326
|
}
|
|
11200
11327
|
|
|
11201
11328
|
// src/apps/balance-topup.ts
|
|
11202
|
-
import { z as
|
|
11329
|
+
import { z as z20 } from "zod";
|
|
11203
11330
|
import {
|
|
11204
11331
|
registerAppTool as registerAppTool3,
|
|
11205
11332
|
registerAppResource as registerAppResource3,
|
|
@@ -11252,9 +11379,9 @@ function registerBalanceTopupApp(server2, ctx) {
|
|
|
11252
11379
|
title: "Balance Top-up Form",
|
|
11253
11380
|
description: "Adjust the OCS reseller-account balance behind one subscriber, through a form panel the user can review before it commits. Defaults to preview: a call that omits `preview` returns a projection and writes nothing. Only `preview: false` performs the OCS write. Params: `iccid` (subscriber identifier used to find the account), `delta` (amount to add when positive, deduct when negative), `preview` (boolean, default true). Requires `admin` scope plus recent MFA, and a client that can render UI panels. Do NOT use this in a headless or scripted context \u2014 call `modify_account_balance` directly, which takes an `accountId` and needs no panel. Do NOT use this for a subscriber's own balance \u2014 the `iccid` here only locates the account; per-subscriber money is `modify_subscriber_balance`. Do NOT use this for the Carrier prepaid wallet or MCP credits \u2014 see `wallet_topup_checkout` and `credit_balance`.",
|
|
11254
11381
|
inputSchema: {
|
|
11255
|
-
iccid:
|
|
11256
|
-
delta:
|
|
11257
|
-
preview:
|
|
11382
|
+
iccid: z20.string().describe("The subscriber ICCID for the account lookup"),
|
|
11383
|
+
delta: z20.number().describe("Amount to add (positive) or deduct (negative) from the account balance"),
|
|
11384
|
+
preview: z20.boolean().default(true).describe("If true, return preview without writing. If false, execute the balance change.")
|
|
11258
11385
|
},
|
|
11259
11386
|
annotations: { destructiveHint: true },
|
|
11260
11387
|
_meta: {
|
|
@@ -11337,7 +11464,7 @@ function registerAllApps(server2, ctx) {
|
|
|
11337
11464
|
}
|
|
11338
11465
|
|
|
11339
11466
|
// src/prompts.ts
|
|
11340
|
-
import { z as
|
|
11467
|
+
import { z as z21 } from "zod";
|
|
11341
11468
|
function registerAllPrompts(server2) {
|
|
11342
11469
|
server2.registerPrompt(
|
|
11343
11470
|
"fleet_health_report",
|
|
@@ -11373,7 +11500,7 @@ Format as a structured report with sections, tables, and actionable recommendati
|
|
|
11373
11500
|
{
|
|
11374
11501
|
title: "Subscriber Deep Dive",
|
|
11375
11502
|
description: "Comprehensive analysis of a single subscriber: profile, packages, usage patterns, location history, recommendations.",
|
|
11376
|
-
argsSchema: { iccid:
|
|
11503
|
+
argsSchema: { iccid: z21.string().describe("The subscriber ICCID to analyse") }
|
|
11377
11504
|
},
|
|
11378
11505
|
async ({ iccid }) => ({
|
|
11379
11506
|
messages: [
|
|
@@ -11472,7 +11599,7 @@ Analyze and present:
|
|
|
11472
11599
|
{
|
|
11473
11600
|
title: "Bulk Operations Planner",
|
|
11474
11601
|
description: "Plan bulk operations safely: mass package assignments, account migrations, balance adjustments, or status changes.",
|
|
11475
|
-
argsSchema: { operation:
|
|
11602
|
+
argsSchema: { operation: z21.string().describe("Describe the bulk operation you want to perform") }
|
|
11476
11603
|
},
|
|
11477
11604
|
async ({ operation }) => ({
|
|
11478
11605
|
messages: [
|
|
@@ -11505,7 +11632,7 @@ NEVER execute destructive operations without confirmation.`
|
|
|
11505
11632
|
}
|
|
11506
11633
|
|
|
11507
11634
|
// src/tools-pricing.ts
|
|
11508
|
-
import { z as
|
|
11635
|
+
import { z as z22 } from "zod";
|
|
11509
11636
|
function registerAllPricingTools(server2, ctx) {
|
|
11510
11637
|
const allTools = [...PRICING_TOOLS, ...PROJECTS_TOOLS];
|
|
11511
11638
|
for (const tool of allTools) {
|
|
@@ -11612,19 +11739,19 @@ function buildZodSchema(schema) {
|
|
|
11612
11739
|
for (const [key, prop] of Object.entries(properties)) {
|
|
11613
11740
|
let field;
|
|
11614
11741
|
if (prop.enum) {
|
|
11615
|
-
field =
|
|
11742
|
+
field = z22.enum(prop.enum);
|
|
11616
11743
|
} else if (prop.type === "number") {
|
|
11617
|
-
field =
|
|
11744
|
+
field = z22.number();
|
|
11618
11745
|
} else if (prop.type === "boolean") {
|
|
11619
|
-
field =
|
|
11746
|
+
field = z22.boolean();
|
|
11620
11747
|
} else if (prop.type === "array") {
|
|
11621
11748
|
if (prop.items?.type === "number") {
|
|
11622
|
-
field =
|
|
11749
|
+
field = z22.array(z22.number());
|
|
11623
11750
|
} else {
|
|
11624
|
-
field =
|
|
11751
|
+
field = z22.array(z22.string());
|
|
11625
11752
|
}
|
|
11626
11753
|
} else {
|
|
11627
|
-
field =
|
|
11754
|
+
field = z22.string();
|
|
11628
11755
|
}
|
|
11629
11756
|
const required = schema.required ?? [];
|
|
11630
11757
|
if (!required.includes(key)) {
|
|
@@ -11639,7 +11766,7 @@ function buildZodSchema(schema) {
|
|
|
11639
11766
|
}
|
|
11640
11767
|
|
|
11641
11768
|
// src/tools-greenzone.ts
|
|
11642
|
-
import { z as
|
|
11769
|
+
import { z as z23 } from "zod";
|
|
11643
11770
|
|
|
11644
11771
|
// src/greenzone-whitelist.ts
|
|
11645
11772
|
var STATE_KEY = "greenzone:state";
|
|
@@ -11930,9 +12057,9 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
11930
12057
|
title: "Greenzone Whitelist \u2014 Add",
|
|
11931
12058
|
description: "Add a hostname and/or IP address to the Bridge4IP Greenzone whitelist. The Greenzone whitelist controls which hosts/IPs are exempt from data charging after a subscriber's bundle is depleted. This operates via browser automation (Kapture) against the OCS portal \u2014 no REST API exists for this surface. Provide `host` (e.g. 'example.com') and/or `ip` (e.g. '203.0.113.0/24'). At least one of host or ip must be provided. Returns Kapture step sequence for Claude Code to execute. Requires 'write' scope and Kapture running in Sam's Chrome.",
|
|
11932
12059
|
inputSchema: {
|
|
11933
|
-
host:
|
|
11934
|
-
ip:
|
|
11935
|
-
dry_run:
|
|
12060
|
+
host: z23.string().optional().describe("Hostname to whitelist (e.g. 'example.com')"),
|
|
12061
|
+
ip: z23.string().optional().describe("IP address or CIDR to whitelist (e.g. '203.0.113.0/24')"),
|
|
12062
|
+
dry_run: z23.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
|
|
11936
12063
|
},
|
|
11937
12064
|
annotations: { destructiveHint: true }
|
|
11938
12065
|
},
|
|
@@ -11991,9 +12118,9 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
11991
12118
|
title: "Greenzone Whitelist \u2014 Remove",
|
|
11992
12119
|
description: "Remove a hostname and/or IP address from the Bridge4IP Greenzone whitelist. This operates via browser automation (Kapture) against the OCS portal. Provide `host` and/or `ip` to remove \u2014 at least one required. Returns Kapture step sequence for Claude Code to execute. Requires 'write' scope and Kapture running in Sam's Chrome.",
|
|
11993
12120
|
inputSchema: {
|
|
11994
|
-
host:
|
|
11995
|
-
ip:
|
|
11996
|
-
dry_run:
|
|
12121
|
+
host: z23.string().optional().describe("Hostname to remove from whitelist"),
|
|
12122
|
+
ip: z23.string().optional().describe("IP address or CIDR to remove from whitelist"),
|
|
12123
|
+
dry_run: z23.boolean().optional().describe("If true, return the Kapture steps without executing portal automation")
|
|
11997
12124
|
},
|
|
11998
12125
|
annotations: { destructiveHint: true }
|
|
11999
12126
|
},
|
|
@@ -12052,7 +12179,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12052
12179
|
title: "Greenzone Whitelist \u2014 List",
|
|
12053
12180
|
description: "List current Greenzone whitelist entries from the KV cache. Returns all whitelisted hosts and IPs, when the state was last synced, and whether the last hourly reconciliation detected drift from the portal. This is a fast read from KV \u2014 no browser automation required. Use `from_portal: true` to trigger a live portal read via Kapture instead of KV. Requires 'read' scope.",
|
|
12054
12181
|
inputSchema: {
|
|
12055
|
-
from_portal:
|
|
12182
|
+
from_portal: z23.boolean().optional().describe(
|
|
12056
12183
|
"When true, return Kapture steps to read live portal state instead of KV cache"
|
|
12057
12184
|
)
|
|
12058
12185
|
},
|
|
@@ -12104,7 +12231,7 @@ function registerGreenzoneTools(server2, ctx) {
|
|
|
12104
12231
|
}
|
|
12105
12232
|
|
|
12106
12233
|
// src/tools-ui-agent-generic.ts
|
|
12107
|
-
import { z as
|
|
12234
|
+
import { z as z24 } from "zod";
|
|
12108
12235
|
var COST_PER_STEP_USD = 0.014;
|
|
12109
12236
|
var SESSION_OVERHEAD_USD = 5e-3;
|
|
12110
12237
|
function estimateCostUsd2(steps) {
|
|
@@ -12146,13 +12273,13 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12146
12273
|
title: "Dispatch Generic Browsing Agent",
|
|
12147
12274
|
description: "Send a plain-language task to a hosted browsing agent, which drives a real browser on the open web \u2014 navigating pages, searching, extracting content, filling forms outside the OCS portal. Each run costs roughly $0.20-1.00 in model spend against your own BROWSER_USE_API_KEY, so treat it as the expensive last resort, not a lookup. Limited to CARRIER_BROWSER_USE_MAX_RUNS runs per CLI session (default 10). Params: `prompt` (1-4000 chars \u2014 name the target URL and the exact data or action), `max_steps` (1-30, default 15; prices the dry-run estimate only), `dry_run` (true returns only `estimated_cost_usd` and creates no session). Returns the finished result: unlike the hosted Worker, a local CLI has no background runtime, so this call blocks for the length of the run. Requires `write` scope. Do NOT use this to work out which Carrier tool answers a request \u2014 that is `carrier_ask`, which costs nothing. Do NOT use this for anything the OCS API already covers; every other tool here is faster, free, and audited.",
|
|
12148
12275
|
inputSchema: {
|
|
12149
|
-
prompt:
|
|
12276
|
+
prompt: z24.string().min(1).max(4e3).describe(
|
|
12150
12277
|
"Natural-language task description for the browsing agent. Be specific: include target URLs, data to extract, or actions to perform."
|
|
12151
12278
|
),
|
|
12152
|
-
max_steps:
|
|
12279
|
+
max_steps: z24.number().int().min(1).max(30).optional().describe(
|
|
12153
12280
|
"Advisory step budget (1\u201330, default 15). Prices the dry-run estimate; the hosted agent sets its own depth."
|
|
12154
12281
|
),
|
|
12155
|
-
dry_run:
|
|
12282
|
+
dry_run: z24.boolean().optional().describe(
|
|
12156
12283
|
"Preview cost estimate without dispatching. Returns estimated_cost_usd only \u2014 no browser session is created."
|
|
12157
12284
|
)
|
|
12158
12285
|
}
|
|
@@ -12289,7 +12416,7 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12289
12416
|
title: "Poll Browsing Agent Task Status",
|
|
12290
12417
|
description: "Returns the current state of a browsing-agent task by `task_id`, including its result once finished. In the local CLI, ui_agent_ask already returns the finished result, so this is mainly for re-reading a task within the same session. Task records do not survive the CLI process exiting. Requires `read` scope.",
|
|
12291
12418
|
inputSchema: {
|
|
12292
|
-
task_id:
|
|
12419
|
+
task_id: z24.string().describe("Task ID returned by ui_agent_ask")
|
|
12293
12420
|
}
|
|
12294
12421
|
},
|
|
12295
12422
|
async (args) => {
|
|
@@ -12417,6 +12544,7 @@ registerAllCarrierAskTools(server, toolCtx);
|
|
|
12417
12544
|
registerListRecentOcsEventsTool(server, toolCtx);
|
|
12418
12545
|
registerRateLimitStatusTool(server, toolCtx);
|
|
12419
12546
|
registerCountryHistoryTool(server, toolCtx);
|
|
12547
|
+
registerNetworkDefaultTools(server, toolCtx);
|
|
12420
12548
|
registerDepletionEventsTool(server, toolCtx);
|
|
12421
12549
|
registerAllApps(server, toolCtx);
|
|
12422
12550
|
registerAllPricingTools(server, toolCtx);
|