untappd-mcp 2.5.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bundle.js +276 -151
- package/dist/cache/db.js +62 -14
- package/dist/tools/beer.js +4 -0
- package/dist/tools/brewery.js +4 -0
- package/dist/tools/cache.js +15 -0
- package/dist/tools/checkin.js +6 -0
- package/dist/tools/discover.js +4 -0
- package/dist/tools/favorites.js +4 -0
- package/dist/tools/feed.js +17 -2
- package/dist/tools/friends.js +6 -0
- package/dist/tools/hints.js +42 -0
- package/dist/tools/menu.js +3 -0
- package/dist/tools/resolve.js +3 -0
- package/dist/tools/trip.js +2 -0
- package/dist/tools/user.js +9 -0
- package/dist/tools/venue.js +6 -0
- package/dist/tools/wishlist.js +3 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/untappd-mcp/SKILL.md +28 -0
package/dist/tools/user.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { foodMeta } from './hints.js';
|
|
2
3
|
import { createHelpfulError, minifiedResult, resolveView, toolAnnotations, viewParam, viewResult } from '@chrischall/mcp-utils';
|
|
3
4
|
import { compactCheckins, compactWishlist, compactUserBeers, UNTAPPD_VIEWS, upstreamCompact } from '../compact.js';
|
|
4
5
|
const UsernameArg = z
|
|
@@ -43,6 +44,7 @@ export function registerUserTools(server, client) {
|
|
|
43
44
|
description: "Get an Untappd user's profile: bio, location, total check-ins, distinct beers, badges, and stats. " +
|
|
44
45
|
'Omit username for your own account. Read-only.',
|
|
45
46
|
annotations: toolAnnotations({ title: 'Get Untappd user profile', readOnly: true, idempotent: true, openWorld: true }),
|
|
47
|
+
_meta: foodMeta('lookup'),
|
|
46
48
|
inputSchema: z.object({
|
|
47
49
|
username: UsernameArg,
|
|
48
50
|
view: viewParam(UNTAPPD_VIEWS, { note: 'compact also asks Untappd for its own slim record, dropping the embedded activity/list blocks; \'full\' returns everything.' }),
|
|
@@ -59,6 +61,7 @@ export function registerUserTools(server, client) {
|
|
|
59
61
|
'any other user Untappd returns just the ~50 most recent check-ins and ignores max_id — use untappd_user_beers ' +
|
|
60
62
|
'(has had) or untappd_user_venues (has been) for their full history. Omit username for your own. Read-only.',
|
|
61
63
|
annotations: toolAnnotations({ title: 'Get Untappd user check-ins', readOnly: true, idempotent: true, openWorld: true }),
|
|
64
|
+
_meta: foodMeta('lookup'),
|
|
62
65
|
inputSchema: z.object({
|
|
63
66
|
username: UsernameArg,
|
|
64
67
|
limit: z.number().int().min(1).max(50).optional().describe('Max check-ins (1–50, default 25)'),
|
|
@@ -74,6 +77,7 @@ export function registerUserTools(server, client) {
|
|
|
74
77
|
title: 'Get Untappd user wishlist',
|
|
75
78
|
description: "Get the beers on a user's wishlist. Supports sorting and paging. Omit username for your own account. Read-only.",
|
|
76
79
|
annotations: toolAnnotations({ title: 'Get Untappd user wishlist', readOnly: true, idempotent: true, openWorld: true }),
|
|
80
|
+
_meta: foodMeta('lookup', { aliases: ['wishlist', 'beers to try'] }),
|
|
77
81
|
inputSchema: z.object({
|
|
78
82
|
username: UsernameArg,
|
|
79
83
|
limit: z.number().int().min(1).max(50).optional().describe('Max beers (1–50, default 25)'),
|
|
@@ -94,6 +98,7 @@ export function registerUserTools(server, client) {
|
|
|
94
98
|
description: "Get the distinct (unique) beers a user has ever checked in, with their rating and check-in count per beer. " +
|
|
95
99
|
'Supports sorting and paging. Omit username for your own account. Read-only.',
|
|
96
100
|
annotations: toolAnnotations({ title: 'Get Untappd distinct beers', readOnly: true, idempotent: true, openWorld: true }),
|
|
101
|
+
_meta: foodMeta('lookup'),
|
|
97
102
|
inputSchema: z.object({
|
|
98
103
|
username: UsernameArg,
|
|
99
104
|
limit: z.number().int().min(1).max(50).optional().describe('Max beers (1–50, default 25)'),
|
|
@@ -113,6 +118,7 @@ export function registerUserTools(server, client) {
|
|
|
113
118
|
title: 'Get Untappd user badges',
|
|
114
119
|
description: "Get the badges a user has earned, most recent first. Omit username for your own account. Read-only.",
|
|
115
120
|
annotations: toolAnnotations({ title: 'Get Untappd user badges', readOnly: true, idempotent: true, openWorld: true }),
|
|
121
|
+
_meta: foodMeta('lookup'),
|
|
116
122
|
inputSchema: z.object({
|
|
117
123
|
username: UsernameArg,
|
|
118
124
|
limit: z.number().int().min(1).max(50).optional().describe('Max badges (1–50, default 25)'),
|
|
@@ -126,6 +132,7 @@ export function registerUserTools(server, client) {
|
|
|
126
132
|
title: 'Get Untappd user friends',
|
|
127
133
|
description: "Get a user's friend list. Omit username for your own account. Read-only.",
|
|
128
134
|
annotations: toolAnnotations({ title: 'Get Untappd user friends', readOnly: true, idempotent: true, openWorld: true }),
|
|
135
|
+
_meta: foodMeta('lookup'),
|
|
129
136
|
inputSchema: z.object({
|
|
130
137
|
username: UsernameArg,
|
|
131
138
|
limit: z.number().int().min(1).max(50).optional().describe('Max friends (1–50, default 25)'),
|
|
@@ -142,6 +149,7 @@ export function registerUserTools(server, client) {
|
|
|
142
149
|
'other users). Untappd returns at most 25 venues per page whatever limit you ask for — advance by the ' +
|
|
143
150
|
"returned pagination offset. Omit username for your own account. Read-only.",
|
|
144
151
|
annotations: toolAnnotations({ title: 'Get venues a user has checked in at', readOnly: true, idempotent: true, openWorld: true }),
|
|
152
|
+
_meta: foodMeta('lookup'),
|
|
145
153
|
inputSchema: z.object({
|
|
146
154
|
username: UsernameArg,
|
|
147
155
|
limit: z.number().int().min(1).max(50).optional().describe('Max venues (1–50; Untappd caps a page at 25)'),
|
|
@@ -166,6 +174,7 @@ export function registerUserTools(server, client) {
|
|
|
166
174
|
description: 'Get the incoming friend requests waiting on YOUR account — the users who have requested to be your ' +
|
|
167
175
|
'friend. Read-only.',
|
|
168
176
|
annotations: toolAnnotations({ title: 'Get your pending friend requests', readOnly: true, idempotent: true, openWorld: true }),
|
|
177
|
+
_meta: foodMeta('lookup'),
|
|
169
178
|
inputSchema: z.object({
|
|
170
179
|
limit: z.number().int().min(1).max(50).optional().describe('Max requests (1–50, default 25)'),
|
|
171
180
|
offset: z.number().int().min(0).optional().describe('Result offset for paging (default 0)'),
|
package/dist/tools/venue.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { foodMeta } from './hints.js';
|
|
2
3
|
import { minifiedResult, resolveView, toolAnnotations, viewParam, viewResult } from '@chrischall/mcp-utils';
|
|
3
4
|
import { compactCheckins, UNTAPPD_VIEWS, upstreamCompact } from '../compact.js';
|
|
4
5
|
import { walkVenueMenu } from '../menu/venue-menu.js';
|
|
@@ -19,6 +20,7 @@ export function registerVenueTools(server, client, cacheProvider) {
|
|
|
19
20
|
description: 'Search Untappd for venues (bars, breweries, restaurants) by name. Returns matches with their venue id, ' +
|
|
20
21
|
'category, and location. Feed a venue id into untappd_venue_info for full detail. Read-only.',
|
|
21
22
|
annotations: toolAnnotations({ title: 'Search Untappd venues', readOnly: true, idempotent: true, openWorld: true }),
|
|
23
|
+
_meta: foodMeta('lookup', { aliases: ['bar', 'pub', 'taproom', 'brewpub'] }),
|
|
22
24
|
inputSchema: z.object({
|
|
23
25
|
query: z.string().min(1).describe('Venue name to search for'),
|
|
24
26
|
limit: z.number().int().min(1).max(50).optional().describe('Max results (1–50, default 25)'),
|
|
@@ -32,6 +34,7 @@ export function registerVenueTools(server, client, cacheProvider) {
|
|
|
32
34
|
description: 'Get full detail for a venue by its Untappd venue id: category, address, contact, rating, total check-ins, ' +
|
|
33
35
|
'and — on view:"full" — top beers and recent activity. Get an id from untappd_search_venue. Read-only.',
|
|
34
36
|
annotations: toolAnnotations({ title: 'Get Untappd venue detail', readOnly: true, idempotent: true, openWorld: true }),
|
|
37
|
+
_meta: foodMeta('lookup'),
|
|
35
38
|
inputSchema: z.object({
|
|
36
39
|
venue_id: z.number().int().positive().describe('Untappd venue id'),
|
|
37
40
|
view: viewParam(UNTAPPD_VIEWS, { note: 'compact also asks Untappd for its own slim record, dropping the embedded activity/list blocks; "full" returns everything.' }),
|
|
@@ -58,6 +61,7 @@ export function registerVenueTools(server, client, cacheProvider) {
|
|
|
58
61
|
'with untappd_favorite_add; default your account) — applied on read, so never stale. Get an id from ' +
|
|
59
62
|
'untappd_search_venue. Read-only.',
|
|
60
63
|
annotations: toolAnnotations({ title: "Get a venue's verified beer menu (section-paged)", readOnly: true, idempotent: true, openWorld: true }),
|
|
64
|
+
_meta: foodMeta('lookup', { aliases: ['on tap', 'tap list', 'beer menu'] }),
|
|
61
65
|
inputSchema: z.object({
|
|
62
66
|
venue_id: z.number().int().positive().describe('Untappd venue id'),
|
|
63
67
|
menu_id: z.number().int().positive().optional().describe('Restrict to a single menu id (from a prior result). Optional.'),
|
|
@@ -137,6 +141,7 @@ export function registerVenueTools(server, client, cacheProvider) {
|
|
|
137
141
|
description: 'Resolve a Foursquare venue id to its Untappd venue. Useful to turn a foursquare_id (e.g. from a check-in) ' +
|
|
138
142
|
'into an Untappd venue you can pass to untappd_venue_info / untappd_venue_activity. Read-only.',
|
|
139
143
|
annotations: toolAnnotations({ title: 'Look up an Untappd venue by Foursquare id', readOnly: true, idempotent: true, openWorld: true }),
|
|
144
|
+
_meta: foodMeta('lookup'),
|
|
140
145
|
inputSchema: z.object({
|
|
141
146
|
foursquare_id: z.string().min(1).describe('Foursquare venue id'),
|
|
142
147
|
}),
|
|
@@ -150,6 +155,7 @@ export function registerVenueTools(server, client, cacheProvider) {
|
|
|
150
155
|
'Page backwards with max_id (the pagination.max_id from a prior call). Cached locally for ' + TTL_HOURS + ' hours ' +
|
|
151
156
|
'per venue + limit + max_id (cache.hit, fetched_at, expires_at); refresh:true re-fetches. Read-only.',
|
|
152
157
|
annotations: toolAnnotations({ title: 'Get recent check-ins at a venue', readOnly: true, idempotent: false, openWorld: true }),
|
|
158
|
+
_meta: foodMeta('lookup'),
|
|
153
159
|
inputSchema: z.object({
|
|
154
160
|
venue_id: z.number().int().positive().describe('Untappd venue id'),
|
|
155
161
|
limit: z.number().int().min(1).max(50).optional().describe('Max check-ins (1–50, default 25)'),
|
package/dist/tools/wishlist.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { foodMeta } from './hints.js';
|
|
2
3
|
import { confirmTokenParam, minifiedResult, toolAnnotations } from '@chrischall/mcp-utils';
|
|
3
4
|
import { CONFIRM_FLOW, confirmWrite } from './confirm.js';
|
|
4
5
|
const BidSchema = z.number().int().positive().describe('Untappd beer id (bid) — from untappd_search_beer');
|
|
@@ -7,6 +8,7 @@ export function registerWishlistTools(server, client) {
|
|
|
7
8
|
title: 'Add a beer to your wishlist',
|
|
8
9
|
description: `Add a beer to YOUR Untappd wishlist by its bid. Writes to your account. ${CONFIRM_FLOW}`,
|
|
9
10
|
annotations: toolAnnotations({ title: 'Add a beer to your wishlist', readOnly: false, idempotent: true, openWorld: true, destructive: false }),
|
|
11
|
+
_meta: foodMeta('change', { aliases: ['want to try', 'save a beer'] }),
|
|
10
12
|
inputSchema: z.object({
|
|
11
13
|
bid: BidSchema,
|
|
12
14
|
confirmToken: confirmTokenParam,
|
|
@@ -33,6 +35,7 @@ export function registerWishlistTools(server, client) {
|
|
|
33
35
|
title: 'Remove a beer from your wishlist',
|
|
34
36
|
description: `Remove a beer from YOUR Untappd wishlist by its bid. Writes to your account. ${CONFIRM_FLOW}`,
|
|
35
37
|
annotations: toolAnnotations({ title: 'Remove a beer from your wishlist', readOnly: false, idempotent: true, openWorld: true, destructive: false }),
|
|
38
|
+
_meta: foodMeta('change'),
|
|
36
39
|
inputSchema: z.object({
|
|
37
40
|
bid: BidSchema,
|
|
38
41
|
confirmToken: confirmTokenParam,
|
package/dist/version.js
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
// json's `extra-files`), and `versionSyncTest` guards that it stays equal to
|
|
4
4
|
// package.json. Import VERSION wherever the version is needed rather than
|
|
5
5
|
// re-declaring it.
|
|
6
|
-
export const VERSION = '2.
|
|
6
|
+
export const VERSION = '2.6.0'; // x-release-please-version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "untappd-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"mcpName": "io.github.chrischall/untappd-mcp",
|
|
5
5
|
"description": "Untappd MCP server for Claude — developed and maintained by AI (Claude Code)",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/untappd-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.6.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "untappd-mcp",
|
|
14
|
-
"version": "2.
|
|
14
|
+
"version": "2.6.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -73,6 +73,12 @@ answer, wrong reason. **The same silence bites the other way**: `compact:
|
|
|
73
73
|
false` no longer buys you the full record either. It is dropped identically
|
|
74
74
|
and you still get compact. `view: "full"` is the only way back.
|
|
75
75
|
|
|
76
|
+
`untappd_activity_feed` also takes a third rung, `view: "brief"`: one line per
|
|
77
|
+
check-in, `<user>: <beer> (<brewery>) at <venue> — <ISO date | no date> —
|
|
78
|
+
<rated N | not rated>`, each at most 120 characters. It is the shape a
|
|
79
|
+
context-group host reads aloud (below); use it when you only need to say what
|
|
80
|
+
friends have been drinking.
|
|
81
|
+
|
|
76
82
|
Compact comes in two tiers here, and they are not the same thing:
|
|
77
83
|
|
|
78
84
|
**A hand-written field projection — 9 tools.**
|
|
@@ -245,3 +251,25 @@ owner-only (dir `0700`, file `0600`).
|
|
|
245
251
|
Every cache read returns a `freshness` block reporting each source's completeness
|
|
246
252
|
separately (plus `coverage_complete` and a `caveat` when incomplete), so you can
|
|
247
253
|
flag a "not found" as possibly a false negative until the relevant sync finishes.
|
|
254
|
+
|
|
255
|
+
## In context groups
|
|
256
|
+
|
|
257
|
+
On an mcp-host connector this server is a member of the **food** group (every
|
|
258
|
+
tool but `untappd_healthcheck` carries a `_meta.mint` hint with
|
|
259
|
+
`domain: "food"`). Its one canonical read is **`untappd_activity_feed`** for
|
|
260
|
+
**what's new** — the friend feed, answered with `view: "brief"`. It takes no
|
|
261
|
+
subject and no date range: Untappd has no listable set of the person's own
|
|
262
|
+
subjects (no children, properties or accounts to fan out over), so the group
|
|
263
|
+
asks it once, not once per person. Nothing here answers what's due or what's
|
|
264
|
+
on; every other read is a `lookup` (`untappd_notifications` is a non-canonical
|
|
265
|
+
what's-new), and the writes are `send` (toast, comment, friend request),
|
|
266
|
+
`change` (check-in, deletes, wishlist, favourites, friend accept/reject/remove)
|
|
267
|
+
or `other` (the cache syncs and the pickers, which refresh the local cache as
|
|
268
|
+
they read).
|
|
269
|
+
|
|
270
|
+
A compound ask looks like *"what's new with food — any new reservations, and
|
|
271
|
+
what have my friends been drinking?"*: the host answers the Untappd half from
|
|
272
|
+
the feed's brief lines beside the group's other members. This server calls the
|
|
273
|
+
Untappd API directly, so it never answers "needs your browser open"; if it is
|
|
274
|
+
not configured, the group reports it as unable to answer and
|
|
275
|
+
`untappd_healthcheck` says which credential is missing.
|