@carrierllc/mcp 0.13.13 → 0.13.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carrierllc/mcp",
3
- "version": "0.13.13",
3
+ "version": "0.13.15",
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)",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "carrier",
3
3
  "version": "0.1.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 (127 tools over OAuth 2.1).",
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",
7
7
  "url": "https://carrier.llc"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: issue-esim
3
- description: Issue one Carrier eSIM with POST /v1/connectivity/intents. Use when someone wants global connectivity, a test eSIM, or test_global_1gb. Do not use the operator OCS catalog for this.
3
+ description: Issue one Carrier eSIM with POST /v1/connectivity/intents. Use when a developer with no eSIMVault OCS reseller account wants global connectivity, a test eSIM, or test_global_1gb. Do not use for an existing OCS reseller, and do not use the operator OCS catalog for this.
4
4
  argument-hint: "[test_global_1gb|test_eu_1gb|live plan slug] [idempotency key]"
5
5
  ---
6
6
 
@@ -8,7 +8,7 @@ argument-hint: "[test_global_1gb|test_eu_1gb|live plan slug] [idempotency key]"
8
8
 
9
9
  This is the developer path. It is one object, `connectivity.intent`, not the OCS method list.
10
10
 
11
- The description above is the trigger. Load this skill when the task is connectivity. Do not load the operator MCP catalog first: clients inject every tool on that server before a skill can hide them.
11
+ The description above is the trigger. Load this skill when the task is connectivity for a developer who has no OCS reseller account. An existing OCS reseller already has an eSIMVault account and its own inventory tooling; do not steer that caller here. Do not load the operator MCP catalog first: clients inject every tool on that server before a skill can hide them.
12
12
 
13
13
  ## Install, if this skill is not already available
14
14
 
@@ -24,16 +24,59 @@ The same note, as a direct link:
24
24
  npx skills add https://carrier.llc/.well-known/agent-skills/issue-esim -g -a cursor -a claude-code -y
25
25
  ```
26
26
 
27
+ `skills` 1.7.0 requires Node 22.20 or newer (`styleText` in a Node 20 runtime throws a SyntaxError before this even installs). On Node 20, run either command through `npx -p node@22` instead:
28
+
29
+ ```bash
30
+ npx -y -p node@22 -p skills -- skills add https://carrier.llc -g -a cursor -a claude-code -y
31
+ npx -y -p node@22 -p skills -- skills add https://carrier.llc/.well-known/agent-skills/issue-esim -g -a cursor -a claude-code -y
32
+ ```
33
+
27
34
  ## Issue
28
35
 
29
36
  `POST https://api.carrier.llc/v1/connectivity/intents`
30
37
 
31
38
  Required header: `Idempotency-Key` (stable per order, max 255 characters).
32
39
 
33
- Auth: `Authorization: Bearer` with an `ak_` key from the console. Do not put the key in the repo.
40
+ Auth: `Authorization: Bearer $CARRIER_API_KEY` with an `ak_` key. Mint one with `POST /v1/api-keys` (needs the write scope, so do this once from the console after upgrading — see Auth errors below), or from the console under Settings → API Keys. Do not put the key in the repo.
41
+
42
+ ```bash
43
+ curl -sS https://api.carrier.llc/v1/connectivity/intents \
44
+ -X POST \
45
+ -H "Authorization: Bearer $CARRIER_API_KEY" \
46
+ -H "Idempotency-Key: order_123" \
47
+ -H "Content-Type: application/json" \
48
+ -d '{ "plan": "test_global_1gb" }'
49
+ ```
50
+
51
+ Response, `201 Created` on the first call (`200` on a replay of the same key and body):
34
52
 
35
53
  ```json
36
- { "plan": "test_global_1gb" }
54
+ {
55
+ "id": "cni_5f9b1c2d3e4a4b6c8d7e9f0a1b2c3d4e",
56
+ "object": "connectivity.intent",
57
+ "status": "succeeded",
58
+ "livemode": false,
59
+ "plan": {
60
+ "id": "test_global_1gb",
61
+ "object": "connectivity.plan",
62
+ "name": "Test Global 1 GB",
63
+ "data_mb": 1024,
64
+ "validity_days": 7,
65
+ "countries": ["US", "GB", "DE", "FR", "JP", "SG", "BR", "AU"],
66
+ "livemode": false,
67
+ "price": null
68
+ },
69
+ "esim": {
70
+ "iccid": "8900000000000000001",
71
+ "activation_code": "LPA:1$test.smdp.carrier.llc$TEST",
72
+ "qr_code": null,
73
+ "smdp_address": "test.smdp.carrier.llc",
74
+ "matching_id": "TEST"
75
+ },
76
+ "metadata": {},
77
+ "error": null,
78
+ "created": 1798761600
79
+ }
37
80
  ```
