Skip to main content
Glama
dvejsada

mealie-mcp

by dvejsada

mealie-mcp

A Model Context Protocol server for Mealie, built with FastMCP and served over the Streamable HTTP transport. Ships as a Docker container.

It exposes tools only — no MCP resources or prompts. It is read-only by default; write tools can be enabled with MEALIE_READONLY=false.

How auth works

There are two independent credentials:

Credential

Header

Purpose

MCP endpoint token

Authorization: Bearer <token>

A static secret (set via MCP_AUTH_TOKEN) that gates the server. Requests without a valid token are rejected with 401.

Mealie API token

X-Mealie-Token: <token>

Supplied per request by each client. The server forwards it to Mealie as a bearer token, so one server can serve many Mealie users.

Optionally, a client can target a different Mealie instance per request with the X-Mealie-Url: https://other-mealie.example.com header (otherwise MEALIE_BASE_URL is used).

Get a Mealie API token from your Mealie profile: Profile → Manage API Tokens.

Related MCP server: Mealie MCP Server

Tools

Read tools (always available)

Recipes — search_recipes, get_recipe, get_recipe_suggestions, get_random_recipe Reference data — list_categories, list_tags, list_tools, list_foods, list_units, list_labels, list_cookbooks Household — get_shopping_lists, get_shopping_list, get_meal_plan, get_todays_meals Instance — get_current_user, get_app_info

Write tools (only when MEALIE_READONLY=false)

Recipes — create_recipe_from_url, create_recipe, update_recipe, delete_recipe, mark_recipe_made Shopping — add_shopping_item, add_shopping_items, set_shopping_item_checked, add_recipe_to_shopping_list Meal plans — create_mealplan_entry, update_mealplan_entry, delete_mealplan_entry

Write tools respect the per-request Mealie token's own permissions, so a read-only Mealie token can never mutate data even when write tools are enabled.

Names, not just IDs. Tools that take a food, unit, or label (e.g. get_recipe_suggestions, add_shopping_item, add_shopping_items) accept a plain name or a UUID — names are resolved server-side, so a client doesn't need a separate list_* lookup first. get_recipe returns a trimmed view by default (pass include_internal=true for the raw Mealie object), and add_shopping_items adds many items in one call.

Run with Docker

cp .env.example .env
# edit .env: set MCP_AUTH_TOKEN (a long random secret) and MEALIE_BASE_URL
docker compose up --build -d

The MCP endpoint is then available at http://<host>:8000/mcp, with an unauthenticated liveness probe at http://<host>:8000/healthz.

The endpoint token is the only thing standing between the internet and your Mealie instance. Put the server behind a reverse proxy with TLS, or on a private network, and use a long random MCP_AUTH_TOKEN.

Run locally (without Docker)

pip install -r requirements.txt
export MCP_AUTH_TOKEN="a-long-random-secret"
export MEALIE_BASE_URL="https://mealie.example.com"
python main.py

Connecting a client

Point your MCP client at the Streamable HTTP endpoint and send both headers. Example with the FastMCP client:

from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport

transport = StreamableHttpTransport(
    url="http://localhost:8000/mcp",
    headers={
        "Authorization": "Bearer <your MCP_AUTH_TOKEN>",
        "X-Mealie-Token": "<your Mealie API token>",
    },
)

async with Client(transport) as client:
    tools = await client.list_tools()
    result = await client.call_tool("search_recipes", {"search": "soup"})

LibreChat

See examples/librechat.yaml for a ready-to-use mcpServers entry. It maps the endpoint bearer token to a LibreChat environment variable and each user's Mealie token to a per-user customUserVars field, so every LibreChat user acts as their own Mealie account.

Set requiresOAuth: false on the server entry. This server uses a static Authorization Bearer gate, not OAuth. Without the flag, LibreChat's OAuth auto-detection probes the endpoint without your headers, gets a 401, and misclassifies the server as OAuth-protected — so it never sends your token (you'll see 401s followed by /.well-known/oauth-* 404s). See Troubleshooting.

Configuration

Variable

Required

Default

Description

MCP_AUTH_TOKEN

yes

—

Comma-separated secret bearer token(s) for the MCP endpoint.

MEALIE_BASE_URL

no¹

—

