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.
Files changed (49) hide show
  1. clover_mcp-0.1.1/.env.example +24 -0
  2. clover_mcp-0.1.1/.github/CODEOWNERS +2 -0
  3. clover_mcp-0.1.1/.github/dependabot.yml +21 -0
  4. clover_mcp-0.1.1/.github/workflows/ci.yml +45 -0
  5. clover_mcp-0.1.1/.github/workflows/codeql.yml +31 -0
  6. clover_mcp-0.1.1/.github/workflows/release.yml +50 -0
  7. clover_mcp-0.1.1/.gitignore +34 -0
  8. clover_mcp-0.1.1/CHANGELOG.md +40 -0
  9. clover_mcp-0.1.1/CLAUDE.md +112 -0
  10. clover_mcp-0.1.1/LICENSE +21 -0
  11. clover_mcp-0.1.1/PKG-INFO +213 -0
  12. clover_mcp-0.1.1/README.md +188 -0
  13. clover_mcp-0.1.1/RELEASING.md +42 -0
  14. clover_mcp-0.1.1/SECURITY.md +31 -0
  15. clover_mcp-0.1.1/docs/endpoints.md +92 -0
  16. clover_mcp-0.1.1/pyproject.toml +65 -0
  17. clover_mcp-0.1.1/scripts/get_sandbox_token.py +162 -0
  18. clover_mcp-0.1.1/src/clover_mcp/__init__.py +1 -0
  19. clover_mcp-0.1.1/src/clover_mcp/__main__.py +9 -0
  20. clover_mcp-0.1.1/src/clover_mcp/auth.py +106 -0
  21. clover_mcp-0.1.1/src/clover_mcp/client.py +159 -0
  22. clover_mcp-0.1.1/src/clover_mcp/config.py +117 -0
  23. clover_mcp-0.1.1/src/clover_mcp/errors.py +56 -0
  24. clover_mcp-0.1.1/src/clover_mcp/formatting.py +50 -0
  25. clover_mcp-0.1.1/src/clover_mcp/server.py +338 -0
  26. clover_mcp-0.1.1/src/clover_mcp/shaping.py +166 -0
  27. clover_mcp-0.1.1/src/clover_mcp/tools/__init__.py +0 -0
  28. clover_mcp-0.1.1/src/clover_mcp/tools/customers.py +157 -0
  29. clover_mcp-0.1.1/src/clover_mcp/tools/inventory.py +238 -0
  30. clover_mcp-0.1.1/src/clover_mcp/tools/merchant.py +18 -0
  31. clover_mcp-0.1.1/src/clover_mcp/tools/orders.py +134 -0
  32. clover_mcp-0.1.1/src/clover_mcp/tools/reporting.py +247 -0
  33. clover_mcp-0.1.1/src/clover_mcp/windowing.py +50 -0
  34. clover_mcp-0.1.1/tests/__init__.py +0 -0
  35. clover_mcp-0.1.1/tests/conftest.py +43 -0
  36. clover_mcp-0.1.1/tests/contract/__init__.py +0 -0
  37. clover_mcp-0.1.1/tests/contract/test_formatting.py +49 -0
  38. clover_mcp-0.1.1/tests/contract/test_permission_check.py +75 -0
  39. clover_mcp-0.1.1/tests/contract/test_region_resolution.py +35 -0
  40. clover_mcp-0.1.1/tests/contract/test_shaping_allowlist.py +128 -0
  41. clover_mcp-0.1.1/tests/contract/test_tool_annotations.py +63 -0
  42. clover_mcp-0.1.1/tests/contract/test_window_splitting.py +71 -0
  43. clover_mcp-0.1.1/tests/test_auth.py +164 -0
  44. clover_mcp-0.1.1/tests/tools/__init__.py +0 -0
  45. clover_mcp-0.1.1/tests/tools/test_customers.py +472 -0
  46. clover_mcp-0.1.1/tests/tools/test_inventory.py +523 -0
  47. clover_mcp-0.1.1/tests/tools/test_merchant.py +65 -0
  48. clover_mcp-0.1.1/tests/tools/test_orders.py +334 -0
  49. 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,2 @@
1
+ # Default owner for everything — required reviewer on protected branches.
2
+ * @SBolivarLoL
@@ -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.
@@ -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).