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)

Recipessearch_recipes, get_recipe, get_recipe_suggestions, get_random_recipe Reference datalist_categories, list_tags, list_tools, list_foods, list_units, list_labels, 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, add_shopping_items, set_shopping_item_checked, add_recipe_to_shopping_list Meal planscreate_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.

A
license - permissive license
-
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
3wRelease cycle
2Releases (12mo)
Commit activity

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

View all related MCP servers

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.

View all MCP Connectors

Latest Blog Posts

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