mcp-paprika
MCP server for Paprika recipe manager enabling search, CRUD operations, grocery lists, meal planning, and menus via natural language, with semantic search and background sync.
README
@bojanrajkovic/mcp-paprika
An MCP server for Paprika recipe manager. Search, browse, create, and manage your recipes from any MCP client.
Features
- 40 tools for recipe, pantry, grocery, meal-planner, and menu management — search, filter, CRUD, categories, pagination, pantry inventory, aisles, grocery lists and items, meal planning (history, meal types, dated planner entries), and menus (recipe collections, their items, and one-shot add-to-planner)
- Semantic search via
discover_recipes— find recipes by natural language description using any OpenAI-compatible embedding provider - AI recipe photos via
generate_photo— generate a styled food photo for a recipe (or restyle its existing one) using OpenRouter image models, and attach it automatically - Background sync — keeps your local cache in sync with Paprika's cloud
- MCP resources — expose recipes as
paprika://recipe/{uid}, grocery lists aspaprika://grocery-list/{uid}, and menus aspaprika://menu/{uid}resources - Two transports — stdio (default, for CLI clients) and Streamable HTTP (for mobile/web clients)
- Container image —
Dockerfileships a distroless runtime ready for self-hosting
Transports
mcp-paprika can speak the MCP protocol over two transports, selected via MCP_TRANSPORT:
| Transport | Default? | Use it for |
|---|---|---|
stdio |
yes | Local CLI clients: Claude Code, Claude Desktop, Cursor, mcp-cli |
http |
no | Streamable HTTP for Claude Mobile and other HTTP-based MCP clients, or self-hosting |
The HTTP transport ships with OAuth 2.1 (authorization code + PKCE, RFC 7591 dynamic client registration). See the HTTP transport quick start below.
Quick start — stdio (Claude Desktop / Claude Code / Cursor)
Add to your MCP client config:
{
"mcpServers": {
"paprika": {
"command": "npx",
"args": ["-y", "@bojanrajkovic/mcp-paprika"],
"env": {
"PAPRIKA_EMAIL": "you@example.com",
"PAPRIKA_PASSWORD": "your-password"
}
}
}
}
Quick start — HTTP transport
The HTTP transport uses OAuth 2.1 with OIDC delegation: mcp-paprika acts as the OAuth authorization server toward MCP clients, and delegates authentication to an upstream identity provider (IdP) of your choice.
Step 1 — Choose an upstream IdP
Pick a preset or supply a raw discovery URL:
| Preset value | IdP | Notes |
|---|---|---|
google |
Discovery URL built-in | |
entra |
Microsoft Entra ID (Azure AD) | Tenant-bound — requires MCP_OIDC_DISCOVERY_URL |
okta |
Okta | Tenant-bound — requires MCP_OIDC_DISCOVERY_URL |
auth0 |
Auth0 | Tenant-bound — requires MCP_OIDC_DISCOVERY_URL |
keycloak |
Keycloak | Tenant-bound — requires MCP_OIDC_DISCOVERY_URL |
| (none) | Custom | Set MCP_OIDC_DISCOVERY_URL directly; omit MCP_OIDC_PRESET |
Step 2 — Register one OAuth client in your IdP
In your IdP's developer console (e.g., Google Cloud Console → APIs & Services → Credentials → OAuth 2.0 Client IDs), create a single OAuth 2.0 client with:
- Application type: Web application
- Redirect URI:
https://<your-MCP_PUBLIC_URL>/oauth/callback
Copy the resulting client ID and client secret — these become MCP_OIDC_CLIENT_ID and MCP_OIDC_CLIENT_SECRET.
Tenant-bound presets (entra, okta, auth0, keycloak): also copy the tenant-specific discovery URL from your IdP. For Entra this is
https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration.
Step 3 — Configure and start the server
Full env-var reference:
| Env var | Required? | Description |
|---|---|---|
MCP_TRANSPORT |
yes | Set to http |
MCP_PUBLIC_URL |
yes | Canonical https:// URL of this server; used as OAuth issuer. No trailing slash. |
MCP_OIDC_PRESET |
one of preset or discoveryUrl | google, entra, okta, auth0, or keycloak |
MCP_OIDC_DISCOVERY_URL |
one of preset or discoveryUrl | Raw OIDC discovery URL; required for tenant-bound presets |
MCP_OIDC_CLIENT_ID |
yes | Client ID from upstream IdP |
MCP_OIDC_CLIENT_SECRET |
yes | Client secret from upstream IdP |
MCP_ALLOWED_EMAILS |
one of emails or subs | Comma-separated list of allowed email addresses |
MCP_ALLOWED_SUBS |
one of emails or subs | Comma-separated list of allowed subject IDs |
MCP_OIDC_SCOPES |
no | Override preset's scope list (comma-separated; default openid email profile) |
MCP_OIDC_EMAIL_VERIFIED_POLICY |
no | strict (default), skip, or if-present |
MCP_OIDC_ALLOWED_ALGS |
no | Override preset's allowed id_token signing algorithms (comma-separated) |
MCP_TRUST_PROXY |
no | true to trust X-Forwarded-For / CF-Connecting-IP for the DCR rate-limit key. Default false (safe for direct exposure). Set true only behind a sanitizing reverse proxy (k8s ingress, Tailscale Funnel, Cloudflare). |
Example startup command:
MCP_TRANSPORT=http \
MCP_PUBLIC_URL=https://mcp.example.com \
MCP_OIDC_PRESET=google \
MCP_OIDC_CLIENT_ID=123456789-abc.apps.googleusercontent.com \
MCP_OIDC_CLIENT_SECRET=GOCSPX-... \
MCP_ALLOWED_EMAILS=you@example.com \
PAPRIKA_EMAIL=you@example.com \
PAPRIKA_PASSWORD=your-password \
npx -y @bojanrajkovic/mcp-paprika
Step 4 — Configure the allowlist
The allowlist uses OR semantics: access is granted if the authenticated user's email is in MCP_ALLOWED_EMAILS OR their subject ID is in MCP_ALLOWED_SUBS. At least one list must be non-empty.
MCP_ALLOWED_EMAILS— comma-separated email addresses. Subject toMCP_OIDC_EMAIL_VERIFIED_POLICY:strict(default): email must be present andemail_verified = trueskip: email is accepted without checkingemail_verifiedif-present: ifemail_verifiedis in the id_token, it must betrue; if absent, the email is accepted
MCP_ALLOWED_SUBS— comma-separated subject IDs (stable per-user opaque identifiers from the IdP). Useful when you want to allow access regardless of email verification status.
Step 5 — Add as a Claude connector
- Open claude.ai → Settings → Connectors
- Click "Add custom connector"
- Enter your server URL:
https://<MCP_PUBLIC_URL>/mcp - Claude will redirect your browser to the upstream IdP for authentication
- After sign-in, you are redirected back and the connector is authorized
Verify the OAuth metadata
curl -sf https://<MCP_PUBLIC_URL>/.well-known/oauth-authorization-server | jq .issuer
# → "https://<MCP_PUBLIC_URL>"
The server also exposes:
POST /mcp— MCP JSON-RPC over Streamable HTTPGET /mcp— long-lived SSE channel for server→client notificationsDELETE /mcp— session terminationGET /healthz— liveness probe returning{ "ok": true, "sessions": <n> }
Quick start — container
The image defaults to MCP_TRANSPORT=http, so a container run needs the same
OAuth environment that the HTTP transport quick start
walks through — MCP_PUBLIC_URL, an OIDC preset (or discovery URL), upstream
client credentials, and a non-empty allowlist. Without those, the server exits
during config validation.
Pull the published image (multi-arch: linux/amd64, linux/arm64):
docker pull ghcr.io/bojanrajkovic/mcp-paprika:latest
docker run --rm \
-e PAPRIKA_EMAIL=you@example.com \
-e PAPRIKA_PASSWORD=your-password \
-e MCP_PUBLIC_URL=https://mcp.example.com \
-e MCP_OIDC_PRESET=google \
-e MCP_OIDC_CLIENT_ID=123456789-abc.apps.googleusercontent.com \
-e MCP_OIDC_CLIENT_SECRET=GOCSPX-... \
-e MCP_ALLOWED_EMAILS=you@example.com \
-v "$(pwd)/data:/data" \
-p 3000:3000 \
ghcr.io/bojanrajkovic/mcp-paprika:latest
The image is signed with sigstore/cosign keyless OIDC and ships SLSA build provenance + an SPDX SBOM as OCI attestations. Verify both before running in untrusted environments — gh attestation verify without --predicate-type only validates the default (provenance) attestation, so the SBOM needs its own verification:
# SLSA build provenance
gh attestation verify oci://ghcr.io/bojanrajkovic/mcp-paprika:latest \
--owner bojanrajkovic \
--predicate-type https://slsa.dev/provenance/v1
# SPDX SBOM
gh attestation verify oci://ghcr.io/bojanrajkovic/mcp-paprika:latest \
--owner bojanrajkovic \
--predicate-type https://spdx.dev/Document/v2.3
Contributors building from source can use docker build -t mcp-paprika:dev . and substitute mcp-paprika:dev for the image reference below.
For a one-shot smoke test that just verifies the image launches (no OAuth, no
remote clients), override the transport to stdio — note that this turns the
container into a CLI process that speaks MCP on stdin/stdout, so the port
mapping isn't used:
docker run --rm -i \
-e MCP_TRANSPORT=stdio \
-e PAPRIKA_EMAIL=you@example.com \
-e PAPRIKA_PASSWORD=your-password \
-v "$(pwd)/data:/data" \
ghcr.io/bojanrajkovic/mcp-paprika:latest
The HTTP-mode image binds on 0.0.0.0:3000 and persists the disk cache and
vector index under /data (the documented mount point). Both /data
sub-directories (config/, cache/) are pre-created with nonroot (UID 65532)
ownership in the image so writes work the first time even on a fresh bind-mount.
If you bind-mount a host directory you created as root, pre-chown it:
mkdir -p ./data && sudo chown -R 65532:65532 ./data
Or use a named volume (Docker handles ownership automatically):
docker run --rm \
-e PAPRIKA_EMAIL=... -e PAPRIKA_PASSWORD=... \
-v mcp-paprika-data:/data \
-p 3000:3000 \
ghcr.io/bojanrajkovic/mcp-paprika:latest
The image also declares a HEALTHCHECK that hits GET /healthz; verify with:
docker inspect --format '{{.State.Health.Status}}' <container>
# → healthy
Deployment patterns (HTTP transport)
The HTTP transport ships with OAuth 2.1 built in. The primary remaining concerns are TLS termination and, optionally, additional network-layer controls.
TLS termination is required — MCP_PUBLIC_URL must be https:// and OAuth requires encrypted connections end-to-end. Recommended options:
- Reverse proxy with TLS (nginx / Caddy) — terminates TLS, passes
X-Forwarded-Forheaders (required for rate limiting), and forwards to the container on a private port. - Cloudflare Tunnel — no inbound port exposed; Cloudflare terminates TLS. Works well with Cloudflare Access for an additional authentication layer if desired.
- Tailscale HTTPS — Tailscale's built-in HTTPS cert provisioning; suitable for homelab setups where all clients are on your tailnet.
Container deployment with Docker Compose:
services:
mcp-paprika:
image: ghcr.io/bojanrajkovic/mcp-paprika:latest
environment:
MCP_TRANSPORT: http
MCP_PUBLIC_URL: https://mcp.example.com
MCP_OIDC_PRESET: google
MCP_OIDC_CLIENT_ID: "<your-client-id>"
MCP_OIDC_CLIENT_SECRET: "<your-client-secret>"
MCP_ALLOWED_EMAILS: "you@example.com"
PAPRIKA_EMAIL: "you@example.com"
PAPRIKA_PASSWORD: "<your-paprika-password>"
volumes:
- mcp-paprika-data:/data
ports:
- "127.0.0.1:3000:3000"
volumes:
mcp-paprika-data:
OAuth provides authentication-level controls; the reverse proxy provides TLS, rate limiting, and any additional network-level restrictions the deployment requires.
Documentation
- Configuration — env vars, config files, transport options, platform paths
- Tools reference — every tool with parameters and examples
- Embedding providers — set up semantic search with Ollama, OpenAI, OpenRouter, etc.
- Architecture — how it works under the hood
- Releasing — maintainer-facing release model, prerelease validation, attestation verification
License
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。