关于 untappd-mcp
面向啤酒爱好者的 Untappd MCP 服务,可查询啤酒、场所和动态,管理签到与愿望清单,并从菜单中挑选未喝过的啤酒。它通过 Untappd 私有移动 API 工作,并将签到记录缓存在本地 SQLite 中。
主要功能
- 01搜索啤酒、酒厂和场所
- 02读取用户资料、动态和愿望清单
- 03经确认后发布签到、举杯和评论
- 04将签到历史缓存到 SQLite
- 05从场所菜单筛选未喝过的啤酒
适用场景
- 01按条件查找啤酒和酒厂
- 02查看用户的签到记录或愿望清单
- 03从酒单中挑选未喝过的啤酒
- 04确认内容后发布签到
MCP 相关
将 Untappd 的读取和写入操作作为 MCP 工具提供。所有写入都需要确认,本地 SQLite 缓存用于查询签到历史。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y untappd-mcpUNTAPPD_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+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
46收录的工具列表 · 更新于 4 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| sort | enum | 否 | 排序顺序:checkin(相关度,默认)、name 或 countcheckin | name | count |
| view | enum | 否 | 响应形式:"compact"(默认)会省略响应中其他位置已有的字段;"full" 会返回此服务端支持的所有字段。compact 会将每个匹配项投影为 {bid, name, style, abv, ibu, brewery, checkin_count, have_had};"full" 会返回 Untappd 完整的约 1.2 KB 搜索项,包括较长的 beer_description 和嵌套的 brewery 记录。compact | full |
| limit | integer | 否 | 最大结果数(1–50,默认 25) |
| query | string | 是 | 要搜索的啤酒名称 |
| offset | integer | 否 | 分页结果偏移量(默认值: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_MODE | ask-user | 在无法显示确认提示词的客户端(claude.ai、Claude Desktop)上,写入操作的处理方式。ask-user:分两步执行——第一次调用不会执行任何操作,而是返回预览和令牌;模型必须先在聊天中获得你的批准,然后才能使用该令牌再次调用。auto:同样分两步执行,但模型自行查看预览后即可使用令牌。refuse:此类客户端上的写入操作会被拒绝。能够显示提示词的客户端(Claude Code)始终会显示实际提示词,除非 MCP_CONFIRM_ELICITATION=off。无法识别的值会按 refuse 处理。 |
MCP_CONFIRM_ELICITATION | on | off 表示永不显示确认提示词,因此所有客户端都会遵循 MCP_CONFIRM_MODE 的设置。对于声称能够显示提示词但实际上从不显示的客户端,请设置此项(写入操作会卡住——opencode 2.0.x)。任何其他值都会按 on 处理,并在 stderr 上发出警告。 |
MCP_CONFIRM_TTL_SECONDS | 600 | 令牌的有效时长。 |
MCP_CONFIRM_SECRET | 每个进程随机生成 | 签名密钥;仅当令牌必须在服务重启后继续有效时才设置。 |
获取客户端 ID / 密钥
Untappd 不会公开这些信息;它们存储在移动应用中。通过 HTTPS 代理捕获你自己的应用流量来获取:
- 安装 mitmproxy 等代理,并在运行 Untappd 应用的设备上信任其 CA 证书。
- 将设备(或者,在运行 iPad 应用的 Apple 芯片 Mac 上,将 Mac 的系统 HTTP/HTTPS 代理)指向该代理。
- 打开 Untappd 并登录。找到
POST https://api.untappd.com/v4/xauth请求——其查询字符串中包含client_id和client_secret。 - 将它们填入
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
}
一次调用即可:
- 更新缓存。 如果用户的不同啤酒列表已经完整,就会增量刷新,并在遇到第一款已缓存且未变化的啤酒时停止(通常只需一页)。如果列表为空或不完整,则会在同一次调用中补全,直到完成或用尽
api_budget(默认 60 次调用,由每个步骤共享)。无需先调用同步工具。 - 将每一行匹配到一款啤酒。 会剥除价格、规格、ABV 和标点,搜索 Untappd(由于 Untappd 搜索会匹配每个词,因此在未找到时会去掉风格词再搜索),并根据酒厂匹配度、啤酒名称匹配度(因此“Homestyle”不会被误读为“Double Homestyle”)、签到量(规范条目优先于只有 3 次签到的重复条目)以及
venue_city为候选项评分。最多同时运行 4 次搜索,遇到 429 会退避重试。匹配结果会缓存在磁盘上,因此下次访问同一场所时无需搜索。 - 处理泛称条目。
<Brewery> SEASONAL/ROTATING会转为该酒厂可能符合季节性特征的啤酒列表,标记为needs_confirmation。没有酒厂的条目(ROTATING CIDER)会返回unresolved: "ask bartender"。 - 将每款啤酒与缓存核对(检查两个来源,与
has_had_many一样)。 - 根据全球评分排列未喝过的啤酒,应用
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
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。