38
81
 
39
82
  Plans:
@@ -41,13 +84,28 @@ Plans:
41
84
  - `test_global_1gb` and `test_eu_1gb` succeed immediately, set `livemode` false, and never call the network. They work on the free tier. The ICCID `8900000000000000001` and SM-DP+ `test.smdp.carrier.llc` are fixtures.
42
85
  - Any other slug is a live catalogue plan. It draws one eSIM from the caller's own account and needs the write scope. Do not fall back to a platform stock account.
43
86
 
44
- The same key and body return the original intent. Do not issue a second SIM to "make sure".
87
+ The same key and body return the original intent (`200`, same `id`). The same key with a different `plan` or `metadata` is refused with `409 idempotency_mismatch` and does not touch the first intent — retry with a new key instead of reusing one across two different orders:
88
+
89
+ ```json
90
+ { "error": { "type": "invalid_request_error", "code": "idempotency_mismatch", "message": "This Idempotency-Key was already used with a different request." } }
91
+ ```
45
92
 
46
93
  Confirm a plan later with `POST /v1/connectivity/intents/:id/confirm` when the first call omitted `plan`.
47
94
 
95
+ ### Auth errors
96
+
97
+ | Cause | Status | Body |
98
+ | --- | --- | --- |
99
+ | No `Authorization` header, or not `Bearer ...` | 401 | `{ "error": "Missing or invalid Authorization header" }` |
100
+ | Bearer token invalid or expired | 401 | `{ "error": "Unauthorized" }` |
101
+ | Live plan (anything but `test_global_1gb`/`test_eu_1gb`) on the free tier | 402 | `{ "error": { "type": "invalid_request_error", "code": "write_required", "message": "Live plans require the write scope. Test plans such as test_global_1gb work on the free tier." } }` |
102
+ | Monthly quota exceeded (free: 1,000 calls/mo, pro: 50,000) | 429 | `{ "error": "Monthly quota exceeded", "limit": 1000, "used": 1000, "reset_at": "...", "upgrade_url": "https://mcp.carrier.llc/upgrade" }` |
103
+
104
+ The write scope (needed for any live, non-`test_` plan and for minting `ak_` keys) comes from the **Pro** or **Enterprise** subscription tier; the free tier only has the read scope. Test plans work on free with no subscription and no eSIMVault account. Get an `ak_` key from Console → Settings → API Keys after upgrading, or headlessly via `POST /v1/api-keys` once you already have write-scoped access from the console session.
105
+
48
106
  ## One-tool MCP
49
107
 
50
- `carrier-intent` is a separate process with one tool, `create_connectivity_intent`. It posts the intent above. It is not registered on `https://mcp.carrier.llc/mcp`.
108
+ `carrier-intent` is a separate process with two tools: `create_connectivity_intent` and `publish_storefront`. This skill uses `create_connectivity_intent`, which posts the intent above. `publish_storefront` belongs to the publish-storefront skill. It is not registered on `https://mcp.carrier.llc/mcp`.
51
109
 
52
110
  ```json
53
111
  {
@@ -66,8 +124,13 @@ Put the `ak_` key in that env block on the machine. Call the tool with `plan` an
66
124
  ## After it succeeds
67
125
 
68
126
  - Read `esim.iccid` and `esim.smdp_address` back to the user.
69
- - Webhooks: `POST /v1/connectivity/webhook_endpoints` with an https URL. The secret is shown once. Deliveries use `Carrier-Signature`.
70
- - Test usage: `POST /v1/connectivity/usage_records` with the intent id and `quantity_mb`. A live intent refuses a self-reported quantity (`network_metered`) because the wallet meter already debits those bytes.
127
+ - Webhooks: `POST /v1/connectivity/webhook_endpoints` with an https URL. The secret (`whsec_...`) is shown once, on create only. Events fire for `esim.issued` (a succeeded intent, including test plans), `esim.first_attach`, and `usage.threshold_reached`; a redelivery is `POST /v1/connectivity/events/:id/retry`, not a new event. Test-plan intents do fire `esim.issued` — it is not gated on `livemode`.
128
+ - Verify `Carrier-Signature`: HMAC-SHA256 over `${timestamp}.${body}` with the endpoint secret, header shaped `t=<unix>,v1=<hex>`. Recompute the hex over the raw body and compare to `v1`. Carrier does not currently enforce a delivery time margin on the send side, so an integration that wants replay protection should reject a `t` more than a few minutes from its own clock.
129
+ - Test usage: `POST /v1/connectivity/usage_records` with the intent id and `quantity_mb`. A live intent refuses a self-reported quantity with `400 network_metered` because the wallet meter already debits those bytes.
130
+
131
+ ## Shared inventory with OCS resellers
132
+
133
+ A live (non-`test_`) plan draws its eSIM from the same eSIMVault account number as everything else on that account — the connectivity intent, the storefront checkout webhook, and a direct OCS `affectPackageToSubscriber` call all pull from one pool. Issuing through this skill does not create separate stock. Each draw is idempotency-locked per channel (`intent:<id>` for this path) so retries cannot double-issue, but two different channels against the same low-stock template can still race each other for the last unit — whichever wins the OCS call gets it, and the other side sees `NO_INVENTORY`/503, not a queued retry. If you also run OCS writes directly, keep template-level headroom rather than assuming this skill's calls are invisible to your other tooling.
71
134
 
72
135
  ## If the person is new, use these sentences
73
136
 
@@ -84,6 +147,8 @@ A country list is the countries someone typed on an offer. Carrier stores that l
84
147
  Steering is which mobile networks a SIM is told to join. Changing it is a job for the person who runs the SIMs.
85
148
  A green zone is a short list of sites that can stay reachable after the data is used up, so someone can get help, pay, or download the SIM again.
86
149
 
150
+ Longer explanations, plus when to use each tool and when to skip it: [references/glossary.md](references/glossary.md).
151
+
87
152
  ## Do not
88
153
 
89
154
  - Do not register `https://mcp.carrier.llc/mcp` to issue this SIM. That server is the operator fleet.
