Skip to main content
Glama
eoset

Systembolaget API MCP Server

by eoset
README.md
# Systembolaget API MCP Server

MCP server that exposes the [Systembolaget](https://www.systembolaget.se/) (Swedish liquor store) product search API as a tool. Uses [FastMCP](https://gofastmcp.com/) and supports configurable launch date range and optional query parameters.

## Setup

1. **Clone or open this project.**

2. **Create a virtual environment and install dependencies:**

   ```bash
   python3 -m venv .venv
   source .venv/bin/activate   # On Windows: .venv\Scripts\activate
   pip install -r requirements.txt
   ```

3. **Set your API key.** The API requires the `Ocp-Apim-Subscription-Key` header. Provide it via environment variable:

   ```bash
   export SYSTEMBOLAGET_SUBSCRIPTION_KEY=your-subscription-key-here
   ```

   Copy `.env.example` to `.env` and fill in the key if you use a tool that loads `.env` (do not commit `.env`).

## Running the server

From the project root (with the venv activated):

```bash
python server.py
```

Or using the FastMCP CLI:

```bash
fastmcp run server.py:mcp
```

The server runs over **stdio** by default (for use with Claude Desktop, Cursor, etc.).

You can also run it with [uv](https://docs.astral.sh/uv/) (same pattern as other local MCP servers):

```bash
uv run --directory /path/to/bolki-mcp python server.py
```

## Cursor MCP configuration

Add this server to Cursor’s MCP settings (e.g. **Cursor Settings → MCP** or `~/.cursor/mcp.json`) using stdio and `uv`, in the same way as other local MCP servers:

```json
{
  "mcpServers": {
    "systembolaget": {
      "type": "stdio",
      "command": "/opt/homebrew/bin/uv",
      "args": [
        "run",
        "--directory",
        "/Users/erik/Development/_dev/bolki-mcp",
        "python",
        "server.py"
      ],
      "env": {
        "SYSTEMBOLAGET_SUBSCRIPTION_KEY": "your-subscription-key-here"
      }
    }
  }
}
```

- Use your actual project path for `--directory` (e.g. `/Users/erik/Development/_dev/bolki-mcp`).
- Use your system’s `uv` path if different (e.g. `uv` if it’s on your PATH).
- Set `SYSTEMBOLAGET_SUBSCRIPTION_KEY` to your API key. Do not commit the key; use a local config or secret.

## Tool: `search_products`

**Fetches all pages automatically** and returns the combined result.

- **Parameters:**
  - `launch_date_min` (required) – Start of launch date range, `YYYY-MM-DD`. Maps to `productLaunch.min`.
  - `launch_date_max` (required) – End of launch date range, `YYYY-MM-DD`. Maps to `productLaunch.max`.
  - `size` (optional, default `100`) – Page size used when fetching; all pages are requested automatically.
  - `sort_by` (optional, default `"Score"`) – Sort field.
  - `sort_direction` (optional, default `"Ascending"`) – Sort direction.
  - `assortment_text` (optional, default `"Tillfälligt sortiment"`) – Assortment filter.
  - `full_response` (optional, default `False`) – If `False`, returns a compact response for speed; if `True`, returns the full API-style JSON.

- **Returns:** By default a **compact response**: `metadata` (docCount, totalPages, nextPage, previousPage) and `products` with key fields only (productId, productNameBold, productNameThin, producerName, price, volume, volumeText, alcoholPercentage, country, categoryLevel1, customCategoryTitle, assortmentText, productLaunchDate, taste, usage, vintage). Set `full_response=True` to get the full response (all fields, filters, etc.) when needed.

### Tool: `list_products_formatted`

Returns a **ready-to-use list of lines** (filtering and formatting done in the MCP; no client-side parsing). **Fetches all pages automatically** so the list is complete.

- **Parameters:** Same `launch_date_min`, `launch_date_max` as above, plus:
  - `product_filter`: `"beer"` (default) – only products with CategoryLevel1 "Öl"; `"all"` – every product.
  - `size` – page size used when fetching (default 100); all pages are requested automatically.
  - `sort_by`, `sort_direction`, `assortment_text` – same as `search_products`.
- **Returns:** Sorted unique list of strings, e.g. `["Vreta klosters Våröl — 330 ml, 5.6%", ...]`. Use this instead of running Python scripts to parse search results.

## License

Use of the Systembolaget API is subject to Systembolaget’s terms. This project is for integration only.

TDQS

A3.5/5.0

Scored across 2 tools

Disambiguation2/5

Both tools perform the same core operation — searching products by launch date range with identical arguments (size, sort_by, sort_direction, assortment_text) and the same auto-pagination behavior. The only real difference is output formatting (raw structured data vs. pre-formatted strings), which makes list_products_formatted a redundant variant of search_products rather than a distinct capability.

Naming Consistency3/5

Both names are snake_case with a verb_noun shape, which is readable and consistent in style. However, the verbs diverge ('search' vs 'list') despite the tools doing the same underlying search, which weakens predictability about which tool to pick for a lookup.

Tool Count3/5

Two tools is thin for a product catalog API, but the second is a formatting convenience wrapper rather than a genuinely separate function, so the effective surface is closer to one tool with a format flag. This is borderline rather than severely mismatched.

Completeness2/5

The surface only supports launch-date-range queries and cannot search by name, producer, category, price, or alcohol content, and there is no get-by-ID, store lookup, or stock/availability tool — all core to the Systembolaget domain. Agents would hit dead ends for most real product-lookup tasks.

Maintenance

ActivityInactive
ResponsivenessNo issues