mcp-paprika

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.

Category
Visit Server

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 as paprika://grocery-list/{uid}, and menus as paprika://menu/{uid} resources
  • Two transports — stdio (default, for CLI clients) and Streamable HTTP (for mobile/web clients)
  • Container imageDockerfile ships 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 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 to MCP_OIDC_EMAIL_VERIFIED_POLICY:
    • strict (default): email must be present and email_verified = true
    • skip: email is accepted without checking email_verified
    • if-present: if email_verified is in the id_token, it must be true; 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

  1. Open claude.ai → Settings → Connectors
  2. Click "Add custom connector"
  3. Enter your server URL: https://<MCP_PUBLIC_URL>/mcp
  4. Claude will redirect your browser to the upstream IdP for authentication
  5. 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 HTTP
  • GET /mcp — long-lived SSE channel for server→client notifications
  • DELETE /mcp — session termination
  • GET /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-For headers (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

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