@@ -0,0 +1,14 @@
1
+ # Glossary, for someone new to this
2
+
3
+ A skill is a note your AI reads so it follows the same steps every time.
4
+ A plug lets an AI app press a button. This plug has one button: give this order a SIM.
5
+ This other plug opens every button a phone company uses to run many SIMs.
6
+ These are the same switchboard buttons, typed into a terminal instead of clicked. People call that a CLI.
7
+ An intent is your ask: please attach a SIM to this order. The same ask twice does not make two SIMs.
8
+ A product is the offer you sell: how much data, how many days, which countries, and the price. Saving it does not charge anyone.
9
+ A package is the data sitting on one SIM after it is issued. A code package, such as the library named @carrierllc/sdk, is a different thing.
10
+ A subscriber is one SIM line, and the person or device that line belongs to.
11
+ On this page a client is the small code library your app uses to ask for a SIM. A customer is the person who buys that SIM.
12
+ A country list is the countries someone typed on an offer. Carrier stores that list. It does not look up a live coverage map.
13
+ Steering is which mobile networks a SIM is told to join. Changing it is a job for the person who runs the SIMs.
14
+ A green zone is a short list of sites that can stay reachable after the data is used up, so someone can get help, pay, or download the SIM again.
@@ -18,10 +18,10 @@ Once per user, so every project sees it:
18
18
  npx skills add https://carrier.llc -g -a cursor -a claude-code -y
19
19
  ```
20
20
 
21
- If that host is not published yet, install this directory:
21
+ `skills` 1.7.0 requires Node 22.20 or newer (`styleText` in a Node 20 runtime throws a SyntaxError before this even installs). On Node 20, run that command through `npx -p node@22` instead:
22
22
 
23
23
  ```bash
24
- npx skills add https://github.com/Lifecycle-Innovations-Limited/carrier.llc/tree/main/apps/mcp-stdio/plugin/carrier/skills/publish-storefront -g -a cursor -a claude-code -y
24
+ npx -y -p node@22 -p skills -- skills add https://carrier.llc -g -a cursor -a claude-code -y
25
25
  ```
26
26
 
27
27
  ## Publish
@@ -70,9 +70,9 @@ A replay of the same key returns the original store. Send theme and copy again o
70
70
  }
71
71
  ```
72
72
 
73
- `price_cents` is the Stripe minor-unit amount. For EUR, 900 is 9.00. For JPY, 1000 is 1000 yen, not 100000. The OCS template id is the package that gets issued. Changing this price does not change another slug. Leave out data, validity, countries, buyer text, or whether the plan is for sale, and those stored values stay. When the shop has one plan, a price alone is enough: the CLI and the intent tool reuse that plan's slug, name, currency, and template id. A new plan can omit currency when every saved plan uses the same code.
73
+ `price_cents` is the Stripe minor-unit amount. For EUR, 900 is 9.00. For JPY, 1000 is 1000 yen, not 100000. `ocs_package_template_id` is the OCS package template that gets issued. Do not look it up in the operator OCS catalog. The default route is `product_id` (below): the connectivity plan already carries its template id, so you never type it. That id was supplied once, when the org created the plan with `POST /v1/connectivity/products` (`package_template_id`) or in the console under Connectivity products. Send `ocs_package_template_id` yourself only when you already hold a template id from your own OCS account. Changing this price does not change another slug. Leave out data, validity, countries, buyer text, or whether the plan is for sale, and those stored values stay. When the shop has one plan, a price alone is enough: the CLI and the intent tool reuse that plan's slug, name, currency, and template id. A new plan can omit currency when every saved plan uses the same code.
74
74
 
