Skip to main content
Glama
README.md
# clover-mcp

MCP server for the Clover POS REST API — gives AI assistants (Claude, Cursor, etc.) read and safe-write access to a Clover merchant's sales, inventory, orders, and customers.

[![PyPI](https://img.shields.io/pypi/v/clover-mcp)](https://pypi.org/project/clover-mcp/)
[![Python versions](https://img.shields.io/pypi/pyversions/clover-mcp)](https://pypi.org/project/clover-mcp/)
[![License: MIT](https://img.shields.io/pypi/l/clover-mcp)](LICENSE)

<!-- mcp-name: io.github.SBolivarLoL/clover-mcp -->

> **Status:** v0.7.0 released; working tree (unreleased) — 56 tools, 6 prompts, both auth modes, 399 tests. Runs locally (stdio, single merchant) or remotely over HTTP with OAuth, single- or multi-tenant (see [docs/DEPLOY.md](docs/DEPLOY.md)). Endpoint contracts are sandbox-verified in [docs/endpoints.md](docs/endpoints.md).

> ⚠️ **Independent project — not affiliated with, endorsed by, or sponsored by Clover Network, LLC or Fiserv, Inc.** "Clover" is a trademark of its respective owner and is used here only nominatively to describe interoperability. Provided **as is**, without warranty — see [Legal & disclaimer](#legal--disclaimer).

## What it can do

- Sales summaries, payment and refund reports
- Inventory lookups and low-stock alerts
- Order history and open-order inspection
- Customer search and creation
- Employee, shift, role, category, modifier, tax, tender, and device lookups; best-selling items
- Pricing config lookups: discount catalogue, tip-suggestion presets, default service charge
- Safe writes: update item prices, set stock quantities, create customers/items/categories/orders, add line items, update customers, rename items, apply order discounts, create modifier groups/modifiers/tags
- AI tools (reason via your client's model — the server holds no LLM key): sales briefings, reorder suggestions, anomaly detection, category suggestions, customer-message drafts
- Predefined prompt workflows: daily briefing, weekly sales report, inventory health check, end-of-day closeout, customer lookup, monthly tax summary

**What it cannot do (by design):** process refunds, capture payments, void charges, delete records. Those stay in the Clover dashboard.

## Tools

| Tool | Kind | Notes |
|---|---|---|
| `get_merchant_info` / `get_merchant_properties` | read | profile + POS config (banking fields never returned) |
| `get_sales_summary` | read | aggregated window (see [Sales summary semantics](#sales-summary-semantics)) |
| `get_sales_by_employee` / `get_tips_by_employee` / `get_sales_by_hour` | read | employee attribution, tip-out, and merchant-local daypart reporting (`PAYMENTS_R`; employee names are best-effort) |
| `list_payments` / `list_refunds` / `list_credits` / `list_tenders` | read | payments, refunds, credits, tender types |
| `list_orders` / `get_order` / `list_open_orders` / `list_order_types` | read | order history + detail |
| `list_items` / `get_item` / `list_low_stock_items` | read | inventory + stock; `get_item(include=[...])` opts in to `modifier_groups`/`tax_rates`/`categories`/`tags` association detail |
| `list_categories` / `list_modifiers` / `list_taxes` / `list_item_groups` / `list_attributes` / `list_tags` / `list_discounts` | read | catalog structure |
| `list_tip_suggestions` / `get_default_service_charge` | read | tip presets + service-charge config |
| `list_devices` / `list_opening_hours` / `list_cash_events` | read | terminals, hours, cash-drawer log |
| `get_top_items` | read | best-sellers by units in a window |
| `list_employees` / `get_employee` / `list_shifts` / `list_active_shifts` / `list_roles` | read | PINs never returned (`EMPLOYEES_R`) |
| `search_customers` / `get_customer` | read | cards never returned |
| `whoami` | read | multi-tenant identity diagnostic (no secrets) |
| `summarize_sales` / `inventory_reorder_suggestions` / `detect_sales_anomalies` / `suggest_item_categories` / `draft_customer_message` | AI | reason via your client's model; read-only suggestions |
| `create_customer` / `update_customer` | write | dup-check + `dry_run`; update confirms via elicitation |
| `create_item` / `create_category` / `create_order` / `add_line_item` | write | guarded: `dry_run` + confirm before writing |
| `set_item_price_cents` / `set_item_stock_quantity` / `update_item_name` | write | optimistic-lock pre-check, bounds, `dry_run` |
| `apply_order_discount` | write | exactly one of `percentage` / `amount_cents` / `catalogue_discount_id`; negates positive `amount_cents` to Clover's required negative wire value; catalogue path resolves name+value client-side; `dry_run` preview includes a client-computed line-item subtotal |
| `create_modifier_group` / `create_modifier` / `create_tag` | write | dup-guard (group/tag) or parent pre-check (modifier); guarded: `dry_run` + confirm before writing |

Every tool carries MCP behaviour annotations (`readOnlyHint` / `destructiveHint` / `idempotentHint`) so clients can parallelize reads and prompt before writes.

## Install

Published on [PyPI](https://pypi.org/project/clover-mcp/) — no clone needed:

```bash
uvx clover-mcp          # run directly (recommended)
# or
pip install clover-mcp  # then launch with: clover-mcp
```

From source (for development):

```bash
git clone https://github.com/SBolivarLoL/clover-mcp-server
cd clover-mcp-server
uv pip install -e .
```

## Configuration

Copy `.env.example` to `.env` and fill in your values:

```bash
cp .env.example .env
```

Required:

| Variable | Description |
|---|---|
| `CLOVER_MERCHANT_ID` | Your Clover merchant ID |
| `CLOVER_ACCESS_TOKEN` | Your Clover API access token |

Optional:

| Variable | Default | Description |
|---|---|---|
| `CLOVER_REGION` | `na` | `na`, `eu`, or `la` |
| `CLOVER_SANDBOX` | `false` | `true` to use the Clover sandbox |
| `CLOVER_AUTH_MODE` | `token` | `token` or `oauth_refresh` |
| `CLOVER_READ_ONLY` | `false` | Refuse every write before making a Clover request |
| `CLOVER_WRITE_LIMIT_COUNT` | `10` | Maximum writes per safety window; `0` disables, negative values are rejected |
| `CLOVER_WRITE_LIMIT_WINDOW_S` | `300` | Positive write-safety window in seconds |

### Auth modes

- **`token`** — paste a static access token. Works for sandbox and single-merchant production use. If the token expires, regenerate it in the Clover Developer Dashboard.
- **`oauth_refresh`** — the server auto-refreshes on expiry and persists the new token pair to `CLOVER_TOKEN_STORE` (default: `~/.config/clover-mcp/tokens.json`, mode 0600). Clover refresh tokens are single-use, so the rotated pair is written back after each refresh. Run `scripts/get_sandbox_token.py` to obtain tokens — it writes them straight to the store, so you only set `CLOVER_AUTH_MODE`, `CLOVER_OAUTH_CLIENT_ID`, `CLOVER_OAUTH_CLIENT_SECRET`, and `CLOVER_MERCHANT_ID` in `.env` (no token values needed). Pasting `CLOVER_ACCESS_TOKEN` / `CLOVER_REFRESH_TOKEN` into `.env` still works as an alternative.

> **Use a least-privilege token.** Grant only the permission scopes the tools you actually use require (see the table below). A read-only deployment needs no `*_W` scopes at all. Don't reuse a production token in sandbox or vice versa.

## Claude Desktop setup

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

```json
{
  "mcpServers": {
    "clover": {
      "command": "uvx",
      "args": ["clover-mcp"],
      "env": {
        "CLOVER_MERCHANT_ID": "your_merchant_id",
        "CLOVER_ACCESS_TOKEN": "your_token",
        "CLOVER_REGION": "na"
      }
    }
  }
}
```

## Cursor setup

Add to `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` globally):

```json
{
  "mcpServers": {
    "clover": {
      "command": "uvx",
      "args": ["clover-mcp"],
      "env": {
        "CLOVER_MERCHANT_ID": "your_merchant_id",
        "CLOVER_ACCESS_TOKEN": "your_token"
      }
    }
  }
}
```

## Required Clover permissions

Your token must have the following Clover permission scopes:

| Permission | Used by |
|---|---|
| `MERCHANT_R` | `get_merchant_info` |
| `ORDERS_R` | `list_orders`, `get_order`, `list_open_orders` |
| `PAYMENTS_R` | `list_payments`, `list_refunds`, `list_credits`, `get_sales_summary`, `get_sales_by_employee`, `get_tips_by_employee`, `get_sales_by_hour` |
| `ORDERS_R` | …also `get_top_items` |
| `INVENTORY_R` | `list_items`, `get_item`, `list_low_stock_items`, `list_categories`, `list_modifiers`, `list_taxes`, `list_discounts`, `list_item_groups`, `list_attributes`, `list_tags` |
| `INVENTORY_W` | `set_item_price_cents`, `set_item_stock_quantity`, `create_item`, `create_category`, `update_item_name`, `create_modifier_group`, `create_modifier`, `create_tag` |
| `ORDERS_W` | `create_order`, `add_line_item`, `apply_order_discount` |
| `CUSTOMERS_R` | `search_customers`, `get_customer` |
| `CUSTOMERS_W` | `create_customer`, `update_customer` |
| `EMPLOYEES_R` | `list_employees`, `get_employee`, `list_shifts`, `list_active_shifts`, `list_roles` (optional) |
| `MERCHANT_R` | …also `list_devices`, `list_tenders`, `list_order_types`, `list_opening_hours`, `list_cash_events`, `list_tip_suggestions`, `get_default_service_charge` |

Read scopes (`*_R`) are probed at startup; the server **warns** about any missing ones (it no longer exits — a hosted server must still start) and the affected tools return a 403 when called. `EMPLOYEES_R` is optional. Write scopes (`*_W`) are **not** probed (a probe would mutate data) — a missing write scope surfaces as a 403 the first time you call that tool. Permission changes on a Clover app require the merchant to reinstall the app.

## Remote / hosted (v2)

By default this runs locally over stdio for a single merchant. To run it remotely:

- **FastMCP Cloud / Horizon (easiest):** deploy with entrypoint `server.py:mcp`,
  enable the platform's built-in auth, and set single-merchant Clover env vars.
  The platform handles OAuth, HTTPS, and transport — no IdP setup, and do **not**
  set `CLOVER_TRANSPORT`/`CLOVER_AUTH_*` (that path needs an IdP and will fail).
- **Self-host:** use `server.py:create_server`, which makes clover-mcp an OAuth
  2.1 **resource server** (validates your IdP's JWTs, publishes Protected Resource
  Metadata per RFC 9728, routes by token claim) and **refuses to start without an
  IdP** so it can't run open.

Full setup for both in **[docs/DEPLOY.md](docs/DEPLOY.md)**. How SSO/SAML, SCIM,
audit, and multi-tenant authorization fit: **[docs/enterprise-identity.md](docs/enterprise-identity.md)**.

## Sales summary semantics

`get_sales_summary` makes the accounting explicit so the LLM can explain it:

- **Gross** = sum of `result=SUCCESS` payment amounts. `FAIL`/`AUTH`/uncaptured `PRE_AUTH` are excluded.
- **Refunds** come from the dedicated `/refunds` endpoint (Clover refunds are separate objects with a positive amount, not negative payments). **Voids** are counted from voided payments. Both are reported separately (`refund_count`/`refund_amount`, `void_count`) — never netted into `payment_count`. `net_sales = gross_sales - refund_amount`.
- **Tips and taxes** are broken out as their own line items.
- **Service charges** are *not* reported separately: Clover exposes them on the order only as a percentage (no computed amount), and what customers actually paid is already in `gross_sales` via payment totals.
- **Offline payments** are included; a `note` flags the window when any are present.
- **Currency** comes from the merchant record, never defaulted.
- Windows longer than 90 days are split and concatenated transparently.

## Development

```bash
uv pip install -e ".[dev]"
pytest
ruff check src/ tests/ scripts/
ruff format --check src/ tests/ scripts/
mypy src/clover_mcp/
```

Correctness eval + latency/load benchmark against a sandbox:
`uv run python scripts/benchmark.py` — methodology, results, and failure analysis
in [docs/eval.md](docs/eval.md).

Architecture (diagrams + module map): [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
Run a 5-minute demo: `uv run python scripts/demo.py` (or the runbook in
[docs/DEMO.md](docs/DEMO.md)).

## Observability

All observability output goes to **stderr** (stdout carries the MCP stdio protocol).

- **Audit logging** (on by default) — every write emits one structured JSON line:
  `{"ts":"2026-07-02T…Z","audit":"write","method":"PUT","path":"/items/…","status":200,"merchant":"…"}`.
  The UTC `ts` records when; in multi-tenant mode a `tenant` field records who.
  No request bodies or secrets. Disable with `CLOVER_AUDIT_LOG=false`.
- **Latency logging** — set `CLOVER_LATENCY_LOG=true` to emit a `latency_ms` line
  per Clover HTTP call.
- **Distributed tracing (optional)** — install the OpenTelemetry extra and point it
  at your collector; every Clover call becomes a span. Without it, tracing is a
  zero-cost no-op (no dependency added):

  ```bash
  uv pip install -e ".[otel]"
  export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
  export OTEL_SERVICE_NAME=clover-mcp
  ```

## Security

See [SECURITY.md](SECURITY.md) for the vulnerability disclosure policy.

## Legal & disclaimer

> This is not legal advice. The notes below describe the project's intent and the
> operator's responsibilities.

- **Not affiliated.** This is an independent, community project. It is **not**
  affiliated with, endorsed by, or sponsored by Clover Network, LLC or Fiserv, Inc.
  "Clover" and related marks are trademarks of their respective owners and are used
  here only **nominatively** — to state that this software interoperates with the
  Clover REST API. No Clover logos or branding are used.
- **No warranty / no liability.** The software is provided **"AS IS"** under the
  [MIT License](LICENSE), without warranty of any kind. The authors are not liable
  for any claim, damage, or loss arising from its use — including incorrect data,
  unintended writes, downtime, or API changes outside the authors' control.
- **You operate it; you're responsible.** You run this server with **your own**
  Clover account and API credentials. You are solely responsible for: complying
  with Clover's developer/API terms and trademark-usage policy; safeguarding your
  tokens; and meeting any data-protection (e.g. GDPR/CCPA) and tax obligations for
  data you access. The write tools **modify live merchant data** — test in the
  sandbox first and use least-privilege tokens.
- **No card data, no payments.** The server never handles payment card data (the
  shaping layer blocks it) and deliberately cannot capture payments, refund, or
  void. It is **not** a PCI-DSS solution.
- **Third-party API.** This project only calls Clover's public REST API using the
  operator's credentials; it bundles no Clover SDK or proprietary code. Clover may
  change or restrict its API at any time, which may break functionality.

## License

MIT — see [LICENSE](LICENSE).

TDQS

A3.8/5.0

Scored across 56 tools

Disambiguation5/5

Each tool targets a distinct resource or action, with clear boundaries even among the many list_* tools (orders, payments, refunds, items, categories, etc.). The only potential overlap, list_open_orders versus list_orders with a state filter, is explicitly documented as a convenience wrapper.

Naming Consistency4/5

Most tools follow a consistent verb_noun pattern (list_, get_, create_, set_, update_). Minor deviations include 'whoami' (single word), 'inventory_reorder_suggestions' (noun phrase instead of verb-first), and the mix of set_item_* vs. update_item_name.

Tool Count1/5

With 56 tools, the server far exceeds the threshold for an appropriate count. Even for a broad POS integration, 50+ tools is an extreme mismatch and will overwhelm agents with too many options.

Completeness2/5

The server provides extensive read-only reporting and some inventory/customer writes, but it lacks critical lifecycle operations: no payment capture/refund/void, no update/delete for orders or inventory items, and no order closing. Creates exist for orders and items but no way to complete or remove them, leaving obvious dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues