Skip to main content
Glama
README.md
# Jumbo MCP Server

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![MCP](https://img.shields.io/badge/MCP-0.2.0-green.svg)](https://modelcontextprotocol.io/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)

A Model Context Protocol (MCP) server for **Jumbo Supermarkten**.

Connect your AI assistant (Claude Desktop, Cursor, Antigravity, OpenClaw, …) to your personal Jumbo account to read **digital receipts (*kassabonnen*)**, itemized prices and discounts, Extra's loyalty points, stores and products, and to manage your **shopping list (*lijstje*)**.

---

## Installation & Setup

Requires Node.js `>= 18`.

### 1. Clone & Build
```bash
git clone https://github.com/Robert-SD/jumbo-mcp.git
cd jumbo-mcp
npm install
npm run build
```

### 2. Authentication (One-time setup)
One-time login through Jumbo's official Auth0 page. The server stores a persistent refresh token locally in `~/.config/jumbo-mcp/tokens.json` and renews access tokens automatically.

```bash
npm run login     # opens browser; paste redirected jumbo://callbacks/... URL back
npm run status    # verify active connection and loyalty points
```

**Headless host (Docker, systemd, remote gateway):** copy `~/.config/jumbo-mcp/tokens.json` to the host, or set the environment variable directly:
```bash
JUMBO_REFRESH_TOKEN="your_refresh_token_here"
```

---

## Client Integration

### Method 1: Local Path (Recommended)
Add the compiled server to your MCP client config:

```json
{
  "mcpServers": {
    "jumbo": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/jumbo-mcp/dist/index.js"]
    }
  }
}
```

| Client | Config file |
| :--- | :--- |
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)<br>`%APPDATA%\Claude\claude_desktop_config.json` (Windows) |
| Cursor | `~/.cursor/mcp.json` |
| Other clients | the same `mcpServers` block |

### Method 2: Global Link (`npm link`)
Run `npm link` inside the cloned directory to register `jumbo-mcp` globally on your system:
```bash
npm link
```
Then use:
```json
{
  "mcpServers": {
    "jumbo": {
      "command": "jumbo-mcp"
    }
  }
}
```

### Method 3: Docker Deployment
A multi-stage [`Dockerfile`](Dockerfile) is included for containerized environments:

```bash
# Build Docker image
docker build -t jumbo-mcp .
```

Run with mounted credentials:
```json
{
  "mcpServers": {
    "jumbo": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-v",
        "${HOME}/.config/jumbo-mcp:/root/.config/jumbo-mcp",
        "jumbo-mcp"
      ]
    }
  }
}
```

---

## Tools

| Tool | Description | Parameters |
| :--- | :--- | :--- |
| `jumbo_login` | Verify the connection and return the loyalty identity and points balance. | None |
| `jumbo_get_profile` | Loyalty profile: name, customer ID, Extra's card, receipt preferences. | None |
| `jumbo_list_receipts` | Recent receipts with timestamps, store and transaction IDs. | `page` (`0`), `page_size` (`10`, max `30`) |
| `jumbo_get_receipt_details` | Full itemized lines, unit prices, discounts, total, payment method, VAT. | `transaction_id` (required) |
| `jumbo_search_receipts` | Find items bought across recent receipts by keyword (e.g. `'havermelk'`). | `query` (required), `max_receipts` (`10`) |
| `jumbo_get_balance` | Live Extra's loyalty points balance. | None |
| `jumbo_get_shopping_list` | The shopping list: `isActive` plus every item. | None |
| `jumbo_add_shopping_list_items` | **Write.** Add free-text items (e.g. `['eieren']`); activates the list if needed. | `items` (required), `ensure_active` (`true`), `dedupe` (`true`) |
| `jumbo_remove_shopping_list_items` | **Write.** Remove items by id. | `item_ids` (required) |
| `jumbo_set_shopping_list_item_completed` | **Write.** Tick an item off or back on. | `item_id`, `completed` (both required) |
| `jumbo_set_shopping_list_active` | **Write.** Activate or deactivate the list. | `active` (required) |
| `jumbo_search_stores` | Nearby stores by city, address or coordinates: address, distance, hours. | `query`, `latitude`, `longitude` (all optional) |
| `jumbo_search_products` | Search the catalog: prices, promotions, unit prices, links, SKUs. | `query` (required), `sort`, `limit` (`20`, max `30`) |
| `jumbo_get_product_recommendations` | Recommendations, alternatives, cross-sells, or weekly promotions. | `sku`, `query`, `promotions_only` (`false`) |
| `jumbo_get_promo_digest` | What's on offer among the things you actually buy (read-only). | `days` (`90`), `min_times` (`2`), `limit` (`25`), `category` |
| `jumbo_get_basket` | Current basket: lines, quantities, unit prices, promotions, subtotal. | None |
| `jumbo_get_deposits` | Statiegeld for one order: catalogue plus deposits returned. | `order_id` (required) |
| `jumbo_get_stamps` | Digital stamps, active saving (spaar) campaigns, buying-stamps balance. | None |

---

## Usage examples

```text
"Show my last 5 receipts."                     → jumbo_list_receipts
"What did I pay for milk last month?"          → jumbo_search_receipts { "query": "melk" }
"How many Extra's points do I have?"           → jumbo_get_balance
"Add eggs and milk to my shopping list."       → jumbo_add_shopping_list_items
"What's on offer that I actually buy?"         → jumbo_get_promo_digest
"Which Jumbo is closest to Utrecht?"           → jumbo_search_stores { "query": "utrecht" }
```

---

## Notes

- Prices are returned as **integer cents** (e.g. `139` = €1.39).
- The shopping-list tools are the only writes. Basket, deposits, stamps and promotion tools are read-only.
- When Jumbo rejects a call, the tool returns an error payload with Jumbo's own `reason` and `errorMessage` rather than a generic message.

---

## Local Development

| Command | Purpose |
| :--- | :--- |
| `npm run dev` | Run the server via `tsx` (development mode) |
| `npm test` | Run the unit tests (mocked HTTP, no live Jumbo calls) |
| `npm run login` | Authentication CLI wizard |
| `npm run status` | Verify the live connection and token status |
| `npm run build` | Compile TypeScript to `dist/` |

---

## License

MIT License - Copyright (c) 2026 Robert Schulze Dieckhoff

TDQS

A3.6/5.0

Scored across 18 tools

Disambiguation4/5

Most tools target a clearly distinct resource+action (stores, products, receipts, shopping list, basket, deposits). However there is real overlap between jumbo_get_balance (Extra's points) and jumbo_get_stamps (stamps/balance), between jumbo_list_receipts and jumbo_search_receipts, and between jumbo_get_product_recommendations (which includes weekly promo recommendations) and jumbo_get_promo_digest. These are recoverable from the descriptions but could cause misselection.

Naming Consistency5/5

All 18 tools use a uniform jumbo_ prefix with snake_case verb_noun structure (jumbo_search_stores, jumbo_get_receipt_details, jumbo_add_shopping_list_items, jumbo_set_shopping_list_active). The only mild deviation is jumbo_login being a bare verb, but it still fits the pattern predictably.

Tool Count4/5

18 tools is reasonable for a full grocery domain covering stores, catalog, receipts, loyalty, shopping list and promos. It leans slightly heavy, with the shopping list alone consuming five tools (get/add/remove/set_active/set_item_completed), but each still earns its place.

Completeness3/5

Coverage spans discovery, receipts, loyalty and shopping list CRUD well, but there is a notable asymmetry: the shopping list is fully writable while the basket is explicitly read-only, with no add-to-cart, checkout, order placement or order-history tools. Also no product-detail-by-SKU operation, which limits deeper workflows.

Maintenance

ActivityMaintained
ResponsivenessNo issues