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

> **Fork of [amercat37/mealie-mcp-server](https://github.com/amercat37/mealie-mcp-server)** (itself based on rldiao/mealie-mcp-server). This fork adds recipe import from URLs, page HTML and social posts (`import_recipe_from_url`, `import_recipe_from_html`, `set_recipe_image`, the `import_recipe` prompt) and Proxmox LXC deployment without Docker (see [`deploy/README.md`](deploy/README.md)). Build notes: [`PLAN.md`](PLAN.md).

A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for [Mealie](https://github.com/mealie-recipes/mealie) that lets AI assistants like Claude and ChatGPT read your recipe database, manage shopping lists, create and edit meal plans, and guide you through cooking — served over HTTP and secured with OAuth 2.0.

## ✨ Features

### šŸ” Single-User OAuth 2.0 Authentication (optional)
- **Authentik integration**: Validates bearer tokens via OIDC discovery + JWKS, with RS256 signature pinning and automatic signing-key rotation
- **Audience-scoped**: rejects tokens minted for other apps that share your Authentik issuer — `AUTHENTIK_AUDIENCE` is **required** when auth is enabled
- **Opt-in**: runs unauthenticated when `AUTHENTIK_ISSUER` is not set
- **Docker-friendly**: supports internal JWKS fetching via `AUTHENTIK_JWKS_URI` + `AUTHENTIK_HOST`

### šŸ½ļø Recipe Management
- **Advanced Search**: Filter by text, categories, tags, and tools with AND/OR logic
- **Dual Detail Levels**: Concise summary for planning, full details for cooking
- **Create Recipes**: Build fully structured recipes from scratch — categories, tags, tools, and ingredients resolved from existing library conventions automatically
- **Duplicate Recipes**: Clone any recipe under a new name for variations or scaling
- **Metadata Tracking**: Mark recipes as made, track last made dates
- **Comments**: Read and write cooking notes per recipe
- **Timeline**: View chronological cooking activity across all recipes
- **Sharing**: Create and list public share links for any recipe — links anyone can open without a login (needs `MEALIE_PUBLIC_URL`)
- **Recipe links**: `get_recipe_detailed` includes the recipe's page in Mealie (open, edit, print or save as PDF) when `MEALIE_PUBLIC_URL` is set

### šŸ›’ Shopping Lists
- **List Management**: Create, rename, and delete shopping lists
- **Label Ordering**: Reorder a list's sections by label name to match a store's layout
- **Item Operations**: Add, update, check off, and remove items
- **Bulk Operations**: Create, update, or delete multiple items at once
- **Recipe Integration**: Add one or many recipes' ingredients to a list at once

### šŸ·ļø Organization
- **Categories**: Browse recipe categories (Breakfast, Dinner, etc.); find unused categories
- **Tags**: Browse and filter by recipe tags; find unused tags
- **Foods**: Search the ingredient food library; add new foods; merge duplicates
- **Tools**: List cooking equipment tagged on recipes
- **Units**: List units of measurement used in ingredients
- **Labels**: List shopping list labels (grocery store sections)
- **Cookbooks**: Browse curated recipe collections (My Recipes, The Autoimmune Solution)

### šŸ“… Meal Planning
- **View & Create**: See all meal plans, today's menu, and add new entries
- **Edit & Delete**: Update or remove individual meal plan entries
- **Bulk Creation**: Add multiple meals at once — all entries are checked first, and the created ids come back
- **Whole-Week Entries**: Plan a meal for "sometime this week" (Mealie v3.30+), aligned to the household's first day of week

### šŸ’¬ Prompts
Six built-in conversation starters that pre-configure the assistant for specific tasks:
- **`recipe_builder`**: Guided recipe creation — fetches similar recipes to match your conventions, resolves foods/tags/categories from your existing library, defaults to My Recipes cookbook
- **`weekly_meal_plan`**: Guided meal planning with tag filtering, sandwich rules, and leftover logic
- **`shopping_trip`**: Consolidated shopping list from your meal plan, grouped by store section with cost estimates
- **`cooking_session`**: Full recipe details, equipment, and cooking notes staged before you start cooking
- **`weekly_review`**: Summary of what you've cooked and eaten, answers "what did I have last Wednesday?"
- **`nutrition_summary`**: Caloric and macro breakdown from your meal plan's nutrition data

## šŸš€ Installation

### Prerequisites

- [Docker](https://docs.docker.com/get-docker/) and Docker Compose
- A running [Mealie](https://github.com/mealie-recipes/mealie) instance, **v3.30.0 or later**, with an API key
  (recipe times use Mealie's structured durations from v3.29 and whole-week meal plans from v3.30;
  an older Mealie silently ignores them)
- An [Authentik](https://goauthentik.io/) instance with a provider, app, and user configured (only required if using OAuth)

### Step 1 — Clone the repository

```bash
git clone https://github.com/amercat37/mealie-mcp-server.git
cd mealie-mcp-server
```

### Step 2 — Configure environment

```bash
cp .env.template .env
# Edit .env with your values
```

Set the variables in `.env`. The `AUTHENTIK_JWKS_URI` + `AUTHENTIK_HOST` pair is only needed if Authentik and this server share a Docker network — it lets the server fetch signing keys from the internal address rather than going through the public domain. Set the optional `MEALIE_PUBLIC_URL` (the address you open Mealie at in a browser) to get working share links and recipe page links; without it those tools say no link can be built.

### Step 3 — Start the server

```bash
docker compose up -d
```

The server listens on port `8000` at `/mcp`. Configure your MCP client to connect to `http://your-host:8000/mcp` (or via a reverse proxy with TLS).

### OAuth / Authentik Setup (optional)

Authentication is opt-in. If `AUTHENTIK_ISSUER` is not set, the server starts without any authentication — useful for local testing.

When enabled, the server validates OAuth 2.0 bearer tokens issued by [Authentik](https://goauthentik.io/). Your MCP client redirects you to your Authentik login page, you log in, and Authentik issues a token. The MCP server checks that token on every request. Your Mealie API key never leaves the server.

#### Step 1 — Create a Provider in Authentik

In the Authentik admin UI go to **Applications → Providers → Create → OAuth2/OpenID Provider**.

| Field | Value |
|---|---|
| Name | `mealie-mcp-server` |
| Authorization flow | `default-provider-authorization-explicit-consent (Authorize Application)` |
| Client type | `Confidential` |
| Redirect URIs | Your MCP client's callback URL (see your client's OAuth setup docs) |
| Signing Key | Select any existing certificate, or create one under **System → Certificates** |
| Access Token Validity | `hours=12` (Authentik's default of `minutes=5` causes frequent reconnection prompts in MCP clients) |
| Refresh Token Validity | `days=30` |
| Scopes | `email`, `profile`, `openid`, `offline_access` (include `offline_access` for refresh token support) |

Callback URI examples:
```
strict: https://claude.ai/api/mcp/auth_callback
strict: https://chatgpt.com/connector/oauth/<your-connector-id>
```

Save the provider. On the detail page, note the **OpenID Configuration Issuer** URL — it looks like:

```
https://auth.your-domain.com/application/o/mealie-mcp-server/
```

This is your `AUTHENTIK_ISSUER` value. On the same provider detail page, note the **Client ID** — this is your `AUTHENTIK_AUDIENCE` value.

> **āš ļø `AUTHENTIK_AUDIENCE` is required when authentication is enabled.** The server verifies that each token's `aud` claim contains this Client ID, so a token minted for a *different* Authentik application can't be replayed against this server. If `AUTHENTIK_ISSUER` is set but `AUTHENTIK_AUDIENCE` is not, **the server will refuse to start**.

#### Step 2 — Create an Application in Authentik

Go to **Applications → Applications → Create**.

| Field | Value |
|---|---|
| Name | `Mealie MCP Server` |
| Slug | `mealie-mcp-server` |
| Provider | Select the provider you just created |

Save.

#### Step 3 — Create and Assign a User

By default, all Authentik users can access the application. To restrict access, open the application → **Policy / Group / User Bindings** tab → **Bind existing policy/group/user** and add the specific users or groups you want to allow.

### Project Structure

```
mealie-mcp-server/
ā”œā”€ā”€ src/
│   ā”œā”€ā”€ mealie/              # API client mixins
│   │   ā”œā”€ā”€ client.py        # Base HTTP client
│   │   ā”œā”€ā”€ recipe.py        # Recipe operations
│   │   ā”œā”€ā”€ shopping_list.py # Shopping list operations
│   │   ā”œā”€ā”€ categories.py    # Category operations
│   │   ā”œā”€ā”€ tags.py          # Tag operations
│   │   ā”œā”€ā”€ mealplan.py      # Meal plan operations
│   │   ā”œā”€ā”€ foods.py         # Food operations
│   │   ā”œā”€ā”€ cookbooks.py     # Cookbook operations
│   │   ā”œā”€ā”€ organizers.py    # Tools, units, and labels
│   │   └── __init__.py      # MealieFetcher aggregator
│   ā”œā”€ā”€ tools/               # MCP tool definitions
│   │   ā”œā”€ā”€ recipe_tools.py
│   │   ā”œā”€ā”€ shopping_list_tools.py
│   │   ā”œā”€ā”€ categories_tools.py
│   │   ā”œā”€ā”€ tags_tools.py
│   │   ā”œā”€ā”€ mealplan_tools.py
│   │   ā”œā”€ā”€ foods_tools.py
│   │   ā”œā”€ā”€ cookbooks_tools.py
│   │   ā”œā”€ā”€ organizers_tools.py
│   │   └── __init__.py
│   ā”œā”€ā”€ models/              # Pydantic models
│   │   ā”œā”€ā”€ mealplan.py
│   │   └── recipe.py
│   ā”œā”€ā”€ auth.py              # OAuth/JWT verification (Authentik)
│   ā”œā”€ā”€ server.py            # MCP server entry point
│   ā”œā”€ā”€ prompts.py           # Server prompts
│   └── utils.py             # Shared utilities
ā”œā”€ā”€ .github/
│   ā”œā”€ā”€ PULL_REQUEST_TEMPLATE.md
│   └── ISSUE_TEMPLATE/
│       ā”œā”€ā”€ bug_report.md
│       └── feature_request.md
ā”œā”€ā”€ tests/
│   ā”œā”€ā”€ test_fetcher.py
│   ā”œā”€ā”€ test_mcp_server.py
│   ā”œā”€ā”€ docker-compose.yml
│   ā”œā”€ā”€ .env.testing.template
│   └── README.md
ā”œā”€ā”€ Dockerfile
ā”œā”€ā”€ docker-compose.yml
ā”œā”€ā”€ .env.template
ā”œā”€ā”€ CHANGELOG.md
└── README.md
```

## šŸ“š Important Notes

### Filtering by Tags/Categories

When filtering recipes, you **must use slugs or UUIDs**, not display names:

āœ… **Correct:**
```
"Get recipes with tags=['quick-meals', 'healthy']"
```

āŒ **Incorrect:**
```
"Get recipes with tags=['Quick Meals', 'Healthy']"
```

Use `get_tags()` or `get_categories()` first to find the correct slugs.

### Recipe Detail Levels

Two tools fetch recipe data — use the right one for the task:

| Tool | Use when |
|---|---|
| `get_recipe_concise` | Meal planning, quick lookups, listing ingredients |
| `get_recipe_detailed` | Cooking, nutrition calculation, full instructions |

### Field Preservation

When updating shopping list items, the server automatically preserves all existing fields. You only need to specify the fields you want to change:

```
# Only updates 'checked' field, preserves note, quantity, etc.
update_shopping_list_item(item_id="...", checked=True)
```

## šŸ”„ Changelog

See [CHANGELOG.md](CHANGELOG.md) for a detailed list of changes and version history.

## šŸ”® Future Enhancements

- **Multi-user support (step 1)**: Store each user's Mealie API key as an attribute in Authentik and include it in the JWT via a property mapping — each user brings their own key, no admin involvement
- **Multi-user support (step 2)**: Configure Mealie as an OAuth provider backed by Authentik and implement OAuth token exchange (RFC 8693) — eliminating API keys entirely

## šŸ¤ Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## šŸ“„ License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## šŸ“ž Support

For issues and questions:
- Check the [CHANGELOG.md](CHANGELOG.md) for recent updates
- Review the [Mealie API documentation](https://docs.mealie.io)
- Open an issue on GitHub

## šŸ”— Related Links

- [Mealie Documentation](https://docs.mealie.io)
- [MCP Protocol Specification](https://modelcontextprotocol.io)
- [Authentik](https://goauthentik.io/)