75
- To copy a connectivity plan the org already created, send its id instead of the template fields. Optional `price_cents` overrides the retail price. The shop address is derived from the plan name.
75
+ This is the standard route: to copy a connectivity plan the org already created, send its id instead of the template fields. Optional `price_cents` overrides the retail price. The shop address is derived from the plan name.
76
76
 
77
77
  ```json
78
78
  { "product_id": "cnp_abcdef0123456789abcdef0123456789", "price_cents": 1200 }
@@ -95,7 +95,7 @@ carrier site publish --slug acme --name "Acme Mobile" \
95
95
  --product cnp_abcdef0123456789abcdef0123456789 --price 12.00
96
96
  ```
97
97
 
98
- `--price 9.00` is the same retail amount as `price_cents` 900. `--price 1000 --currency JPY` stores 1000. `--data-gb "1 GB"` stores 1. `--data-gb Unlimited` stores 0, which the shop shows as Unlimited. `--validity-days "7 days"` stores 7. `--countries "France, UK"` stores FR and GB. `--logo` is an https address or a PNG, JPEG, WebP, or GIF file under 70 KB. The intent field `logo_url` accepts that https address or the same picture as a data URL. An SVG or a `javascript:` address is refused.
98
+ `--price 9.00` is the same retail amount as `price_cents` 900. `--price 1000 --currency JPY` stores 1000. `--data-gb "1 GB"` stores 1. `--data-gb Unlimited` stores 0 and also sets `unlimited: true`, which the shop shows as Unlimited. A bare 0 without that flag is refused: a forgotten field must never turn into an unlimited plan by accident. `--validity-days "7 days"` stores 7. `--countries "France, UK"` stores FR and GB. `--logo` is an https address or a PNG, JPEG, WebP, or GIF file under 70 KB. The intent field `logo_url` accepts that https address or the same picture as a data URL. An SVG or a `javascript:` address is refused.
99
99
 
100
100
  ```bash
101
101
  npx -y -p @carrierllc/mcp carrier site publish --slug acme --name "Acme Mobile" \
@@ -111,4 +111,12 @@ The CLI reads the existing store and merges color and copy before the PATCH. A s
111
111
 
112
112
  `description` is the sentence buyers read under the plan name. Leave it out to keep the saved sentence. The intent tool field is `plan_description`. The CLI flag is `--plan-text`.
113
113
 
114
- The developer MCP tool is `publish_storefront` on the intent server. It accepts `accent`, `hero`, `product_id`, `plan_slug`, `plan_name`, `price` (`9.00`, or `1000` with currency JPY) or `price_cents`, `currency`, `ocs_package_template_id`, `data_gb` (`1 GB`), `validity_days` (`7 days`), `regions` (France or DE), and `plan_description`. `product_id` copies a connectivity plan and does not need a template id. It is not an operator OCS tool.
114
+ The intent server (`carrier-intent`, package `@carrierllc/mcp-intent`) has two tools: `create_connectivity_intent`, which the issue-esim skill covers, and `publish_storefront`, which this skill covers. Neither is an operator OCS tool. `publish_storefront` accepts `accent`, `hero`, `product_id`, `plan_slug`, `plan_name`, `price` (`9.00`, or `1000` with currency JPY) or `price_cents`, `currency`, `ocs_package_template_id`, `data_gb` (`1 GB`, or the text `Unlimited`), `unlimited` (boolean; the only way to sell an unlimited plan — a bare `data_gb: 0` without it is refused), `validity_days` (`7 days`), `regions` (France or DE), and `plan_description`. `product_id` copies a connectivity plan and does not need a template id.
115
+
116
+ ## Who is paid
117
+
118
+ State of the code today, not a commercial agreement:
119
+
120
+ - A hosted shop sells through Stripe Checkout created by Carrier's checkout service with Carrier's own Stripe key. The session sets no connected account, application fee, or transfer, so the payment is collected on Carrier's Stripe account.
121
+ - Nothing in this API pays the buyer's money out to the brand, and it does not define a fee, a tax or VAT setting, or terms of sale. The `legalName` you send is printed in the shop footer. It is display text, not a merchant registration.
122
+ - Settlement, fees, VAT, and terms between the brand and Carrier are not set by this API. Confirm them with Carrier before you take live orders.