Cocktail Recipes
БесплатноНе проверенA Dockerized Python MCP server that provides tools to manage cocktail recipes, ingredients, and related operations via the Aephir/cocktail-recipes REST API, wit
Описание
A Dockerized Python MCP server that provides tools to manage cocktail recipes, ingredients, and related operations via the Aephir/cocktail-recipes REST API, with dry-run safety defaults and automatic re-authentication.
README
Dockerized Python 3.11 MCP server for the Aephir/cocktail-recipes REST API.
This server is designed for Claude Desktop and other MCP clients. It authenticates against the cocktail app via session-cookie login (POST /api/auth/login) and uses REST-only calls.
Features
- Python 3.11 MCP server with typed models and validation
- Self-contained OAuth 2.1 authorization server for remote Claude connectors
- Session-cookie auth with automatic re-login on
401 - Safe retries for idempotent calls (read-only and dry-run operations)
- No blind retries for non-idempotent apply calls
- Dry-run safety defaults for destructive tools
- Structured response and error schema
- Capability probing tool for backend endpoint readiness
- Operation log tool for recent MCP actions
Implemented Tools
list_recipesget_recipecreate_recipe(defaultsdry_run=true)update_recipe(defaultsdry_run=true)delete_recipe(defaultsdry_run=true)list_ingredientscreate_ingredient(defaultsdry_run=true)update_ingredient(defaultsdry_run=true)delete_ingredient(defaultsdry_run=true, supportsforce=true)list_toolscreate_tool(defaultsdry_run=true)update_tool(defaultsdry_run=true)delete_tool(defaultsdry_run=true, supportsforce=true)merge_ingredients(dry_run=truedefault)merge_tools(dry_run=truedefault)recategorize_recipes(dry_run=truedefault)bulk_update_recipes(dry_run=truedefault)update_tags_bulk(dry_run=truedefault)operation_log_recentapi_capabilities
Safety Behavior
All destructive tools default to dry_run=true (create/update/delete/merge/recategorize/tag bulk).
To apply, pass explicit dry_run=false.
Each destructive tool returns:
- preview (
affectedIDs and count) apply_executedboolean- backend result payload
Apply integrity guard:
- If a tool is called with
dry_run=falsebut the backend responds withdry_run=true, MCP returns an error (apply_not_executed) instead of success. - If the backend explicitly reports zero changed rows/ids for an apply request, MCP returns an error (
apply_not_executed) so silent no-ops are surfaced.
If backend admin endpoints are not available yet, tool responses return structured not_implemented with an actionable message.
Recipe update safety:
update_recipenow uses fetch-merge-send behavior on apply: MCP first reads the current recipe, merges your provided fields, and sends a full update payload.- This prevents accidental clearing of
toolsorgarnisheswhen those fields are omitted from the request. - To intentionally clear a list field, pass it explicitly as an empty list.
Delete safeguards for ingredients/tools:
delete_ingredientanddelete_toolperform a recipe-reference precheck.- If references are found, apply mode is blocked unless
force=true. - Dry-run output includes reference count and sample impacted recipes for review.
Tool Permission Hints
This server publishes MCP tool annotations so clients can distinguish read-only tools from mutating tools for permission workflows.
- Read-only tools (
readOnlyHint=true):list_recipes,get_recipe,list_ingredients,list_tools,operation_log_recent,api_capabilities - Additive write tools (
readOnlyHint=false,destructiveHint=false):create_recipe,create_ingredient,create_tool - Mutating/destructive tools (
readOnlyHint=false,destructiveHint=true):update_recipe,delete_recipe,update_ingredient,delete_ingredient,update_tool,delete_tool,merge_ingredients,merge_tools,recategorize_recipes,bulk_update_recipes,update_tags_bulk
In clients that support bulk approvals, this enables "always allow" for the read-only set while keeping mutating tools gated.
Required Environment Variables
COCKTAIL_API_BASE_URLCOCKTAIL_API_USERNAMECOCKTAIL_API_PASSWORDPUBLIC_BASE_URLwhen usingstreamable-httpAUTH_USERNAMEwhen usingstreamable-httpAUTH_PASSWORDwhen usingstreamable-httpOAUTH_STORAGE_HOST_DIRwhen using a host-path mount for OAuth state
Optional variables are in .env.example.
Transport Modes
stdio(default in.env.example): process-local MCP over stdin/stdout, no published port.streamable-http: network-reachable MCP over HTTP with embedded OAuth 2.1, requires a published port or reverse proxy.
For Claude running on a different machine, use streamable-http.
Embedded OAuth
When MCP_TRANSPORT=streamable-http, this container acts as both:
- the OAuth authorization server
- the OAuth-protected MCP resource server
Implemented endpoints:
GET /.well-known/oauth-authorization-serverGET /.well-known/oauth-protected-resourceGET /.well-known/jwks.jsonPOST /registerGET|POST /authorizePOST /tokenPOST|GET /mcpprotected byAuthorization: Bearer <token>
OAuth details:
- Authorization Code flow with PKCE (
S256required) - Dynamic Client Registration for public clients (
token_endpoint_auth_method: none) - Built-in login form and consent screen using
AUTH_USERNAMEandAUTH_PASSWORD - JWT access tokens signed by a persisted RSA key
- Rotating refresh tokens for public clients
Persisted OAuth state:
- signing key
- registered clients
- authorization codes
- refresh tokens
If you want this state to live on the host filesystem, mount a directory such as:
/mnt/storage_1/docker/cocktail-recipes-mcp/oauth
Keep OAUTH_STORAGE_DIR=/data/oauth inside the container and set OAUTH_STORAGE_HOST_DIR to the host path above in Portainer or compose.
Local Run
- Create env file:
cp .env.example .env
- Install dependencies and run:
python3.11 -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e .[test]
python -m cocktail_recipes_mcp.main
Docker Run
Build image:
docker build -t cocktail-recipes-mcp:latest .
Run container:
docker run --rm -i --env-file .env cocktail-recipes-mcp:latest
Compose example:
docker compose up --build cocktail-recipes-mcp
Note: docker-compose.yml reads values from environment variables. For local CLI usage, export them or place them in a local .env file before running docker compose.
For remote clients, set MCP_TRANSPORT=streamable-http, configure PUBLIC_BASE_URL, AUTH_USERNAME, AUTH_PASSWORD, and publish MCP_HTTP_PUBLISH_PORT.
Portainer Deployment (Recommended)
Use this when you run both the cocktail app and this MCP server as containers.
For Portainer "Create stack from repository":
- Keep all compose YAML in this repository.
- Do not add manual YAML overrides in the Portainer UI.
- Set Stack environment variables in the UI using the table below.
Portainer Stack Environment Variables
When both stacks share the same external Docker network (cocktail_net), use the app service name in COCKTAIL_API_BASE_URL.
| Key | Example value | Required | Notes |
|---|---|---|---|
COCKTAIL_API_BASE_URL |
http://cocktail-app:3000 |
Yes | Use service-name URL on shared Docker network. |
COCKTAIL_API_USERNAME |
admin |
Yes | Cocktail app login username. |
COCKTAIL_API_PASSWORD |
change-me |
Yes | Cocktail app login password. |
MCP_TRANSPORT |
streamable-http |
Yes (remote Claude) | Use stdio only for local process clients. |
MCP_HTTP_HOST |
0.0.0.0 |
Yes (remote Claude) | Container bind address. |
MCP_HTTP_PORT |
8000 |
Yes (remote Claude) | Internal container port. |
MCP_HTTP_PATH |
/mcp |
Yes (remote Claude) | MCP endpoint path. |
MCP_HTTP_PUBLISH_PORT |
8011 |
Yes (remote Claude) | Host-published port. |
PUBLIC_BASE_URL |
https://cocktail-mcp.example.com |
Yes (remote Claude) | Public HTTPS origin used by OAuth metadata. |
AUTH_USERNAME |
connector-admin |
Yes (remote Claude) | Login used on OAuth consent screen. Set anything, then use the same when logging in from Claude |
AUTH_PASSWORD |
change-me |
Yes (remote Claude) | Password for the connector login above. Set anything, then use the same when logging in from Claude |
OAUTH_STORAGE_DIR |
/data/oauth |
Recommended | Keep as shown unless you changed image internals. |
OAUTH_STORAGE_HOST_DIR |
/mnt/storage_1/docker/cocktail-recipes-mcp/oauth |
Recommended | Host path for persisted OAuth keys/tokens. |
Then:
- Ensure the host path in
OAUTH_STORAGE_HOST_DIRexists and is writable by Docker. - Deploy/redeploy the stack in Portainer.
- In Claude, add a custom connector pointing to
https://<your-fqdn>/mcp.
Portainer Error: PermissionError: [Errno 13] Permission denied: '/data/oauth/jwt_signing_key.pem'
Cause:
- The container runs as a non-root user (
appuser). - The bind-mounted host directory from
OAUTH_STORAGE_HOST_DIRis not writable by that user.
Fix on the Docker host:
- Create the directory if needed:
sudo mkdir -p /mnt/storage_1/docker/cocktail-recipes-mcp/oauth
- Confirm container UID/GID for
appuser:
docker run --rm --entrypoint sh cocktail-recipes-mcp:latest -c 'id -u appuser; id -g appuser'
- Set ownership using that output. Example if UID is
100and GID is101:
sudo chown -R 100:101 /mnt/storage_1/docker/cocktail-recipes-mcp/oauth
sudo chmod -R u+rwX,g+rwX /mnt/storage_1/docker/cocktail-recipes-mcp/oauth
- Redeploy the stack.
Quick test fallback (not for long-term use):
sudo chmod -R 777 /mnt/storage_1/docker/cocktail-recipes-mcp/oauth
Portainer Error: .env not found
If you saw an error like env file /data/compose/<id>/.env not found, the stack was expecting a physical .env file inside Portainer's compose directory.
This repo now avoids that requirement by reading COCKTAIL_API_* from stack environment variables directly.
In Portainer Stack deployment:
- Add these variables in the Stack
Environment variablesUI:COCKTAIL_API_BASE_URLCOCKTAIL_API_USERNAMECOCKTAIL_API_PASSWORDPUBLIC_BASE_URLAUTH_USERNAMEAUTH_PASSWORDOAUTH_STORAGE_HOST_DIR
- Redeploy the stack.
Your example values are valid for this setup.
Portainer Error: pull access denied for cocktail-recipes-mcp
This happens when Portainer tries to pull cocktail-recipes-mcp:latest from a registry.
This project is intended to build from source in the stack, not pull from Docker Hub.
Resolution:
- Use the current compose file from this repo (it uses
buildandpull_policy: never). - In Portainer stack deployment, disable any "pull latest image" behavior for this stack.
- Redeploy the stack so the image is built locally on the Docker host.
FQDN vs Internal URL
- If MCP and cocktail app are in the same Docker network, prefer internal URL (
http://service-name:port). - If MCP is outside that network, use a reachable external URL/FQDN (for example
https://cocktails.example.com).
For MCP client access:
- Same LAN/VPN:
http://<host-ip>:<published-port><MCP_HTTP_PATH> - Internet-facing: put SWAG/Nginx in front and use
https://<fqdn><MCP_HTTP_PATH>
SWAG Nginx Config (Redacted Template)
Use this as a template for SWAG when proxying to an MCP host IP and published port. Replace placeholder values with your own environment values.
# /config/nginx/site-confs/cocktail-mcp.conf
server {
listen 443 ssl http2;
server_name mcp.example.com;
include /config/nginx/ssl.conf;
include /config/nginx/proxy.conf;
client_max_body_size 0;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;
# SWAG not on shared Docker network: use MCP host IP + published port.
set $mcp_upstream http://192.168.1.50:8011;
# OAuth discovery endpoints + JWKS
location ~ ^/\.well-known/(oauth-authorization-server|oauth-protected-resource|jwks\.json)$ {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Authorization $http_authorization;
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
proxy_pass $mcp_upstream;
}
# MCP and OAuth flow endpoints
location ~ ^/(mcp|register|authorize|token|oauth/login|oauth/consent)$ {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Authorization $http_authorization;
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
proxy_pass $mcp_upstream;
}
location = /healthz {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass $mcp_upstream/healthz;
}
location / {
return 404;
}
}
Expected quick checks:
GET /.well-known/oauth-authorization-serverreturns200GET /.well-known/oauth-protected-resourcereturns200GET /mcpwithout token returns401
Go-Live Checklist
- MCP and app containers share a network.
- MCP env vars are set in Portainer, including
PUBLIC_BASE_URL,AUTH_USERNAME, andAUTH_PASSWORD. - Login endpoint is reachable from MCP (
POST /api/auth/login). - OAuth state host path is mounted to
/data/oauth. GET /.well-known/oauth-authorization-serversucceeds.GET /.well-known/oauth-protected-resourcesucceeds.- Unauthenticated
GET /mcpreturns401withWWW-Authenticatepointing at protected-resource metadata. - For admin operations, run
dry_run=truefirst and apply only withdry_run=false.
Claude Desktop Connection
Add an MCP server entry to your Claude Desktop config.
Local Python command
{
"mcpServers": {
"cocktail-recipes": {
"command": "/absolute/path/to/cocktail-recipes-mcp/.venv/bin/python",
"args": ["-m", "cocktail_recipes_mcp.main"],
"env": {
"COCKTAIL_API_BASE_URL": "http://localhost:3000",
"COCKTAIL_API_USERNAME": "admin",
"COCKTAIL_API_PASSWORD": "change-me"
}
}
}
}
Docker command
{
"mcpServers": {
"cocktail-recipes": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"--env-file",
"/absolute/path/to/cocktail-recipes-mcp/.env",
"cocktail-recipes-mcp:latest"
]
}
}
}
Remote HTTP MCP (different machine)
When running in Portainer with MCP_TRANSPORT=streamable-http, use your reachable URL:
- Direct host/port example:
http://10.0.30.51:8000/mcp - SWAG/FQDN example:
https://mcp.example.com/mcp
For Claude custom connector setup:
- URL:
https://mcp.example.com/mcp - Leave
OAuth Client IDempty. - Leave
OAuth Client Secretempty. - Claude should dynamically register as a public client and complete OAuth against this server.
Do not put COCKTAIL_API_USERNAME or COCKTAIL_API_PASSWORD into Claude. Those remain server-side only.
Tests
Run minimal tests:
pytest
Manual Verification
Discovery endpoints:
curl -i https://cocktail-mcp.example.com/.well-known/oauth-authorization-server
curl -i https://cocktail-mcp.example.com/.well-known/oauth-protected-resource
curl -i https://cocktail-mcp.example.com/.well-known/jwks.json
Protected MCP endpoint without a token:
curl -i https://cocktail-mcp.example.com/mcp
Expected result:
401 UnauthorizedWWW-Authenticate: Bearer ... resource_metadata="https://cocktail-mcp.example.com/.well-known/oauth-protected-resource"
Automated tests now cover:
- metadata discovery
- dynamic client registration
- authorization code flow with PKCE
- refresh token rotation
/mcpbearer protection
Example Transcript
See docs/example_transcripts.md for a dry_run/apply merge example (Cucumber vs cucumber).
Установка Cocktail Recipes
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/Aephir/cocktail-recipes-mcpFAQ
Cocktail Recipes MCP бесплатный?
Да, Cocktail Recipes MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Cocktail Recipes?
Нет, Cocktail Recipes работает без API-ключей и переменных окружения.
Cocktail Recipes — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Cocktail Recipes в Claude Desktop, Claude Code или Cursor?
Открой Cocktail Recipes на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare Cocktail Recipes with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