Default Mealie base URL (e.g. https://mealie.example.com).

MEALIE_READONLY

no

true

Set false to also register write tools.

MEALIE_TIMEOUT

no

30

Outbound request timeout in seconds.

MEALIE_VERIFY_SSL

no

true

Set false for self-signed Mealie certs.

MCP_HOST

no

0.0.0.0

Bind address.

MCP_PORT

no

8000

Bind port.

MCP_PATH

no

/mcp

Endpoint path.

MCP_LOG_LEVEL

no

info

uvicorn log level.

MCP_AUTH_DEBUG

no

false

Log a masked diagnostic for each request hitting the auth gate (to troubleshoot 401s). See Troubleshooting.

¹ Required unless every client sends the X-Mealie-Url header.

Troubleshooting 401 Unauthorized

A 401 means the endpoint token (the Authorization: Bearer <token> gate, not your Mealie token) was missing or didn't match MCP_AUTH_TOKEN. Many MCP clients react to a 401 by probing for OAuth (GET /.well-known/oauth-*, which this server returns 404 for, since it uses static tokens, not OAuth) — those 404s are a symptom of the 401, not a separate problem.

To see exactly what the gate receives, set MCP_AUTH_DEBUG=true and reconnect. Each request to the gate is logged with a masked summary (the token itself is never logged — only its length and a SHA-256 fingerprint):

auth-debug enabled: accepting 1 token(s) with fingerprints ['0c45a1f1']
auth-debug: POST /mcp authorization=scheme=Bearer token_len=10 fp=0c45a1f1 matches_configured=True -> 200
auth-debug: POST /mcp authorization=scheme=Bearer token_len=5 fp=02b60b3b matches_configured=False -> 401
auth-debug: POST /mcp authorization=absent (no Authorization header reached the server) -> 401
auth-debug: POST /mcp authorization=scheme=Bearer <empty token> -> 401

Read it as:

  • matches_configured=False — the client sent a token, but it doesn't equal any MCP_AUTH_TOKEN value. Check for typos, quoting, or trailing whitespace.

  • absent — no Authorization header reached the server. The client isn't sending it, or a reverse proxy/ingress stripped it before it arrived. With LibreChat this is usually OAuth auto-detection probing without your headers — set requiresOAuth: false on the server entry (see above).

  • <empty token> — the client sent Authorization: Bearer with no value, typically an unset ${...} variable in the client config.

  • matches_configured=True -> 200 — the gate is fine; the problem is elsewhere.

Docker Hub images

Released versions are published to Docker Hub at georgx22/mealie-mcp (multi-arch: linux/amd64, linux/arm64):

docker run -d -p 8000:8000 \
  -e MCP_AUTH_TOKEN="a-long-random-secret" \
  -e MEALIE_BASE_URL="https://mealie.example.com" \
  georgx22/mealie-mcp:latest

CI/CD

Three GitHub Actions workflows are included (.github/workflows/):

  • ci.yml — runs ruff, pyright, and pytest on every push/PR (Python 3.11–3.13).

  • docker-publish.yml — builds and pushes the multi-arch image to Docker Hub when a GitHub Release is published (tags X.Y.Z, X.Y, X, and latest).

  • claude.yml — runs Claude Code when someone mentions @claude in an issue, PR, or review comment.

Configure these repository secrets (Settings → Secrets and variables → Actions):

Secret

Used by

How to get it

DOCKERHUB_USERNAME

docker-publish

Your Docker Hub username (with push access to georgx22/mealie-mcp).

DOCKERHUB_TOKEN

docker-publish

A Docker Hub access token (Account Settings → Security → New Access Token).

CLAUDE_CODE_OAUTH_TOKEN

claude

Run claude setup-token locally (Claude Pro/Max), paste the token.

To cut a release (which triggers the image build):

gh release create v0.1.0 --generate-notes

Development

pip install -e ".[dev]"
ruff check .     # lint
pyright          # type check
pytest -q        # tests

See CONTRIBUTING.md for details. Changes are tracked in CHANGELOG.md. Licensed under MIT.

Related MCP Connectors

  • Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.

  • Your Recipes, Beautifully Kept. weReci MCP server lets Claude and other MCP clients work with your personal weReci cookbook, the recipes you've imported from the web, social video and scanned family books. Interactive UI in the chat. weReci supports MCP Apps, so in clients that support it, tools return live views instead of plain text: recipe cards, shopping lists and your recipe graph. Clients without MCP Apps support get the same results as text. Find and read recipes: search your collection in plain language, open any recipe in full, or get an overview of what's in your cookbook. Cook with them: scale a recipe to any serving count, with cooking adjustments as well as amounts. Get substitution suggestions with ratios and caveats. Explore connections: browse your recipe graph (shared ingredients, techniques and cuisines), trace the connection between two recipes, and look up where a dish sits on the cuisine map. Themed collections: list the themed groups weReci curates from your cookbook, or ask it to reshuffle them. Shop: build a shopping list from one or more recipes, add or update items, and read the list back. Share: email a recipe to someone. Longer jobs like conceit reshuffles run in the background, with tools to check their progress. Everything is scoped to your own cookbook, or to a shared one you've joined.

  • An MCP server that provides read access to your cloud storage providers, bank accounts and more.

  • Read-only MCP server exposing a user ORANO library to their own AI agent.

Related MCP Servers