{
  "markdown": "# untappd-mcp\n\nAn MCP server for [Untappd](https://untappd.com). It talks to Untappd's mobile\n(v4) API using your own account — search beers, breweries, and venues; read\nprofiles, check-ins, wishlists, distinct beers, badges, friends, and your friend\nactivity feed; and post check-ins, toasts, and comments.\n\n> Developed and maintained by AI (Claude Code). Use at your own discretion. This\n> is an unofficial client that uses Untappd's private mobile API; it is not\n> affiliated with or endorsed by Untappd.\n\n## How it works\n\nUntappd's iPad/iPhone app authenticates with a username/password **xauth** login\n(`POST https://api.untappd.com/v4/xauth`) that returns an access token, then\ncalls the v4 API. This server reproduces that exactly:\n\n- Reads carry the token as an `access_token` query param.\n- Writes carry it as an `Authorization: Bearer` header (with the app's client\n  credentials in the query), matching the app's real requests.\n\nThe token is fetched on demand, cached in memory, and refreshed automatically if\nit goes stale.\n\n## Configuration\n\n| Variable | Required | Description |\n| --- | --- | --- |\n| `UNTAPPD_ACCESS_TOKEN` | no | An access token you already hold. Supply this and **no password is needed** — the xauth login is skipped entirely. |\n| `UNTAPPD_USERNAME` | if no token | Your Untappd username or login email. |\n| `UNTAPPD_PASSWORD` | if no token | Your Untappd password (used only for the xauth login that mints a token). |\n| `UNTAPPD_CLIENT_ID` | yes | The Untappd mobile app client id (see below). |\n| `UNTAPPD_CLIENT_SECRET` | yes | The Untappd mobile app client secret. |\n| `UNTAPPD_DEVICE_ID` | no | Stable device UUID the token is keyed to (a default is provided). |\n| `UNTAPPD_UTV` | no | API version param (default `4.0.0`). |\n| `UNTAPPD_USER_AGENT` | no | Override the User-Agent (default mimics the app). |\n| `UNTAPPD_CACHE_DB` | no | Path to the local check-in cache SQLite file (default `~/.untappd-mcp/checkins.db`). Local/stdio only. |\n\nCopy `.env.example` to `.env` and fill it in for local use.\n\n### Obtaining the client id / secret\n\nUntappd does not publish these; they live in the mobile app. Capture them from\nyour own app's traffic with an HTTPS proxy:\n\n1. Install a proxy such as [mitmproxy](https://mitmproxy.org) and trust its CA\n   certificate on the device running the Untappd app.\n2. Point the device (or, on an Apple-silicon Mac running the iPad app, the Mac's\n   system HTTP/HTTPS proxy) at the proxy.\n3. Open Untappd and sign in. Find the `POST https://api.untappd.com/v4/xauth`\n   request — its **query string** contains `client_id` and `client_secret`.\n4. Put those into `UNTAPPD_CLIENT_ID` / `UNTAPPD_CLIENT_SECRET`.\n\nKeep these values private; do not commit them.\n\n## Tools\n\nReads: `untappd_search_beer`, `untappd_beer_info`, `untappd_beer_activity`,\n`untappd_search_brewery`, `untappd_brewery_info`, `untappd_brewery_beers`,\n`untappd_search_venue`, `untappd_venue_info`, `untappd_venue_activity`,\n`untappd_user_info`, `untappd_user_checkins`, `untappd_user_wishlist`,\n`untappd_user_beers`, `untappd_user_badges`, `untappd_user_friends`, `untappd_pending_friends`,\n`untappd_activity_feed`, `untappd_checkin_info`, `untappd_resolve`, `untappd_open_url`, `untappd_user_venues`, `untappd_venue_by_foursquare`, `untappd_trending`,\n`untappd_notifications`, `untappd_local_checkins`, `untappd_healthcheck`.\n\nWrites (confirm-gated — return a dry-run preview unless called with\n`confirm: true`): `untappd_toast`, `untappd_add_comment`, `untappd_delete_comment`, `untappd_checkin`,\n`untappd_wishlist_add`, `untappd_wishlist_remove`, `untappd_delete_checkin`,\n`untappd_add_friend`, `untappd_accept_friend`, `untappd_reject_friend`, `untappd_remove_friend`.\n\nCheck-in cache: `untappd_sync_checkins`, `untappd_sync_user_beers`,\n`untappd_cache_has_had`, `untappd_cache_has_had_many`, `untappd_cache_not_had`,\n`untappd_cache_query`, `untappd_top_not_had`.\n\n## Check-in cache\n\nThe Untappd API only exposes paged lists (50 per page) and has **no** \"has this\nuser ever had beer X?\" lookup — answering that from the API alone means paging an\nentire history (often 11k+ check-ins) against a tight ~100-calls/hour rate limit.\nThese tools maintain a SQLite mirror so the question is answered instantly,\noffline, with zero API calls. The mirror is a local file\n(`node:sqlite`, path via `UNTAPPD_CACHE_DB`); the store is injectable, so another\ndeployment can back it differently without the tools changing.\n\nTwo sync sources fill the cache:\n\n- **`untappd_sync_user_beers`** pages `user/beers` — the user's **complete\n  distinct-beers list** (thousands of rows, not tens of thousands of check-ins).\n  This is the **cheapest way to get full \"has had\" coverage** and, unlike\n  `user/checkins`, it pages fully for any public/friend account. **Start here**\n  for has-had questions.\n- **`untappd_sync_checkins`** pages `user/checkins` for detailed check-ins\n  (venue, date, comment). Only your **own** account pages fully — Untappd returns\n  just the ~50 most recent for anyone else and won't page further, which the tool\n  reports as `history_truncated` (it never falsely claims `backfill_complete`).\n  Pass `force_backfill: true` to reset a cache wrongly marked complete and\n  re-page from newest (cached rows are kept). Use this for recent venue/date\n  detail; use `untappd_sync_user_beers` for coverage.\n\nBoth are resumable: they fetch `max_pages` per call (default 10), persist\nprogress after every page, and set `another_run_needed: true` until done — just\ncall again until it's `false`.\n\n**Query** the cache with no further API calls. The has-had tools consult **both**\nsources (a hit in either counts as had):\n\n- `untappd_cache_has_had` — has the user had a beer, by exact `bid` or a\n  case-insensitive `beer_name` substring; returns count, best rating, last date,\n  matching sources, and any detailed check-ins.\n- `untappd_cache_has_had_many` — cross-check a whole list of `bids` in one call\n  (e.g. a venue's menu) → had/not-had per beer.\n- `untappd_cache_not_had` — given a list of `bids`, return just the ones the user\n  has **not** had — the \"what's new to me on this menu?\" filter.\n- `untappd_top_not_had` — from a list of `bids`, return the **top N not-had\n  beers ranked by Untappd global rating**, with an optional `style` filter (the\n  \"what should I order off this tap list?\" tool). Not-had filtering is\n  cache-only; beer ratings come from a metadata cache (`beer_meta`) that's seeded\n  opportunistically by `untappd_beer_info` / `untappd_search_beer` and topped up\n  via `beer/info` only on a cache miss or entries older than 30 days — capped at\n  `api_budget` calls/run (default 25), returning `partial: true` /\n  `another_run_needed: true` when more are needed.\n- `untappd_cache_query` — filter cached **check-ins** by brewery, style,\n  `min_rating`, venue, and/or date range, with sorting and a limit.\n\nEvery read result carries a `freshness` block that reports **each source's\ncompleteness separately** (`checkins.backfill_complete` / `history_truncated`,\n`beers.complete`, per-source percentages) plus `coverage_complete`, and a\n`caveat` while coverage is incomplete — so a \"not found\" can be flagged as\npossibly a false negative until the relevant sync finishes.\n\nSyncing **another** user goes through the same authed endpoint as\n`untappd_user_checkins`, so Untappd's privacy rules apply: it only works if that\naccount is public or your friend. Otherwise the sync returns a clear error\ntelling you to add them as a friend first.\n\nA cache holds only the check-ins the account it belongs to was allowed to\nfetch. `untappd_healthcheck` reports the running version and the exact tool set\n(count + names + a stable hash), so you can confirm which build is serving.\n\n## Development\n\n```sh\nnpm install\nnpm run build\nnpm test\n```\n\n## License\n\nMIT\n",
  "bytes": 7798,
  "sha": "86d7a7f84e0c9778d483c27c7aae7bcc1f2c8054037bef59dc55d7a9de848119",
  "repo_slug": "chrischall/untappd-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_chrischall_untappd_mcp_7e07221f/readme"
}