mealie-mcp
A read-only MCP server for Mealie that enables searching recipes, managing shopping lists, meal plans, and retrieving household/instance info via tools. Supports secure per-user authentication and multiple Mealie instances.
README
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.
Tools
Read tools (always available)
Recipes — search_recipes, get_recipe, get_recipe_suggestions
Reference data — list_categories, list_tags, list_tools, list_foods, list_units, 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, set_shopping_item_checked, add_recipe_to_shopping_list
Meal plans — create_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.
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: falseon 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 a401, and misclassifies the server as OAuth-protected — so it never sends your token (you'll see401s 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 anyMCP_AUTH_TOKENvalue. Check for typos, quoting, or trailing whitespace.absent— noAuthorizationheader 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 — setrequiresOAuth: falseon the server entry (see above).<empty token>— the client sentAuthorization: Bearerwith 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— runsruff,pyright, andpyteston 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 (tagsX.Y.Z,X.Y,X, andlatest).claude.yml— runs Claude Code when someone mentions@claudein 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.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.