Skip to main content
Glama
README.md
# Upmind MCP Server v3

Model Context Protocol server for the [Upmind](https://upmind.com) platform: bundled API documentation search plus **97 live admin tools** covering the daily operations of a hosting business — tickets, billing, services, clients, orders, provisioning, promotions, security, and reporting.

Built for and verified against a production Upmind organisation.

## Highlights

- **97 tools** across 15 toolsets (read-only / write / destructive all annotated)
- **Secure auth CLI** — session tokens stored in the **macOS Keychain**, never plaintext; `upmind-mcp token set` imports a fresh token straight from your clipboard
- **Read-only mode** (`UPMIND_READ_ONLY=true`) blocks every write tool
- **Token-efficient responses** — list tools return compact summaries (pass `full: true` for raw records)
- **Resilient API client** — retry with backoff on 429/5xx, timeouts, Upmind field-error extraction, 401 renew-and-replay
- **Refreshable docs** — `npm run update-docs` pulls the latest reference from [apidocs.upmind.com](https://apidocs.upmind.com/) (1,270+ endpoints incl. the web hosting module)

## Install

### Without cloning (recommended)

```bash
claude mcp add --transport stdio --scope user upmind -- npx -y github:spidyhost-crm/upmind-mcp-server
```

npx fetches and builds the server automatically; your config and tokens live in `~/.upmind-mcp/` + the Keychain, so updates never touch them.

### From a clone

```bash
git clone git@github.com:spidyhost-crm/upmind-mcp-server.git && cd upmind-mcp-server
npm install && npm run build
claude mcp add --transport stdio --scope user upmind -- node $(pwd)/dist/index.js
```

## Authentication

Your admin token controls the whole business — treat it accordingly. Two modes:

### Browser temp-token mode (recommended — maximum control)

Nothing long-lived is ever stored. Session tokens expire after ~1 hour.

1. Log into your Upmind admin as usual
2. DevTools → Network → any `api.upmind.io` request → copy the Bearer token
3. `npx upmind-mcp token set` — reads the clipboard, validates against the live API, stores in the **macOS Keychain**

When it expires, tools return a clear error telling you to repeat step 2–3.

### Auto-renewal mode (convenience)

`upmind-mcp login` (email + password + 2FA) stores a session **and refresh token** in the Keychain; the server renews access tokens itself and rotates the stored pair. Revoke anytime by logging that session out in Upmind or `upmind-mcp token clear`.

### CLI reference

```
upmind-mcp token set|show|clear   manage the stored token (Keychain-backed)
upmind-mcp login                  full login with auto-renewal
upmind-mcp whoami                 verify credentials against the live API
upmind-mcp status                 show config, backend, token expiry
```

## Security model

- **Keychain storage** (macOS): tokens encrypted at rest by the OS; `UPMIND_TOKEN_BACKEND=file` falls back to `~/.upmind-mcp/tokens.json` chmod 600
- **Least privilege**: create a dedicated staff/API user in Upmind with a restricted role for this server instead of using an owner account
- **IP whitelisting**: use the built-in `list/add/remove_whitelisted_ip` tools to restrict org admin access to known IPs — a leaked token becomes useless elsewhere. Add your current IP first!
- **Read-only mode**: `UPMIND_READ_ONLY=true` hard-blocks all 48 write tools server-side
- **Destructive-tool annotations**: cancels/refunds/deletes carry `destructiveHint` so MCP clients can require confirmation
- Never paste tokens into chats or commit them — `.env`, `.tokens.json` are gitignored; the CLI clipboard flow avoids both

## Configuration

Config is layered: real env vars → project `.env` → `~/.upmind-mcp/config.env`.

| Env | Effect |
|---|---|
| `UPMIND_ORIGIN` | Your admin URL (e.g. `https://secure.spidyhost.com`) — required for OAuth flows |
| `UPMIND_API_BASE` | Default `https://api.upmind.io/api/admin` |
| `UPMIND_READ_ONLY=true` | Every write tool refuses to run |
| `UPMIND_DEFAULT_BRAND_ID` | Applied when tools accept `brand_id` and none is given |
| `UPMIND_TOOLSETS=tickets,billing` | Expose only selected toolsets (docs always on) |
| `UPMIND_TOKEN_BACKEND` | `keychain` / `file` (default auto) |
| `UPMIND_API_TOKEN` / `UPMIND_REFRESH_TOKEN` / `UPMIND_USERNAME`+`UPMIND_PASSWORD` | Env-based auth alternatives to the CLI |
| `UPMIND_TIMEOUT_MS` / `UPMIND_MAX_RETRIES` / `UPMIND_DEBUG` | Client tuning |

## Tool catalog

**docs** — `search_endpoints`, `get_endpoint_details`, `list_api_groups`, `get_group_endpoints`

**brands** — `list_brands`, `get_brand`

**catalog** — `list_categories`, `create_category`, `list_products`, `get_product`, `create_product`, `update_product`, `duplicate_product`, `copy_product_to_brand`, `reorder_products`, `list_pricelists`, `get_product_prices`, `sync_pricing`, `list_currencies`, `get_upgrade_paths`, `set_upgrade_paths`, `remove_upgrade_path`

**provisioning** — `list_provision_fields`, `get_provision_field_values`, `update_provision_field_values`, `list_provision_configs`, `list_provision_functions`

**contracts** (services) — `list_contracts`, `get_contract`, `count_active_services`, `cancel_contract`, `cancel_contract_product`, `activate_contract_product`, `set_contract_product_renewal`, `update_contract_product_dates`, `update_contract_product_price`, `change_contract_product` (upgrades, with `dry_run`), `trigger_provision`

**clients** — `list_clients`, `get_client`, `create_client`, `update_client`, `list_client_accounts`, `reset_client_password`, `resend_client_verification`

**orders** — `list_orders`, `get_order`, `create_order` (quick order → invoice), `cancel_order`, `convert_order_to_invoice`

**billing** — `list_invoices`, `get_invoice`, `credit_invoice`, `apply_client_credit`, `refund_invoice`, `create_renewal_invoice`, `refresh_invoice`, `list_payments`, `add_payment`, `refund_payment`, `list_gateways`, `get_wallet_balance`, `list_wallet_transactions`, `refund_from_wallet`

**tickets** — `list_tickets`, `get_ticket`, `get_ticket_messages`, `create_ticket`, `reply_ticket` (incl. private staff notes), `update_ticket`, `assign_ticket`, `list_ticket_departments`

**marketing** — `list_promotions`, `get_promotion`, `create_promotion`, `update_promotion`, `disable_promotion`, `list_leads`, `create_lead`, `update_lead`, `convert_lead_to_client`

**emails** — `list_email_history`, `get_email`, `resend_email`

**webhooks** — `list_webhooks`, `list_webhook_events`, `create_webhook`, `update_webhook`, `delete_webhook`

**users** — `list_users`, `get_user`

**reports** — `get_stats`, `list_reports`, `run_report`

**security** — `list_whitelisted_ips`, `add_whitelisted_ip`, `remove_whitelisted_ip`

## Upmind API knowledge baked in

Verified against production:

- **Admin prefix**: staff tokens call `https://api.upmind.io/api/admin/...`; doc URLs starting `admin/` map to the same path with the prefix stripped.
- **Pagination**: Upmind returns 10 results by default — always pass `limit`/`offset`; responses carry `total`. The client's `getAll()` auto-paginates (capped at 1,000 items).
- **Filters**: `filter[field]=value`, e.g. `filter[status.code]`, `filter[product.id]`, `filter[contract.client_id]`. ⚠️ `filter[status]=...` on invoices causes a **500 database error** — always filter status via `status.code`.
- **Status codes** are prefixed: `contract_active`, `contract_suspended`, `invoice_unpaid`, `invoice_paid`, `ticket_in_progress`, `ticket_waiting_response` (short forms like `unpaid` also match).
- **Relations** load via `with=a,b,c`; raw list rows only carry `*_id` fields (e.g. tickets have `status_id` but no status object without `with=status`).
- **OAuth**: `POST https://api.upmind.io/oauth/access_token` with `admin_password` / `admin_twofa` / `refresh_token` grants; the `Origin` header of your admin domain is required when no oauth client id is sent. Access tokens live ~1 hour; refresh tokens rotate on every use.
- **Emails**: the API exposes delivery metadata (`sent`, `bounced`, `error_message`) but not rendered bodies.

## Architecture

```
src/
├── index.ts            # stdio server entry (existing MCP registrations)
├── cli.ts              # upmind-mcp CLI: token/login/whoami/status + serve
├── serve.ts            # shared server bootstrap
├── server.ts           # assembles McpServer from the tool registry
├── registry.ts         # ToolDef type + shared schema fragments
├── config.ts           # layered env/.env/~/.upmind-mcp config
├── api/
│   ├── client.ts       # HTTP client: retries, timeouts, error shaping
│   ├── auth.ts         # TokenManager: OAuth renewal, expiry handling
│   └── token-store.ts  # Keychain (macOS) / file token storage
├── docs/               # documentation cache, search, and doc tools
├── tools/              # one file per domain, exporting ToolDef[]
└── utils/compact.ts    # response summarisation helpers
scripts/
├── update-docs.mjs     # refresh bundled docs (npm run update-docs)
└── smoke.mjs           # read-only live smoke test (npm run smoke)
data/                   # bundled apidoc JSON (refreshable)
```

Adding a tool = adding one entry to a domain file. `server.ts` handles registration, read-only enforcement, annotations, and error shaping centrally.

## Development

```bash
npm run build        # compile
npm run typecheck    # type check only
npm run smoke        # read-only smoke test against the live API
npm run update-docs  # refresh bundled API docs
```

## License

MIT

TDQS

A3.6/5.0

Scored across 97 tools

Disambiguation4/5

Most tools have clear, distinct purposes with detailed descriptions, and each targets a unique resource+action. However, the large number of similar product/contract/update tools (e.g., update_contract_product_dates vs update_contract_product_price) creates some risk of misselection, though descriptions mitigate this.

Naming Consistency5/5

All tools follow a consistent snake_case verb_noun pattern (list_, get_, create_, update_, delete_, etc.). Minor verb variations like change, sync, and apply do not break the overall predictable naming convention.

Tool Count1/5

With 97 tools, this is an extreme mismatch for an MCP server. The sheer number overwhelms an agent's ability to efficiently select the right tool, far exceeding the recommended 3-15 range and even the 25+ threshold.

Completeness4/5

The tool set covers comprehensive CRUD and lifecycle operations for clients, products, contracts, orders, invoices, payments, and tickets. Minor gaps exist (e.g., no delete_product, no create_department, no update_invoice), but agents can typically work around these omissions.

Maintenance

ActivityStale
ResponsivenessNo issues