Mealie MCP Server
by jasmith590
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/)