emf-bar-mcp
MCP server for the EMF Camp bar API that enables fuzzy drink discovery by one or two keywords, showing the bar and live stock for each drink.
README
emf-bar-mcp
A small, public MCP server over the EMF Camp bar API, built for a low‑context voice model: fuzzy drink discovery by one or two keywords, the bar each drink is served at, and live stock — while touching the upstream API as little as possible.
- Discovery is free. The whole menu (124 drinks) and which bar carries each one is pulled in
one call to
/api/stocktypes.jsonat container start, then refreshed slowly in the background.find_drinksandlist_barsnever hit the network. - Live checks are cheap and targeted.
check_stockreads one drink via/api/stocktype/<id>.json;whats_on_tapreads the two on‑tap endpoints. Both sit behind short TTL caches, so a burst of voice traffic collapses to one upstream request.
The paste‑into‑a‑prompt guide for the voice model is PROMPT.md.
Live instance: https://emf-bar-mcp-37viybmxjq-ew.a.run.app/mcp — Cloud Run (llm-voice, europe‑west1), public, scale‑to‑zero. Health: /health.
Quick start
npm install
npm start # HTTP server on http://0.0.0.0:8787/mcp (default)
# or
npm run stdio # stdio transport, for desktop MCP clients
Health check: curl localhost:8787/health
Docker
docker build -t emf-bar-mcp .
docker run -p 8787:8787 emf-bar-mcp
Connecting a client
Streamable HTTP (remote / public) — point your MCP client at http://<host>:8787/mcp.
stdio (e.g. Claude Desktop claude_desktop_config.json):
{
"mcpServers": {
"emf-bar": { "command": "node", "args": ["/abs/path/to/emf-bar-mcp/src/index.js"], "env": { "MCP_TRANSPORT": "stdio" } }
}
}
Tools
| Tool | Args | Live call? | Returns |
|---|---|---|---|
list_bars |
— | no | The 3 bars (slug, name, drink count, map link) |
find_drinks |
query (1–2 keywords), bar?, category?, include_unavailable?, limit? |
no | Ranked drinks: id, name, abv, price, category, bars, dietary flags |
check_stock |
drink (name or id), bar? |
1 | status (on_sale/in_stock_not_on_sale/out_of_stock), onSale, inStock, level, servingsRemaining+servingUnit, percentRemaining, containerPercentRemaining (cask/keg), price, bars, plus source/live/checkedAt freshness |
whats_on_tap |
bar? |
1 (cached) | Casks/kegs/ciders pouring now, each with remainingPct / level |
opening_hours |
bar? |
1 (cached) | open, closesAt/nextOpen, upcoming schedule. Site-wide schedule (EMF publishes no per-bar hours) |
Every tool returns a short spoken‑style content string and machine‑readable
structuredContent. check_stock returns a candidates list when a name is ambiguous.
On sale vs in stock. EMF only serves some draught beers/ciders at a time ("not all draught
beers and ciders are on sale at the same time"). The API models this: a product is on sale when it
has a stockline (a place to buy it), and in stock when base_units_remaining > 0. So a cask in
the cellar can be in stock but not on sale. check_stock.status surfaces the three cases —
on_sale, in_stock_not_on_sale, out_of_stock — and find_drinks hides not‑on‑sale drinks unless
include_unavailable: true.
Bar names are fuzzy: robotarms/"Robot Arms"/"main bar", cybar/"Cybar"/"Null Sector",
spacebar/"SpaceBAR"/"space bar" all resolve.
Configuration (env vars)
| Var | Default | Purpose |
|---|---|---|
MCP_TRANSPORT |
http |
http or stdio |
PORT / HOST |
8787 / 0.0.0.0 |
HTTP bind |
EMF_BAR_BASE |
https://bar.emf.camp |
Upstream API base |
CATALOG_REFRESH_MS |
900000 (15 min) |
Background catalog refresh interval |
STOCK_TTL_MS |
20000 |
Per‑drink live‑stock cache TTL |
ONTAP_TTL_MS |
30000 |
On‑tap cache TTL |
SESSIONS_TTL_MS |
300000 |
Opening‑times cache TTL |
HTTP_TIMEOUT_MS |
8000 |
Upstream request timeout |
How it works
startup + every CATALOG_REFRESH_MS
/api/stocktypes.json ───────────────► catalog cache (drinks, tags, bar placement, Fuse index)
│
find_drinks / list_bars ◄──────────────────┘ (0 upstream calls)
check_stock ──► /api/stocktype/<id>.json (TTL STOCK_TTL_MS)
whats_on_tap ──► /api/on-tap.json + /api/cybar-on-tap.json (TTL ONTAP_TTL_MS)
Discovery matching is deterministic (keyword/synonym/style/flavour tags derived from each drink, including "hoppy"/"gluten free"/"alcohol free"), with a Fuse.js fuzzy fallback for typos and mis‑transcriptions. The HTTP transport is stateless (a fresh MCP server per request; the catalog cache is a shared singleton), so it scales and hosts simply.
The upstream /api/stocktypes.json is flagged expensive — do not poll; this server honours that
by pulling it only at start‑up and on the slow refresh timer, never per request.
Notes
- Not affiliated with EMF; uses the public read‑only bar API. Data © EMF.
- Node ≥ 18 (uses global
fetch). MIT licensed.
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.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
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.