emf-bar-mcp

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.

Category
访问服务器

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.json at container start, then refreshed slowly in the background. find_drinks and list_bars never hit the network.
  • Live checks are cheap and targeted. check_stock reads one drink via /api/stocktype/<id>.json; whats_on_tap reads 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.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选