mcpField
U
untappd-mcp

untappd-mcp

本地服务
github.com/chrischall社交媒体01 天前更新

关于 untappd-mcp

面向啤酒爱好者的 Untappd MCP 服务,可查询啤酒、场所和动态,管理签到与愿望清单,并从菜单中挑选未喝过的啤酒。它通过 Untappd 私有移动 API 工作,并将签到记录缓存在本地 SQLite 中。

主要功能

  1. 01搜索啤酒、酒厂和场所
  2. 02读取用户资料、动态和愿望清单
  3. 03经确认后发布签到、举杯和评论
  4. 04将签到历史缓存到 SQLite
  5. 05从场所菜单筛选未喝过的啤酒

适用场景

  1. 01按条件查找啤酒和酒厂
  2. 02查看用户的签到记录或愿望清单
  3. 03从酒单中挑选未喝过的啤酒
  4. 04确认内容后发布签到

MCP 相关

将 Untappd 的读取和写入操作作为 MCP 工具提供。所有写入都需要确认,本地 SQLite 缓存用于查询签到历史。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"chrischall-untappd": {
"command": "npx",
"args": [
"-y",
"untappd-mcp"
],
"env": {
"UNTAPPD_ACCESS_TOKEN": "<UNTAPPD_ACCESS_TOKEN>",
"UNTAPPD_USERNAME": "<UNTAPPD_USERNAME>",
"UNTAPPD_PASSWORD": "<UNTAPPD_PASSWORD>",
"UNTAPPD_CLIENT_ID": "<UNTAPPD_CLIENT_ID>",
"UNTAPPD_CLIENT_SECRET": "<UNTAPPD_CLIENT_SECRET>",
"UNTAPPD_DEVICE_ID": "<UNTAPPD_DEVICE_ID>",
"UNTAPPD_UTV": "<UNTAPPD_UTV>",
"UNTAPPD_USER_AGENT": "<UNTAPPD_USER_AGENT>",
"UNTAPPD_TIMEZONE": "<UNTAPPD_TIMEZONE>",
"UNTAPPD_PHOTO_DIR": "<UNTAPPD_PHOTO_DIR>",
"UNTAPPD_CACHE_DB": "<UNTAPPD_CACHE_DB>",
"UNTAPPD_CACHE_DIR": "<UNTAPPD_CACHE_DIR>"
}
}
}
}
启动命令npx -y untappd-mcp
需要配置的环境变量
UNTAPPD_ACCESS_TOKEN密钥An Untappd access token you already hold. Supply it and no password is needed (username/password become optional).
UNTAPPD_USERNAMEYour Untappd username or login email. Required unless UNTAPPD_ACCESS_TOKEN is set.
UNTAPPD_PASSWORD密钥Your Untappd account password (used only for xauth login). Required unless UNTAPPD_ACCESS_TOKEN is set.
UNTAPPD_CLIENT_ID密钥Untappd mobile app client id (obtained by intercepting the app; see README). Needed to log in with username/password and for every write; a pre-seeded access token can read without it.
UNTAPPD_CLIENT_SECRET密钥Untappd mobile app client secret. Needed to log in with username/password and for every write; a pre-seeded access token can read without it.
UNTAPPD_DEVICE_IDStable device UUID the token is keyed to (a default is provided).
UNTAPPD_UTVAPI version param (default 4.0.0).
UNTAPPD_USER_AGENTOverride the User-Agent (default mimics the app).
UNTAPPD_TIMEZONEIANA timezone (e.g. America/New_York) that check-ins are stamped with when the call doesn't pass timezone. Set it when the server runs somewhere other than the drinker's zone (e.g. a hosted connector, usually UTC). Defaults to the server process's zone.
UNTAPPD_PHOTO_DIRThe directory untappd_checkin's photo_path must be inside (several allowed, separated by :). Defaults to ~/Downloads/untappd-mcp — put a photo there to attach it. Any other file is refused, so a prompt-injected shout or comment can't publish an arbitrary image from your disk.
UNTAPPD_CACHE_DBPath to the local check-in cache SQLite file (default ~/.untappd-mcp/checkins.db; created owner-only — dir 0700, file 0600). Local/stdio only.
UNTAPPD_CACHE_DIRDirectory for the cache instead of ~/.untappd-mcp (the file is checkins.db inside it). UNTAPPD_CACHE_DB wins if both are set. Local/stdio only.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmuntappd-mcp2.4.0

工具

46

收录的工具列表 · 更新于 4 天前

