@carrierllc/mcp 0.13.39 → 0.13.40
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 +11 -1
- package/dist/{chunk-I3Z5WYDG.js → chunk-WVXII7ZR.js} +13 -6
- package/dist/{chunk-I3Z5WYDG.js.map → chunk-WVXII7ZR.js.map} +1 -1
- package/dist/cli.js +28 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.js +155 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
verifyStorefront,
|
|
81
81
|
withOcsListSummary,
|
|
82
82
|
withTemplateResellerId
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-WVXII7ZR.js";
|
|
84
84
|
import "./chunk-SHKKVIIA.js";
|
|
85
85
|
|
|
86
86
|
// src/index.ts
|
|
@@ -89,8 +89,95 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
89
89
|
|
|
90
90
|
// src/tools.ts
|
|
91
91
|
import { z } from "zod";
|
|
92
|
+
|
|
93
|
+
// src/durable-issuance.ts
|
|
94
|
+
async function issueInventory(ctx, args, extra) {
|
|
95
|
+
const key = args.request_key;
|
|
96
|
+
const error = (code, message, detail = {}) => ({ isError: true, content: [{ type: "text", text: JSON.stringify({ ...detail, code, message, request_key: key }) }] });
|
|
97
|
+
const token2 = await ctx.getApiToken?.(extra);
|
|
98
|
+
if (!token2) return error("CARRIER_API_AUTH_REQUIRED", "An authenticated Carrier API credential is required. No dispatch made.");
|
|
99
|
+
const body2 = {
|
|
100
|
+
packageTemplateId: args.packageTemplateId,
|
|
101
|
+
...args.account_for_subs !== void 0 ? { accountId: args.account_for_subs } : {},
|
|
102
|
+
...Object.fromEntries(["startDate", "endDate", "validityPeriod"].filter((k) => args[k] !== void 0).map((k) => [k, args[k]])),
|
|
103
|
+
...args.business_reference !== void 0 ? { businessReference: args.business_reference } : {}
|
|
104
|
+
};
|
|
105
|
+
try {
|
|
106
|
+
const response = await fetch(`${ctx.env.CARRIER_API_BASE_URL ?? "https://api.carrier.llc"}/v1/subscribers/issue-from-inventory`, {
|
|
107
|
+
method: "POST",
|
|
108
|
+
redirect: "error",
|
|
109
|
+
headers: { "Authorization": `Bearer ${token2}`, "Content-Type": "application/json", "X-Idempotency-Key": String(key) },
|
|
110
|
+
body: JSON.stringify(body2)
|
|
111
|
+
});
|
|
112
|
+
const payload = await response.json();
|
|
113
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload) || response.ok && !payload.data) throw new Error("Malformed API response");
|
|
114
|
+
if (response.status >= 500 && payload.code !== "OUTCOME_UNKNOWN" && payload.upstream_dispatched !== false) {
|
|
115
|
+
return error("OUTCOME_UNKNOWN", "Issuance outcome is unknown. Preserve this request_key for reconciliation; do not issue with a new key.", { api_error: payload });
|
|
116
|
+
}
|
|
117
|
+
return { ...!response.ok ? { isError: true } : {}, content: [{ type: "text", text: JSON.stringify({ ...payload, request_key: key }) }] };
|
|
118
|
+
} catch {
|
|
119
|
+
return error("OUTCOME_UNKNOWN", "Issuance outcome is unknown. Preserve this request_key for reconciliation; do not issue with a new key.");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/tools.ts
|
|
92
124
|
import * as Sentry from "@sentry/cloudflare";
|
|
93
125
|
|
|
126
|
+
// src/ocs-scoping.ts
|
|
127
|
+
function positiveAccountId(raw) {
|
|
128
|
+
if (typeof raw !== "number" && (typeof raw !== "string" || !/^[1-9]\d*$/.test(raw))) return void 0;
|
|
129
|
+
const id = Number(raw);
|
|
130
|
+
return Number.isSafeInteger(id) && id > 0 ? id : void 0;
|
|
131
|
+
}
|
|
132
|
+
function tenantAccountIds(auth) {
|
|
133
|
+
if (auth.managed) {
|
|
134
|
+
const id = positiveAccountId(auth.accountId);
|
|
135
|
+
return id === void 0 ? [] : [id];
|
|
136
|
+
}
|
|
137
|
+
if (auth.restricted) return (auth.allowedAccountIds ?? []).filter((id) => positiveAccountId(id) !== void 0);
|
|
138
|
+
if (auth.preferredAccountId !== void 0) {
|
|
139
|
+
const id = positiveAccountId(auth.preferredAccountId);
|
|
140
|
+
return id === void 0 ? [] : [id];
|
|
141
|
+
}
|
|
142
|
+
return void 0;
|
|
143
|
+
}
|
|
144
|
+
function managedOwnAccountId(auth) {
|
|
145
|
+
const ids = tenantAccountIds(auth);
|
|
146
|
+
return ids === void 0 ? void 0 : ids[0] ?? -1;
|
|
147
|
+
}
|
|
148
|
+
function subscriberAccountId(data) {
|
|
149
|
+
if (!data || typeof data !== "object") return void 0;
|
|
150
|
+
const root = data;
|
|
151
|
+
const sub = root.subscriber && typeof root.subscriber === "object" ? root.subscriber : root;
|
|
152
|
+
const raw = sub.accountId ?? sub.account_id ?? sub.account;
|
|
153
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
154
|
+
return positiveAccountId(raw);
|
|
155
|
+
}
|
|
156
|
+
function isManagedSubscriberDenied(auth, subscriberData) {
|
|
157
|
+
const ids = tenantAccountIds(auth);
|
|
158
|
+
if (ids === void 0) return false;
|
|
159
|
+
const subAccountId = subscriberAccountId(subscriberData);
|
|
160
|
+
return subAccountId === void 0 || !ids.includes(subAccountId);
|
|
161
|
+
}
|
|
162
|
+
async function assertManagedIccidAccess(baseUrl2, auth, iccid) {
|
|
163
|
+
const ids = tenantAccountIds(auth);
|
|
164
|
+
if (ids === void 0) return { ok: true };
|
|
165
|
+
if (ids.length === 0) return { ok: false, error: "Subscriber not found" };
|
|
166
|
+
try {
|
|
167
|
+
const client2 = new OcsClient(baseUrl2, auth.token);
|
|
168
|
+
const data = await client2.call("getSingleSubscriber", { iccid });
|
|
169
|
+
if (isManagedSubscriberDenied(auth, data)) {
|
|
170
|
+
return { ok: false, error: "Subscriber not found" };
|
|
171
|
+
}
|
|
172
|
+
return { ok: true };
|
|
173
|
+
} catch (err9) {
|
|
174
|
+
return {
|
|
175
|
+
ok: false,
|
|
176
|
+
error: err9 instanceof Error ? err9.message : String(err9)
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
94
181
|
// ../../packages/carrier-ai/dist/index.js
|
|
95
182
|
import { AwsClient } from "aws4fetch";
|
|
96
183
|
var AI_TIER_POLICY = {
|
|
@@ -1211,8 +1298,19 @@ var DESTRUCTIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1211
1298
|
// S6 live-smoke gap fill (CAR-105)
|
|
1212
1299
|
"change_network_profile_of_location_zone"
|
|
1213
1300
|
]);
|
|
1301
|
+
function assignPackageRefusal(args) {
|
|
1302
|
+
const hasIccid = typeof args.iccid === "string" && args.iccid.trim().length > 0;
|
|
1303
|
+
const conflict = hasIccid && (args.account_for_subs !== void 0 || args.business_reference !== void 0);
|
|
1304
|
+
const invalidKey = !hasIccid && (typeof args.request_key !== "string" || !/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,199}$/.test(args.request_key));
|
|
1305
|
+
if (conflict || invalidKey) return { isError: true, content: [{ type: "text", text: JSON.stringify({
|
|
1306
|
+
code: conflict ? "INVALID_ASSIGN_PACKAGE_INPUT" : "IDEMPOTENCY_KEY_REQUIRED",
|
|
1307
|
+
message: conflict ? "iccid and inventory/business reference are mutually exclusive. No changes made." : "Inventory requires a stable request_key (1\u2013200 characters: letters, digits, underscore, dot, colon, hyphen). No changes made.",
|
|
1308
|
+
request_key: args.request_key
|
|
1309
|
+
}) }] };
|
|
1310
|
+
return void 0;
|
|
1311
|
+
}
|
|
1214
1312
|
function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler, options) {
|
|
1215
|
-
return async (args) => {
|
|
1313
|
+
return async (args, extra) => {
|
|
1216
1314
|
const start = Date.now();
|
|
1217
1315
|
const isDryRun = args.dry_run === true;
|
|
1218
1316
|
if (!ctx.props.scope.includes(requiredScope)) {
|
|
@@ -1256,6 +1354,11 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler, options)
|
|
|
1256
1354
|
]
|
|
1257
1355
|
};
|
|
1258
1356
|
}
|
|
1357
|
+
const refusal = options?.validateArgs?.(args);
|
|
1358
|
+
if (refusal) {
|
|
1359
|
+
ctx.audit({ tool_name: toolName, ocs_method: ocsMethod, status: "error", dry_run: isDryRun, duration_ms: 0 });
|
|
1360
|
+
return refusal;
|
|
1361
|
+
}
|
|
1259
1362
|
if (isDryRun && DESTRUCTIVE_TOOLS.has(toolName)) {
|
|
1260
1363
|
ctx.audit({
|
|
1261
1364
|
tool_name: toolName,
|
|
@@ -1276,8 +1379,9 @@ function wrapHandler(toolName, ocsMethod, requiredScope, ctx, handler, options)
|
|
|
1276
1379
|
}
|
|
1277
1380
|
let result2;
|
|
1278
1381
|
try {
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1382
|
+
const skipToken = typeof options?.skipUserToken === "function" ? options.skipUserToken(args) : options?.skipUserToken;
|
|
1383
|
+
const token2 = skipToken ? "" : await ctx.getUserToken(ctx.props.sub);
|
|
1384
|
+
result2 = extra === void 0 ? await handler(args, token2) : await handler(args, token2, extra);
|
|
1281
1385
|
} catch (err9) {
|
|
1282
1386
|
try {
|
|
1283
1387
|
Sentry.captureException(err9, {
|
|
@@ -1931,16 +2035,21 @@ function registerAllTools(server2, ctx) {
|
|
|
1931
2035
|
server2.registerTool(
|
|
1932
2036
|
"assign_package",
|
|
1933
2037
|
{
|
|
1934
|
-
title: "Assign Package
|
|
1935
|
-
description: "
|
|
1936
|
-
inputSchema: {
|
|
1937
|
-
iccid: z.string().describe("
|
|
1938
|
-
packageTemplateId: z.number().describe("The package template ID to assign"),
|
|
1939
|
-
account_for_subs: z.number().optional().describe(
|
|
1940
|
-
|
|
1941
|
-
),
|
|
2038
|
+
title: "Assign Package or Issue From Inventory",
|
|
2039
|
+
description: "Assign a one-time prepaid package to an existing subscriber using iccid + packageTemplateId. Inventory issuance uses authenticated Carrier API POST /v1/subscribers/issue-from-inventory with request_key as X-Idempotency-Key. API authorization and its durable ledger are authoritative. Keep the same key after unknown outcomes; never automatically issue with a new key. Do not send both iccid and account_for_subs. Use assign_recurring_package for auto-renew.",
|
|
2040
|
+
inputSchema: z.object({
|
|
2041
|
+
iccid: z.string().optional().describe("Existing subscriber ICCID"),
|
|
2042
|
+
packageTemplateId: z.number().int().positive().describe("The package template ID to assign"),
|
|
2043
|
+
account_for_subs: z.number().int().positive().optional().describe("Inventory account sent as API accountId"),
|
|
2044
|
+
request_key: z.string().max(200).regex(/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/).optional().describe("Required stable inventory key; preserve after unknown outcomes"),
|
|
2045
|
+
startDate: z.string().optional(),
|
|
2046
|
+
endDate: z.string().optional(),
|
|
2047
|
+
validityPeriod: z.number().int().positive().optional(),
|
|
2048
|
+
business_reference: z.object({ kind: z.literal("storefront_order"), id: z.string().min(1), line: z.literal("esim"), verb: z.literal("issue") }).strict().optional().describe("API-authorized existing order reference; not proof of ownership"),
|
|
1942
2049
|
...DRY_RUN_FIELD
|
|
1943
|
-
},
|
|
2050
|
+
}).refine((d) => !((d.account_for_subs !== void 0 || d.business_reference !== void 0) && typeof d.iccid === "string" && d.iccid.trim().length > 0), {
|
|
2051
|
+
message: "iccid and account_for_subs are mutually exclusive \u2014 choose one path"
|
|
2052
|
+
}),
|
|
1944
2053
|
annotations: { destructiveHint: true }
|
|
1945
2054
|
},
|
|
1946
2055
|
wrapHandler(
|
|
@@ -1948,17 +2057,34 @@ function registerAllTools(server2, ctx) {
|
|
|
1948
2057
|
"affectPackageToSubscriber",
|
|
1949
2058
|
TOOL_SCOPES["assign_package"],
|
|
1950
2059
|
ctx,
|
|
1951
|
-
async (
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
}
|
|
1957
|
-
|
|
2060
|
+
async (args, token2, extra) => {
|
|
2061
|
+
const { iccid, packageTemplateId } = args;
|
|
2062
|
+
if (typeof iccid !== "string" || !iccid.trim()) return issueInventory(ctx, args, extra);
|
|
2063
|
+
const access = await assertManagedIccidAccess(
|
|
2064
|
+
ctx.env.CARRIER_OCS_BASE_URL,
|
|
2065
|
+
{ ...ctx.managedAuth, token: token2 },
|
|
2066
|
+
iccid
|
|
2067
|
+
);
|
|
2068
|
+
if (!access.ok) throw new Error(access.error);
|
|
1958
2069
|
return ocsCall(ctx.env, token2, "affectPackageToSubscriber", {
|
|
1959
2070
|
subscriber: { iccid },
|
|
1960
2071
|
packageTemplateId
|
|
1961
2072
|
});
|
|
2073
|
+
},
|
|
2074
|
+
{
|
|
2075
|
+
validateArgs: assignPackageRefusal,
|
|
2076
|
+
skipUserToken: (args) => typeof args.iccid !== "string" || !args.iccid.trim(),
|
|
2077
|
+
dryRunPayload: (args) => typeof args.iccid === "string" && args.iccid.trim() ? { subscriber: { iccid: args.iccid }, packageTemplateId: args.packageTemplateId } : {
|
|
2078
|
+
method: "POST",
|
|
2079
|
+
path: "/v1/subscribers/issue-from-inventory",
|
|
2080
|
+
headers: { "X-Idempotency-Key": args.request_key },
|
|
2081
|
+
body: {
|
|
2082
|
+
packageTemplateId: args.packageTemplateId,
|
|
2083
|
+
...args.account_for_subs !== void 0 ? { accountId: args.account_for_subs } : {},
|
|
2084
|
+
...Object.fromEntries(["startDate", "endDate", "validityPeriod"].filter((k) => args[k] !== void 0).map((k) => [k, args[k]])),
|
|
2085
|
+
...args.business_reference !== void 0 ? { businessReference: args.business_reference } : {}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
1962
2088
|
}
|
|
1963
2089
|
)
|
|
1964
2090
|
);
|
|
@@ -10586,45 +10712,6 @@ function buildExamples(toolName) {
|
|
|
10586
10712
|
// src/list-recent-ocs-events.ts
|
|
10587
10713
|
import { z as z16 } from "zod";
|
|
10588
10714
|
|
|
10589
|
-
// src/ocs-scoping.ts
|
|
10590
|
-
function managedOwnAccountId(auth) {
|
|
10591
|
-
if (!auth.managed || auth.accountId === void 0) return void 0;
|
|
10592
|
-
const n = Number(auth.accountId);
|
|
10593
|
-
return Number.isFinite(n) ? n : void 0;
|
|
10594
|
-
}
|
|
10595
|
-
function subscriberAccountId(data) {
|
|
10596
|
-
if (!data || typeof data !== "object") return void 0;
|
|
10597
|
-
const root = data;
|
|
10598
|
-
const sub = root.subscriber && typeof root.subscriber === "object" ? root.subscriber : root;
|
|
10599
|
-
const raw = sub.accountId ?? sub.account_id ?? sub.account;
|
|
10600
|
-
if (raw === void 0 || raw === null) return void 0;
|
|
10601
|
-
const n = Number(raw);
|
|
10602
|
-
return Number.isFinite(n) ? n : void 0;
|
|
10603
|
-
}
|
|
10604
|
-
function isManagedSubscriberDenied(auth, subscriberData) {
|
|
10605
|
-
const ownId = managedOwnAccountId(auth);
|
|
10606
|
-
if (ownId === void 0) return false;
|
|
10607
|
-
const subAccountId = subscriberAccountId(subscriberData);
|
|
10608
|
-
if (subAccountId === void 0) return true;
|
|
10609
|
-
return subAccountId !== ownId;
|
|
10610
|
-
}
|
|
10611
|
-
async function assertManagedIccidAccess(baseUrl2, auth, iccid) {
|
|
10612
|
-
if (managedOwnAccountId(auth) === void 0) return { ok: true };
|
|
10613
|
-
try {
|
|
10614
|
-
const client2 = new OcsClient(baseUrl2, auth.token);
|
|
10615
|
-
const data = await client2.call("getSingleSubscriber", { iccid });
|
|
10616
|
-
if (isManagedSubscriberDenied(auth, data)) {
|
|
10617
|
-
return { ok: false, error: "Subscriber not found" };
|
|
10618
|
-
}
|
|
10619
|
-
return { ok: true };
|
|
10620
|
-
} catch (err9) {
|
|
10621
|
-
return {
|
|
10622
|
-
ok: false,
|
|
10623
|
-
error: err9 instanceof Error ? err9.message : String(err9)
|
|
10624
|
-
};
|
|
10625
|
-
}
|
|
10626
|
-
}
|
|
10627
|
-
|
|
10628
10715
|
// ../../packages/ocs-spec/src/ocs-event-buffer-read.ts
|
|
10629
10716
|
var LIFECYCLE_RESELLER_ID2 = 1170;
|
|
10630
10717
|
function ocsEventRingBufferIccidFromDigitsReference(raw) {
|
|
@@ -12857,13 +12944,14 @@ function registerUiAgentGenericTools(server2, ctx, runtime) {
|
|
|
12857
12944
|
// src/index.ts
|
|
12858
12945
|
var baseUrl = process.env.CARRIER_OCS_BASE_URL ?? process.env.ESIMVAULT_BASE_URL ?? "https://ocs.esimvault.cloud";
|
|
12859
12946
|
var token = process.env.CARRIER_OCS_API_TOKEN ?? process.env.ESIMVAULT_API_TOKEN;
|
|
12860
|
-
if (!token) {
|
|
12947
|
+
if (!token && !process.env.CARRIER_API_TOKEN) {
|
|
12861
12948
|
console.error(
|
|
12862
|
-
"ERROR:
|
|
12949
|
+
"ERROR: Set CARRIER_API_TOKEN for durable inventory or CARRIER_OCS_API_TOKEN for explicit OCS tools."
|
|
12863
12950
|
);
|
|
12864
12951
|
process.exit(1);
|
|
12865
12952
|
}
|
|
12866
12953
|
var stdioEnv = {
|
|
12954
|
+
CARRIER_API_BASE_URL: process.env.CARRIER_API_BASE_URL ?? "https://api.carrier.llc",
|
|
12867
12955
|
CARRIER_OCS_BASE_URL: baseUrl,
|
|
12868
12956
|
// Empty on purpose. rotate_credentials reads this and throws a clear
|
|
12869
12957
|
// message rather than pretending a local install can rotate the vault.
|
|
@@ -12931,13 +13019,17 @@ var props = {
|
|
|
12931
13019
|
};
|
|
12932
13020
|
var audit = (_row) => {
|
|
12933
13021
|
};
|
|
12934
|
-
var getUserToken = async (_sub) =>
|
|
13022
|
+
var getUserToken = async (_sub) => {
|
|
13023
|
+
if (!token) throw new Error("CARRIER_OCS_API_TOKEN is required for explicit OCS tools");
|
|
13024
|
+
return token;
|
|
13025
|
+
};
|
|
12935
13026
|
var toolCtx = {
|
|
12936
13027
|
env: stdioEnv,
|
|
12937
13028
|
props,
|
|
12938
13029
|
managedAuth: {},
|
|
12939
13030
|
audit,
|
|
12940
|
-
getUserToken
|
|
13031
|
+
getUserToken,
|
|
13032
|
+
getApiToken: async () => process.env.CARRIER_API_TOKEN || void 0
|
|
12941
13033
|
};
|
|
12942
13034
|
var server = new McpServer(
|
|
12943
13035
|
{ name: "carrier-mcp", version: CARRIER_VERSION },
|