mealie-mcp

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.

Category
Visit Server

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)

Recipessearch_recipes, get_recipe, get_recipe_suggestions Reference datalist_categories, list_tags, list_tools, list_foods, list_units, list_cookbooks Householdget_shopping_lists, get_shopping_list, get_meal_plan, get_todays_meals Instanceget_current_user, get_app_info

Write tools (only when MEALIE_READONLY=false)

Recipescreate_recipe_from_url, create_recipe, update_recipe, delete_recipe, mark_recipe_made Shoppingadd_shopping_item, set_shopping_item_checked, add_recipe_to_shopping_list Meal planscreate_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: 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.

Recommended Servers

playwright-mcp

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.

Official
Featured
TypeScript
Magic Component Platform (MCP)

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.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

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.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

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.

Official
Featured
TypeScript
Kagi MCP Server

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.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

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.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured