@carrierllc/mcp 0.13.29 → 0.13.30

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
@@ -80,7 +80,7 @@ import {
80
80
  verifyStorefront,
81
81
  withOcsListSummary,
82
82
  withTemplateResellerId
83
- } from "./chunk-LJPYVILK.js";
83
+ } from "./chunk-ZJY7H2QW.js";
84
84
  import "./chunk-SHKKVIIA.js";
85
85
 
86
86
  // src/index.ts
@@ -0,0 +1,57 @@
1
+ // macOS built-in JXA Objective-C bridge. No compiler, downloads or owner actions.
2
+ // Apple SecItem.h: AuthenticationUIFail means errSecInteractionNotAllowed, NOT skip.
3
+ ObjC.import('Foundation');
4
+ ObjC.import('Security');
5
+ ObjC.bindFunction('SecKeychainSetUserInteractionAllowed', ['int', ['bool']]);
6
+ ObjC.bindFunction('SecItemCopyMatching', ['int', ['id', 'id *']]);
7
+ function run() {
8
+ var output = $.NSFileHandle.alloc.initWithFileDescriptorCloseOnDealloc(3, false);
9
+ function send(result) {
10
+ var data = $(JSON.stringify(result)).dataUsingEncoding($.NSUTF8StringEncoding);
11
+ output.writeData(data);
12
+ }
13
+ try {
14
+ var input = $.NSFileHandle.fileHandleWithStandardInput.readDataToEndOfFile;
15
+ if (Number(input.length) > 16384) { send({status:'malformed'}); return; }
16
+ var request = JSON.parse(ObjC.unwrap($.NSString.alloc.initWithDataEncoding(input, $.NSUTF8StringEncoding)));
17
+ if (typeof request.service !== 'string' || !/^Claude Code(?:-custom-oauth)?-credentials(?:-[a-f0-9]{8})?$/.test(request.service) ||
18
+ typeof request.account !== 'string' || !/^[a-zA-Z0-9._-]+$/.test(request.account) ||
19
+ typeof request.key !== 'string' || !/^[a-zA-Z0-9_-]+\|[a-f0-9]{16}$/.test(request.key)) {
20
+ send({status:'malformed'}); return;
21
+ }
22
+ // Global legacy-Keychain UI inhibition is process-local. This child never restores
23
+ // it to true; SecItem's modern auth path independently uses AuthenticationUIFail.
24
+ if ($.SecKeychainSetUserInteractionAllowed(false) !== 0) { send({status:'unavailable'}); return; }
25
+ var query = $.NSMutableDictionary.alloc.init;
26
+ query.setObjectForKey(ObjC.castRefToObject($.kSecClassGenericPassword), ObjC.castRefToObject($.kSecClass));
27
+ query.setObjectForKey($(request.service), ObjC.castRefToObject($.kSecAttrService));
28
+ query.setObjectForKey($(request.account), ObjC.castRefToObject($.kSecAttrAccount));
29
+ query.setObjectForKey(ObjC.castRefToObject($.kSecMatchLimitOne), ObjC.castRefToObject($.kSecMatchLimit));
30
+ query.setObjectForKey($.NSNumber.numberWithBool(true), ObjC.castRefToObject($.kSecReturnData));
31
+ query.setObjectForKey(ObjC.castRefToObject($.kSecUseAuthenticationUIFail), ObjC.castRefToObject($.kSecUseAuthenticationUI));
32
+ if (Number(query.count) !== 6) { send({status:'malformed'}); return; }
33
+ var result = Ref();
34
+ var code = $.SecItemCopyMatching(query, result);
35
+ if (code !== 0) {
36
+ send({status: code === -25300 ? 'absent' : code === -25308 ? 'locked' :
37
+ code === -25293 || code === -128 ? 'denied' : 'unavailable'}); return;
38
+ }
39
+ var data = ObjC.castRefToObject(result[0]);
40
+ if (!data || Number(data.length) > 1048576) { send({status:'malformed'}); return; }
41
+ var text = ObjC.unwrap($.NSString.alloc.initWithDataEncoding(data, $.NSUTF8StringEncoding));
42
+ var root = JSON.parse(text), entry = root && root.mcpOAuth && root.mcpOAuth[request.key];
43
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) { send({status:'absent-entry'}); return; }
44
+ // Never transport unrelated credentials or the owner's rotating/registration secrets.
45
+ var selected = {};
46
+ ['serverName','serverUrl','issuer','resource','accessToken','expiresAt','scope','clientId',
47
+ 'organizationId','organization_id','orgId','accountId'].forEach(function(k) {
48
+ if (Object.prototype.hasOwnProperty.call(entry,k)) selected[k] = entry[k];
49
+ });
50
+ if (entry.discoveryState) {
51
+ selected.discoveryState = {authorizationServerUrl:entry.discoveryState.authorizationServerUrl,
52
+ resourceMetadataUrl:entry.discoveryState.resourceMetadataUrl};
53
+ }
54
+ send({status:'ok',entry:selected});
55
+ } catch (_) { send({status:'malformed'}); }
56
+ // Nothing on stdout/stderr. Only the inherited private pipe receives the result.
57
+ }
package/package.json CHANGED
@@ -1,15 +1,21 @@
1
1
  {
2
2
  "name": "@carrierllc/mcp",
3
- "version": "0.13.29",
3
+ "version": "0.13.30",
4
4
  "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).",
5
5
  "license": "MIT",
6
6
  "author": "Carrier (Lifecycle Innovations Limited)",
7
7
  "homepage": "https://mcp.carrier.llc",
8
8
  "type": "module",
9
+ "carrierCompatibility": {
10
+ "claudeOAuthOwnerVersion": "2.1.277",
11
+ "claudeOAuthOwnerVersions": ["2.1.277", "2.1.296"]
12
+ },
9
13
  "main": "./dist/index.js",
10
14
  "files": [
11
15
  "dist",
12
16
  "plugin",
17
+ "shell",
18
+ "native",
13
19
  "README.md",
14
20
  "templates",
15
21
  "!templates/**/node_modules",
@@ -27,7 +33,9 @@
27
33
  "lint": "eslint src",
28
34
  "prepublishOnly": "pnpm run build",
29
35
  "build:test": "tsup --config tsup.test.config.ts",
30
- "test": "pnpm run build && pnpm run build:test && node --test test/*.test.js",
36
+ "test": "pnpm run build && pnpm run build:test && node --test-concurrency=1 --test test/*.test.js && pnpm run test:oauth-repair",
37
+ "test:oauth-repair": "node --experimental-transform-types --test-concurrency=1 --test test/oauth-repair.test.mjs test/claude-owner.test.mjs test/current-owner.test.mjs",
38
+ "test:oauth-shell": "node scripts/check-oauth-shell.mjs",
31
39
  "check:pack": "node scripts/check-pack-size.mjs",
32
40
  "generate:domains": "node scripts/generate-domains.mjs"
33
41
  },
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Carrier — natural-language MVNO/eSIM fleet control + white-label storefront for Claude Code.",
9
- "version": "0.1.4"
9
+ "version": "0.1.5"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "carrier",
14
14
  "source": "./carrier",
15
15
  "description": "Provision eSIMs, manage subscribers/packages/billing/usage/SMS/churn/fleet, and roll out a white-labeled storefront — all in natural language via the Carrier MCP.",
16
- "version": "0.1.4",
16
+ "version": "0.1.5",
17
17
  "author": {
18
18
  "name": "Lifecycle Innovations Limited",
19
19
  "url": "https://carrier.llc"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carrier",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Natural-language control of your MVNO/eSIM fleet — the Stripe of telecom. Provision eSIMs, manage subscribers, packages, billing, usage, SMS, churn, and fleet health, and roll out a white-labeled storefront. Wraps the Carrier MCP (130 tools over OAuth 2.1).",
5
5
  "author": {
6
6
  "name": "Lifecycle Innovations Limited",
@@ -0,0 +1,56 @@
1
+ # Static, explicitly sourced bash integration. No startup edit or secret eval.
2
+ # Needed to see caller ERR traps; opt-in loading enables Bash errtrace.
3
+ set -E
4
+ # Refuse command traps rather than executing them with newly loaded secrets.
5
+ carrier_auth_export() {
6
+ set +x +v
7
+ local _carrier_fd _carrier_token _carrier_origin _carrier_expiry _carrier_handoff _carrier_binding _carrier_rc=0
8
+ if [[ -n $(trap -p) ]]; then
9
+ printf '%s\n' 'Carrier export refused: remove shell traps first.' >&2
10
+ # Do not restore tracing into an unsafe trap context.
11
+ return 1
12
+ fi
13
+ # A replaced API key remains explicit; the resolver ignores a stale fingerprint.
14
+ # Bash 3.2 (macOS /bin/bash) has no {fd} allocation. A fifo keeps the handoff
15
+ # off the command line and off a world-readable temp file.
16
+ local _carrier_dir _carrier_pid
17
+ _carrier_dir=$(mktemp -d "${TMPDIR:-/tmp}/carrier-handoff.XXXXXX") || return 1
18
+ mkfifo "$_carrier_dir/pipe" || { rm -rf "$_carrier_dir"; return 1; }
19
+ command carrier credential-handoff >"$_carrier_dir/pipe" 3>&1 1>/dev/null &
20
+ _carrier_pid=$!
21
+ # One open reader. Separate redirections close the FIFO after the first
22
+ # line, so a fast writer drops the rest and the next read blocks forever.
23
+ # Bash 3.2 has no {fd} allocation; a compound redirect stays open for all five.
24
+ {
25
+ IFS= read -r _carrier_token || _carrier_rc=1
26
+ IFS= read -r _carrier_origin || _carrier_rc=1
27
+ IFS= read -r _carrier_expiry || _carrier_rc=1
28
+ IFS= read -r _carrier_handoff || _carrier_rc=1
29
+ IFS= read -r _carrier_binding || _carrier_rc=1
30
+ } <"$_carrier_dir/pipe"
31
+ if [[ $_carrier_rc -ne 0 ]]; then
32
+ :
33
+ elif [[ ! $_carrier_expiry =~ ^[0-9]+$ || -z $_carrier_token ]]; then
34
+ _carrier_rc=1
35
+ else
36
+ export CARRIER_API_KEY="$_carrier_token"
37
+ export CARRIER_OAUTH_ORIGIN="$_carrier_origin" CARRIER_OAUTH_EXPIRES_AT="$_carrier_expiry"
38
+ if [[ $_carrier_handoff == - ]]; then unset CARRIER_OAUTH_HANDOFF CARRIER_OAUTH_BINDING
39
+ else export CARRIER_OAUTH_HANDOFF="$_carrier_handoff" CARRIER_OAUTH_BINDING="$_carrier_binding"; fi
40
+ fi
41
+ wait "$_carrier_pid" || _carrier_rc=1
42
+ rm -rf "$_carrier_dir"
43
+ unset _carrier_token
44
+ # Xtrace remains disabled after successful export: the next caller command
45
+ # could expand the environment. Re-enable only knowingly, outside this helper.
46
+ [[ $_carrier_rc == 0 ]] || printf '%s\n' 'Carrier export failed; no environment was changed.' >&2
47
+ return "$_carrier_rc"
48
+ }
49
+ # Trace attribute makes caller DEBUG/RETURN traps visible for fail-closed refusal.
50
+ declare -ft carrier_auth_export
51
+ carrier_auth_clear() {
52
+ set +x +v
53
+ if [[ -n $(trap -p) ]]; then return 1; fi
54
+ unset CARRIER_API_KEY CARRIER_OAUTH_ORIGIN CARRIER_OAUTH_EXPIRES_AT CARRIER_OAUTH_HANDOFF CARRIER_OAUTH_BINDING
55
+ }
56
+ declare -ft carrier_auth_clear
@@ -0,0 +1,35 @@
1
+ # Static, explicitly sourced zsh integration. No startup edit or secret eval.
2
+ carrier_auth_export() {
3
+ # localoptions restores options on return, so deliberately do not use it:
4
+ # tracing stays off after export to protect the next caller expansion.
5
+ unsetopt xtrace verbose
6
+ if (( $+functions[TRAPDEBUG] || $+functions[TRAPZERR] )) || [[ -n $(trap) ]]; then
7
+ print -u2 -- 'Carrier export refused: remove command traps first.'
8
+ return 1
9
+ fi
10
+ local _carrier_fd _carrier_token _carrier_origin _carrier_expiry _carrier_handoff _carrier_binding _carrier_rc=0
11
+ exec {_carrier_fd}< <(command carrier credential-handoff 3>&1 1>/dev/null)
12
+ if ! IFS= read -r _carrier_token <&$_carrier_fd ||
13
+ ! IFS= read -r _carrier_origin <&$_carrier_fd ||
14
+ ! IFS= read -r _carrier_expiry <&$_carrier_fd ||
15
+ ! IFS= read -r _carrier_handoff <&$_carrier_fd ||
16
+ ! IFS= read -r _carrier_binding <&$_carrier_fd; then
17
+ _carrier_rc=1
18
+ elif [[ $_carrier_expiry != <-> || -z $_carrier_token ]]; then
19
+ _carrier_rc=1
20
+ else
21
+ export CARRIER_API_KEY="$_carrier_token"
22
+ export CARRIER_OAUTH_ORIGIN="$_carrier_origin" CARRIER_OAUTH_EXPIRES_AT="$_carrier_expiry"
23
+ if [[ $_carrier_handoff == - ]]; then unset CARRIER_OAUTH_HANDOFF CARRIER_OAUTH_BINDING
24
+ else export CARRIER_OAUTH_HANDOFF="$_carrier_handoff" CARRIER_OAUTH_BINDING="$_carrier_binding"; fi
25
+ fi
26
+ exec {_carrier_fd}<&-
27
+ unset _carrier_token
28
+ (( _carrier_rc == 0 )) || print -u2 -- 'Carrier export failed; no environment was changed.'
29
+ return $_carrier_rc
30
+ }
31
+ carrier_auth_clear() {
32
+ unsetopt xtrace verbose
33
+ if (( $+functions[TRAPDEBUG] || $+functions[TRAPZERR] )) || [[ -n $(trap) ]]; then return 1; fi
34
+ unset CARRIER_API_KEY CARRIER_OAUTH_ORIGIN CARRIER_OAUTH_EXPIRES_AT CARRIER_OAUTH_HANDOFF CARRIER_OAUTH_BINDING
35
+ }
@@ -1,3 +1,10 @@
1
1
  import { defineCloudflareConfig } from "@opennextjs/cloudflare";
2
2
 
3
- export default defineCloudflareConfig({});
3
+ const config = {
4
+ ...defineCloudflareConfig({}),
5
+ // Run the installed CLI directly: nested npm reads pnpm-only environment
6
+ // settings and warns. This is the same Next build, not a skipped build.
7
+ buildCommand: "node node_modules/next/dist/bin/next build",
8
+ };
9
+
10
+ export default config;
@@ -2,7 +2,7 @@
2
2
  "$schema": "node_modules/wrangler/config-schema.json",
3
3
  "main": ".open-next/worker.js",
4
4
  "name": "storefront",
5
- "compatibility_date": "2025-05-01",
5
+ "compatibility_date": "2026-09-26",
6
6
  "compatibility_flags": ["nodejs_compat"],
7
7
  "assets": {
8
8
  "directory": ".open-next/assets",