clover-mcp 0.1.1__tar.gz
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.
- clover_mcp-0.1.1/.env.example +24 -0
- clover_mcp-0.1.1/.github/CODEOWNERS +2 -0
- clover_mcp-0.1.1/.github/dependabot.yml +21 -0
- clover_mcp-0.1.1/.github/workflows/ci.yml +45 -0
- clover_mcp-0.1.1/.github/workflows/codeql.yml +31 -0
- clover_mcp-0.1.1/.github/workflows/release.yml +50 -0
- clover_mcp-0.1.1/.gitignore +34 -0
- clover_mcp-0.1.1/CHANGELOG.md +40 -0
- clover_mcp-0.1.1/CLAUDE.md +112 -0
- clover_mcp-0.1.1/LICENSE +21 -0
- clover_mcp-0.1.1/PKG-INFO +213 -0
- clover_mcp-0.1.1/README.md +188 -0
- clover_mcp-0.1.1/RELEASING.md +42 -0
- clover_mcp-0.1.1/SECURITY.md +31 -0
- clover_mcp-0.1.1/docs/endpoints.md +92 -0
- clover_mcp-0.1.1/pyproject.toml +65 -0
- clover_mcp-0.1.1/scripts/get_sandbox_token.py +162 -0
- clover_mcp-0.1.1/src/clover_mcp/__init__.py +1 -0
- clover_mcp-0.1.1/src/clover_mcp/__main__.py +9 -0
- clover_mcp-0.1.1/src/clover_mcp/auth.py +106 -0
- clover_mcp-0.1.1/src/clover_mcp/client.py +159 -0
- clover_mcp-0.1.1/src/clover_mcp/config.py +117 -0
- clover_mcp-0.1.1/src/clover_mcp/errors.py +56 -0
- clover_mcp-0.1.1/src/clover_mcp/formatting.py +50 -0
- clover_mcp-0.1.1/src/clover_mcp/server.py +338 -0
- clover_mcp-0.1.1/src/clover_mcp/shaping.py +166 -0
- clover_mcp-0.1.1/src/clover_mcp/tools/__init__.py +0 -0
- clover_mcp-0.1.1/src/clover_mcp/tools/customers.py +157 -0
- clover_mcp-0.1.1/src/clover_mcp/tools/inventory.py +238 -0
- clover_mcp-0.1.1/src/clover_mcp/tools/merchant.py +18 -0
- clover_mcp-0.1.1/src/clover_mcp/tools/orders.py +134 -0
- clover_mcp-0.1.1/src/clover_mcp/tools/reporting.py +247 -0
- clover_mcp-0.1.1/src/clover_mcp/windowing.py +50 -0
- clover_mcp-0.1.1/tests/__init__.py +0 -0
- clover_mcp-0.1.1/tests/conftest.py +43 -0
- clover_mcp-0.1.1/tests/contract/__init__.py +0 -0
- clover_mcp-0.1.1/tests/contract/test_formatting.py +49 -0
- clover_mcp-0.1.1/tests/contract/test_permission_check.py +75 -0
- clover_mcp-0.1.1/tests/contract/test_region_resolution.py +35 -0
- clover_mcp-0.1.1/tests/contract/test_shaping_allowlist.py +128 -0
- clover_mcp-0.1.1/tests/contract/test_tool_annotations.py +63 -0
- clover_mcp-0.1.1/tests/contract/test_window_splitting.py +71 -0
- clover_mcp-0.1.1/tests/test_auth.py +164 -0
- clover_mcp-0.1.1/tests/tools/__init__.py +0 -0
- clover_mcp-0.1.1/tests/tools/test_customers.py +472 -0
- clover_mcp-0.1.1/tests/tools/test_inventory.py +523 -0
- clover_mcp-0.1.1/tests/tools/test_merchant.py +65 -0
- clover_mcp-0.1.1/tests/tools/test_orders.py +334 -0
- clover_mcp-0.1.1/tests/tools/test_reporting.py +334 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# ── Required ─────────────────────────────────────────────────────────────────
|
|
2
|
+
CLOVER_MERCHANT_ID=your_merchant_id_here
|
|
3
|
+
CLOVER_ACCESS_TOKEN=your_access_token_here
|
|
4
|
+
|
|
5
|
+
# ── Region (na | eu | la) — default: na ──────────────────────────────────────
|
|
6
|
+
CLOVER_REGION=na
|
|
7
|
+
|
|
8
|
+
# ── Sandbox mode (true | false) — default: false ─────────────────────────────
|
|
9
|
+
# Set true to use apisandbox.dev.clover.com instead of production
|
|
10
|
+
CLOVER_SANDBOX=false
|
|
11
|
+
|
|
12
|
+
# ── Auth mode (token | oauth_refresh) — default: token ───────────────────────
|
|
13
|
+
# token → static access token; operator regenerates manually when it expires
|
|
14
|
+
# oauth_refresh → server auto-refreshes using OAuth refresh token (v1 production)
|
|
15
|
+
CLOVER_AUTH_MODE=token
|
|
16
|
+
|
|
17
|
+
# ── OAuth refresh fields (required only when AUTH_MODE=oauth_refresh) ─────────
|
|
18
|
+
# CLOVER_REFRESH_TOKEN=your_refresh_token
|
|
19
|
+
# CLOVER_OAUTH_CLIENT_ID=your_oauth_client_id
|
|
20
|
+
# CLOVER_OAUTH_CLIENT_SECRET=your_oauth_client_secret
|
|
21
|
+
|
|
22
|
+
# ── Token store path (oauth_refresh mode only) ────────────────────────────────
|
|
23
|
+
# Default: ~/.config/clover-mcp/tokens.json (created with 0600 permissions)
|
|
24
|
+
# CLOVER_TOKEN_STORE=~/.config/clover-mcp/tokens.json
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# Python dependencies (pyproject.toml)
|
|
4
|
+
- package-ecosystem: "pip"
|
|
5
|
+
directory: "/"
|
|
6
|
+
schedule:
|
|
7
|
+
interval: "weekly"
|
|
8
|
+
open-pull-requests-limit: 5
|
|
9
|
+
groups:
|
|
10
|
+
python-deps:
|
|
11
|
+
patterns: ["*"]
|
|
12
|
+
|
|
13
|
+
# GitHub Actions — keeps SHA-pinned actions current
|
|
14
|
+
- package-ecosystem: "github-actions"
|
|
15
|
+
directory: "/"
|
|
16
|
+
schedule:
|
|
17
|
+
interval: "weekly"
|
|
18
|
+
open-pull-requests-limit: 5
|
|
19
|
+
groups:
|
|
20
|
+
actions:
|
|
21
|
+
patterns: ["*"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ["3.11", "3.12"]
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
run: uv python install ${{ matrix.python-version }}
|
|
28
|
+
|
|
29
|
+
- name: Create virtual environment
|
|
30
|
+
run: uv venv --python ${{ matrix.python-version }}
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: uv pip install -e ".[dev]"
|
|
34
|
+
|
|
35
|
+
- name: Lint (ruff)
|
|
36
|
+
run: uv run ruff check src/ tests/
|
|
37
|
+
|
|
38
|
+
- name: Format check (ruff)
|
|
39
|
+
run: uv run ruff format --check src/ tests/
|
|
40
|
+
|
|
41
|
+
- name: Type check (mypy)
|
|
42
|
+
run: uv run mypy src/clover_mcp/
|
|
43
|
+
|
|
44
|
+
- name: Test (pytest)
|
|
45
|
+
run: uv run pytest tests/ -v --tb=short
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: "0 6 * * 1" # weekly, Monday 06:00 UTC
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
analyze:
|
|
16
|
+
name: Analyze (python)
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
permissions:
|
|
19
|
+
security-events: write # upload CodeQL results
|
|
20
|
+
contents: read
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
23
|
+
|
|
24
|
+
- name: Initialize CodeQL
|
|
25
|
+
uses: github/codeql-action/init@dd903d2e4f5405488e5ef1422510ee31c8b32357 # v3
|
|
26
|
+
with:
|
|
27
|
+
languages: python
|
|
28
|
+
queries: security-extended
|
|
29
|
+
|
|
30
|
+
- name: Perform CodeQL analysis
|
|
31
|
+
uses: github/codeql-action/analyze@dd903d2e4f5405488e5ef1422510ee31c8b32357 # v3
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Tag-triggered. Push a vX.Y.Z tag to build artifacts, create a GitHub Release,
|
|
4
|
+
# and publish to PyPI via trusted publishing (OIDC — no token stored in the repo).
|
|
5
|
+
# PyPI publish requires a one-time "trusted publisher" setup on PyPI for this
|
|
6
|
+
# repo + workflow. See RELEASING.md.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags:
|
|
10
|
+
- "v*"
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write # create the GitHub Release
|
|
14
|
+
id-token: write # PyPI trusted publishing (OIDC)
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
release:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
|
|
24
|
+
|
|
25
|
+
- name: Verify tag matches package version
|
|
26
|
+
run: |
|
|
27
|
+
PKG=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
28
|
+
TAG="${GITHUB_REF_NAME#v}"
|
|
29
|
+
if [ "$PKG" != "$TAG" ]; then
|
|
30
|
+
echo "::error::Tag $GITHUB_REF_NAME does not match pyproject version $PKG" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
echo "Releasing version $PKG"
|
|
34
|
+
|
|
35
|
+
- name: Build sdist + wheel
|
|
36
|
+
run: uv build
|
|
37
|
+
|
|
38
|
+
- name: Create GitHub Release
|
|
39
|
+
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
|
|
40
|
+
with:
|
|
41
|
+
files: |
|
|
42
|
+
dist/*.whl
|
|
43
|
+
dist/*.tar.gz
|
|
44
|
+
generate_release_notes: true
|
|
45
|
+
fail_on_unmatched_files: true
|
|
46
|
+
|
|
47
|
+
# Requires a PyPI trusted publisher configured for this repo + workflow.
|
|
48
|
+
# Until that's set up this step fails without affecting the GitHub Release above.
|
|
49
|
+
- name: Publish to PyPI (trusted publishing)
|
|
50
|
+
run: uv publish
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
*.egg
|
|
10
|
+
|
|
11
|
+
# uv
|
|
12
|
+
.uv/
|
|
13
|
+
uv.lock
|
|
14
|
+
|
|
15
|
+
# env / secrets
|
|
16
|
+
.env
|
|
17
|
+
.env.*
|
|
18
|
+
!.env.example
|
|
19
|
+
*.json
|
|
20
|
+
!docs/*.json
|
|
21
|
+
|
|
22
|
+
# token store
|
|
23
|
+
~/.config/clover-mcp/
|
|
24
|
+
|
|
25
|
+
# test / coverage
|
|
26
|
+
.pytest_cache/
|
|
27
|
+
.coverage
|
|
28
|
+
htmlcov/
|
|
29
|
+
.mypy_cache/
|
|
30
|
+
.ruff_cache/
|
|
31
|
+
|
|
32
|
+
# OS
|
|
33
|
+
.DS_Store
|
|
34
|
+
Thumbs.db
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
5
|
+
this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.1] — unreleased
|
|
10
|
+
### Added
|
|
11
|
+
- Legal & disclaimer section: nominative trademark / not-affiliated notice,
|
|
12
|
+
AS-IS no-warranty statement, and operator responsibilities (Clover terms,
|
|
13
|
+
data-protection, least-privilege tokens).
|
|
14
|
+
- PyPI publishing from the release workflow via trusted publishing (OIDC).
|
|
15
|
+
### Fixed
|
|
16
|
+
- Release workflow attaches only the wheel + sdist (glob narrowed from `dist/*`).
|
|
17
|
+
|
|
18
|
+
## [0.1.0] — 2026-06-21 (GitHub release)
|
|
19
|
+
### Added
|
|
20
|
+
- 11 read tools: `get_merchant_info`, `get_sales_summary`, `list_payments`,
|
|
21
|
+
`list_orders`, `get_order`, `list_open_orders`, `list_items`, `get_item`,
|
|
22
|
+
`list_low_stock_items`, `search_customers`, `get_customer`.
|
|
23
|
+
- 3 safe write tools: `create_customer` (idempotency dup-check, `dry_run`),
|
|
24
|
+
`set_item_price_cents`, `set_item_stock_quantity` (optimistic-lock pre-check,
|
|
25
|
+
bounds, `dry_run`, absolute set — not a delta).
|
|
26
|
+
- Two auth modes: static `token` and `oauth_refresh` (single-use refresh-token
|
|
27
|
+
rotation, 0600 token store, refresh-on-401).
|
|
28
|
+
- MCP tool annotations (`readOnlyHint` / `destructiveHint` / `idempotentHint` /
|
|
29
|
+
`openWorldHint`) on all 14 tools.
|
|
30
|
+
- Allowlist response shaping (no card / PII / PIN leakage), 90-day windowing,
|
|
31
|
+
currency-aware money formatting, startup permission self-check.
|
|
32
|
+
- Sandbox-verified endpoint audit ([docs/endpoints.md](docs/endpoints.md)).
|
|
33
|
+
|
|
34
|
+
### Out of scope (by design)
|
|
35
|
+
- Refunds, voids, payment capture, charge creation, record deletes.
|
|
36
|
+
- Employee/shift tools (planned v1.1), multi-merchant hosted mode + MCP-level
|
|
37
|
+
OAuth 2.1 (planned v2).
|
|
38
|
+
|
|
39
|
+
[Unreleased]: https://github.com/SBolivarLoL/clover-mcp-server/compare/v0.1.0...HEAD
|
|
40
|
+
[0.1.0]: https://github.com/SBolivarLoL/clover-mcp-server/releases/tag/v0.1.0
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# CLAUDE.md — Development Guidelines for clover-mcp
|
|
2
|
+
|
|
3
|
+
This file defines the coding principles and best practices that every contributor (human or AI) must follow in this project.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Principles
|
|
8
|
+
|
|
9
|
+
### 1. KISS — Keep It Simple, Stupid
|
|
10
|
+
|
|
11
|
+
Prefer the simplest solution that correctly solves the problem. Complexity is a liability.
|
|
12
|
+
|
|
13
|
+
- A new function should do one thing and do it clearly.
|
|
14
|
+
- If you find yourself explaining what a block of code does, simplify it first.
|
|
15
|
+
- Avoid abstractions until they are earned by repetition (see DRY below). A premature abstraction is worse than a copy.
|
|
16
|
+
- When two implementations both work, the shorter one wins — unless clarity suffers.
|
|
17
|
+
|
|
18
|
+
**Applied here:** `resolve_base_url()` in `config.py` is a single dict lookup, not a class hierarchy. `_pick()` in `shaping.py` is three lines. Keep new helpers at that level.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
### 2. DRY — Don't Repeat Yourself
|
|
23
|
+
|
|
24
|
+
Every piece of knowledge has a single authoritative home. Duplication is the root of maintenance bugs.
|
|
25
|
+
|
|
26
|
+
- If the same logic appears in two tools, extract a shared helper in `src/clover_mcp/` (formatting, shaping, windowing, etc.).
|
|
27
|
+
- Region → base-URL mapping lives **only** in `config.resolve_base_url()`. Never hardcode a hostname elsewhere.
|
|
28
|
+
- Allowlist field names live **only** in `shaping.py`. Never re-list them in a tool or a test.
|
|
29
|
+
- Shared test fixtures live **only** in `tests/conftest.py`.
|
|
30
|
+
|
|
31
|
+
**Red flag:** if you copy-paste a block and change one variable, that is a signal to parameterise, not to paste.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### 3. SOLID Principles — The Core of OOP
|
|
36
|
+
|
|
37
|
+
Applied pragmatically to this Python/async codebase:
|
|
38
|
+
|
|
39
|
+
| Principle | What it means here |
|
|
40
|
+
|---|---|
|
|
41
|
+
| **S** — Single Responsibility | Each module has one job: `config.py` loads config, `client.py` sends HTTP, `shaping.py` projects responses, `windowing.py` splits date ranges. Don't blur those lines. |
|
|
42
|
+
| **O** — Open/Closed | Add new Clover resources by adding a new file under `tools/` and registering it in `server.py`. Do not modify `client.py` or `shaping.py` to accommodate a specific tool's quirks. |
|
|
43
|
+
| **L** — Liskov Substitution | Not deeply relevant in a mostly-functional codebase, but: if you subclass `CloverClient`, the subclass must be a drop-in replacement — do not weaken the contract. |
|
|
44
|
+
| **I** — Interface Segregation | Keep tool signatures narrow. A tool that only reads inventory should not accept payment-related parameters. Pass only what is needed. |
|
|
45
|
+
| **D** — Dependency Inversion | Tools depend on `CloverClient` (the abstraction), not on `httpx` directly. Tests inject a mock-backed client — not the real network. Never import `httpx` inside a tool file. |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### 4. Separation of Concerns (SoC)
|
|
50
|
+
|
|
51
|
+
Each layer of the stack is responsible for exactly one concern. Do not let concerns leak across layers.
|
|
52
|
+
|
|
53
|
+
| Layer | Its concern | Must NOT do |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `config.py` | Read env, validate, resolve URLs | Make HTTP calls |
|
|
56
|
+
| `auth.py` | Token lifecycle, file storage, refresh | Business logic |
|
|
57
|
+
| `client.py` | HTTP transport, retries, pagination | Know about Clover resources |
|
|
58
|
+
| `shaping.py` | Allowlist projection, PII removal | Know about tools or auth |
|
|
59
|
+
| `windowing.py` | Date chunking, ms conversion | Know about HTTP or tools |
|
|
60
|
+
| `formatting.py` | Money and time display | Know about the API |
|
|
61
|
+
| `tools/*.py` | Orchestrate a user-facing action | Duplicate HTTP logic or shaping |
|
|
62
|
+
| `server.py` | Register tools with FastMCP, run | Implement business logic directly |
|
|
63
|
+
| `tests/` | Verify behaviour | Call real network or read `.env` |
|
|
64
|
+
|
|
65
|
+
If you find yourself importing `httpx` in a tool file, or importing `fastmcp` in `client.py`, that is a SoC violation — stop and refactor.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Best Practices
|
|
70
|
+
|
|
71
|
+
### 1. Write Clean and Readable Code
|
|
72
|
+
|
|
73
|
+
- **Name things after what they are**, not what they do to get there. `shape_customer()` not `strip_and_flatten_customer_dict()`.
|
|
74
|
+
- **No abbreviations** beyond universally understood ones (`id`, `ts`, `ms`, `mId`). Write `merchant_id`, not `mid` or `m`.
|
|
75
|
+
- **One concept per line.** Avoid chaining more than two method calls on a single expression.
|
|
76
|
+
- **No magic numbers.** `max_days=90`, `MAX_PRICE_CENTS = 100_000_000` — name every constant.
|
|
77
|
+
- **Comments explain WHY, not WHAT.** The code says what; a comment earns its place only when the reasoning is non-obvious (e.g. "Clover returns 200 with empty body on some PUTs").
|
|
78
|
+
- **Keep functions short.** If a function doesn't fit on one screen, it has more than one responsibility.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### 2. Follow Design Principles & Architecture
|
|
83
|
+
|
|
84
|
+
- **Module boundaries are real.** Before adding code to a file, ask: is this the right module? Resist the urge to add a "quick fix" in the wrong layer.
|
|
85
|
+
- **The endpoint audit gate is a design constraint.** No tool ships until its endpoint row in `docs/endpoints.md` is verified against the Clover sandbox. Do not skip this.
|
|
86
|
+
- **Write tools as thin orchestrators.** A tool function should: validate inputs → call `client.*` → run output through the appropriate `shaping.*` function → return a clean dict. Nothing more.
|
|
87
|
+
- **No global mutable state** beyond the module-level `_client` in `server.py` and the asyncio lock in `auth.py`. Both are intentional and documented.
|
|
88
|
+
- **Async all the way down.** All I/O — HTTP calls, file reads in `auth.py` — must be non-blocking. Do not call `open()` or `requests` in an `async def` function.
|
|
89
|
+
- **Write tools is a privilege, not a default.** Any new write tool must have: explicit ID parameter, expected-current-value pre-check, `dry_run` support, input bounds, and a description that starts with "Modifies merchant data."
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### 3. Always Do Testing
|
|
94
|
+
|
|
95
|
+
- **Every tool gets a test file** in `tests/tools/test_<tool_name>.py` with at minimum: one happy-path test and one error-path test (rotate through 401, 403, 404, 429).
|
|
96
|
+
- **Contract tests are first-class.** `tests/contract/` tests cover the guarantees that would silently break if a module drifts: region resolution, window splitting, shaping allowlist (PII/card/PIN leak prevention), money formatting.
|
|
97
|
+
- **Use `respx` to mock `httpx`.** Tests must never make real network calls. Use `conftest.py` fixtures — do not re-create the mock router in each test.
|
|
98
|
+
- **The allowlist test is a security gate.** `test_shaping_allowlist.py` must pass before any shaping change merges. Add new banned field names to `BANNED_KEYS` whenever a new sensitive Clover field is discovered.
|
|
99
|
+
- **Coverage floors:** `client.py`, `auth.py`, `windowing.py`, `formatting.py`, `shaping.py`, `config.py` ≥ 85%; tool modules ≥ 60%.
|
|
100
|
+
- **CI must be green before merging** — ruff lint, ruff format, mypy strict (on core modules), and pytest.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### 4. Validate User Input and Implement Graceful Error Handling
|
|
105
|
+
|
|
106
|
+
- **Validate at the boundary.** Tool parameters are the system boundary. Validate there — not inside `client.py` or `shaping.py`.
|
|
107
|
+
- **Fail fast and loudly on config errors.** `load_config()` raises a `RuntimeError` listing every missing variable before the server starts. Never silently default a required value.
|
|
108
|
+
- **Surface Clover errors verbatim.** Do not paraphrase a 403 or 404 — pass through Clover's original message so the operator can act on it.
|
|
109
|
+
- **Write tools require pre-checks.** Before any `PUT` or `POST`, verify the current state matches what the caller expects (`expected_current_price_cents`, `expected_current_quantity`). Fail with a diff, not silently.
|
|
110
|
+
- **Never retry non-idempotent writes on 5xx.** A retry on a `PUT /items/{id}` or `POST /customers` may duplicate the action. Reads get one retry; writes surface the error immediately.
|
|
111
|
+
- **Bound all numeric inputs.** Price: `0 ≤ price_cents ≤ 100_000_000`. Stock: `0 ≤ quantity ≤ 1_000_000`. Reject out-of-range values before the HTTP call.
|
|
112
|
+
- **Log to stderr only.** The stdio MCP transport uses stdout for the protocol. Any print or logging must go to stderr. Never log token values, customer PII, or card data.
|
clover_mcp-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mikel Diaz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clover-mcp
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: MCP server for the Clover POS REST API — sales, inventory, orders, customers, and employees for small businesses
|
|
5
|
+
Author-email: Mikel Diaz <mikeldev62@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: ai,anthropic,clover,mcp,pos,small-business
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Requires-Dist: fastmcp>=2.0
|
|
16
|
+
Requires-Dist: httpx>=0.27
|
|
17
|
+
Requires-Dist: python-dotenv>=1.0
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
23
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# clover-mcp
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
> **Status:** v1 candidate — 14 tools, both auth modes, 152 tests. Single-merchant, local (stdio). See [docs/endpoints.md](docs/endpoints.md) for the sandbox-verified endpoint contracts.
|
|
31
|
+
|
|
32
|
+
> ⚠️ **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).
|
|
33
|
+
|
|
34
|
+
## What it can do
|
|
35
|
+
|
|
36
|
+
- Sales summaries and payment reports
|
|
37
|
+
- Inventory lookups and low-stock alerts
|
|
38
|
+
- Order history and open-order inspection
|
|
39
|
+
- Customer search and creation
|
|
40
|
+
- Safe writes: update item prices, set stock quantities, create customers
|
|
41
|
+
|
|
42
|
+
**What it cannot do (by design):** process refunds, capture payments, void charges, delete records. Those stay in the Clover dashboard. Employee/shift reporting and a multi-merchant hosted mode are planned (v1.1 / v2).
|
|
43
|
+
|
|
44
|
+
## Tools
|
|
45
|
+
|
|
46
|
+
| Tool | Kind | Notes |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `get_merchant_info` | read | name, currency, timezone, country |
|
|
49
|
+
| `get_sales_summary` | read | aggregated window (see [Sales summary semantics](#sales-summary-semantics)) |
|
|
50
|
+
| `list_payments` | read | SUCCESS payments in a window |
|
|
51
|
+
| `list_orders` / `get_order` / `list_open_orders` | read | order history + detail |
|
|
52
|
+
| `list_items` / `get_item` / `list_low_stock_items` | read | inventory + stock |
|
|
53
|
+
| `search_customers` / `get_customer` | read | cards never returned |
|
|
54
|
+
| `create_customer` | write | additive; idempotency dup-check + `dry_run` |
|
|
55
|
+
| `set_item_price_cents` | write | optimistic-lock pre-check, bounds, `dry_run` |
|
|
56
|
+
| `set_item_stock_quantity` | write | absolute (not delta), pre-check, `dry_run` |
|
|
57
|
+
|
|
58
|
+
Every tool carries MCP behaviour annotations (`readOnlyHint` / `destructiveHint` / `idempotentHint`) so clients can parallelize reads and prompt before writes.
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uvx clover-mcp # coming soon after PyPI publish
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Or from source:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/SBolivarLoL/clover-mcp-server
|
|
70
|
+
cd clover-mcp-server
|
|
71
|
+
uv pip install -e .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
Copy `.env.example` to `.env` and fill in your values:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
cp .env.example .env
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Required:
|
|
83
|
+
|
|
84
|
+
| Variable | Description |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `CLOVER_MERCHANT_ID` | Your Clover merchant ID |
|
|
87
|
+
| `CLOVER_ACCESS_TOKEN` | Your Clover API access token |
|
|
88
|
+
|
|
89
|
+
Optional:
|
|
90
|
+
|
|
91
|
+
| Variable | Default | Description |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `CLOVER_REGION` | `na` | `na`, `eu`, or `la` |
|
|
94
|
+
| `CLOVER_SANDBOX` | `false` | `true` to use the Clover sandbox |
|
|
95
|
+
| `CLOVER_AUTH_MODE` | `token` | `token` or `oauth_refresh` |
|
|
96
|
+
|
|
97
|
+
### Auth modes
|
|
98
|
+
|
|
99
|
+
- **`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.
|
|
100
|
+
- **`oauth_refresh`** — supply a refresh token + OAuth client credentials; 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.
|
|
101
|
+
|
|
102
|
+
> **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.
|
|
103
|
+
|
|
104
|
+
## Claude Desktop setup
|
|
105
|
+
|
|
106
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"mcpServers": {
|
|
111
|
+
"clover": {
|
|
112
|
+
"command": "uvx",
|
|
113
|
+
"args": ["clover-mcp"],
|
|
114
|
+
"env": {
|
|
115
|
+
"CLOVER_MERCHANT_ID": "your_merchant_id",
|
|
116
|
+
"CLOVER_ACCESS_TOKEN": "your_token",
|
|
117
|
+
"CLOVER_REGION": "na"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Cursor setup
|
|
125
|
+
|
|
126
|
+
Add to `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` globally):
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"mcpServers": {
|
|
131
|
+
"clover": {
|
|
132
|
+
"command": "uvx",
|
|
133
|
+
"args": ["clover-mcp"],
|
|
134
|
+
"env": {
|
|
135
|
+
"CLOVER_MERCHANT_ID": "your_merchant_id",
|
|
136
|
+
"CLOVER_ACCESS_TOKEN": "your_token"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Required Clover permissions
|
|
144
|
+
|
|
145
|
+
Your token must have the following Clover permission scopes:
|
|
146
|
+
|
|
147
|
+
| Permission | Used by |
|
|
148
|
+
|---|---|
|
|
149
|
+
| `MERCHANT_R` | `get_merchant_info` |
|
|
150
|
+
| `ORDERS_R` | `list_orders`, `get_order`, `list_open_orders`, `get_sales_summary` |
|
|
151
|
+
| `PAYMENTS_R` | `list_payments`, `get_sales_summary` |
|
|
152
|
+
| `INVENTORY_R` | `list_items`, `get_item`, `list_low_stock_items` |
|
|
153
|
+
| `INVENTORY_W` | `set_item_price_cents`, `set_item_stock_quantity` |
|
|
154
|
+
| `CUSTOMERS_R` | `search_customers`, `get_customer` |
|
|
155
|
+
| `CUSTOMERS_W` | `create_customer` |
|
|
156
|
+
|
|
157
|
+
Read scopes (`*_R`) are probed at startup; the server reports any missing ones and exits. 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.
|
|
158
|
+
|
|
159
|
+
## Sales summary semantics
|
|
160
|
+
|
|
161
|
+
`get_sales_summary` makes the accounting explicit so the LLM can explain it:
|
|
162
|
+
|
|
163
|
+
- **Gross** = sum of `result=SUCCESS` payment amounts. `FAIL`/`AUTH`/uncaptured `PRE_AUTH` are excluded.
|
|
164
|
+
- **Voids and refunds are reported separately** (`void_count`, `refund_count`, `refund_amount`) — never silently netted into `payment_count`.
|
|
165
|
+
- **Tips and taxes** are broken out as their own line items.
|
|
166
|
+
- **Service charges** are summed from orders (Clover does not expose them on payments) and reported as `service_charges_collected` — requires `ORDERS_R`.
|
|
167
|
+
- **Offline payments** are included; a `note` flags the window when any are present.
|
|
168
|
+
- **Currency** comes from the merchant record, never defaulted.
|
|
169
|
+
- Windows longer than 90 days are split and concatenated transparently.
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
uv pip install -e ".[dev]"
|
|
175
|
+
pytest
|
|
176
|
+
ruff check src/
|
|
177
|
+
mypy src/clover_mcp/
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Security
|
|
181
|
+
|
|
182
|
+
See [SECURITY.md](SECURITY.md) for the vulnerability disclosure policy.
|
|
183
|
+
|
|
184
|
+
## Legal & disclaimer
|
|
185
|
+
|
|
186
|
+
> This is not legal advice. The notes below describe the project's intent and the
|
|
187
|
+
> operator's responsibilities.
|
|
188
|
+
|
|
189
|
+
- **Not affiliated.** This is an independent, community project. It is **not**
|
|
190
|
+
affiliated with, endorsed by, or sponsored by Clover Network, LLC or Fiserv, Inc.
|
|
191
|
+
"Clover" and related marks are trademarks of their respective owners and are used
|
|
192
|
+
here only **nominatively** — to state that this software interoperates with the
|
|
193
|
+
Clover REST API. No Clover logos or branding are used.
|
|
194
|
+
- **No warranty / no liability.** The software is provided **"AS IS"** under the
|
|
195
|
+
[MIT License](LICENSE), without warranty of any kind. The authors are not liable
|
|
196
|
+
for any claim, damage, or loss arising from its use — including incorrect data,
|
|
197
|
+
unintended writes, downtime, or API changes outside the authors' control.
|
|
198
|
+
- **You operate it; you're responsible.** You run this server with **your own**
|
|
199
|
+
Clover account and API credentials. You are solely responsible for: complying
|
|
200
|
+
with Clover's developer/API terms and trademark-usage policy; safeguarding your
|
|
201
|
+
tokens; and meeting any data-protection (e.g. GDPR/CCPA) and tax obligations for
|
|
202
|
+
data you access. The write tools **modify live merchant data** — test in the
|
|
203
|
+
sandbox first and use least-privilege tokens.
|
|
204
|
+
- **No card data, no payments.** The server never handles payment card data (the
|
|
205
|
+
shaping layer blocks it) and deliberately cannot capture payments, refund, or
|
|
206
|
+
void. It is **not** a PCI-DSS solution.
|
|
207
|
+
- **Third-party API.** This project only calls Clover's public REST API using the
|
|
208
|
+
operator's credentials; it bundles no Clover SDK or proprietary code. Clover may
|
|
209
|
+
change or restrict its API at any time, which may break functionality.
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT — see [LICENSE](LICENSE).
|