# @carrierllc/mcp

Natural language control of your MVNO/eSIM fleet. 43 OCS tools + 9 intelligence composites + 5 prompts. Direct stdio mode — no OAuth required.

[![npm](https://img.shields.io/npm/v/@carrierllc/mcp)](https://www.npmjs.com/package/@carrierllc/mcp)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/Lifecycle-Innovations-Limited/carrier.llc/blob/main/LICENSE)

---

## Quick start

### Interactive CLI (non-developers)

```bash
npx -y -p @carrierllc/mcp carrier-mcp
# opens the Carrier TUI: status, sign-up/sign-in, install MCP, fleet NL examples
```

```bash
carrier status          # plugin + MCP + next steps
carrier plugin install  # zero-cred MCP URL; OAuth on first use in Claude
carrier open signup     # create account
carrier examples        # paste-ready “talk to fleet” prompts
carrier ask "fleet health"   # natural-language fallback; needs CARRIER_API_KEY=ak_…
```

**Entry A:** register `https://mcp.carrier.llc/mcp` with no token. The first tool
call in Claude/Cursor opens Clerk OAuth (Google / GitHub / email).

### Domain commands

With a headless key in the environment, each capability has its own command,
grouped by the same domains the console uses: `subscribers`, `packages`,
`templates`, `zones`, `steering`, `intelligence`.

```bash
carrier subscribers list --account-id 1170
carrier subscribers usage --iccid 8944… --json     # last 7 days by default
carrier packages list --iccid 8944…
carrier intelligence fleet-health
carrier steering assign --iccid 8944… --list-id 12 --commit
```

`carrier <domain> --help` lists the verbs, `carrier <domain> <verb> --help` the
flags. Two rules apply everywhere:

- `--json` prints the raw tool response on stdout and nothing else, so it pipes
  into `jq`.
- Commands that change something preview as a dry run. Add `--commit` to apply.

### Stdio server (token env)

```bash
ESIMVAULT_API_TOKEN=your_key npx -y -p @carrierllc/mcp carrier-mcp
```

Get a key at [mcp.carrier.llc](https://mcp.carrier.llc) or an org API key from the console.

---

## Install in Claude Desktop

Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):

```json
{
  "mcpServers": {
    "carrier": {
      "command": "npx",
      "args": ["-y", "-p", "@carrierllc/mcp", "carrier-mcp"],
      "env": {
        "ESIMVAULT_API_TOKEN": "YOUR_KEY"
      }
    }
  }
}
```

## Install in Cursor

Add to `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "carrier": {
      "command": "npx",
      "args": ["-y", "-p", "@carrierllc/mcp", "carrier-mcp"],
      "env": {
        "ESIMVAULT_API_TOKEN": "YOUR_KEY"
      }
    }
  }
}
```

## Install in Windsurf

Add to `~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "carrier": {
      "command": "npx",
      "args": ["-y", "-p", "@carrierllc/mcp", "carrier-mcp"],
      "env": {
        "ESIMVAULT_API_TOKEN": "YOUR_KEY"
      }
    }
  }
}
```

## Remote HTTP (OAuth 2.1)

For multi-user deployments with OAuth 2.1 + PKCE, use the remote endpoint:

```json
{
  "mcpServers": {
    "carrier": {
      "type": "http",
      "url": "https://mcp.carrier.llc/mcp"
    }
  }
}
```

---

## Environment variables

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `ESIMVAULT_API_TOKEN` | Yes | — | Your eSIMVault OCS API token |
| `CARRIER_OCS_API_TOKEN` | Alternative | — | Alias for `ESIMVAULT_API_TOKEN` |
| `CARRIER_OCS_BASE_URL` | No | `https://ocs.esimvault.cloud` | OCS base URL (no trailing slash) |
| `BROWSER_USE_API_KEY` | No | — | Browser Use Cloud key. Enables the browsing agent (`ui_agent_ask`). Runs are billed to this key at roughly **$0.20–1.00 each** |
| `CARRIER_BROWSER_USE_MAX_RUNS` | No | `10` | Browser-agent runs allowed per CLI process. `0` disables the agent. See the caveats below |
| `CARRIER_ALLOW_PORTAL_PASSWORD` | No | unset | Set to `1` to allow the ten OCS-dashboard tools to use a portal login from this machine. See below |
| `CARRIER_OCS_PORTAL_USERNAME` | No | — | OCS portal username. Only used when the flag above is set |
| `CARRIER_OCS_PORTAL_PASSWORD` | No | — | OCS portal password. Only used when the flag above is set |
| `CARRIER_OCS_DASHBOARD_URL` | No | `https://ocs.esimvault.cloud` | Dashboard the portal agent logs into |

### The browser agent costs real money

`ui_agent_ask` and the OCS-dashboard tools dispatch a hosted browser agent.
Each run costs roughly **$0.20–1.00**, billed to your own `BROWSER_USE_API_KEY`.

`CARRIER_BROWSER_USE_MAX_RUNS` caps how many runs one CLI process may start.
Read what it does not do:

- It caps **runs, not dollars**. The vendor prices a run after it finishes.
- It does **not survive a restart**. Each new CLI process starts a fresh count,
  so a caller that respawns the CLI between runs is not bounded by it.

The only hard financial ceiling is a spend limit set in your Browser Use
account. Set one.

### Opting in to the OCS portal password

Ten `ui_*` tools — steering lists, sub-account creation, destination lists,
package-template deletion, location-zone edits — have no OCS REST equivalent.
They work by driving the OCS web dashboard, which means logging into it. By
default the CLI has no portal login and these tools refuse.

You can supply one. Understand what you are supplying first:

- **This is a portal password, not a scoped API token.** It grants full
  dashboard access to anyone who holds it. `CARRIER_OCS_API_TOKEN` is a
  different kind of secret and is not a precedent for this.
- **An environment variable is a poor place for one.** It is readable from shell
  history, from `ps` output by any process on the machine, and from CI logs.
- **It is sent to Browser Use Cloud**, which types it into the OCS login form.

Carrier never logs it, never returns it in tool output, and never writes it to
an audit row — but the exposure above is yours to accept.

Two independent things must both be true, and neither alone does anything:

```bash
export CARRIER_ALLOW_PORTAL_PASSWORD=1
export CARRIER_OCS_PORTAL_USERNAME='...'
export CARRIER_OCS_PORTAL_PASSWORD='...'
```

With the flag set, the CLI prints the warning to stderr at startup. Without both
halves, the ten tools return the same "requires the remote Worker" refusal they
returned before this option existed.

Every one of the ten accepts `dry_run: true`, which returns the agent prompt
with the credentials redacted and dispatches nothing. Use it first.

`ui_request_reseller_relay_change` is not one of the ten and is never gated: it
drafts text and touches no portal.

---

## Tools (52 total)

### Reseller (6)

| Tool | Scope | Description |
|------|-------|-------------|
| `list_reseller_accounts` | read | List all accounts across all resellers |
| `get_reseller_info` | read | Retrieve reseller details |
| `esim_status_per_account` | read | eSIM status breakdown per account |
| `list_sponsors` | read | List all sponsor networks |
| `list_steering_lists` | read | List all network steering lists |
| `modify_account_balance` | write | Adjust or set reseller account balance |

### Subscriber (13)

| Tool | Scope | Description |
|------|-------|-------------|
| `get_subscriber` | read | Get full subscriber details by ICCID or MSISDN |
| `list_subscribers` | read | List subscribers with optional filters |
| `get_sim_provider_status` | read | Check SIM provider-level status |
| `get_subscriber_location` | read | Get last known subscriber location |
| `hlr_get_bitrate` | read | Get HLR bitrate for a subscriber |
| `modify_subscriber_balance` | write | Adjust or set subscriber balance |
| `modify_subscriber_status` | write | Change subscriber OCS status |
| `change_sim_status` | write | Change SIM status at provider level |
| `modify_subscriber_contact_info` | write | Update subscriber contact info |
| `set_subscriber_traffic_restrictions` | write | Configure traffic restrictions |
| `modify_subscriber_steering_list` | write | Change network steering list for a subscriber |
| `hlr_set_bitrate` | write | Set HLR bitrate for a subscriber |
| `move_subscriber_range_to_account` | write | Move subscriber range to another account |

### Packages (9)

| Tool | Scope | Description |
|------|-------|-------------|
| `list_subscriber_packages` | read | List all prepaid packages for a subscriber |
| `list_package_templates` | read | List all prepaid package templates |
| `assign_package` | write | Assign package template to subscriber |
| `assign_recurring_package` | write | Assign recurring package to subscriber |
| `modify_package_limits` | write | Change data/voice/SMS limits on active package |
| `modify_package_expiry` | write | Change expiration date of active package |
| `modify_package_status` | write | Activate or deactivate a subscriber package |
| `stop_resume_recurring_package` | write | Stop or resume recurring package auto-renewal |
| `delete_subscriber_package` | write | Remove a package from a subscriber |

### Templates & Zones (7)

| Tool | Scope | Description |
|------|-------|-------------|
| `clean_all_packages` | write | Remove ALL packages from a subscriber |
| `create_package_template` | write | Create a new prepaid package template |
| `modify_template_core` | write | Modify core settings of a package template |
| `modify_template_recurring` | write | Modify recurring settings of a package template |
| `modify_template_throttling` | write | Modify throttling settings of a package template |
| `list_location_zones` | read | List countries/networks in a location zone |
| `list_detailed_location_zones` | read | Get detailed location zone definitions |

### Statistics & Misc (8)

| Tool | Scope | Description |
|------|-------|-------------|
| `subscriber_usage` | read | Get daily usage for a subscriber (max 7 days) |
| `subscriber_network_events` | read | Get network events for a subscriber (max 7 days) |
| `subscriber_active_period` | read | Get subscriber active period |
| `get_tariff` | read | Retrieve customer tariff/pricing table |
| `list_destination_prefixes` | read | List phone number prefixes in destination lists |
| `list_network_profiles` | read | List all available network profiles |
| `create_location_zone` | write | Create a new location zone |
| `send_sms` | write | Send MT SMS to a subscriber |

### Intelligence (9)

AI composites that chain multiple OCS calls to produce structured insights:

| Tool | Description |
|------|-------------|
| `diagnose_subscriber` | Chains 5+ OCS calls to diagnose connectivity issues |
| `fleet_health` | Single-call fleet overview: accounts, eSIM counts, low-balance alerts |
| `detect_usage_anomalies` | Detect abnormal usage patterns and burn-rate risks |
| `optimize_package` | Match subscriber usage to the best available package |
| `churn_risk` | Score churn risk (0-100) with retention recommendations |
| `audit_network_coverage` | Audit network coverage and steering effectiveness |
| `marketing_intelligence` | Market segmentation and upsell opportunities |
| `high_cost_subscribers` | Identify highest-cost subscribers for margin optimization |
| `detect_country_entry` | Resolve the current country from the last MCC and compare it to an expected country |

---

## Prompts (5)

| Prompt | Args | Description |
|--------|------|-------------|
| `fleet_health_report` | — | Full fleet health overview |
| `subscriber_deep_dive` | `iccid` | Deep diagnostic for a single subscriber |
| `revenue_optimization` | — | Revenue analysis and upsell opportunities |
| `coverage_analysis` | — | Network coverage audit and steering recommendations |
| `bulk_operations_planner` | `operation` | Plan and dry-run a bulk fleet operation |

---

## Destructive tools and dry_run

All write/admin tools accept an optional `dry_run: true` argument. When set, the tool returns the would-be request without calling OCS — safe for previewing operations.

```
"Suspend ICCID 8988... but check what it would do first (dry_run)"
```

---

## Links

- Product page: [mcp.carrier.llc](https://mcp.carrier.llc)
- Source: [github.com/Lifecycle-Innovations-Limited/carrier.llc](https://github.com/Lifecycle-Innovations-Limited/carrier.llc)
- OCS docs: [docs.esimvault.cloud/ocs-api](https://docs.esimvault.cloud/ocs-api)

---

MIT License. Copyright 2025 Lifecycle Innovations Limited.
