mealie-mcp
Provides tools for searching and retrieving recipes, listing reference data (categories, tags, tools, foods, units, cookbooks), managing shopping lists, viewing meal plans, and accessing user and app info from a Mealie instance.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mealie-mcpsearch for quick dinner recipes"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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 |
| A static secret (set via |
Mealie API 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 separatelist_*lookup first.get_recipereturns a trimmed view by default (passinclude_internal=truefor the raw Mealie object), andadd_shopping_itemsadds 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 -dThe 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.pyConnecting 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 |
| yes | — | Comma-separated secret bearer token(s) for the MCP endpoint. |
| no¹ | — | Default Mealie base URL (e.g. |
| no |
| Set |
| no |
| Outbound request timeout in seconds. |
| no |
| Set |
| no |
| Bind address. |
| no |
| Bind port. |
| no |
| Endpoint path. |
| no |
| uvicorn log level. |
| no |
| Log a masked diagnostic for each request hitting the auth gate (to troubleshoot |
¹ 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> -> 401Read 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:latestCI/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 |
| docker-publish | Your Docker Hub username (with push access to |
| docker-publish | A Docker Hub access token (Account Settings → Security → New Access Token). |
| claude | Run |
To cut a release (which triggers the image build):
gh release create v0.1.0 --generate-notesDevelopment
pip install -e ".[dev]"
ruff check . # lint
pyright # type check
pytest -q # testsSee CONTRIBUTING.md for details. Changes are tracked in CHANGELOG.md. Licensed under MIT.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceAn MCP server that provides read-only access to the Paprika Recipe Manager, allowing users to list and retrieve recipes, grocery items, and meal plans. It enables seamless interaction with recipe details and category information through the Paprika API.Last updated4MIT
- Alicense-qualityBmaintenanceEnables interaction with Mealie for managing recipes, meal plans, shopping lists, foods, units, tags, categories, and more through MCP.Last updatedMIT
- AlicenseBqualityBmaintenanceMCP server for Mealie that exposes its REST API to manage recipes, meal plans, shopping lists, cookbooks, and taxonomy through natural language.Last updated75MIT
- Alicense-qualityDmaintenanceAn MCP server for managing recipes, meal plans, shopping lists, and more through a self-hosted Mealie instance.Last updated1MIT
Related MCP Connectors
Recipes MCP — wraps TheMealDB API (free tier, no auth)
Read-only MCP server for ClassQuill, a tutoring-business-management platform.
Markdown-first MCP server for Notion API with 8 composite tools and 39 actions.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/dvejsada/mealie-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server