Skip to main content
Glama
cacack

mcp-server-brewfather

by cacack
README.md
# mcp-server-brewfather

An MCP server for the [Brewfather](https://brewfather.app) API. It lets an LLM read
your batches, recipes, fermentation readings, and inventory, and make the routine
writes that come up while brewing: advancing a batch's status, logging measured
gravities and volumes, tweaking a recipe, and adjusting stock after brew day.

Brewfather has no official MCP server; this wraps the public
[v2 API](https://docs.brewfather.app/api) directly.

## Tools

| Tool | What it does |
|------|--------------|
| `find_batches(name?, status?)` | Find batches by name substring and/or status → `{id, name, batch_no, status, brewer, brew_date, recipe}` |
| `get_batch(batch_id)` | Batch summary, measured values, and embedded recipe (stats + ingredient bill) |
| `get_readings(batch_id, limit?)` | Most recent hydrometer/sensor readings, oldest→newest (`limit=0` for all) |
| `update_batch(batch_id, status?, measurements?)` | Set status and/or `measured*` values (validated before sending) |
| `find_recipes(name?)` | Find recipes by name substring → `{id, name, author, type, style, equipment}` |
| `get_recipe(recipe_id)` | Target stats (OG, FG, ABV, IBU, color, …) and ingredient bill |
| `update_recipe(recipe_id, fields?, ingredients?)` | Change settings (batch size, boil time, efficiency, …) and add/change/remove ingredients |
| `list_inventory(kind, name?, in_stock_only?)` | Fermentables, hops, miscs, or yeasts in stock |
| `set_inventory(kind, item_id, amount? \| adjust?)` | Set absolute stock, or add/subtract |

All values are metric (SG, liters, kg/g, °C) — the API accepts nothing else.
Timestamps are returned as ISO-8601 UTC.

Brewfather computes recipe stats (OG, FG, ABV, IBU, color) in the app, not the API.
After `update_recipe`, the app shows correct stats as soon as you open the recipe,
but `get_recipe` returns the stored values, which the API never recalculates.
Stats can't be written through this server.

## Setup

### 1. Generate an API key

In Brewfather: **Settings → API → Generate API Key**. Pick scopes to match what you
want the server to do (see [Security posture](#security-posture)). Note the
**User ID** shown alongside the key.

### 2. Configure credentials

```bash
cp .env.example .env
# edit .env with your user id / API key, then:
source .env
```

### 3. Install

```bash
uv sync          # or: pip install -e .
```

## Register with Claude

Claude Code:

```bash
claude mcp add brewfather --scope user \
  -e BREWFATHER_USER_ID=your_user_id -e BREWFATHER_API_KEY=your_api_key \
  -- uv --directory /path/to/mcp-server-brewfather run mcp-server-brewfather
```

Claude Desktop (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "brewfather": {
      "command": "uv",
      "args": ["--directory", "/path/to/mcp-server-brewfather", "run", "mcp-server-brewfather"],
      "env": {
        "BREWFATHER_USER_ID": "your_user_id",
        "BREWFATHER_API_KEY": "your_api_key"
      }
    }
  }
}
```

## Security posture

- **The API key's scopes are the trust boundary.** For read-only use, grant only
  `batches.read`, `recipes.read`, `inventory.read`. Add `batches.write` /
  `recipes.write` / `inventory.write` to enable `update_batch` / `update_recipe` /
  `set_inventory`. **Never grant `*.delete`** — no tool uses it.
- No delete tools. Every write is checked against an allowlist of fields before
  it's sent, because the API silently accepts unknown fields.
- **Two dependencies only** (`mcp`, `httpx` — the latter already required by `mcp`);
  pinned via the committed `uv.lock`.
- Credentials live in a gitignored `.env` / Claude config.

## Rate limits

Brewfather allows **500 calls per hour per API key**. List tools page 50 items per
call, so `find_*`/`list_inventory` cost one call per 50 items. A rate-limited call
surfaces as an error naming the `Retry-After` delay.

## Development

```bash
uv sync                          # install deps (incl. dev group)
uv run ruff check .              # lint
uv run ruff format .             # format
uv run pytest                    # unit tests (acceptance auto-skipped)
uv run pytest --run-acceptance   # + live read-only API checks (needs BREWFATHER_* creds)
```

CI (GitHub Actions) runs the PR-title check, ruff lint/format, and the unit tests
on every PR; the `CI Success` job is the aggregate gate. Acceptance tests are not
run in CI — they need live credentials and stay local/manual. They are read-only
and never modify your brewing data.

## License

MIT — see [LICENSE](LICENSE).

TDQS

A3.9/5.0

Scored across 9 tools

Disambiguation5/5

Each tool targets a distinct resource+action pair: find/get/update for batches, get for readings, find/get/update for recipes, list/set for inventory. The find-versus-get distinction (search by name/status vs. read one by id) is spelled out in descriptions, leaving little room for misselection.

Naming Consistency4/5

All tools use consistent snake_case verb_noun form and the read/update pattern repeats cleanly across batches and recipes. Minor deviation: retrieval uses three different verbs (find_*, get_*, list_*) and mutation uses both update_* and set_*, which is defensible but slightly inconsistent.

Tool Count5/5

Nine tools is well within a comfortable range and the surface is tightly scoped to the domain's three entities (batches, recipes, inventory). Every tool earns its place with no redundancy.

Completeness3/5

Coverage is read- and update-heavy: batches and recipes can be searched, read, and edited, but there are no create or delete operations for batches, recipes, or inventory items. This is a notable lifecycle gap for an agent trying to start new batches or add recipes, though the operational update path is solid.

Maintenance

ActivityMaintained
ResponsivenessNo issues