参数类型必填说明
sortenum否排序顺序:checkin(相关度,默认)、name 或 countcheckin | name | count
viewenum否响应形式:"compact"(默认)会省略响应中其他位置已有的字段;"full" 会返回此服务端支持的所有字段。compact 会将每个匹配项投影为 {bid, name, style, abv, ibu, brewery, checkin_count, have_had};"full" 会返回 Untappd 完整的约 1.2 KB 搜索项,包括较长的 beer_description 和嵌套的 brewery 记录。compact | full
limitinteger否最大结果数(1–50,默认 25)
querystring是要搜索的啤酒名称
offsetinteger否分页结果偏移量(默认值:0)

详细介绍

untappd-mcp

适用于 Untappd 的 MCP 服务。它使用你自己的账户访问 Untappd 的移动端 (v4) API,可搜索啤酒、酿酒厂和场所;读取个人资料、签到、愿望清单、不同啤酒、徽章、好友和好友动态;还可发布签到、碰杯和评论。

由 AI(Claude Code)开发和维护。请自行斟酌使用。这是一个使用 Untappd 私有移动端 API 的非官方客户端;与 Untappd 无关,也未获其认可。

工作原理

Untappd 的 iPad/iPhone 应用使用用户名/密码通过 xauth 登录进行身份验证 (POST https://api.untappd.com/v4/xauth),获取访问令牌,然后调用 v4 API。本服务复现了这一流程,但令牌的传递方式有所不同:

  • 读取请求通过 Authorization: Bearer 标头携带令牌,绝不将令牌放在 URL 中 (应用使用 access_token 查询参数;API 两种方式都接受)。
  • 写入请求通过 Authorization: Bearer 标头携带令牌(并在查询中携带应用的客户端凭据),与应用的实际请求一致。

令牌会按需获取、缓存在内存中,并在过期时自动刷新。

配置

变量必需说明
UNTAPPD_ACCESS_TOKEN否你已有的访问令牌。提供此项后无需密码——将完全跳过 xauth 登录。
UNTAPPD_USERNAME无令牌时需要你的 Untappd 用户名或登录邮箱。
UNTAPPD_PASSWORD无令牌时需要你的 Untappd 密码(仅用于获取令牌的 xauth 登录)。
UNTAPPD_CLIENT_ID登录和写入时需要Untappd 移动应用客户端 ID(见下文)。预先提供的访问令牌无需此项即可读取。
UNTAPPD_CLIENT_SECRET登录和写入时需要Untappd 移动应用客户端密钥。
UNTAPPD_DEVICE_ID否与令牌关联的固定设备 UUID(提供默认值)。
UNTAPPD_UTV否API 版本参数(默认为 4.0.0)。
UNTAPPD_USER_AGENT否覆盖 User-Agent(默认值模拟应用)。
UNTAPPD_TIMEZONE否IANA 时区(例如 America/New_York),当调用未传入 timezone 时,用于标记签到时间。如果服务运行位置与饮酒者所在时区不同(例如托管连接器通常使用 UTC),请设置此项。默认使用服务进程所在时区。
UNTAPPD_PHOTO_DIR否untappd_checkin 工具的 photo_path 必须位于其中的目录(可指定多个目录,以 : 分隔)。默认为 ~/Downloads/untappd-mcp——将照片放在那里即可附加。其他文件都会被拒绝,因此提示词注入的动态或评论无法从你的磁盘发布任意图片。
UNTAPPD_CACHE_DB否本地签到缓存 SQLite 文件的路径(默认为 ~/.untappd-mcp/checkins.db)。相对路径或以 ~ 开头的路径会相对于你的主目录解析,而不是工作目录,因此每个宿主(Claude Desktop、Claude Code,……)都能找到同一个文件;解析后的路径会在启动时记录到 stderr。数据库文件始终仅对所有者可访问(0600),服务创建的目录以及默认的 ~/.untappd-mcp 目录也一样;如果指定的目录已存在,则保留其权限。仅限本地/stdio。
UNTAPPD_CACHE_DIR否缓存目录,替代 ~/.untappd-mcp(文件为其中的 checkins.db)。如果两者都已设置,则以 UNTAPPD_CACHE_DB 为准。

将 .env.example 复制为 .env,并填写内容以供本地使用。

确认

每次写入都需要先确认。能够显示确认提示词的客户端(Claude Code)会显示提示词(除非 MCP_CONFIRM_ELICITATION=off)。在其他客户端上,写入分两次调用:第一次不会执行任何操作,而是返回确切请求的预览和一个 confirmToken;只有使用相同参数和该令牌再次调用时,才会执行操作。令牌只能使用一次,且仅适用于对应工具和参数;任何内容发生变化,调用都会被拒绝,并返回新的预览。

变量默认值
MCP_CONFIRM_MODEask-user在无法显示确认提示词的客户端(claude.ai、Claude Desktop)上,写入操作的处理方式。ask-user:分两步执行——第一次调用不会执行任何操作,而是返回预览和令牌;模型必须先在聊天中获得你的批准,然后才能使用该令牌再次调用。auto:同样分两步执行,但模型自行查看预览后即可使用令牌。refuse:此类客户端上的写入操作会被拒绝。能够显示提示词的客户端(Claude Code)始终会显示实际提示词,除非 MCP_CONFIRM_ELICITATION=off。无法识别的值会按 refuse 处理。
MCP_CONFIRM_ELICITATIONonoff 表示永不显示确认提示词,因此所有客户端都会遵循 MCP_CONFIRM_MODE 的设置。对于声称能够显示提示词但实际上从不显示的客户端,请设置此项(写入操作会卡住——opencode 2.0.x)。任何其他值都会按 on 处理,并在 stderr 上发出警告。
MCP_CONFIRM_TTL_SECONDS600令牌的有效时长。
MCP_CONFIRM_SECRET每个进程随机生成签名密钥;仅当令牌必须在服务重启后继续有效时才设置。
获取客户端 ID / 密钥

Untappd 不会公开这些信息;它们存储在移动应用中。通过 HTTPS 代理捕获你自己的应用流量来获取:

  1. 安装 mitmproxy 等代理,并在运行 Untappd 应用的设备上信任其 CA 证书。
  2. 将设备(或者,在运行 iPad 应用的 Apple 芯片 Mac 上,将 Mac 的系统 HTTP/HTTPS 代理)指向该代理。
  3. 打开 Untappd 并登录。找到 POST https://api.untappd.com/v4/xauth 请求——其查询字符串中包含 client_id 和 client_secret。
  4. 将它们填入 UNTAPPD_CLIENT_ID / UNTAPPD_CLIENT_SECRET。

请妥善保管这些值;不要将它们提交到代码库。

工具

读取:untappd_search_beer、untappd_beer_info、untappd_beer_activity、 untappd_search_brewery、untappd_brewery_info、untappd_brewery_beers、 untappd_search_venue、untappd_venue_info、untappd_venue_activity、 untappd_user_info、untappd_user_checkins、untappd_user_wishlist、 untappd_user_beers、untappd_user_badges、untappd_user_friends、untappd_pending_friends、 untappd_activity_feed、untappd_checkin_info、untappd_resolve、untappd_open_url、untappd_user_venues、untappd_venue_by_foursquare、untappd_trending、 untappd_notifications、untappd_local_checkins、untappd_healthcheck。

写入(每次都需要先确认——参见确认):untappd_toast、untappd_add_comment、untappd_delete_comment、untappd_checkin、 untappd_wishlist_add、untappd_wishlist_remove、untappd_delete_checkin、 untappd_add_friend、untappd_accept_friend、untappd_reject_friend、untappd_remove_friend。

签到缓存:untappd_sync_checkins、untappd_sync_user_beers、 untappd_cache_has_had、untappd_cache_has_had_many、untappd_cache_not_had、 untappd_cache_query、untappd_top_not_had、untappd_cache_status、untappd_cache_forget。

菜单推荐:untappd_menu_pick(输入酒单上的条目,输出按排名排列的未喝过啤酒)、 untappd_venue_pick(相同功能,使用场所的 Untappd 菜单)。

从菜单上挑选啤酒

untappd_menu_pick 将从菜单照片中“为 Mer 找一款啤酒”变成一次调用。 将从菜单板上读到的条目传入:

{
  "username": "mer",
  "items": [
    "HAP & HARRY'S TENNESSEE LAGER $6",
    "I BELIEVE IN NASHVILLE BLONDE",
    "JACKALOPE FENNARIO IPA",
    "BEARDED IRIS HOMESTYLE",
    "BLACK ABBEY SEASONAL",
    "ROTATING CIDER"
  ],
  "venue_city": "Nashville",
  "top_n": 2
}

一次调用即可:

  1. 更新缓存。 如果用户的不同啤酒列表已经完整,就会增量刷新,并在遇到第一款已缓存且未变化的啤酒时停止(通常只需一页)。如果列表为空或不完整,则会在同一次调用中补全,直到完成或用尽 api_budget(默认 60 次调用,由每个步骤共享)。无需先调用同步工具。
  2. 将每一行匹配到一款啤酒。 会剥除价格、规格、ABV 和标点,搜索 Untappd(由于 Untappd 搜索会匹配每个词,因此在未找到时会去掉风格词再搜索),并根据酒厂匹配度、啤酒名称匹配度(因此“Homestyle”不会被误读为“Double Homestyle”)、签到量(规范条目优先于只有 3 次签到的重复条目)以及 venue_city 为候选项评分。最多同时运行 4 次搜索,遇到 429 会退避重试。匹配结果会缓存在磁盘上,因此下次访问同一场所时无需搜索。
  3. 处理泛称条目。 <Brewery> SEASONAL / ROTATING 会转为该酒厂可能符合季节性特征的啤酒列表,标记为 needs_confirmation。没有酒厂的条目(ROTATING CIDER)会返回 unresolved: "ask bartender"。
  4. 将每款啤酒与缓存核对(检查两个来源,与 has_had_many 一样)。
  5. 根据全球评分排列未喝过的啤酒,应用 style_filter,并将前 top_n 项作为 picks 返回。

上面调用的输出(假设 Mer 喝过 Fennario;此处有所删节:not_had 还列出评分为 3.33 的 Tennessee Lager,possible 列出六个候选项,且会附加一个 freshness 块):

{
  "picks": [
    { "item": "BEARDED IRIS HOMESTYLE", "bid": 1502190, "name": "Homestyle", "brewery": "Bearded Iris Brewing",
      "style": "IPA - New England / Hazy", "abv": 6, "global_rating": 3.98,
      "reason": "not had · 3.98 global rating · #1 of 3 not-had beers on this list" },
    { "item": "I BELIEVE IN NASHVILLE BLONDE", "bid": 3780741, "name": "I Believe In Nashville",
      "brewery": "I Believe Brewing Company", "style": "Blonde / Golden Ale - American", "abv": 4.5,
      "global_rating": 3.54, "reason": "not had · 3.54 global rating · #2 of 3 not-had beers on this list" }
  ],
  "had": [
    { "item": "JACKALOPE FENNARIO IPA", "bid": 1893601, "name": "Fennario", "brewery": "Jackalope Brewing Company",
      "count": 3, "last_date": "2025-08-01T20:00:00.000Z", "user_rating": 3.75 }
  ],
  "possible": [
    { "item": "BLACK ABBEY SEASONAL", "brewery": "The Black Abbey Brewing Company",
      "reason": "seasonal-looking beers from this brewery",
      "candidates": [
        { "bid": 1223176, "name": "Chronicles Festbier", "style": "Festbier", "abv": 4.9, "global_rating": 3.672, "had": false }
      ],
      "needs_confirmation": true }
  ],
  "unresolved": [{ "item": "ROTATING CIDER", "reason": "ask bartender" }],
  "sync": { "mode": "backfill", "api_calls": 1 },
  "api_calls": 11,
  "api_budget": 60,
  "another_run_needed": false
}

sync 会说明刷新情况;覆盖情况则保存在 freshness 中。如果补全在完成前耗尽预算,freshness.coverage_complete 会是 false,another_run_needed 会是 true,并且 freshness 的说明会警告,“未喝过”的判断可能是假阴性。再次调用会从停止的位置继续。

配额。 当 Untappd 响应包含 X-Ratelimit-Remaining 时,服务会在这一小时内记住该值,并将每个受预算限制的工具(menu_pick、venue_pick、top_not_had、两个同步工具)的预算限制在剩余配额以内。结果随后会包含 quota_remaining,并在实际应用了此上限时包含 budget_clamped_to_quota: true。Untappd 的公共 API 文档说明了这个标头;如果此服务使用的私有 API 不发送该标头,预算机制会与之前完全相同。

untappd_venue_pick 接受 Untappd 场所 URL(https://untappd.com/v/<slug>/<id>)或 id,而不是 items。它会翻页遍历该场所经过验证的完整菜单,从中获取场所所在城市,并执行相同的核对和排序。菜单条目已包含啤酒 id,因此无需搜索。

签到缓存

Untappd API 只提供分页列表(每页 50 条),没有“该用户是否曾喝过啤酒 X?”的查询接口——仅靠 API 回答这个问题,就意味着要遍历完整的历史记录(通常有 11k+ 条签到),而每小时调用次数上限又很低,约为 100 次。这些工具会维护一个 SQLite 镜像,让查询可以即时、离线完成,且不产生任何 API 调用。该镜像是一个本地 SQLite 文件(node:sqlite,默认路径为 ~/.untappd-mcp/checkins.db,可通过 UNTAPPD_CACHE_DB / UNTAPPD_CACHE_DIR 覆盖)。存储层可注入,因此其他部署可以采用不同的后端,而无需更改工具。

持久化。 文件在重启后仍会保留;如果补全因限流、崩溃或主机退出而中断,下次同步时会从保存的偏移量继续。该文件使用 WAL 模式,设置了忙碌超时,并将每次写入都放在事务中,因此两台主机可以同时打开并共享它。架构带有版本号(PRAGMA user_version),且只会向前迁移,通过添加列和表进行升级,因此升级绝不会清空数据。untappd_cache_status 会显示解析后的路径、文件大小、架构版本和各用户的覆盖情况。 两个同步来源会填充缓存:

  • untappd_sync_user_beers 分页读取 user/beers,获取用户的完整去重啤酒列表(数千条,而非数万条签到记录)。 这是获取完整“喝过”覆盖范围的最省力方式,而且不同于 user/checkins,它能为任何公开账号或好友账号完整分页。查询是否喝过时,请从这里开始。
  • untappd_sync_checkins 分页读取 user/checkins,获取详细签到信息 (场所、日期、评论)。只有你自己的账号能完整分页;对于其他任何人,Untappd 只会返回最近约 50 条记录,且不会继续分页。该工具会将此情况报告为 history_truncated(绝不会错误地声称 backfill_complete)。 传入 force_backfill: true 可重置错误标记为已完成的缓存,并从最新记录重新分页(保留缓存行)。需要查看近期场所/日期详情时使用此工具;需要覆盖范围时使用 untappd_sync_user_beers。

两者都支持断点续传:每次调用获取 max_pages 页(默认值为 10),每页完成后都会保存进度,并在尚未完成时设置 another_run_needed: true——只需反复调用,直到其值变为 false。

查询缓存时不会再调用 API。“喝过”类工具会查询两个来源(任一来源命中即算喝过):

  • untappd_cache_has_had — 按精确的 bid 或不区分大小写的 beer_name 子字符串查询用户是否喝过某款啤酒;返回数量、最高评分、最近日期、匹配来源以及任何详细签到信息。
  • untappd_cache_has_had_many — 一次调用即可交叉核对整份 bids 列表(例如某个场所的菜单)→ 返回每款啤酒是否喝过。
  • untappd_cache_not_had — 给定一份 bids 列表,仅返回用户没喝过的啤酒——用于筛选“这份菜单上有哪些我没喝过的?”。
  • untappd_top_not_had — 从一份 bids 列表中,返回按 Untappd 全局评分排名的前 N 款未喝过啤酒,并可选用 style 筛选(用于回答“这份生啤酒单上该点什么?”)。未喝过的筛选仅查询缓存;啤酒评分来自元数据缓存(beer_meta),该缓存会由 untappd_beer_info / untappd_search_beer 适时填充,并且仅在缓存未命中或条目超过 30 天时通过 beer/info 补充——每次运行最多调用 api_budget 次(默认值为 25);需要更多调用时会返回 partial: true / another_run_needed: true。
  • untappd_cache_query — 按啤酒厂、风格、min_rating、场所和/或日期范围筛选缓存的签到记录,并支持排序和设置数量上限。

每个读取结果都包含一个 freshness 区块,分别报告每个来源的完整度(checkins.backfill_complete / history_truncated、beers.complete、各来源的百分比)以及 coverage_complete;在覆盖范围尚不完整时,还会提供 caveat——因此,“未找到”可能是假阴性的情况会被标出,直到相关同步完成。

同步其他用户的数据会通过与 untappd_user_checkins 相同的已认证端点,因此受 Untappd 的隐私规则约束:仅当该账号是公开账号或你的好友时才可使用。否则,同步会返回明确错误,提示你先添加对方为好友。

**保留期限。**缓存中的内容不会过期:同步的行——包括其他用户带日期的签到记录、评论和场所——都会一直保留,直到你将其删除。该文件创建时仅所有者可访问(目录权限为 0700,数据库权限为 0600,并且打开时会收紧旧安装的权限)。untappd_cache_forget 会在确认后删除某个用户的缓存签到记录、去重啤酒列表和同步状态(预览会显示用户名和行数);它只会操作本地缓存,不会修改 Untappd,之后可以再次同步以重新获取数据。删除该文件即可移除所有内容。

缓存仅包含其所属账号获准获取的签到记录。untappd_healthcheck 会报告运行版本和确切的工具集(数量 + 名称 + 稳定哈希),以便你确认当前提供服务的构建版本。

开发

npm install
npm run build
npm test

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。