Barkeeper
A simple MCP server for tracking a home bar's inventory and cocktail recipes, with tools to manage shelf items and recipe details.
README
Barkeeper
An MCP server that tracks a home bar: what's on the shelf, and the cocktail recipes you want to keep.
It is deliberately small — 7 tools, roughly 900 tokens of tool schema and instructions combined — so it stays cheap to run against local models.
Design
Inventory is presence-only. An item is either on the shelf or it isn't — there are no quantities or units to keep up to date. Each item carries a category so the listing can be grouped.
Recipes are a name, ingredient lines, and a method. Ingredients are plain
text ("2 oz bourbon"), so there is no per-ingredient schema for a model to
get wrong.
The model does the cross-referencing. There is no what_can_i_make tool.
To answer that, the model calls list_inventory and list_recipes and compares
the two — it already knows that a recipe calling for bourbon is satisfied by a
bottle named "Buffalo Trace". The server's instructions tell it to match
generously.
Tools
| Tool | Arguments | Purpose |
|---|---|---|
list_inventory |
— | Everything on the shelf |
add_item |
name, category |
Put one item on the shelf |
remove_item |
name |
Take one item off |
list_recipes |
— | Every recipe with ingredients, without methods |
get_recipe |
name |
One recipe in full |
save_recipe |
name, ingredients, instructions? |
Save or replace a recipe |
delete_recipe |
name |
Delete a recipe |
list_recipes omits the method so the common "what can I make?" question stays
cheap; get_recipe fills in the detail for the one drink you settle on.
Categories
list_inventory groups by category, in shelf order, skipping any that are empty:
spirit: Buffalo Trace bourbon, Tanqueray gin
liqueur: Campari
bitters: Angostura
mixer: Fever-Tree tonic water
| Category | What belongs there |
|---|---|
spirit |
Base liquor — gin, vodka, whiskey, rum, tequila, brandy |
liqueur |
Sweetened or fortified — Campari, Cointreau, amaro, vermouth, sherry |
wine |
Still or sparkling — prosecco, Champagne |
bitters |
Cocktail bitters, dashed rather than poured — Angostura, Peychaud's |
mixer |
Non-alcoholic liquids — tonic, soda, juice, cola |
syrup |
Sweeteners — simple, orgeat, grenadine, honey |
garnish |
Citrus, herbs, olives, cherries |
other |
Anything else |
Bitters gets its own category because it fits neither of the obvious two: it's alcoholic, so it isn't a mixer, but it's measured in dashes rather than poured, so grouping it with the spirits misrepresents the shelf.
The enum says spirit rather than liquor deliberately — liquor and
liqueur differ by one letter, and asking a small model to choose between two
near-identical strings invites silent miscategorisation.
Re-adding an item that's already on the shelf updates its category, so a wrong guess is corrected by just adding it again.
Names are matched case- and punctuation-insensitively, and partial names work
when they're unambiguous — get_recipe("old fash") finds "Old Fashioned".
An ambiguous partial returns an error listing the candidates rather than
guessing.
Run it
The server speaks two transports. HTTP is the default in Docker: one port, plugged in by URL. stdio is the default for a local install, since that's how a client spawns it as a subprocess.
Docker (HTTP)
docker run -d --name barkeeper -p 8000:8000 -v barkeeper-data:/data \
ghcr.io/nickmowen/mcp-barkeeper
Then point any MCP client at http://localhost:8000/mcp:
{
"mcpServers": {
"barkeeper": {
"type": "http",
"url": "http://localhost:8000/mcp"
}
}
}
Or with Claude Code:
claude mcp add --transport http barkeeper http://localhost:8000/mcp
Compose is included — docker compose up -d gives the same thing with the
volume and port already wired.
The image runs as a non-root user (uid 1000), exposes 8000, and keeps its
database in the /data volume. Prefer a named volume, as above: a bind
mount to a host directory has to be writable by uid 1000 or the server can't
create its database. A healthcheck confirms the port is bound.
Local (stdio)
uv sync
claude mcp add barkeeper -- uv --directory /path/to/mcp-barkeeper run barkeeper
Which HTTP transport?
--transport http serves Streamable HTTP at /mcp — the current MCP
transport, which POSTs requests and streams replies back over SSE. This is what
you want.
--transport sse serves the older HTTP+SSE transport, a separate /sse
stream plus a /messages/ endpoint. It was deprecated in MCP 2025-03-26 and is
here only for clients that haven't moved yet.
Configuration
Flags beat environment variables, which beat defaults.
| Variable | Flag | Default | Purpose |
|---|---|---|---|
BARKEEPER_TRANSPORT |
--transport |
stdio (http in Docker) |
stdio, http, or sse |
BARKEEPER_HOST |
--host |
127.0.0.1 (0.0.0.0 in Docker) |
Interface to bind |
BARKEEPER_PORT |
--port |
8000 |
Port to bind |
BARKEEPER_DB_PATH |
— | platform data dir | SQLite file; :memory: for throwaway |
BARKEEPER_ALLOWED_HOSTS |
— | unset | Comma-separated; enables DNS-rebinding protection |
BARKEEPER_ALLOWED_ORIGINS |
— | unset | Comma-separated; enables DNS-rebinding protection |
Binding to 0.0.0.0 puts the server on every interface the container can reach,
with no authentication — keep it on a private network or bound to 127.0.0.1.
Setting either allow-list switches on DNS-rebinding protection, which rejects
requests whose Host or Origin header isn't named:
BARKEEPER_ALLOWED_HOSTS="localhost:*,127.0.0.1:*"
Both are left unset by default because enabling protection with an empty allow-list rejects every request.
Storage
A single SQLite file. In Docker it lives at /data/barkeeper.db; locally it is
created on first use at:
- macOS:
~/Library/Application Support/barkeeper/barkeeper.db - Linux:
~/.local/share/barkeeper/barkeeper.db - Windows:
%APPDATA%\barkeeper\barkeeper.db
Override with BARKEEPER_DB_PATH. Use :memory: for a throwaway session.
Development
uv run pytest
create_server(conn) accepts a connection, so tests run against an in-memory
database with no global state.
Recommended Servers
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.
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.
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.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
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.
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.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
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.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.