untappd-mcp 2.5.2 → 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 +238 -141
- 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/bundle.js
CHANGED
|
@@ -282,14 +282,14 @@ var require_util = __commonJS({
|
|
|
282
282
|
}
|
|
283
283
|
function randomString2(length = 10) {
|
|
284
284
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
285
|
-
let
|
|
285
|
+
let str3 = "";
|
|
286
286
|
for (let i = 0; i < length; i++) {
|
|
287
|
-
|
|
287
|
+
str3 += chars[Math.floor(Math.random() * chars.length)];
|
|
288
288
|
}
|
|
289
|
-
return
|
|
289
|
+
return str3;
|
|
290
290
|
}
|
|
291
|
-
function esc2(
|
|
292
|
-
return JSON.stringify(
|
|
291
|
+
function esc2(str3) {
|
|
292
|
+
return JSON.stringify(str3);
|
|
293
293
|
}
|
|
294
294
|
function slugify2(input2) {
|
|
295
295
|
return input2.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -404,8 +404,8 @@ var require_util = __commonJS({
|
|
|
404
404
|
"symbol",
|
|
405
405
|
"undefined"
|
|
406
406
|
]);
|
|
407
|
-
function escapeRegex2(
|
|
408
|
-
return
|
|
407
|
+
function escapeRegex2(str3) {
|
|
408
|
+
return str3.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
409
409
|
}
|
|
410
410
|
function clone2(inst, def, params) {
|
|
411
411
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -671,13 +671,13 @@ var require_util = __commonJS({
|
|
|
671
671
|
return "unknown";
|
|
672
672
|
}
|
|
673
673
|
var highSurrogate2 = /[\uD800-\uDBFF]/;
|
|
674
|
-
function codePointLength2(
|
|
675
|
-
const units =
|
|
676
|
-
if (!highSurrogate2.test(
|
|
674
|
+
function codePointLength2(str3) {
|
|
675
|
+
const units = str3.length;
|
|
676
|
+
if (!highSurrogate2.test(str3))
|
|
677
677
|
return units;
|
|
678
678
|
let count2 = units;
|
|
679
679
|
for (let i = 0; i < units - 1; i++) {
|
|
680
|
-
if ((
|
|
680
|
+
if ((str3.charCodeAt(i) & 64512) === 55296 && (str3.charCodeAt(i + 1) & 64512) === 56320) {
|
|
681
681
|
count2--;
|
|
682
682
|
i++;
|
|
683
683
|
}
|
|
@@ -29160,7 +29160,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
29160
29160
|
}
|
|
29161
29161
|
exports._ = _;
|
|
29162
29162
|
const plus = new _Code("+");
|
|
29163
|
-
function
|
|
29163
|
+
function str3(strs, ...args) {
|
|
29164
29164
|
const expr = [safeStringify(strs[0])];
|
|
29165
29165
|
let i = 0;
|
|
29166
29166
|
while (i < args.length) {
|
|
@@ -29171,7 +29171,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
29171
29171
|
optimize(expr);
|
|
29172
29172
|
return new _Code(expr);
|
|
29173
29173
|
}
|
|
29174
|
-
exports.str =
|
|
29174
|
+
exports.str = str3;
|
|
29175
29175
|
function addCodeArg(code2, arg) {
|
|
29176
29176
|
if (arg instanceof _Code) code2.push(...arg._items);
|
|
29177
29177
|
else if (arg instanceof Name) code2.push(arg);
|
|
@@ -29204,7 +29204,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
29204
29204
|
if (typeof b == "string" && b[0] === '"' && !(a instanceof Name)) return `"${a}${b.slice(1)}`;
|
|
29205
29205
|
}
|
|
29206
29206
|
function strConcat(c1, c2) {
|
|
29207
|
-
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 :
|
|
29207
|
+
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str3`${c1}${c2}`;
|
|
29208
29208
|
}
|
|
29209
29209
|
exports.strConcat = strConcat;
|
|
29210
29210
|
function interpolate(x) {
|
|
@@ -30083,21 +30083,21 @@ var require_util2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
30083
30083
|
return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
|
|
30084
30084
|
}
|
|
30085
30085
|
exports.schemaRefOrVal = schemaRefOrVal;
|
|
30086
|
-
function unescapeFragment(
|
|
30087
|
-
return unescapeJsonPointer(decodeURIComponent(
|
|
30086
|
+
function unescapeFragment(str3) {
|
|
30087
|
+
return unescapeJsonPointer(decodeURIComponent(str3));
|
|
30088
30088
|
}
|
|
30089
30089
|
exports.unescapeFragment = unescapeFragment;
|
|
30090
|
-
function escapeFragment(
|
|
30091
|
-
return encodeURIComponent(escapeJsonPointer(
|
|
30090
|
+
function escapeFragment(str3) {
|
|
30091
|
+
return encodeURIComponent(escapeJsonPointer(str3));
|
|
30092
30092
|
}
|
|
30093
30093
|
exports.escapeFragment = escapeFragment;
|
|
30094
|
-
function escapeJsonPointer(
|
|
30095
|
-
if (typeof
|
|
30096
|
-
return
|
|
30094
|
+
function escapeJsonPointer(str3) {
|
|
30095
|
+
if (typeof str3 == "number") return `${str3}`;
|
|
30096
|
+
return str3.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
30097
30097
|
}
|
|
30098
30098
|
exports.escapeJsonPointer = escapeJsonPointer;
|
|
30099
|
-
function unescapeJsonPointer(
|
|
30100
|
-
return
|
|
30099
|
+
function unescapeJsonPointer(str3) {
|
|
30100
|
+
return str3.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
30101
30101
|
}
|
|
30102
30102
|
exports.unescapeJsonPointer = unescapeJsonPointer;
|
|
30103
30103
|
function eachItem(xs, f) {
|
|
@@ -30983,8 +30983,8 @@ var require_json_schema_traverse = /* @__PURE__ */ __commonJSMin(((exports, modu
|
|
|
30983
30983
|
post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
|
|
30984
30984
|
}
|
|
30985
30985
|
}
|
|
30986
|
-
function escapeJsonPtr(
|
|
30987
|
-
return
|
|
30986
|
+
function escapeJsonPtr(str3) {
|
|
30987
|
+
return str3.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
30988
30988
|
}
|
|
30989
30989
|
}));
|
|
30990
30990
|
var require_resolve = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
@@ -31929,9 +31929,9 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
31929
31929
|
isIPV6: true
|
|
31930
31930
|
};
|
|
31931
31931
|
}
|
|
31932
|
-
function findToken(
|
|
31932
|
+
function findToken(str3, token) {
|
|
31933
31933
|
let ind = 0;
|
|
31934
|
-
for (let i = 0; i <
|
|
31934
|
+
for (let i = 0; i < str3.length; i++) if (str3[i] === token) ind++;
|
|
31935
31935
|
return ind;
|
|
31936
31936
|
}
|
|
31937
31937
|
function removeDotSegments(path) {
|
|
@@ -32746,7 +32746,7 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
32746
32746
|
const util_1 = require_util2();
|
|
32747
32747
|
const $dataRefSchema = require_data();
|
|
32748
32748
|
const uri_1 = require_uri();
|
|
32749
|
-
const defaultRegExp = (
|
|
32749
|
+
const defaultRegExp = (str3, flags) => new RegExp(str3, flags);
|
|
32750
32750
|
defaultRegExp.code = "new RegExp";
|
|
32751
32751
|
const META_IGNORE_OPTIONS = [
|
|
32752
32752
|
"removeAdditional",
|
|
@@ -33452,16 +33452,16 @@ var require_multipleOf = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
33452
33452
|
}));
|
|
33453
33453
|
var require_ucs2length = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
33454
33454
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33455
|
-
function ucs2length(
|
|
33456
|
-
const len =
|
|
33455
|
+
function ucs2length(str3) {
|
|
33456
|
+
const len = str3.length;
|
|
33457
33457
|
let length = 0;
|
|
33458
33458
|
let pos = 0;
|
|
33459
33459
|
let value;
|
|
33460
33460
|
while (pos < len) {
|
|
33461
33461
|
length++;
|
|
33462
|
-
value =
|
|
33462
|
+
value = str3.charCodeAt(pos++);
|
|
33463
33463
|
if (value >= 55296 && value <= 56319 && pos < len) {
|
|
33464
|
-
value =
|
|
33464
|
+
value = str3.charCodeAt(pos);
|
|
33465
33465
|
if ((value & 64512) === 56320) pos++;
|
|
33466
33466
|
}
|
|
33467
33467
|
}
|
|
@@ -36172,8 +36172,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
36172
36172
|
30,
|
|
36173
36173
|
31
|
|
36174
36174
|
];
|
|
36175
|
-
function date5(
|
|
36176
|
-
const matches = DATE.exec(
|
|
36175
|
+
function date5(str3) {
|
|
36176
|
+
const matches = DATE.exec(str3);
|
|
36177
36177
|
if (!matches) return false;
|
|
36178
36178
|
const year = +matches[1];
|
|
36179
36179
|
const month = +matches[2];
|
|
@@ -36188,8 +36188,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
36188
36188
|
}
|
|
36189
36189
|
const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
36190
36190
|
function getTime(strictTimeZone) {
|
|
36191
|
-
return function time3(
|
|
36192
|
-
const matches = TIME.exec(
|
|
36191
|
+
return function time3(str3) {
|
|
36192
|
+
const matches = TIME.exec(str3);
|
|
36193
36193
|
if (!matches) return false;
|
|
36194
36194
|
const hr = +matches[1];
|
|
36195
36195
|
const min = +matches[2];
|
|
@@ -36226,8 +36226,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
36226
36226
|
const DATE_TIME_SEPARATOR = /t|\s/i;
|
|
36227
36227
|
function getDateTime(strictTimeZone) {
|
|
36228
36228
|
const time3 = getTime(strictTimeZone);
|
|
36229
|
-
return function date_time(
|
|
36230
|
-
const dateTime =
|
|
36229
|
+
return function date_time(str3) {
|
|
36230
|
+
const dateTime = str3.split(DATE_TIME_SEPARATOR);
|
|
36231
36231
|
return dateTime.length === 2 && date5(dateTime[0]) && time3(dateTime[1]);
|
|
36232
36232
|
};
|
|
36233
36233
|
}
|
|
@@ -36248,13 +36248,13 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
36248
36248
|
}
|
|
36249
36249
|
const NOT_URI_FRAGMENT = /\/|:/;
|
|
36250
36250
|
const URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
|
|
36251
|
-
function uri(
|
|
36252
|
-
return NOT_URI_FRAGMENT.test(
|
|
36251
|
+
function uri(str3) {
|
|
36252
|
+
return NOT_URI_FRAGMENT.test(str3) && URI.test(str3);
|
|
36253
36253
|
}
|
|
36254
36254
|
const BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
|
|
36255
|
-
function byte(
|
|
36255
|
+
function byte(str3) {
|
|
36256
36256
|
BYTE.lastIndex = 0;
|
|
36257
|
-
return BYTE.test(
|
|
36257
|
+
return BYTE.test(str3);
|
|
36258
36258
|
}
|
|
36259
36259
|
const MIN_INT32 = -(2 ** 31);
|
|
36260
36260
|
const MAX_INT32 = 2 ** 31 - 1;
|
|
@@ -36268,10 +36268,10 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
36268
36268
|
return true;
|
|
36269
36269
|
}
|
|
36270
36270
|
const Z_ANCHOR = /[^\\]\\Z/;
|
|
36271
|
-
function regex(
|
|
36272
|
-
if (Z_ANCHOR.test(
|
|
36271
|
+
function regex(str3) {
|
|
36272
|
+
if (Z_ANCHOR.test(str3)) return false;
|
|
36273
36273
|
try {
|
|
36274
|
-
new RegExp(
|
|
36274
|
+
new RegExp(str3);
|
|
36275
36275
|
return true;
|
|
36276
36276
|
} catch (e) {
|
|
36277
36277
|
return false;
|
|
@@ -38954,9 +38954,9 @@ function redactValues(text, values, opts = {}) {
|
|
|
38954
38954
|
return out;
|
|
38955
38955
|
}
|
|
38956
38956
|
function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX, opts = {}) {
|
|
38957
|
-
const
|
|
38957
|
+
const str3 = text === null || text === void 0 ? "" : String(text);
|
|
38958
38958
|
const cap = Math.max(ERROR_REDACTION_INPUT_MAX, max);
|
|
38959
|
-
const capped =
|
|
38959
|
+
const capped = str3.length > cap ? str3.slice(0, cap) : str3;
|
|
38960
38960
|
const scrubbed = opts.values !== void 0 ? redactValues(capped, opts.values, opts.placeholder !== void 0 ? { placeholder: opts.placeholder } : {}) : capped;
|
|
38961
38961
|
const redacted = redactSecrets(scrubbed);
|
|
38962
38962
|
if (redacted.length <= max)
|
|
@@ -39804,14 +39804,14 @@ function promiseAllObject(promisesObj) {
|
|
|
39804
39804
|
}
|
|
39805
39805
|
function randomString(length = 10) {
|
|
39806
39806
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
39807
|
-
let
|
|
39807
|
+
let str3 = "";
|
|
39808
39808
|
for (let i = 0; i < length; i++) {
|
|
39809
|
-
|
|
39809
|
+
str3 += chars[Math.floor(Math.random() * chars.length)];
|
|
39810
39810
|
}
|
|
39811
|
-
return
|
|
39811
|
+
return str3;
|
|
39812
39812
|
}
|
|
39813
|
-
function esc(
|
|
39814
|
-
return JSON.stringify(
|
|
39813
|
+
function esc(str3) {
|
|
39814
|
+
return JSON.stringify(str3);
|
|
39815
39815
|
}
|
|
39816
39816
|
function slugify(input2) {
|
|
39817
39817
|
return input2.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -39925,8 +39925,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
|
|
|
39925
39925
|
"symbol",
|
|
39926
39926
|
"undefined"
|
|
39927
39927
|
]);
|
|
39928
|
-
function escapeRegex(
|
|
39929
|
-
return
|
|
39928
|
+
function escapeRegex(str3) {
|
|
39929
|
+
return str3.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
39930
39930
|
}
|
|
39931
39931
|
function clone(inst, def, params) {
|
|
39932
39932
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -40192,13 +40192,13 @@ function getSizableOrigin(input2) {
|
|
|
40192
40192
|
return "unknown";
|
|
40193
40193
|
}
|
|
40194
40194
|
var highSurrogate = /[\uD800-\uDBFF]/;
|
|
40195
|
-
function codePointLength(
|
|
40196
|
-
const units =
|
|
40197
|
-
if (!highSurrogate.test(
|
|
40195
|
+
function codePointLength(str3) {
|
|
40196
|
+
const units = str3.length;
|
|
40197
|
+
if (!highSurrogate.test(str3))
|
|
40198
40198
|
return units;
|
|
40199
40199
|
let count2 = units;
|
|
40200
40200
|
for (let i = 0; i < units - 1; i++) {
|
|
40201
|
-
if ((
|
|
40201
|
+
if ((str3.charCodeAt(i) & 64512) === 55296 && (str3.charCodeAt(i + 1) & 64512) === 56320) {
|
|
40202
40202
|
count2--;
|
|
40203
40203
|
i++;
|
|
40204
40204
|
}
|
|
@@ -59906,7 +59906,7 @@ function toolAnnotations(opts = {}) {
|
|
|
59906
59906
|
}
|
|
59907
59907
|
|
|
59908
59908
|
// src/version.ts
|
|
59909
|
-
var VERSION = "2.
|
|
59909
|
+
var VERSION = "2.6.0";
|
|
59910
59910
|
|
|
59911
59911
|
// src/client.ts
|
|
59912
59912
|
import { dirname as dirname2, join as join4 } from "path";
|
|
@@ -60419,6 +60419,41 @@ var UntappdClient = class {
|
|
|
60419
60419
|
}
|
|
60420
60420
|
};
|
|
60421
60421
|
|
|
60422
|
+
// src/tools/hints.ts
|
|
60423
|
+
function foodMeta(intent, extra = {}) {
|
|
60424
|
+
return { mint: { domain: "food", intent, ...extra } };
|
|
60425
|
+
}
|
|
60426
|
+
var BRIEF_MAX = 120;
|
|
60427
|
+
function str(v) {
|
|
60428
|
+
return typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
|
|
60429
|
+
}
|
|
60430
|
+
function dict(v) {
|
|
60431
|
+
return v && typeof v === "object" ? v : {};
|
|
60432
|
+
}
|
|
60433
|
+
function isoInstant(v) {
|
|
60434
|
+
const s = str(v);
|
|
60435
|
+
if (!s) return void 0;
|
|
60436
|
+
const t = Date.parse(s);
|
|
60437
|
+
return Number.isNaN(t) ? void 0 : new Date(t).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
60438
|
+
}
|
|
60439
|
+
function briefCheckinLine(item) {
|
|
60440
|
+
const c = dict(item);
|
|
60441
|
+
const who = str(dict(c.user).user_name) ?? "someone";
|
|
60442
|
+
const beer = str(dict(c.beer).beer_name) ?? "a beer";
|
|
60443
|
+
const brewery = str(dict(c.brewery).brewery_name);
|
|
60444
|
+
const venue = str(dict(c.venue).venue_name);
|
|
60445
|
+
const title = `${who}: ${beer}${brewery ? ` (${brewery})` : ""}${venue ? ` at ${venue}` : ""}`;
|
|
60446
|
+
const rating = typeof c.rating_score === "number" && c.rating_score > 0 ? `rated ${c.rating_score}` : "not rated";
|
|
60447
|
+
const tail = ` \u2014 ${isoInstant(c.created_at) ?? "no date"} \u2014 ${rating}`;
|
|
60448
|
+
const room = BRIEF_MAX - tail.length;
|
|
60449
|
+
const shown = title.length > room ? `${title.slice(0, Math.max(room - 1, 0))}\u2026` : title;
|
|
60450
|
+
return `${shown}${tail}`;
|
|
60451
|
+
}
|
|
60452
|
+
function briefCheckins(resp) {
|
|
60453
|
+
const items = dict(dict(resp).checkins).items;
|
|
60454
|
+
return Array.isArray(items) ? items.map(briefCheckinLine).join("\n") : "";
|
|
60455
|
+
}
|
|
60456
|
+
|
|
60422
60457
|
// src/compact.ts
|
|
60423
60458
|
function asDict(v) {
|
|
60424
60459
|
return v && typeof v === "object" ? v : void 0;
|
|
@@ -60546,7 +60581,7 @@ function numLoose(v) {
|
|
|
60546
60581
|
if (typeof v === "string" && v.trim() !== "") return num(Number(v));
|
|
60547
60582
|
return num(v);
|
|
60548
60583
|
}
|
|
60549
|
-
function
|
|
60584
|
+
function str2(v) {
|
|
60550
60585
|
return typeof v === "string" && v.length > 0 ? v : null;
|
|
60551
60586
|
}
|
|
60552
60587
|
function mapCheckinRow(username, item) {
|
|
@@ -60560,15 +60595,15 @@ function mapCheckinRow(username, item) {
|
|
|
60560
60595
|
checkin_id,
|
|
60561
60596
|
username,
|
|
60562
60597
|
bid: num(beer.bid),
|
|
60563
|
-
beer_name:
|
|
60598
|
+
beer_name: str2(beer.beer_name),
|
|
60564
60599
|
brewery_id: num(brewery.brewery_id),
|
|
60565
|
-
brewery_name:
|
|
60566
|
-
beer_style:
|
|
60600
|
+
brewery_name: str2(brewery.brewery_name),
|
|
60601
|
+
beer_style: str2(beer.beer_style),
|
|
60567
60602
|
abv: num(beer.beer_abv),
|
|
60568
60603
|
rating: num(c.rating_score),
|
|
60569
|
-
comment:
|
|
60604
|
+
comment: str2(c.checkin_comment),
|
|
60570
60605
|
venue_id: num(venue.venue_id),
|
|
60571
|
-
venue_name:
|
|
60606
|
+
venue_name: str2(venue.venue_name),
|
|
60572
60607
|
created_at: normaliseDate(c.created_at)
|
|
60573
60608
|
};
|
|
60574
60609
|
}
|
|
@@ -60582,10 +60617,10 @@ function mapBeerRow(username, item) {
|
|
|
60582
60617
|
return {
|
|
60583
60618
|
username,
|
|
60584
60619
|
bid,
|
|
60585
|
-
beer_name:
|
|
60620
|
+
beer_name: str2(beer.beer_name),
|
|
60586
60621
|
brewery_id: num(brewery.brewery_id),
|
|
60587
|
-
brewery_name:
|
|
60588
|
-
beer_style:
|
|
60622
|
+
brewery_name: str2(brewery.brewery_name),
|
|
60623
|
+
beer_style: str2(beer.beer_style),
|
|
60589
60624
|
abv: num(beer.beer_abv),
|
|
60590
60625
|
rating: rating !== null && rating > 0 ? rating : null,
|
|
60591
60626
|
had_count: num(c.count),
|
|
@@ -60603,12 +60638,12 @@ function mapUserVenueItem(_username, item) {
|
|
|
60603
60638
|
return {
|
|
60604
60639
|
venue: {
|
|
60605
60640
|
venue_id,
|
|
60606
|
-
name:
|
|
60607
|
-
category:
|
|
60608
|
-
address:
|
|
60609
|
-
city:
|
|
60610
|
-
state:
|
|
60611
|
-
country:
|
|
60641
|
+
name: str2(v.venue_name),
|
|
60642
|
+
category: str2(v.primary_category),
|
|
60643
|
+
address: str2(loc.venue_address),
|
|
60644
|
+
city: str2(loc.venue_city),
|
|
60645
|
+
state: str2(loc.venue_state),
|
|
60646
|
+
country: str2(loc.venue_country),
|
|
60612
60647
|
lat: numLoose(loc.lat),
|
|
60613
60648
|
lng: numLoose(loc.lng),
|
|
60614
60649
|
is_verified: typeof verified === "boolean" || typeof verified === "number" ? verified ? 1 : 0 : null
|
|
@@ -60632,8 +60667,8 @@ function mapBreweryBeerItem(breweryId, item, fetchedAt, popularityRank) {
|
|
|
60632
60667
|
return {
|
|
60633
60668
|
brewery_id: breweryId,
|
|
60634
60669
|
bid,
|
|
60635
|
-
beer_name:
|
|
60636
|
-
style:
|
|
60670
|
+
beer_name: str2(b.beer_name),
|
|
60671
|
+
style: str2(b.beer_style),
|
|
60637
60672
|
abv: num(b.beer_abv),
|
|
60638
60673
|
ibu: num(b.beer_ibu),
|
|
60639
60674
|
rating_score: num(b.rating_score),
|
|
@@ -60652,10 +60687,10 @@ function mapWishlistItem(item) {
|
|
|
60652
60687
|
const brw = asDict2(i.brewery);
|
|
60653
60688
|
return {
|
|
60654
60689
|
bid,
|
|
60655
|
-
beer_name:
|
|
60690
|
+
beer_name: str2(b.beer_name),
|
|
60656
60691
|
brewery_id: num(brw.brewery_id),
|
|
60657
|
-
brewery_name:
|
|
60658
|
-
style:
|
|
60692
|
+
brewery_name: str2(brw.brewery_name),
|
|
60693
|
+
style: str2(b.beer_style),
|
|
60659
60694
|
abv: num(b.beer_abv),
|
|
60660
60695
|
added_at: normaliseDate(i.created_at)
|
|
60661
60696
|
};
|
|
@@ -60668,11 +60703,11 @@ function mapBreweryLikeItem(item) {
|
|
|
60668
60703
|
const loc = asDict2(b.location);
|
|
60669
60704
|
return {
|
|
60670
60705
|
brewery_id: id,
|
|
60671
|
-
brewery_name:
|
|
60672
|
-
brewery_type:
|
|
60673
|
-
country:
|
|
60674
|
-
city:
|
|
60675
|
-
state:
|
|
60706
|
+
brewery_name: str2(b.brewery_name),
|
|
60707
|
+
brewery_type: str2(b.brewery_type),
|
|
60708
|
+
country: str2(b.country_name),
|
|
60709
|
+
city: str2(loc.brewery_city),
|
|
60710
|
+
state: str2(loc.brewery_state),
|
|
60676
60711
|
lat: num(loc.lat),
|
|
60677
60712
|
lng: num(loc.lng),
|
|
60678
60713
|
liked_at: normaliseDate(i.created_at)
|
|
@@ -60685,11 +60720,11 @@ function beerMetaFrom(beer, brewery, fetchedAt) {
|
|
|
60685
60720
|
const brw = asDict2(brewery ?? b.brewery);
|
|
60686
60721
|
return {
|
|
60687
60722
|
bid,
|
|
60688
|
-
name:
|
|
60689
|
-
brewery:
|
|
60723
|
+
name: str2(b.beer_name),
|
|
60724
|
+
brewery: str2(brw.brewery_name),
|
|
60690
60725
|
brewery_id: num(brw.brewery_id),
|
|
60691
|
-
style:
|
|
60692
|
-
parent_style:
|
|
60726
|
+
style: str2(b.beer_style),
|
|
60727
|
+
parent_style: str2(b.parent_style_name),
|
|
60693
60728
|
abv: num(b.beer_abv),
|
|
60694
60729
|
ibu: num(b.beer_ibu),
|
|
60695
60730
|
weighted_rating_score: num(b.weighted_rating_score),
|
|
@@ -60699,7 +60734,7 @@ function beerMetaFrom(beer, brewery, fetchedAt) {
|
|
|
60699
60734
|
};
|
|
60700
60735
|
}
|
|
60701
60736
|
function normaliseDate(v) {
|
|
60702
|
-
const s =
|
|
60737
|
+
const s = str2(v);
|
|
60703
60738
|
if (!s) return null;
|
|
60704
60739
|
const t = Date.parse(s);
|
|
60705
60740
|
return Number.isNaN(t) ? s : new Date(t).toISOString();
|
|
@@ -61181,12 +61216,12 @@ var CheckinStoreCore = class {
|
|
|
61181
61216
|
for (const r of rows) {
|
|
61182
61217
|
out.push({
|
|
61183
61218
|
venue_id: Number(r.venue_id),
|
|
61184
|
-
name:
|
|
61185
|
-
category:
|
|
61186
|
-
address:
|
|
61187
|
-
city:
|
|
61188
|
-
state:
|
|
61189
|
-
country:
|
|
61219
|
+
name: str2(r.name),
|
|
61220
|
+
category: str2(r.category),
|
|
61221
|
+
address: str2(r.address),
|
|
61222
|
+
city: str2(r.city),
|
|
61223
|
+
state: str2(r.state),
|
|
61224
|
+
country: str2(r.country),
|
|
61190
61225
|
lat: num(r.lat),
|
|
61191
61226
|
lng: num(r.lng),
|
|
61192
61227
|
is_verified: num(r.is_verified)
|
|
@@ -61204,7 +61239,7 @@ var CheckinStoreCore = class {
|
|
|
61204
61239
|
next_offset: num(r.next_offset),
|
|
61205
61240
|
total_count: num(r.total_count),
|
|
61206
61241
|
complete: Number(r.complete) === 1,
|
|
61207
|
-
last_synced_at:
|
|
61242
|
+
last_synced_at: str2(r.last_synced_at)
|
|
61208
61243
|
};
|
|
61209
61244
|
}
|
|
61210
61245
|
/** Read-merge-write in one write transaction (see {@link setState}). */
|
|
@@ -61322,7 +61357,7 @@ var CheckinStoreCore = class {
|
|
|
61322
61357
|
oldest_max_id: num(row.oldest_max_id),
|
|
61323
61358
|
newest_checkin_id: num(row.newest_checkin_id),
|
|
61324
61359
|
catchup_max_id: num(row.catchup_max_id),
|
|
61325
|
-
last_synced_at:
|
|
61360
|
+
last_synced_at: str2(row.last_synced_at),
|
|
61326
61361
|
backfill_complete: Number(row.backfill_complete) === 1,
|
|
61327
61362
|
total_checkins: num(row.total_checkins),
|
|
61328
61363
|
checkins_truncated: Number(row.checkins_truncated) === 1,
|
|
@@ -61437,7 +61472,7 @@ var CheckinStoreCore = class {
|
|
|
61437
61472
|
perBid.set(bid, hc ?? perBid.get(bid) ?? 1);
|
|
61438
61473
|
const rating = num(r.rating);
|
|
61439
61474
|
if (rating !== null && (beersBest === null || rating > beersBest)) beersBest = rating;
|
|
61440
|
-
const last =
|
|
61475
|
+
const last = str2(r.last_had) ?? str2(r.first_had);
|
|
61441
61476
|
if (last && (beersLast === null || last > beersLast)) beersLast = last;
|
|
61442
61477
|
}
|
|
61443
61478
|
const cAgg = this.db.get(
|
|
@@ -61453,7 +61488,7 @@ var CheckinStoreCore = class {
|
|
|
61453
61488
|
let count2 = 0;
|
|
61454
61489
|
for (const v of perBid.values()) count2 += v;
|
|
61455
61490
|
const bestCandidates = [num(cAgg?.best), beersBest].filter((x) => x !== null);
|
|
61456
|
-
const lastCandidates = [
|
|
61491
|
+
const lastCandidates = [str2(cAgg?.last), beersLast].filter((x) => x !== null);
|
|
61457
61492
|
const sources = [];
|
|
61458
61493
|
if (checkinRows > 0) sources.push("checkins");
|
|
61459
61494
|
if (beersRows > 0) sources.push("beers");
|
|
@@ -61489,7 +61524,7 @@ var CheckinStoreCore = class {
|
|
|
61489
61524
|
v.had = true;
|
|
61490
61525
|
v.count = Number(r.c);
|
|
61491
61526
|
v.best_rating = num(r.best);
|
|
61492
|
-
v.last_date =
|
|
61527
|
+
v.last_date = str2(r.last);
|
|
61493
61528
|
v.sources.push("checkins");
|
|
61494
61529
|
}
|
|
61495
61530
|
for (const r of this.db.all(
|
|
@@ -61501,7 +61536,7 @@ var CheckinStoreCore = class {
|
|
|
61501
61536
|
v.count = num(r.had_count) ?? (v.count || 1);
|
|
61502
61537
|
const rating = num(r.rating);
|
|
61503
61538
|
if (rating !== null && (v.best_rating === null || rating > v.best_rating)) v.best_rating = rating;
|
|
61504
|
-
const last =
|
|
61539
|
+
const last = str2(r.last_had) ?? str2(r.first_had);
|
|
61505
61540
|
if (last && (v.last_date === null || last > v.last_date)) v.last_date = last;
|
|
61506
61541
|
v.sources.push("beers");
|
|
61507
61542
|
}
|
|
@@ -61685,8 +61720,8 @@ var CheckinStoreCore = class {
|
|
|
61685
61720
|
).map((r) => ({
|
|
61686
61721
|
brewery_id: Number(r.brewery_id),
|
|
61687
61722
|
bid: Number(r.bid),
|
|
61688
|
-
beer_name:
|
|
61689
|
-
style:
|
|
61723
|
+
beer_name: str2(r.beer_name),
|
|
61724
|
+
style: str2(r.style),
|
|
61690
61725
|
abv: num(r.abv),
|
|
61691
61726
|
ibu: num(r.ibu),
|
|
61692
61727
|
rating_score: num(r.rating_score),
|
|
@@ -61698,7 +61733,7 @@ var CheckinStoreCore = class {
|
|
|
61698
61733
|
}));
|
|
61699
61734
|
return {
|
|
61700
61735
|
brewery_id: breweryId,
|
|
61701
|
-
brewery_name:
|
|
61736
|
+
brewery_name: str2(h.brewery_name),
|
|
61702
61737
|
total_count: num(h.total_count),
|
|
61703
61738
|
ranked_count: Number(h.ranked_count ?? 0),
|
|
61704
61739
|
fetched_at: String(h.fetched_at),
|
|
@@ -61837,12 +61872,12 @@ var CheckinStoreCore = class {
|
|
|
61837
61872
|
const key = username.toLowerCase();
|
|
61838
61873
|
const map2 = (r) => ({
|
|
61839
61874
|
bid: Number(r.bid),
|
|
61840
|
-
beer_name:
|
|
61875
|
+
beer_name: str2(r.beer_name),
|
|
61841
61876
|
brewery_id: num(r.brewery_id),
|
|
61842
|
-
brewery_name:
|
|
61843
|
-
style:
|
|
61877
|
+
brewery_name: str2(r.brewery_name),
|
|
61878
|
+
style: str2(r.style),
|
|
61844
61879
|
abv: num(r.abv),
|
|
61845
|
-
added_at:
|
|
61880
|
+
added_at: str2(r.added_at)
|
|
61846
61881
|
});
|
|
61847
61882
|
if (bids === void 0) {
|
|
61848
61883
|
return this.db.all("SELECT * FROM wishlist WHERE username = ? ORDER BY added_at DESC, bid", [key]).map(map2);
|
|
@@ -61919,14 +61954,14 @@ var CheckinStoreCore = class {
|
|
|
61919
61954
|
const key = username.toLowerCase();
|
|
61920
61955
|
const map2 = (r) => ({
|
|
61921
61956
|
brewery_id: Number(r.brewery_id),
|
|
61922
|
-
brewery_name:
|
|
61923
|
-
brewery_type:
|
|
61924
|
-
country:
|
|
61925
|
-
city:
|
|
61926
|
-
state:
|
|
61957
|
+
brewery_name: str2(r.brewery_name),
|
|
61958
|
+
brewery_type: str2(r.brewery_type),
|
|
61959
|
+
country: str2(r.country),
|
|
61960
|
+
city: str2(r.city),
|
|
61961
|
+
state: str2(r.state),
|
|
61927
61962
|
lat: num(r.lat),
|
|
61928
61963
|
lng: num(r.lng),
|
|
61929
|
-
liked_at:
|
|
61964
|
+
liked_at: str2(r.liked_at)
|
|
61930
61965
|
});
|
|
61931
61966
|
const order = "ORDER BY liked_at IS NULL, liked_at DESC, brewery_id";
|
|
61932
61967
|
if (breweryIds === void 0) {
|
|
@@ -62023,10 +62058,10 @@ var CheckinStoreCore = class {
|
|
|
62023
62058
|
venue_id: Number(r.venue_id),
|
|
62024
62059
|
checkin_id: Number(r.checkin_id),
|
|
62025
62060
|
bid: num(r.bid),
|
|
62026
|
-
beer_name:
|
|
62061
|
+
beer_name: str2(r.beer_name),
|
|
62027
62062
|
brewery_id: num(r.brewery_id),
|
|
62028
|
-
brewery_name:
|
|
62029
|
-
created_at:
|
|
62063
|
+
brewery_name: str2(r.brewery_name),
|
|
62064
|
+
created_at: str2(r.created_at)
|
|
62030
62065
|
});
|
|
62031
62066
|
}
|
|
62032
62067
|
}
|
|
@@ -62043,7 +62078,7 @@ var CheckinStoreCore = class {
|
|
|
62043
62078
|
out = { added: true, note: n2, created_at: createdAt };
|
|
62044
62079
|
return;
|
|
62045
62080
|
}
|
|
62046
|
-
const n = note === void 0 ?
|
|
62081
|
+
const n = note === void 0 ? str2(cur.note) : note;
|
|
62047
62082
|
this.db.run("UPDATE favorites SET note = ? WHERE username = ? AND kind = ? AND id = ?", [n, key, kind, id]);
|
|
62048
62083
|
out = { added: false, note: n, created_at: String(cur.created_at) };
|
|
62049
62084
|
});
|
|
@@ -62085,9 +62120,9 @@ var CheckinStoreCore = class {
|
|
|
62085
62120
|
).map((r) => ({
|
|
62086
62121
|
kind: String(r.kind),
|
|
62087
62122
|
id: Number(r.id),
|
|
62088
|
-
note:
|
|
62123
|
+
note: str2(r.note),
|
|
62089
62124
|
created_at: String(r.created_at),
|
|
62090
|
-
name:
|
|
62125
|
+
name: str2(r.name)
|
|
62091
62126
|
}));
|
|
62092
62127
|
let rows;
|
|
62093
62128
|
if (opts.ids === void 0) rows = run("", []);
|
|
@@ -62131,7 +62166,7 @@ var CheckinStoreCore = class {
|
|
|
62131
62166
|
FROM (${this.perBeerSql("")}) WHERE brewery_id IS NOT NULL${filter} GROUP BY brewery_id`;
|
|
62132
62167
|
const map2 = (r) => ({
|
|
62133
62168
|
brewery_id: Number(r.brewery_id),
|
|
62134
|
-
brewery_name:
|
|
62169
|
+
brewery_name: str2(r.brewery_name),
|
|
62135
62170
|
beers: Number(r.beers),
|
|
62136
62171
|
rated_beers: Number(r.rated),
|
|
62137
62172
|
avg_rating: num(r.avg_rating)
|
|
@@ -62147,9 +62182,9 @@ var CheckinStoreCore = class {
|
|
|
62147
62182
|
const key = username.toLowerCase();
|
|
62148
62183
|
const map2 = (r) => ({
|
|
62149
62184
|
bid: Number(r.bid),
|
|
62150
|
-
beer_name:
|
|
62185
|
+
beer_name: str2(r.beer_name),
|
|
62151
62186
|
brewery_id: num(r.brewery_id),
|
|
62152
|
-
brewery_name:
|
|
62187
|
+
brewery_name: str2(r.brewery_name),
|
|
62153
62188
|
rating: num(r.best),
|
|
62154
62189
|
times_had: Number(r.times_had)
|
|
62155
62190
|
});
|
|
@@ -62387,22 +62422,22 @@ function escapeLike(s) {
|
|
|
62387
62422
|
function visitFrom(r) {
|
|
62388
62423
|
return {
|
|
62389
62424
|
venue_id: Number(r.venue_id),
|
|
62390
|
-
first_visit:
|
|
62425
|
+
first_visit: str2(r.first_visit),
|
|
62391
62426
|
first_checkin_id: num(r.first_checkin_id),
|
|
62392
|
-
recent_visit:
|
|
62427
|
+
recent_visit: str2(r.recent_visit),
|
|
62393
62428
|
recent_checkin_id: num(r.recent_checkin_id),
|
|
62394
62429
|
total_count: num(r.total_count)
|
|
62395
62430
|
};
|
|
62396
62431
|
}
|
|
62397
62432
|
function visitedFrom(r) {
|
|
62398
|
-
const name =
|
|
62433
|
+
const name = str2(r.name);
|
|
62399
62434
|
return {
|
|
62400
62435
|
name,
|
|
62401
|
-
category:
|
|
62402
|
-
address:
|
|
62403
|
-
city:
|
|
62404
|
-
state:
|
|
62405
|
-
country:
|
|
62436
|
+
category: str2(r.category),
|
|
62437
|
+
address: str2(r.address),
|
|
62438
|
+
city: str2(r.city),
|
|
62439
|
+
state: str2(r.state),
|
|
62440
|
+
country: str2(r.country),
|
|
62406
62441
|
lat: num(r.lat),
|
|
62407
62442
|
lng: num(r.lng),
|
|
62408
62443
|
is_verified: num(r.is_verified),
|
|
@@ -62451,6 +62486,7 @@ function registerBeerTools(server, client2, cache) {
|
|
|
62451
62486
|
title: "Search Untappd beers",
|
|
62452
62487
|
description: 'Search Untappd for beers by name (optionally "Brewery Beer"). Returns ranked matches with their beer id (bid), brewery, style, ABV, IBU, and global rating. Feed a bid into untappd_beer_info for full detail. Read-only.',
|
|
62453
62488
|
annotations: toolAnnotations({ title: "Search Untappd beers", readOnly: true, idempotent: true, openWorld: true }),
|
|
62489
|
+
_meta: foodMeta("lookup", { aliases: ["beer", "ipa", "stout", "brew"] }),
|
|
62454
62490
|
inputSchema: external_exports.object({
|
|
62455
62491
|
query: external_exports.string().min(1).describe("Beer name to search for"),
|
|
62456
62492
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max results (1\u201350, default 25)"),
|
|
@@ -62475,6 +62511,7 @@ function registerBeerTools(server, client2, cache) {
|
|
|
62475
62511
|
title: "Get Untappd beer detail",
|
|
62476
62512
|
description: 'Get full detail for a beer by its Untappd beer id (bid): description, style, ABV, IBU, brewery, rating, total check-in count, and \u2014 on view:"full" \u2014 recent activity. Get a bid from untappd_search_beer. Read-only.',
|
|
62477
62513
|
annotations: toolAnnotations({ title: "Get Untappd beer detail", readOnly: true, idempotent: true, openWorld: true }),
|
|
62514
|
+
_meta: foodMeta("lookup"),
|
|
62478
62515
|
inputSchema: external_exports.object({
|
|
62479
62516
|
bid: BidSchema,
|
|
62480
62517
|
// No local projection here: compact is forwarded to Untappd as its OWN
|
|
@@ -62496,6 +62533,7 @@ function registerBeerTools(server, client2, cache) {
|
|
|
62496
62533
|
title: "Get recent check-ins for a beer",
|
|
62497
62534
|
description: "Get the recent public check-ins for a beer by its bid \u2014 who drank it, their rating, comment, and venue. Page backwards with max_id (the pagination.max_id from a prior call). Read-only.",
|
|
62498
62535
|
annotations: toolAnnotations({ title: "Get recent check-ins for a beer", readOnly: true, idempotent: false, openWorld: true }),
|
|
62536
|
+
_meta: foodMeta("lookup"),
|
|
62499
62537
|
inputSchema: external_exports.object({
|
|
62500
62538
|
bid: BidSchema,
|
|
62501
62539
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max check-ins (1\u201350, default 25)"),
|
|
@@ -62829,6 +62867,7 @@ function registerBreweryTools(server, client2, cacheProvider) {
|
|
|
62829
62867
|
title: "Search Untappd breweries",
|
|
62830
62868
|
description: "Search Untappd for breweries by name. Returns matches with their brewery id, location, type, and beer count. Feed a brewery id into untappd_brewery_info for full detail. Read-only.",
|
|
62831
62869
|
annotations: toolAnnotations({ title: "Search Untappd breweries", readOnly: true, idempotent: true, openWorld: true }),
|
|
62870
|
+
_meta: foodMeta("lookup", { aliases: ["brewery", "brewer"] }),
|
|
62832
62871
|
inputSchema: external_exports.object({
|
|
62833
62872
|
query: external_exports.string().min(1).describe("Brewery name to search for"),
|
|
62834
62873
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max results (1\u201350, default 25)"),
|
|
@@ -62846,6 +62885,7 @@ function registerBreweryTools(server, client2, cacheProvider) {
|
|
|
62846
62885
|
title: "Get Untappd brewery detail",
|
|
62847
62886
|
description: `Get full detail for a brewery by its Untappd brewery id: description, location, type, rating, total check-ins, and popular beers. Get an id from untappd_search_brewery. Read-only. Its claimed_status.follow_status is whether the SIGNED-IN account follows/likes the brewery \u2014 never another user's; for theirs, run untappd_sync_brewery_likes and read untappd_favorites_list (source "untappd").`,
|
|
62848
62887
|
annotations: toolAnnotations({ title: "Get Untappd brewery detail", readOnly: true, idempotent: true, openWorld: true }),
|
|
62888
|
+
_meta: foodMeta("lookup"),
|
|
62849
62889
|
inputSchema: external_exports.object({
|
|
62850
62890
|
brewery_id: external_exports.number().int().positive().describe("Untappd brewery id"),
|
|
62851
62891
|
view: viewParam(UNTAPPD_VIEWS, { note: 'compact also asks Untappd for its own slim record, dropping the embedded activity/list blocks; "full" returns everything.' })
|
|
@@ -62863,6 +62903,7 @@ function registerBreweryTools(server, client2, cacheProvider) {
|
|
|
62863
62903
|
title: "Get a brewery's beer list",
|
|
62864
62904
|
description: `Get the beers a brewery makes, by brewery id, with per-beer global rating, check-in count, ABV, style and in_production. Supports sorting and paging. Get an id from untappd_search_brewery. Lists are CACHED locally for ${BREWERY_BEERS_TTL_MS / 864e5} days (source:"cache"|"api", fetched_at, expires_at): a fresh cached list answers without an API call \u2014 the default popularity order from the cached top-of-list, other sorts once the whole list is cached (sorted locally). refresh:true re-fetches. The compact result has the same keys with or without the cache (source:"api" and null fetched_at/expires_at when no cache is available), with pagination {offset, limit, next_offset}. Untappd's per-beer has_had / your-count on this endpoint describe the AUTHENTICATED account, never the user you are asking about, so have_had / your_count are never cached or returned on either path \u2014 use untappd_cache_has_had_many for any user. With the cache, the result carries favorite {is_favorite, sources, reason} for the brewery and on each favorite beer (derived from username's history or added with untappd_favorite_add; default your account). view:"full" always calls Untappd and returns its raw page. Read-only.`,
|
|
62865
62905
|
annotations: toolAnnotations({ title: "Get a brewery's beer list", readOnly: true, idempotent: true, openWorld: true }),
|
|
62906
|
+
_meta: foodMeta("lookup"),
|
|
62866
62907
|
inputSchema: external_exports.object({
|
|
62867
62908
|
brewery_id: external_exports.number().int().positive().describe("Untappd brewery id"),
|
|
62868
62909
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max beers (1\u201350, default 25)"),
|
|
@@ -63017,6 +63058,7 @@ function registerVenueTools(server, client2, cacheProvider) {
|
|
|
63017
63058
|
title: "Search Untappd venues",
|
|
63018
63059
|
description: "Search Untappd for venues (bars, breweries, restaurants) by name. Returns matches with their venue id, category, and location. Feed a venue id into untappd_venue_info for full detail. Read-only.",
|
|
63019
63060
|
annotations: toolAnnotations({ title: "Search Untappd venues", readOnly: true, idempotent: true, openWorld: true }),
|
|
63061
|
+
_meta: foodMeta("lookup", { aliases: ["bar", "pub", "taproom", "brewpub"] }),
|
|
63020
63062
|
inputSchema: external_exports.object({
|
|
63021
63063
|
query: external_exports.string().min(1).describe("Venue name to search for"),
|
|
63022
63064
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max results (1\u201350, default 25)")
|
|
@@ -63033,6 +63075,7 @@ function registerVenueTools(server, client2, cacheProvider) {
|
|
|
63033
63075
|
title: "Get Untappd venue detail",
|
|
63034
63076
|
description: 'Get full detail for a venue by its Untappd venue id: category, address, contact, rating, total check-ins, and \u2014 on view:"full" \u2014 top beers and recent activity. Get an id from untappd_search_venue. Read-only.',
|
|
63035
63077
|
annotations: toolAnnotations({ title: "Get Untappd venue detail", readOnly: true, idempotent: true, openWorld: true }),
|
|
63078
|
+
_meta: foodMeta("lookup"),
|
|
63036
63079
|
inputSchema: external_exports.object({
|
|
63037
63080
|
venue_id: external_exports.number().int().positive().describe("Untappd venue id"),
|
|
63038
63081
|
view: viewParam(UNTAPPD_VIEWS, { note: 'compact also asks Untappd for its own slim record, dropping the embedded activity/list blocks; "full" returns everything.' })
|
|
@@ -63050,6 +63093,7 @@ function registerVenueTools(server, client2, cacheProvider) {
|
|
|
63050
63093
|
title: "Get a venue's verified beer menu (section-paged)",
|
|
63051
63094
|
description: "Return a venue's verified beer menu as a flat, compact list of beers. untappd_venue_info returns only the FIRST section of each menu (Untappd defaults the section list to one), so it silently under-reports any venue whose menu spans multiple sections \u2014 e.g. a 23-beer wall that comes back with 2 items. This tool forwards the section_limit / section_offset paging params venue/info echoes back but never receives, walks sections up to a per-call max_pages budget (respecting the ~100 calls/hour limit \u2014 it does NOT loop to completion in one call), and flattens to [{bid, name, brewery, brewery_id, style, abv, price, serving_type, menu, section}]. Like the sync tools it is resumable: when the budget runs out before full coverage it returns another_run_needed:true plus next_section_offset to pass back on the next call. truncated:true means the upstream returned no more sections short of total_count (e.g. it ignored the paging params) \u2014 not resumable. Results are cached locally for " + TTL_HOURS + " hours per venue + paging params (cache.hit, fetched_at, expires_at) so repeated trip planning does not re-spend calls; refresh:true re-fetches. With the cache, the result also carries favorite {is_favorite, sources, reason} for the venue, and favorite / brewery_favorite on each beer that is one (derived from username's history or added with untappd_favorite_add; default your account) \u2014 applied on read, so never stale. Get an id from untappd_search_venue. Read-only.",
|
|
63052
63095
|
annotations: toolAnnotations({ title: "Get a venue's verified beer menu (section-paged)", readOnly: true, idempotent: true, openWorld: true }),
|
|
63096
|
+
_meta: foodMeta("lookup", { aliases: ["on tap", "tap list", "beer menu"] }),
|
|
63053
63097
|
inputSchema: external_exports.object({
|
|
63054
63098
|
venue_id: external_exports.number().int().positive().describe("Untappd venue id"),
|
|
63055
63099
|
menu_id: external_exports.number().int().positive().optional().describe("Restrict to a single menu id (from a prior result). Optional."),
|
|
@@ -63122,6 +63166,7 @@ function registerVenueTools(server, client2, cacheProvider) {
|
|
|
63122
63166
|
title: "Look up an Untappd venue by Foursquare id",
|
|
63123
63167
|
description: "Resolve a Foursquare venue id to its Untappd venue. Useful to turn a foursquare_id (e.g. from a check-in) into an Untappd venue you can pass to untappd_venue_info / untappd_venue_activity. Read-only.",
|
|
63124
63168
|
annotations: toolAnnotations({ title: "Look up an Untappd venue by Foursquare id", readOnly: true, idempotent: true, openWorld: true }),
|
|
63169
|
+
_meta: foodMeta("lookup"),
|
|
63125
63170
|
inputSchema: external_exports.object({
|
|
63126
63171
|
foursquare_id: external_exports.string().min(1).describe("Foursquare venue id")
|
|
63127
63172
|
})
|
|
@@ -63137,6 +63182,7 @@ function registerVenueTools(server, client2, cacheProvider) {
|
|
|
63137
63182
|
title: "Get recent check-ins at a venue",
|
|
63138
63183
|
description: "Get the recent public check-ins at a venue by its id \u2014 who was there, what they drank, and their ratings. Page backwards with max_id (the pagination.max_id from a prior call). Cached locally for " + TTL_HOURS + " hours per venue + limit + max_id (cache.hit, fetched_at, expires_at); refresh:true re-fetches. Read-only.",
|
|
63139
63184
|
annotations: toolAnnotations({ title: "Get recent check-ins at a venue", readOnly: true, idempotent: false, openWorld: true }),
|
|
63185
|
+
_meta: foodMeta("lookup"),
|
|
63140
63186
|
inputSchema: external_exports.object({
|
|
63141
63187
|
venue_id: external_exports.number().int().positive().describe("Untappd venue id"),
|
|
63142
63188
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max check-ins (1\u201350, default 25)"),
|
|
@@ -63188,6 +63234,7 @@ function registerUserTools(server, client2) {
|
|
|
63188
63234
|
title: "Get Untappd user profile",
|
|
63189
63235
|
description: "Get an Untappd user's profile: bio, location, total check-ins, distinct beers, badges, and stats. Omit username for your own account. Read-only.",
|
|
63190
63236
|
annotations: toolAnnotations({ title: "Get Untappd user profile", readOnly: true, idempotent: true, openWorld: true }),
|
|
63237
|
+
_meta: foodMeta("lookup"),
|
|
63191
63238
|
inputSchema: external_exports.object({
|
|
63192
63239
|
username: UsernameArg,
|
|
63193
63240
|
view: viewParam(UNTAPPD_VIEWS, { note: "compact also asks Untappd for its own slim record, dropping the embedded activity/list blocks; 'full' returns everything." })
|
|
@@ -63205,6 +63252,7 @@ function registerUserTools(server, client2) {
|
|
|
63205
63252
|
title: "Get Untappd user check-ins",
|
|
63206
63253
|
description: "Get a user's recent check-ins (most recent first): the beer, rating, comment, venue, and toasts/comments. Page backwards with max_id (the pagination.max_id from a prior call). Only your OWN history pages fully: for any other user Untappd returns just the ~50 most recent check-ins and ignores max_id \u2014 use untappd_user_beers (has had) or untappd_user_venues (has been) for their full history. Omit username for your own. Read-only.",
|
|
63207
63254
|
annotations: toolAnnotations({ title: "Get Untappd user check-ins", readOnly: true, idempotent: true, openWorld: true }),
|
|
63255
|
+
_meta: foodMeta("lookup"),
|
|
63208
63256
|
inputSchema: external_exports.object({
|
|
63209
63257
|
username: UsernameArg,
|
|
63210
63258
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max check-ins (1\u201350, default 25)"),
|
|
@@ -63224,6 +63272,7 @@ function registerUserTools(server, client2) {
|
|
|
63224
63272
|
title: "Get Untappd user wishlist",
|
|
63225
63273
|
description: "Get the beers on a user's wishlist. Supports sorting and paging. Omit username for your own account. Read-only.",
|
|
63226
63274
|
annotations: toolAnnotations({ title: "Get Untappd user wishlist", readOnly: true, idempotent: true, openWorld: true }),
|
|
63275
|
+
_meta: foodMeta("lookup", { aliases: ["wishlist", "beers to try"] }),
|
|
63227
63276
|
inputSchema: external_exports.object({
|
|
63228
63277
|
username: UsernameArg,
|
|
63229
63278
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max beers (1\u201350, default 25)"),
|
|
@@ -63244,6 +63293,7 @@ function registerUserTools(server, client2) {
|
|
|
63244
63293
|
title: "Get Untappd distinct beers",
|
|
63245
63294
|
description: "Get the distinct (unique) beers a user has ever checked in, with their rating and check-in count per beer. Supports sorting and paging. Omit username for your own account. Read-only.",
|
|
63246
63295
|
annotations: toolAnnotations({ title: "Get Untappd distinct beers", readOnly: true, idempotent: true, openWorld: true }),
|
|
63296
|
+
_meta: foodMeta("lookup"),
|
|
63247
63297
|
inputSchema: external_exports.object({
|
|
63248
63298
|
username: UsernameArg,
|
|
63249
63299
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max beers (1\u201350, default 25)"),
|
|
@@ -63264,6 +63314,7 @@ function registerUserTools(server, client2) {
|
|
|
63264
63314
|
title: "Get Untappd user badges",
|
|
63265
63315
|
description: "Get the badges a user has earned, most recent first. Omit username for your own account. Read-only.",
|
|
63266
63316
|
annotations: toolAnnotations({ title: "Get Untappd user badges", readOnly: true, idempotent: true, openWorld: true }),
|
|
63317
|
+
_meta: foodMeta("lookup"),
|
|
63267
63318
|
inputSchema: external_exports.object({
|
|
63268
63319
|
username: UsernameArg,
|
|
63269
63320
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max badges (1\u201350, default 25)"),
|
|
@@ -63281,6 +63332,7 @@ function registerUserTools(server, client2) {
|
|
|
63281
63332
|
title: "Get Untappd user friends",
|
|
63282
63333
|
description: "Get a user's friend list. Omit username for your own account. Read-only.",
|
|
63283
63334
|
annotations: toolAnnotations({ title: "Get Untappd user friends", readOnly: true, idempotent: true, openWorld: true }),
|
|
63335
|
+
_meta: foodMeta("lookup"),
|
|
63284
63336
|
inputSchema: external_exports.object({
|
|
63285
63337
|
username: UsernameArg,
|
|
63286
63338
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max friends (1\u201350, default 25)"),
|
|
@@ -63298,6 +63350,7 @@ function registerUserTools(server, client2) {
|
|
|
63298
63350
|
title: "Get venues a user has checked in at",
|
|
63299
63351
|
description: "Get the venues a user has checked in at, most recent first, with per-venue first/recent visit and check-in counts. Pages fully for any public/friend account (unlike user check-ins, which stop at ~50 for other users). Untappd returns at most 25 venues per page whatever limit you ask for \u2014 advance by the returned pagination offset. Omit username for your own account. Read-only.",
|
|
63300
63352
|
annotations: toolAnnotations({ title: "Get venues a user has checked in at", readOnly: true, idempotent: true, openWorld: true }),
|
|
63353
|
+
_meta: foodMeta("lookup"),
|
|
63301
63354
|
inputSchema: external_exports.object({
|
|
63302
63355
|
username: UsernameArg,
|
|
63303
63356
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max venues (1\u201350; Untappd caps a page at 25)"),
|
|
@@ -63322,6 +63375,7 @@ function registerUserTools(server, client2) {
|
|
|
63322
63375
|
title: "Get your pending friend requests",
|
|
63323
63376
|
description: "Get the incoming friend requests waiting on YOUR account \u2014 the users who have requested to be your friend. Read-only.",
|
|
63324
63377
|
annotations: toolAnnotations({ title: "Get your pending friend requests", readOnly: true, idempotent: true, openWorld: true }),
|
|
63378
|
+
_meta: foodMeta("lookup"),
|
|
63325
63379
|
inputSchema: external_exports.object({
|
|
63326
63380
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max requests (1\u201350, default 25)"),
|
|
63327
63381
|
offset: external_exports.number().int().min(0).optional().describe("Result offset for paging (default 0)")
|
|
@@ -63335,6 +63389,9 @@ function registerUserTools(server, client2) {
|
|
|
63335
63389
|
}
|
|
63336
63390
|
|
|
63337
63391
|
// src/tools/feed.ts
|
|
63392
|
+
var FEED_VIEW = external_exports.enum(["compact", "full", "brief"]).optional().describe(
|
|
63393
|
+
`Response shape: "compact" (default) projects each check-in to {id, user, beer, brewery, venue, rating, comment, toast/comment counts}; "full" returns Untappd's whole ~5 KB record; "brief" returns one line per check-in: <user>: <beer> (<brewery>) at <venue> \u2014 <ISO date or "no date"> \u2014 <rated N|not rated>.`
|
|
63394
|
+
);
|
|
63338
63395
|
function registerFeedTools(server, client2) {
|
|
63339
63396
|
server.registerTool(
|
|
63340
63397
|
"untappd_activity_feed",
|
|
@@ -63342,14 +63399,16 @@ function registerFeedTools(server, client2) {
|
|
|
63342
63399
|
title: "Get Untappd friend activity feed",
|
|
63343
63400
|
description: "Get your Untappd friend activity feed \u2014 the recent check-ins from people you follow, newest first. Page backwards with max_id (the pagination.max_id from a prior call). Read-only.",
|
|
63344
63401
|
annotations: toolAnnotations({ title: "Get Untappd friend activity feed", readOnly: true, idempotent: false, openWorld: true }),
|
|
63402
|
+
_meta: foodMeta("whats-new", { canonical: true, aliases: ["beer", "check-ins", "friends drinking", "what are my friends drinking", "craft beer"] }),
|
|
63345
63403
|
inputSchema: external_exports.object({
|
|
63346
63404
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max check-ins (1\u201350, default 25)"),
|
|
63347
63405
|
max_id: external_exports.number().int().positive().optional().describe("Return check-ins older than this id (for paging)"),
|
|
63348
|
-
view:
|
|
63406
|
+
view: FEED_VIEW
|
|
63349
63407
|
})
|
|
63350
63408
|
},
|
|
63351
63409
|
async ({ limit, max_id, view }) => {
|
|
63352
63410
|
const data = await client2.get("/checkin/recent", { limit, max_id });
|
|
63411
|
+
if (view === "brief") return { content: [{ type: "text", text: briefCheckins(data) }] };
|
|
63353
63412
|
const v = resolveView(view, UNTAPPD_VIEWS);
|
|
63354
63413
|
return viewResult(v, v === "compact" ? compactCheckins(data) : data);
|
|
63355
63414
|
}
|
|
@@ -63360,6 +63419,7 @@ function registerFeedTools(server, client2) {
|
|
|
63360
63419
|
title: "Get Untappd check-in detail",
|
|
63361
63420
|
description: "Get full detail for a single check-in by its id: the beer, rating, comment, photos, venue, badges earned, toasts, and comments. Get a check-in id from a feed or user-checkins result. Read-only.",
|
|
63362
63421
|
annotations: toolAnnotations({ title: "Get Untappd check-in detail", readOnly: true, idempotent: true, openWorld: true }),
|
|
63422
|
+
_meta: foodMeta("lookup"),
|
|
63363
63423
|
inputSchema: external_exports.object({
|
|
63364
63424
|
checkin_id: external_exports.number().int().positive().describe("Untappd check-in id")
|
|
63365
63425
|
})
|
|
@@ -63407,6 +63467,7 @@ function registerResolveTools(server, client2) {
|
|
|
63407
63467
|
title: "Resolve an Untappd URL",
|
|
63408
63468
|
description: "Parse an untappd.com URL (a beer /b/, brewery /w/, venue /v/, user /user/, or check-in link) into its entity type and id, and name the tool to call next. Pure local parsing \u2014 no network. Read-only.",
|
|
63409
63469
|
annotations: toolAnnotations({ title: "Resolve an Untappd URL", readOnly: true, idempotent: true, openWorld: false }),
|
|
63470
|
+
_meta: foodMeta("lookup"),
|
|
63410
63471
|
inputSchema: external_exports.object({
|
|
63411
63472
|
url: external_exports.string().min(1).describe("An untappd.com URL to resolve")
|
|
63412
63473
|
})
|
|
@@ -63427,6 +63488,7 @@ function registerResolveTools(server, client2) {
|
|
|
63427
63488
|
title: "Open an Untappd URL (resolve + fetch)",
|
|
63428
63489
|
description: "Resolve an untappd.com URL AND fetch the entity detail in one call \u2014 the convenience combination of untappd_resolve + the matching info tool. Returns { resolved, detail }. Read-only.",
|
|
63429
63490
|
annotations: toolAnnotations({ title: "Open an Untappd URL", readOnly: true, idempotent: true, openWorld: true }),
|
|
63491
|
+
_meta: foodMeta("lookup"),
|
|
63430
63492
|
inputSchema: external_exports.object({
|
|
63431
63493
|
url: external_exports.string().min(1).describe("An untappd.com URL to resolve and fetch")
|
|
63432
63494
|
})
|
|
@@ -63453,6 +63515,7 @@ function registerDiscoverTools(server, client2) {
|
|
|
63453
63515
|
title: "Get trending beers",
|
|
63454
63516
|
description: "Get the beers trending on Untappd right now, split into macro (big/widely-available) and micro (craft/independent) lists. Read-only.",
|
|
63455
63517
|
annotations: toolAnnotations({ title: "Get trending beers", readOnly: true, idempotent: false, openWorld: true }),
|
|
63518
|
+
_meta: foodMeta("lookup", { aliases: ["popular beers", "trending beers"] }),
|
|
63456
63519
|
inputSchema: external_exports.object({})
|
|
63457
63520
|
},
|
|
63458
63521
|
async () => {
|
|
@@ -63466,6 +63529,7 @@ function registerDiscoverTools(server, client2) {
|
|
|
63466
63529
|
title: "Get your Untappd notifications",
|
|
63467
63530
|
description: "Get your Untappd notifications \u2014 toasts, comments, friend requests, and badges earned on YOUR account, plus news items. Read-only.",
|
|
63468
63531
|
annotations: toolAnnotations({ title: "Get your Untappd notifications", readOnly: true, idempotent: false, openWorld: true }),
|
|
63532
|
+
_meta: foodMeta("whats-new", { aliases: ["toasts", "badges", "friend requests", "beer notifications"] }),
|
|
63469
63533
|
inputSchema: external_exports.object({
|
|
63470
63534
|
limit: external_exports.number().int().min(1).max(50).optional().describe("Max notifications (1\u201350, default 25)"),
|
|
63471
63535
|
offset: external_exports.number().int().min(0).optional().describe("Result offset for paging (default 0)")
|
|
@@ -63482,6 +63546,7 @@ function registerDiscoverTools(server, client2) {
|
|
|
63482
63546
|
title: "Get nearby Untappd check-ins",
|
|
63483
63547
|
description: "Get recent check-ins near a location (lat/lng) \u2014 what people are drinking nearby right now. Optionally widen the search radius. Read-only.",
|
|
63484
63548
|
annotations: toolAnnotations({ title: "Get nearby Untappd check-ins", readOnly: true, idempotent: false, openWorld: true }),
|
|
63549
|
+
_meta: foodMeta("lookup", { aliases: ["drinking nearby"] }),
|
|
63485
63550
|
inputSchema: external_exports.object({
|
|
63486
63551
|
lat: external_exports.number().min(-90).max(90).describe("Latitude of the location"),
|
|
63487
63552
|
lng: external_exports.number().min(-180).max(180).describe("Longitude of the location"),
|
|
@@ -63525,28 +63590,32 @@ var ACTIONS = [
|
|
|
63525
63590
|
path: "request",
|
|
63526
63591
|
title: "Send an Untappd friend request",
|
|
63527
63592
|
verb: "send a friend request to",
|
|
63528
|
-
detail: "Sends a friend request to another user, who will be notified."
|
|
63593
|
+
detail: "Sends a friend request to another user, who will be notified.",
|
|
63594
|
+
intent: "send"
|
|
63529
63595
|
},
|
|
63530
63596
|
{
|
|
63531
63597
|
tool: "untappd_accept_friend",
|
|
63532
63598
|
path: "accept",
|
|
63533
63599
|
title: "Accept an Untappd friend request",
|
|
63534
63600
|
verb: "accept the incoming friend request from",
|
|
63535
|
-
detail: "Accepts an incoming friend request (see untappd_pending_friends for pending uids)."
|
|
63601
|
+
detail: "Accepts an incoming friend request (see untappd_pending_friends for pending uids).",
|
|
63602
|
+
intent: "change"
|
|
63536
63603
|
},
|
|
63537
63604
|
{
|
|
63538
63605
|
tool: "untappd_reject_friend",
|
|
63539
63606
|
path: "reject",
|
|
63540
63607
|
title: "Reject an Untappd friend request",
|
|
63541
63608
|
verb: "reject the incoming friend request from",
|
|
63542
|
-
detail: "Rejects/ignores an incoming friend request (see untappd_pending_friends for pending uids)."
|
|
63609
|
+
detail: "Rejects/ignores an incoming friend request (see untappd_pending_friends for pending uids).",
|
|
63610
|
+
intent: "change"
|
|
63543
63611
|
},
|
|
63544
63612
|
{
|
|
63545
63613
|
tool: "untappd_remove_friend",
|
|
63546
63614
|
path: "remove",
|
|
63547
63615
|
title: "Remove an Untappd friend",
|
|
63548
63616
|
verb: "remove your friendship with",
|
|
63549
|
-
detail: "Removes an existing friend, or cancels a friend request you sent."
|
|
63617
|
+
detail: "Removes an existing friend, or cancels a friend request you sent.",
|
|
63618
|
+
intent: "change"
|
|
63550
63619
|
}
|
|
63551
63620
|
];
|
|
63552
63621
|
function registerFriendActionTools(server, client2) {
|
|
@@ -63561,6 +63630,7 @@ function registerFriendActionTools(server, client2) {
|
|
|
63561
63630
|
// un-sent, and a rejection cannot be un-rejected, so none of them has an
|
|
63562
63631
|
// inverse in this tool set.
|
|
63563
63632
|
annotations: toolAnnotations({ title: action.title, readOnly: false, idempotent: true, openWorld: true, destructive: true }),
|
|
63633
|
+
_meta: foodMeta(action.intent, action.intent === "send" ? { aliases: ["friend request", "add a friend"] } : {}),
|
|
63564
63634
|
inputSchema: external_exports.object({
|
|
63565
63635
|
target_uid: TargetUidSchema,
|
|
63566
63636
|
confirmToken: confirmTokenParam
|
|
@@ -63596,6 +63666,7 @@ function registerWishlistTools(server, client2) {
|
|
|
63596
63666
|
title: "Add a beer to your wishlist",
|
|
63597
63667
|
description: `Add a beer to YOUR Untappd wishlist by its bid. Writes to your account. ${CONFIRM_FLOW}`,
|
|
63598
63668
|
annotations: toolAnnotations({ title: "Add a beer to your wishlist", readOnly: false, idempotent: true, openWorld: true, destructive: false }),
|
|
63669
|
+
_meta: foodMeta("change", { aliases: ["want to try", "save a beer"] }),
|
|
63599
63670
|
inputSchema: external_exports.object({
|
|
63600
63671
|
bid: BidSchema2,
|
|
63601
63672
|
confirmToken: confirmTokenParam
|
|
@@ -63625,6 +63696,7 @@ function registerWishlistTools(server, client2) {
|
|
|
63625
63696
|
title: "Remove a beer from your wishlist",
|
|
63626
63697
|
description: `Remove a beer from YOUR Untappd wishlist by its bid. Writes to your account. ${CONFIRM_FLOW}`,
|
|
63627
63698
|
annotations: toolAnnotations({ title: "Remove a beer from your wishlist", readOnly: false, idempotent: true, openWorld: true, destructive: false }),
|
|
63699
|
+
_meta: foodMeta("change"),
|
|
63628
63700
|
inputSchema: external_exports.object({
|
|
63629
63701
|
bid: BidSchema2,
|
|
63630
63702
|
confirmToken: confirmTokenParam
|
|
@@ -63770,6 +63842,7 @@ function registerCheckinTools(server, client2) {
|
|
|
63770
63842
|
description: `Toast (like) a check-in on YOUR account. This endpoint is a TOGGLE: calling it on a check-in you have already toasted removes the toast. Writes to your Untappd account and is visible to others. ${CONFIRM_FLOW}`,
|
|
63771
63843
|
// Destructive: a toast lands in the check-in owner's notifications, and toggling it back off cannot un-notify them.
|
|
63772
63844
|
annotations: toolAnnotations({ title: "Toast an Untappd check-in", readOnly: false, idempotent: false, openWorld: true, destructive: true }),
|
|
63845
|
+
_meta: foodMeta("send", { aliases: ["toast", "cheers"] }),
|
|
63773
63846
|
inputSchema: external_exports.object({
|
|
63774
63847
|
checkin_id: CheckinIdSchema,
|
|
63775
63848
|
confirmToken: confirmTokenParam
|
|
@@ -63808,6 +63881,7 @@ function registerCheckinTools(server, client2) {
|
|
|
63808
63881
|
description: "Post a comment on a check-in from YOUR account. Writes to your Untappd account and is visible to others. " + CONFIRM_FLOW,
|
|
63809
63882
|
// Destructive: the comment reaches the check-in owner (and other readers); untappd_delete_comment removes it but cannot un-notify anyone.
|
|
63810
63883
|
annotations: toolAnnotations({ title: "Comment on an Untappd check-in", readOnly: false, idempotent: false, openWorld: true, destructive: true }),
|
|
63884
|
+
_meta: foodMeta("send", { aliases: ["comment"] }),
|
|
63811
63885
|
inputSchema: external_exports.object({
|
|
63812
63886
|
checkin_id: CheckinIdSchema,
|
|
63813
63887
|
comment: external_exports.string().min(1).max(2e3).describe("Comment text to post"),
|
|
@@ -63847,6 +63921,7 @@ function registerCheckinTools(server, client2) {
|
|
|
63847
63921
|
title: "Delete a comment from an Untappd check-in",
|
|
63848
63922
|
description: `Delete one of YOUR comments by its comment id (the id from a check-in's comments list). ${CONFIRM_FLOW}`,
|
|
63849
63923
|
annotations: toolAnnotations({ title: "Delete a comment from an Untappd check-in", readOnly: false, idempotent: true, openWorld: true, destructive: true }),
|
|
63924
|
+
_meta: foodMeta("change"),
|
|
63850
63925
|
inputSchema: external_exports.object({
|
|
63851
63926
|
comment_id: external_exports.number().int().positive().describe("Untappd comment id (from a check-in's comments.items)"),
|
|
63852
63927
|
confirmToken: confirmTokenParam
|
|
@@ -63876,6 +63951,7 @@ function registerCheckinTools(server, client2) {
|
|
|
63876
63951
|
title: "Delete an Untappd check-in",
|
|
63877
63952
|
description: `Permanently delete one of YOUR check-ins by its id. This is destructive and cannot be undone. ${CONFIRM_FLOW}`,
|
|
63878
63953
|
annotations: toolAnnotations({ title: "Delete an Untappd check-in", readOnly: false, idempotent: true, openWorld: true, destructive: true }),
|
|
63954
|
+
_meta: foodMeta("change"),
|
|
63879
63955
|
inputSchema: external_exports.object({
|
|
63880
63956
|
checkin_id: CheckinIdSchema,
|
|
63881
63957
|
confirmToken: confirmTokenParam
|
|
@@ -63911,6 +63987,7 @@ function registerCheckinTools(server, client2) {
|
|
|
63911
63987
|
description: `Post a NEW beer check-in to YOUR Untappd account \u2014 this publishes to your public feed. Provide the beer id (bid) from untappd_search_beer; optionally a rating (0\u20135 in 0.25 steps), a shout (comment), a venue via foursquare_id, and a local photo via photo_path (JPEG/PNG). The preview shows the exact fields and photo that will be posted. ${CONFIRM_FLOW}`,
|
|
63912
63988
|
// Destructive: it publishes to your public feed, where friends see it; untappd_delete_checkin cannot unpublish what they saw.
|
|
63913
63989
|
annotations: toolAnnotations({ title: "Check in a beer on Untappd", readOnly: false, idempotent: false, openWorld: true, destructive: true }),
|
|
63990
|
+
_meta: foodMeta("change", { aliases: ["check in", "log a beer", "rate a beer", "drinking"] }),
|
|
63914
63991
|
inputSchema: external_exports.object({
|
|
63915
63992
|
bid: external_exports.number().int().positive().describe("Untappd beer id to check in (from untappd_search_beer)"),
|
|
63916
63993
|
rating: RatingSchema.optional().describe("Rating 0\u20135 in 0.25 increments (omit for no rating)"),
|
|
@@ -65049,6 +65126,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65049
65126
|
title: "Show the local cache location, schema and per-user coverage",
|
|
65050
65127
|
description: "Diagnostics for the LOCAL check-in cache \u2014 NO API call. Returns the resolved cache file path, its size on disk (db + WAL), journal mode, schema version, the number of cached menu-line resolutions and beer-metadata rows, cached venues, and for every cached user: cached vs total distinct beers, cached check-ins, coverage_complete, the resume offset of an unfinished user/beers backfill, last_synced_at, and venue-history coverage (venues: cached vs total, complete, percent, next_offset; null if never synced), wishlist and Untappd brewery-likes mirror coverage (wishlist / brewery_likes: cached vs total, complete; null if never synced). Also reports rate_limit: API calls made in the last hour (this process plus any other sharing the cache file), the ~100/hour limit, the estimated calls left, the raw X-Ratelimit-* headers (the v4 API sends Remaining: 0 on every success, so it is ignored until it reports a live value) and any 429 wait. Use it to confirm the cache persisted across restarts, to see how far a backfill has got, or to check whether a sync can run now.",
|
|
65051
65128
|
annotations: toolAnnotations({ title: "Show the local cache location, schema and per-user coverage", readOnly: true, idempotent: true, openWorld: false }),
|
|
65129
|
+
_meta: foodMeta("lookup"),
|
|
65052
65130
|
inputSchema: external_exports.object({})
|
|
65053
65131
|
},
|
|
65054
65132
|
async () => {
|
|
@@ -65072,6 +65150,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65072
65150
|
title: "Sync a user's check-ins into the cache",
|
|
65073
65151
|
description: "Fetch a user's detailed check-ins (venue, date, comment) into the cache from user/checkins. Incremental and resumable: pages backwards up to max_pages per call, persisting progress every page; run again until another_run_needed is false. NOTE: Untappd only returns the ~50 most recent check-ins for accounts other than your own and will not page further \u2014 such a sync reports history_truncated and you should use untappd_sync_user_beers for full has-had coverage. backfill_complete is only reported once ~all of total_checkins is cached. Pass force_backfill: true to reset a cache wrongly marked complete and re-page the whole history (cached rows are kept). Stops BEFORE the ~100 calls/hour rate limit (or cleanly on a 429) with stopped_for_rate_limit and rate_limit.resume_at \u2014 progress is saved; re-run after that time. Omit username for your own account.",
|
|
65074
65152
|
annotations: toolAnnotations({ title: "Sync a user's check-ins into the cache", readOnly: false, idempotent: false, openWorld: true, destructive: false }),
|
|
65153
|
+
_meta: foodMeta("other"),
|
|
65075
65154
|
inputSchema: external_exports.object({
|
|
65076
65155
|
username: UsernameArg2,
|
|
65077
65156
|
max_pages: external_exports.number().int().min(1).max(50).optional().describe("Pages (50 check-ins each) to fetch this run (default 10). Keep modest to respect the ~100 calls/hour rate limit."),
|
|
@@ -65094,6 +65173,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65094
65173
|
title: "Sync a user's complete distinct-beers list into the cache",
|
|
65095
65174
|
description: `Fetch a user's COMPLETE distinct-beers list (every unique beer they've ever had, with their rating, times-had count, and first/last dates) into the cache from user/beers. This is the cheapest way to get full "has had" coverage \u2014 thousands of beers instead of tens of thousands of check-ins \u2014 and, unlike user/checkins, it pages fully for any public/friend account. Offset-paged and resumable: fetches max_pages per call and persists progress; run again until another_run_needed is false. Feeds the same untappd_cache_has_had / has_had_many / not_had tools. Stops BEFORE the ~100 calls/hour rate limit (or cleanly on a 429) with stopped_for_rate_limit and rate_limit.resume_at \u2014 progress is saved; re-run after that time. Omit username for your own account.`,
|
|
65096
65175
|
annotations: toolAnnotations({ title: "Sync a user's complete distinct-beers list into the cache", readOnly: false, idempotent: false, openWorld: true, destructive: false }),
|
|
65176
|
+
_meta: foodMeta("other"),
|
|
65097
65177
|
inputSchema: external_exports.object({
|
|
65098
65178
|
username: UsernameArg2,
|
|
65099
65179
|
max_pages: external_exports.number().int().min(1).max(50).optional().describe("Pages (50 beers each) to fetch this run (default 10). Keep modest to respect the ~100 calls/hour rate limit.")
|
|
@@ -65112,6 +65192,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65112
65192
|
title: "Sync a user's complete venue history into the cache",
|
|
65113
65193
|
description: `Fetch a user's COMPLETE venue history (every venue they've checked in at, with first/most-recent visit dates and visit count) into the cache from user/venues. Unlike user/checkins \u2014 which only returns the ~50 most recent check-ins for anyone but yourself \u2014 user/venues pages any public/friend account's whole history, so this is how to answer "has this person been here?" (via untappd_cache_has_visited). Untappd serves 25 venues per page, so a 2,700-venue history is ~110 calls: resumable \u2014 fetches max_pages per call, saves progress after every page; run again until another_run_needed is false. Once complete, a re-run is a cheap refresh from the top that stops at the first page with no changed visits (a refresh cut short resumes where it stopped; another_run_needed stays true until it finishes). Stops BEFORE the ~100 calls/hour rate limit (or cleanly on a 429) with stopped_for_rate_limit and rate_limit.resume_at. Omit username for your own account.`,
|
|
65114
65194
|
annotations: toolAnnotations({ title: "Sync a user's complete venue history into the cache", readOnly: false, idempotent: false, openWorld: true, destructive: false }),
|
|
65195
|
+
_meta: foodMeta("other"),
|
|
65115
65196
|
inputSchema: external_exports.object({
|
|
65116
65197
|
username: UsernameArg2,
|
|
65117
65198
|
max_pages: external_exports.number().int().min(1).max(VENUES_MAX_PAGES_CAP).optional().describe("Pages (25 venues each) to fetch this run (default 10, max 50). Keep modest to respect the ~100 calls/hour rate limit.")
|
|
@@ -65130,6 +65211,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65130
65211
|
title: "Sync a user's wishlist into the cache",
|
|
65131
65212
|
description: "Mirror a user's Untappd wishlist (user/wishlist) into the local cache, so pick tools can boost wishlisted beers without an API call. Each sync is a full pass from the top (~25 beers per call; wishlists are small), adding new beers and \u2014 once the pass finishes \u2014 dropping ones no longer wishlisted (a partial pass never deletes). Resumable: saves progress every page; run again until another_run_needed is false. Stops BEFORE the ~100 calls/hour rate limit (or cleanly on a 429) with stopped_for_rate_limit and rate_limit.resume_at. Only reads Untappd \u2014 never changes the wishlist (that is untappd_wishlist_add / _remove). Omit username for your own account; another user's wishlist needs a public account or friendship.",
|
|
65132
65213
|
annotations: toolAnnotations({ title: "Sync a user's wishlist into the cache", readOnly: false, idempotent: false, openWorld: true, destructive: false }),
|
|
65214
|
+
_meta: foodMeta("other"),
|
|
65133
65215
|
inputSchema: external_exports.object({
|
|
65134
65216
|
username: UsernameArg2,
|
|
65135
65217
|
max_pages: external_exports.number().int().min(1).max(WISHLIST_MAX_PAGES_CAP).optional().describe(`Pages (~25 beers each) to fetch this run (default 10, max ${WISHLIST_MAX_PAGES_CAP}).`)
|
|
@@ -65154,6 +65236,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65154
65236
|
openWorld: true,
|
|
65155
65237
|
destructive: false
|
|
65156
65238
|
}),
|
|
65239
|
+
_meta: foodMeta("other"),
|
|
65157
65240
|
inputSchema: external_exports.object({
|
|
65158
65241
|
username: UsernameArg2,
|
|
65159
65242
|
max_pages: external_exports.number().int().min(1).max(BREWERY_LIKES_MAX_PAGES_CAP).optional().describe(`Pages (~50 breweries each) to fetch this run (default 10, max ${BREWERY_LIKES_MAX_PAGES_CAP}).`)
|
|
@@ -65178,6 +65261,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65178
65261
|
openWorld: true,
|
|
65179
65262
|
destructive: false
|
|
65180
65263
|
}),
|
|
65264
|
+
_meta: foodMeta("other"),
|
|
65181
65265
|
inputSchema: external_exports.object({
|
|
65182
65266
|
username: UsernameArg2,
|
|
65183
65267
|
max_calls: external_exports.number().int().min(1).max(VENUE_DRINKS_MAX_CALLS_CAP).optional().describe(`checkin/view calls this run (default ${VENUE_DRINKS_DEFAULT_CALLS}, max ${VENUE_DRINKS_MAX_CALLS_CAP}). Each is one of the ~100 hourly calls.`),
|
|
@@ -65210,6 +65294,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65210
65294
|
title: "Check if a user has been to a venue (from the cache)",
|
|
65211
65295
|
description: `Answer "has this user been here?" for one or many places in ONE call, from the cached venue history only \u2014 NO API call. Each query matches by exact venue_ids, by name (case-insensitive substring or all significant words, optionally narrowed by city/state), and/or by lat/lng within radius_m (default 150 m); venue_ids are unioned with the name/geo match, and name + lat/lng together must both hold. Name/location matching matters because a brewery_id is NOT its taproom's venue_id, a brewery can have several venues, and Untappd keeps "\u2026 (duplicate of \u2026)" venues \u2014 so expect, and check, several matches. Returns per query: visited, the matched venues (first_visit, recent_visit, total_count, is_duplicate, distance_m, and \u2014 once untappd_sync_venue_drinks has fetched them \u2014 drinks: the beer from the first/recent visit check-ins \u2014 and favorite {is_favorite, sources, reason}: derived from the visit history or added with untappd_favorite_add), and total_visits; plus a freshness block (last_synced_at, coverage_complete, caveat). Run untappd_sync_user_venues first.`,
|
|
65212
65296
|
annotations: toolAnnotations({ title: "Check if a user has been to a venue (from the cache)", readOnly: true, idempotent: true, openWorld: false }),
|
|
65297
|
+
_meta: foodMeta("lookup"),
|
|
65213
65298
|
inputSchema: external_exports.object({
|
|
65214
65299
|
username: UsernameArg2,
|
|
65215
65300
|
queries: external_exports.array(VenueQueryArg).min(1).max(50).describe("Places to check (1\u201350); each needs venue_ids, name, or lat+lng"),
|
|
@@ -65285,6 +65370,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65285
65370
|
title: "Check if a user has had a beer (from the cache)",
|
|
65286
65371
|
description: 'Answer "has this user ever checked in this beer?" from the cache only \u2014 NO API call. Consults BOTH cached sources (check-ins and the distinct-beers list); a hit in either counts as had. Match by exact bid or a case-insensitive substring of the beer name. Returns whether they had it, the times-had count, best rating, last date, which sources matched, any detailed check-ins, and favorite {is_favorite, sources, reason} (rated \u22654.25 / had \u22653 times, or added with untappd_favorite_add). Reports per-source freshness so you can caveat incomplete data. Requires bid or beer_name. Run untappd_sync_user_beers first for full coverage.',
|
|
65287
65372
|
annotations: toolAnnotations({ title: "Check if a user has had a beer (from the cache)", readOnly: true, idempotent: true, openWorld: false }),
|
|
65373
|
+
_meta: foodMeta("lookup", { aliases: ["have i had", "tried before"] }),
|
|
65288
65374
|
inputSchema: external_exports.object({
|
|
65289
65375
|
username: UsernameArg2,
|
|
65290
65376
|
bid: external_exports.number().int().positive().optional().describe("Exact Untappd beer id to look for"),
|
|
@@ -65310,6 +65396,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65310
65396
|
title: "Batch-check many beers against the cache",
|
|
65311
65397
|
description: `Cross-check a list of beer ids against a user's cached history in ONE call \u2014 NO API call. Consults BOTH sources (check-ins + distinct beers). Returns had/not-had per bid (with count and last date when had), plus favorite {is_favorite, sources, reason} per bid and brewery_favorite when its brewery (from cached beer metadata) is a favorite. Ideal for checking a whole venue menu at once. Pass usernames (instead of username) to check a GROUP: each bid then lists had_by / not_had_by and qualifies per mode \u2014 "none_had" (default: new to everyone) or "any_not_had" (at least one of them still needs it) \u2014 with qualifying_bids and a per-user freshness block. Run untappd_sync_user_beers first (for every user); the freshness block flags if coverage is incomplete.`,
|
|
65312
65398
|
annotations: toolAnnotations({ title: "Batch-check many beers against the cache", readOnly: true, idempotent: true, openWorld: false }),
|
|
65399
|
+
_meta: foodMeta("lookup"),
|
|
65313
65400
|
inputSchema: external_exports.object({
|
|
65314
65401
|
username: UsernameArg2,
|
|
65315
65402
|
usernames: UsernamesArg,
|
|
@@ -65369,6 +65456,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65369
65456
|
title: "From a list of beers, return the ones a user has NOT had",
|
|
65370
65457
|
description: 'Given a list of beer ids, return only the ones the user has NOT had \u2014 the "what here is new to me?" filter for a venue menu, a brewery lineup, or a festival list. Consults BOTH cached sources; reads the cache only, NO API call. Returns the not-had bids (plus the had bids and counts) and cache freshness. Run untappd_sync_user_beers first; if coverage is incomplete the freshness caveat flags that a "not had" may be a false negative.',
|
|
65371
65458
|
annotations: toolAnnotations({ title: "From a list of beers, return the ones a user has NOT had", readOnly: true, idempotent: true, openWorld: false }),
|
|
65459
|
+
_meta: foodMeta("lookup"),
|
|
65372
65460
|
inputSchema: external_exports.object({
|
|
65373
65461
|
username: UsernameArg2,
|
|
65374
65462
|
bids: external_exports.array(external_exports.number().int().positive()).min(1).max(500).describe("Candidate beer ids to filter (1\u2013500)")
|
|
@@ -65398,6 +65486,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65398
65486
|
title: "Query cached check-ins with filters",
|
|
65399
65487
|
description: "Query a user's cached CHECK-INS by brewery, style, minimum rating, venue, and/or date range, with sorting and a limit \u2014 from the cache only, NO API call. Reflects the detailed check-ins table (venue/date), which for non-self accounts is only the recent window; for full coverage of which beers a user has had, use untappd_cache_has_had / not_had instead. Run untappd_sync_checkins first.",
|
|
65400
65488
|
annotations: toolAnnotations({ title: "Query cached check-ins with filters", readOnly: true, idempotent: true, openWorld: false }),
|
|
65489
|
+
_meta: foodMeta("lookup"),
|
|
65401
65490
|
inputSchema: external_exports.object({
|
|
65402
65491
|
username: UsernameArg2,
|
|
65403
65492
|
brewery_id: external_exports.number().int().positive().optional().describe("Exact brewery id"),
|
|
@@ -65425,6 +65514,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65425
65514
|
title: "Top-rated beers a user has NOT had, from a candidate list",
|
|
65426
65515
|
description: `The "what should I order off this tap list?" tool. From a list of candidate beer ids, return the top N the user has NOT yet had, ranked by Untappd global rating, with an optional style filter. Not-had filtering uses the cache only (both sources, no API call). Beer ratings/styles come from a metadata cache; a beer/info API call is made only on a cache miss or if the cached metadata is >30 days old, capped at api_budget calls per run (~100 calls/hour limit) \u2014 if more are needed it returns partial: true / another_run_needed: true, so re-running fills the rest. A beer whose refresh is deferred or fails is still ranked on its older cached metadata and marked stale: true. Reports the same freshness/caveat block as untappd_cache_not_had. Omit username for your own account. For a GROUP pass usernames: mode "none_had" (default) ranks only beers new to everyone, "any_not_had" ranks beers at least one of them still needs (each ranked beer lists not_had_by / had_by). prefer_favorites (default true) lifts wishlisted beers (+${WISHLIST_BOOST}) and beers from a favorite brewery (+${FAVORITE_BREWERY_BOOST}) \u2014 liked on Untappd, derived or manual, any user's in a group; the wishlist needs untappd_sync_wishlist and Untappd likes untappd_sync_brewery_likes \u2014 and says so per beer (score, boosted_by, wishlisted, brewery_favorite/favorite).`,
|
|
65427
65516
|
annotations: toolAnnotations({ title: "Top-rated beers a user has NOT had, from a candidate list", readOnly: false, idempotent: false, openWorld: true, destructive: false }),
|
|
65517
|
+
_meta: foodMeta("other", { aliases: ["what should i order", "beers i have not had"] }),
|
|
65428
65518
|
inputSchema: external_exports.object({
|
|
65429
65519
|
username: UsernameArg2,
|
|
65430
65520
|
usernames: UsernamesArg,
|
|
@@ -65561,6 +65651,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
65561
65651
|
title: "Forget a user's cached check-ins",
|
|
65562
65652
|
description: `Delete everything the LOCAL cache holds for one user \u2014 their cached check-ins (beer, rating, comment, venue, date), distinct-beers list, venue history, wishlist copy, local favorites, venue drinks and sync state \u2014 e.g. after syncing a friend you no longer want a history of. Only the local cache is touched; nothing on Untappd changes, and a later sync can re-fetch it (manual favorites cannot be re-fetched). Shared beer, venue and brewery facts are kept. The preview shows the username and exactly how many rows will be removed. Omit username for your own account. ${CONFIRM_FLOW}`,
|
|
65563
65653
|
annotations: toolAnnotations({ title: "Forget a user's cached check-ins", readOnly: false, idempotent: true, openWorld: false, destructive: true }),
|
|
65654
|
+
_meta: foodMeta("change"),
|
|
65564
65655
|
inputSchema: external_exports.object({
|
|
65565
65656
|
username: UsernameArg2,
|
|
65566
65657
|
confirmToken: confirmTokenParam
|
|
@@ -65617,6 +65708,7 @@ function registerFavoriteTools(server, client2, cacheProvider) {
|
|
|
65617
65708
|
title: "Mark a beer, brewery or venue as a favorite (local)",
|
|
65618
65709
|
description: `Add a MANUAL favorite \u2014 a beer, brewery or venue \u2014 with an optional note, to the local cache. ${LOCAL_ONLY} Re-adding an existing favorite updates its note (omit note to keep it) and keeps its created_at. Manual favorites show in untappd_favorites_list (source "manual") and as the \`favorite\` field on has_had, has_had_many, has_visited, brewery_beers, venue_menu, top_not_had and trip_check results; a favorite brewery also boosts its beers in untappd_top_not_had. Omit username for your own account.`,
|
|
65619
65710
|
annotations: toolAnnotations({ title: "Mark a beer, brewery or venue as a favorite (local)", readOnly: false, idempotent: true, openWorld: false, destructive: false }),
|
|
65711
|
+
_meta: foodMeta("change"),
|
|
65620
65712
|
inputSchema: external_exports.object({
|
|
65621
65713
|
username: UsernameArg2,
|
|
65622
65714
|
kind: KindArg,
|
|
@@ -65638,6 +65730,7 @@ function registerFavoriteTools(server, client2, cacheProvider) {
|
|
|
65638
65730
|
title: "Remove a manual favorite (local)",
|
|
65639
65731
|
description: `Remove a MANUAL favorite (beer, brewery or venue) from the local cache. ${LOCAL_ONLY} Returns removed:false when there was none. A DERIVED favorite (from the user's history) cannot be removed \u2014 it is computed from the thresholds; change them in untappd_favorites_list instead. Omit username for your own account.`,
|
|
65640
65732
|
annotations: toolAnnotations({ title: "Remove a manual favorite (local)", readOnly: false, idempotent: true, openWorld: false, destructive: false }),
|
|
65733
|
+
_meta: foodMeta("change"),
|
|
65641
65734
|
inputSchema: external_exports.object({ username: UsernameArg2, kind: KindArg, id: IdArg })
|
|
65642
65735
|
},
|
|
65643
65736
|
async ({ username, kind, id }) => {
|
|
@@ -65652,6 +65745,7 @@ function registerFavoriteTools(server, client2, cacheProvider) {
|
|
|
65652
65745
|
title: "List a user's favorite beers, breweries and venues",
|
|
65653
65746
|
description: `List a user's favorites from the cache only \u2014 NO API call. Each item appears ONCE with every source that names it (sources: "derived" = qualifies from their cached history, "manual" = added with untappd_favorite_add, "untappd" = a brewery the user likes/follows on Untappd, mirrored by untappd_sync_brewery_likes \u2014 breweries only: Untappd has no followed/favorite venue list) and a reason ("liked on Untappd (since 2021-03)", "12 check-ins since 2015", "avg 4.1 across 9 beers", "manual: <note>"). Derived rules, each overridable via thresholds: venue = \u2265${D.venue_min_checkins} check-ins OR visits spanning \u2265${D.venue_min_months} distinct months (ranked by check-ins); brewery = \u2265${D.brewery_min_beers} beers had AND avg rating \u2265${D.brewery_min_avg_rating}, OR \u2265${D.brewery_min_beers_any_rating} beers (ranked by count blended with rating); beer = rated \u2265${D.beer_min_rating} OR had \u2265${D.beer_min_times_had} times. Derived favorites are only as complete as the syncs behind them (untappd_sync_user_venues, untappd_sync_user_beers); "untappd" ones as the last untappd_sync_brewery_likes (freshness.brewery_likes). Filter by kind and source. Omit username for your own account.`,
|
|
65654
65747
|
annotations: toolAnnotations({ title: "List a user's favorite beers, breweries and venues", readOnly: true, idempotent: true, openWorld: false }),
|
|
65748
|
+
_meta: foodMeta("lookup"),
|
|
65655
65749
|
inputSchema: external_exports.object({
|
|
65656
65750
|
username: UsernameArg2,
|
|
65657
65751
|
kind: external_exports.enum(FAVORITE_KINDS).optional().describe("Only this kind (default all three)"),
|
|
@@ -65721,6 +65815,7 @@ function registerTripTools(server, client2, cacheProvider) {
|
|
|
65721
65815
|
idempotent: true,
|
|
65722
65816
|
openWorld: true
|
|
65723
65817
|
}),
|
|
65818
|
+
_meta: foodMeta("lookup", { aliases: ["brewery crawl", "bar crawl", "beer trip"] }),
|
|
65724
65819
|
inputSchema: external_exports.object({
|
|
65725
65820
|
username: UsernameArg2,
|
|
65726
65821
|
usernames: external_exports.array(external_exports.string().min(1)).min(1).max(MAX_GROUP_USERS).optional().describe(`Check a GROUP (1\u2013${MAX_GROUP_USERS}) instead of one user; use instead of username`),
|
|
@@ -66104,13 +66199,13 @@ function searchQueries(line) {
|
|
|
66104
66199
|
const noStyle = line.tokens.filter((t) => !STYLE_WORDS.has(t)).join(" ");
|
|
66105
66200
|
return [...new Set([full, noStyle].filter(Boolean))];
|
|
66106
66201
|
}
|
|
66107
|
-
var
|
|
66202
|
+
var dict2 = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
66108
66203
|
var numOr = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
|
|
66109
66204
|
var strOr = (v) => typeof v === "string" && v.trim() ? v.trim() : null;
|
|
66110
66205
|
function candidateFromSearch(item) {
|
|
66111
|
-
const i =
|
|
66112
|
-
const beer =
|
|
66113
|
-
const brewery =
|
|
66206
|
+
const i = dict2(item);
|
|
66207
|
+
const beer = dict2(i.beer);
|
|
66208
|
+
const brewery = dict2(i.brewery);
|
|
66114
66209
|
const bid = numOr(beer.bid, null);
|
|
66115
66210
|
if (bid === null) return null;
|
|
66116
66211
|
return {
|
|
@@ -66118,7 +66213,7 @@ function candidateFromSearch(item) {
|
|
|
66118
66213
|
name: strOr(beer.beer_name) ?? "",
|
|
66119
66214
|
brewery: strOr(brewery.brewery_name) ?? "",
|
|
66120
66215
|
brewery_id: numOr(brewery.brewery_id, null),
|
|
66121
|
-
brewery_city: strOr(
|
|
66216
|
+
brewery_city: strOr(dict2(brewery.location).brewery_city),
|
|
66122
66217
|
style: strOr(beer.beer_style),
|
|
66123
66218
|
abv: numOr(beer.beer_abv, null),
|
|
66124
66219
|
checkin_count: numOr(i.checkin_count, 0) ?? 0,
|
|
@@ -66604,6 +66699,7 @@ function registerMenuTools(server, client2, cacheProvider, deps = {}) {
|
|
|
66604
66699
|
title: "Pick beers off a tap list that a user hasn't had",
|
|
66605
66700
|
description: `One call from a tap list to "order this": give the raw menu lines as read from a photo and get back the top-rated beers the user has NOT had. It (1) refreshes the user's distinct-beers cache \u2014 an incremental refresh when the cache is complete, otherwise it backfills automatically within api_budget, no separate sync needed; (2) resolves each line to an Untappd beer (prices/punctuation stripped, scored on brewery + name match and check-in volume, venue_city as tie-breaker; resolutions are cached locally so repeat venues are instant); (3) treats "<Brewery> SEASONAL/ROTATING" as a list of possible beers (needs_confirmation) and "ROTATING CIDER"-style lines as unresolved ("ask bartender"); (4) checks every beer against the cache; (5) ranks the not-had ones by global rating. Returns picks, not_had, had, possible, unresolved, a sync block (what the refresh did) and freshness (coverage); if coverage is still incomplete a "not had" may be a false negative and another_run_needed is true. api_budget is also capped to the quota Untappd reports, when it reports one. Omit username for your own account.`,
|
|
66606
66701
|
annotations: toolAnnotations({ title: "Pick beers off a tap list that a user hasn't had", readOnly: false, idempotent: false, openWorld: true, destructive: false }),
|
|
66702
|
+
_meta: foodMeta("other", { aliases: ["what should i order", "tap list", "beer menu"] }),
|
|
66607
66703
|
inputSchema: external_exports.object({
|
|
66608
66704
|
username: UsernameArg2,
|
|
66609
66705
|
items: external_exports.array(external_exports.string().min(1).max(200)).min(1).max(60).describe('Raw menu lines as read from the menu, e.g. ["JACKALOPE FENNARIO IPA $7", "BLACK ABBEY SEASONAL", "ROTATING CIDER"].'),
|
|
@@ -66641,6 +66737,7 @@ function registerMenuTools(server, client2, cacheProvider, deps = {}) {
|
|
|
66641
66737
|
title: "Pick beers off a venue's Untappd menu that a user hasn't had",
|
|
66642
66738
|
description: "Like untappd_menu_pick, but takes an Untappd venue (URL or id) instead of menu lines: it pulls the venue's FULL verified menu (walking every section page until complete, within api_budget), refreshes the user's distinct-beers cache (incremental, or an automatic backfill), checks every menu beer against it and ranks the not-had ones by global rating. Returns the same picks / not_had / had / sync shape plus a venue block (menu_complete is false only if the budget ran out mid-menu). A venue with no verified menu comes back unresolved \u2014 use untappd_menu_pick with the tap list instead. Omit username for your own account.",
|
|
66643
66739
|
annotations: toolAnnotations({ title: "Pick beers off a venue's Untappd menu that a user hasn't had", readOnly: false, idempotent: false, openWorld: true, destructive: false }),
|
|
66740
|
+
_meta: foodMeta("other", { aliases: ["what should i order", "on tap"] }),
|
|
66644
66741
|
inputSchema: external_exports.object({
|
|
66645
66742
|
venue: external_exports.union([external_exports.string().min(1), external_exports.number().int().positive()]).describe("Untappd venue URL (https://untappd.com/v/<slug>/<id>) or numeric venue id."),
|
|
66646
66743
|
username: UsernameArg2,
|