untappd-mcp 2.3.0 → 2.3.2
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 +2 -2
- package/.mcp.json +3 -2
- package/README.md +7 -6
- package/dist/bundle.js +290 -102
- package/dist/cache/db.js +15 -6
- package/dist/cache/store.js +13 -11
- package/dist/cache/sync.js +6 -0
- package/dist/client.js +68 -25
- package/dist/tools/cache.js +22 -4
- package/dist/tools/checkin.js +53 -30
- package/dist/tools/confirm.js +3 -0
- package/dist/tools/friends.js +3 -1
- package/dist/tools/utilities.js +8 -2
- package/dist/tools/wishlist.js +6 -2
- package/dist/version.js +1 -1
- package/package.json +2 -3
- package/server.json +59 -10
- package/skills/untappd-mcp/SKILL.md +25 -9
package/dist/bundle.js
CHANGED
|
@@ -84,6 +84,7 @@ var require_util = __commonJS({
|
|
|
84
84
|
exports.explicitlyAborted = explicitlyAborted2;
|
|
85
85
|
exports.prefixIssues = prefixIssues2;
|
|
86
86
|
exports.unwrapMessage = unwrapMessage2;
|
|
87
|
+
exports.sizeCaps = sizeCaps2;
|
|
87
88
|
exports.attachSchema = attachSchema2;
|
|
88
89
|
exports.finalizeIssue = finalizeIssue2;
|
|
89
90
|
exports.getSizableOrigin = getSizableOrigin2;
|
|
@@ -618,6 +619,18 @@ var require_util = __commonJS({
|
|
|
618
619
|
function unwrapMessage2(message) {
|
|
619
620
|
return typeof message === "string" ? message : message?.message;
|
|
620
621
|
}
|
|
622
|
+
function sizeCaps2(defChecks) {
|
|
623
|
+
const caps = [];
|
|
624
|
+
for (const ch of defChecks ?? []) {
|
|
625
|
+
const d = ch._zod.def;
|
|
626
|
+
if (!/_(length|size)$|_equals$/.test(d.check))
|
|
627
|
+
break;
|
|
628
|
+
const max = d.maximum ?? d.length ?? d.size;
|
|
629
|
+
if (max >= 0)
|
|
630
|
+
caps.push([max, ch]);
|
|
631
|
+
}
|
|
632
|
+
return caps;
|
|
633
|
+
}
|
|
621
634
|
function attachSchema2(issues, start, inst) {
|
|
622
635
|
var _a3;
|
|
623
636
|
for (let i = start; i < issues.length; i++) {
|
|
@@ -2282,7 +2295,7 @@ var require_versions = __commonJS({
|
|
|
2282
2295
|
exports.version = {
|
|
2283
2296
|
major: 4,
|
|
2284
2297
|
minor: 6,
|
|
2285
|
-
patch:
|
|
2298
|
+
patch: 6
|
|
2286
2299
|
};
|
|
2287
2300
|
(function() {
|
|
2288
2301
|
var keys = Object.getOwnPropertyNames(exports);
|
|
@@ -3116,6 +3129,31 @@ var require_schemas = __commonJS({
|
|
|
3116
3129
|
return payload;
|
|
3117
3130
|
};
|
|
3118
3131
|
});
|
|
3132
|
+
function capSize2(inst, size) {
|
|
3133
|
+
const caps = util.sizeCaps(inst._zod.def.checks);
|
|
3134
|
+
if (!caps.length)
|
|
3135
|
+
return;
|
|
3136
|
+
const cap = Math.min(...caps.map((c) => c[0]));
|
|
3137
|
+
const parse3 = inst._zod.parse;
|
|
3138
|
+
inst._zod.parse = (payload, ctx) => {
|
|
3139
|
+
const n = size(payload.value);
|
|
3140
|
+
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
3141
|
+
for (const [max, ch] of util.sizeCaps(inst._zod.def.checks)) {
|
|
3142
|
+
if (n > max && ch._zod.def.when(payload)) {
|
|
3143
|
+
const start = payload.issues.length;
|
|
3144
|
+
ch._zod.check(payload);
|
|
3145
|
+
payload.issues[start].continue = false;
|
|
3146
|
+
util.attachSchema(payload.issues, start, inst);
|
|
3147
|
+
return payload;
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
return parse3(payload, ctx);
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3154
|
+
var arraySize2 = (input2) => Array.isArray(input2) ? input2.length : -1;
|
|
3155
|
+
var mapSize2 = (input2) => input2 instanceof Map ? input2.size : -1;
|
|
3156
|
+
var setSize2 = (input2) => input2 instanceof Set ? input2.size : -1;
|
|
3119
3157
|
function handleArrayResult2(result, final, index) {
|
|
3120
3158
|
if (result.issues.length) {
|
|
3121
3159
|
final.issues.push(...util.prefixIssues(index, result.issues));
|
|
@@ -3159,6 +3197,7 @@ var require_schemas = __commonJS({
|
|
|
3159
3197
|
}
|
|
3160
3198
|
return payload;
|
|
3161
3199
|
};
|
|
3200
|
+
capSize2(inst, arraySize2);
|
|
3162
3201
|
});
|
|
3163
3202
|
function handlePropertyResult2(result, final, key, input2, optin, optout) {
|
|
3164
3203
|
const isPresent = key in input2;
|
|
@@ -4115,6 +4154,7 @@ var require_schemas = __commonJS({
|
|
|
4115
4154
|
return Promise.all(proms).then(() => payload);
|
|
4116
4155
|
return payload;
|
|
4117
4156
|
};
|
|
4157
|
+
capSize2(inst, mapSize2);
|
|
4118
4158
|
});
|
|
4119
4159
|
function handleMapResult2(keyResult, valueResult, final, key, input2, inst, ctx) {
|
|
4120
4160
|
if (keyResult.issues.length) {
|
|
@@ -4181,6 +4221,7 @@ var require_schemas = __commonJS({
|
|
|
4181
4221
|
return Promise.all(proms).then(() => payload);
|
|
4182
4222
|
return payload;
|
|
4183
4223
|
};
|
|
4224
|
+
capSize2(inst, setSize2);
|
|
4184
4225
|
});
|
|
4185
4226
|
function handleSetResult2(result, final) {
|
|
4186
4227
|
if (result.issues.length) {
|
|
@@ -4851,7 +4892,7 @@ var require_memoizer = __commonJS({
|
|
|
4851
4892
|
var NONE2 = 0;
|
|
4852
4893
|
var ASSUMED2 = 1;
|
|
4853
4894
|
var PROVEN2 = 2;
|
|
4854
|
-
function isRecursive2(inst, stack,
|
|
4895
|
+
function isRecursive2(inst, stack, resolve4) {
|
|
4855
4896
|
const cached2 = recursive2.get(inst);
|
|
4856
4897
|
if (cached2 !== void 0)
|
|
4857
4898
|
return cached2 ? PROVEN2 : NONE2;
|
|
@@ -4861,7 +4902,7 @@ var require_memoizer = __commonJS({
|
|
|
4861
4902
|
let result = NONE2;
|
|
4862
4903
|
const check2 = (child) => {
|
|
4863
4904
|
if (result !== PROVEN2 && child?._zod) {
|
|
4864
|
-
const answer = isRecursive2(child, stack,
|
|
4905
|
+
const answer = isRecursive2(child, stack, resolve4);
|
|
4865
4906
|
if (answer > result)
|
|
4866
4907
|
result = answer;
|
|
4867
4908
|
}
|
|
@@ -4872,7 +4913,7 @@ var require_memoizer = __commonJS({
|
|
|
4872
4913
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
4873
4914
|
if (spread && !desc.enumerable)
|
|
4874
4915
|
continue;
|
|
4875
|
-
const child = desc.get ? ASSUMED2 : desc.value?._zod ? isRecursive2(desc.value, stack,
|
|
4916
|
+
const child = desc.get ? ASSUMED2 : desc.value?._zod ? isRecursive2(desc.value, stack, resolve4) : NONE2;
|
|
4876
4917
|
if (child > answer)
|
|
4877
4918
|
answer = child;
|
|
4878
4919
|
}
|
|
@@ -4936,7 +4977,7 @@ var require_memoizer = __commonJS({
|
|
|
4936
4977
|
break;
|
|
4937
4978
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
4938
4979
|
case "lazy": {
|
|
4939
|
-
const inner = def._cachedInner ?? (
|
|
4980
|
+
const inner = def._cachedInner ?? (resolve4 ? inst._zod.innerType : void 0);
|
|
4940
4981
|
merge2(inner ? isRecursive2(inner, stack, false) : ASSUMED2);
|
|
4941
4982
|
break;
|
|
4942
4983
|
}
|
|
@@ -16285,9 +16326,15 @@ ${code2}
|
|
|
16285
16326
|
doc.write(`}`);
|
|
16286
16327
|
return outputVar;
|
|
16287
16328
|
}
|
|
16329
|
+
function generateSizeGate2(doc, schema, size) {
|
|
16330
|
+
for (const [max, check2] of util.sizeCaps(schema._zod.def.checks)) {
|
|
16331
|
+
doc.write(`if (${size} > ${numericOperand2(max, check2._zod.def.check)}) return INVALID;`);
|
|
16332
|
+
}
|
|
16333
|
+
}
|
|
16288
16334
|
function generateArrayCheck2(doc, ctx, schema, accessor, buildsValue = true) {
|
|
16289
16335
|
const def = schema._zod.def;
|
|
16290
16336
|
doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
|
|
16337
|
+
generateSizeGate2(doc, schema, `${accessor}.length`);
|
|
16291
16338
|
const outputVar = buildsValue ? newVar2(ctx) : null;
|
|
16292
16339
|
const iVar = newVar2(ctx);
|
|
16293
16340
|
const elemVar = newVar2(ctx);
|
|
@@ -16687,6 +16734,7 @@ ${code2}
|
|
|
16687
16734
|
function generateMapCheck2(doc, ctx, schema, accessor) {
|
|
16688
16735
|
const def = schema._zod.def;
|
|
16689
16736
|
doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
|
|
16737
|
+
generateSizeGate2(doc, schema, `${accessor}.size`);
|
|
16690
16738
|
const outputVar = newVar2(ctx);
|
|
16691
16739
|
const kVar = newVar2(ctx);
|
|
16692
16740
|
const valVar = newVar2(ctx);
|
|
@@ -16703,6 +16751,7 @@ ${code2}
|
|
|
16703
16751
|
function generateSetCheck2(doc, ctx, schema, accessor) {
|
|
16704
16752
|
const def = schema._zod.def;
|
|
16705
16753
|
doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
|
|
16754
|
+
generateSizeGate2(doc, schema, `${accessor}.size`);
|
|
16706
16755
|
const outputVar = newVar2(ctx);
|
|
16707
16756
|
const valVar = newVar2(ctx);
|
|
16708
16757
|
doc.write(`const ${outputVar} = new Set();`);
|
|
@@ -23400,7 +23449,7 @@ function declaredDialect(schema, remedy) {
|
|
|
23400
23449
|
throw new Error(`JSON Schema declares an unsupported dialect ("$schema": "${schema.$schema.slice(0, 200)}"). The default validator supports JSON Schema 2020-12, 2019-09, draft-07, and draft-06; ${remedy}`);
|
|
23401
23450
|
}
|
|
23402
23451
|
|
|
23403
|
-
// node_modules/@modelcontextprotocol/core/dist/auth-BNDyLTqp.mjs
|
|
23452
|
+
// node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core/dist/auth-BNDyLTqp.mjs
|
|
23404
23453
|
var z = __toESM(require_v4(), 1);
|
|
23405
23454
|
var LATEST_PROTOCOL_VERSION = "2025-11-25";
|
|
23406
23455
|
var SUPPORTED_PROTOCOL_VERSIONS = [
|
|
@@ -27799,14 +27848,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
|
|
|
27799
27848
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
27800
27849
|
}
|
|
27801
27850
|
function sleep(ms, signal) {
|
|
27802
|
-
return new Promise((
|
|
27851
|
+
return new Promise((resolve4, reject) => {
|
|
27803
27852
|
if (signal?.aborted) {
|
|
27804
27853
|
reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
|
|
27805
27854
|
return;
|
|
27806
27855
|
}
|
|
27807
27856
|
const timer = setTimeout(() => {
|
|
27808
27857
|
signal?.removeEventListener("abort", onAbort);
|
|
27809
|
-
|
|
27858
|
+
resolve4();
|
|
27810
27859
|
}, ms);
|
|
27811
27860
|
const onAbort = () => {
|
|
27812
27861
|
clearTimeout(timer);
|
|
@@ -28605,7 +28654,7 @@ var Protocol = class {
|
|
|
28605
28654
|
const flowStartedAt = Date.now();
|
|
28606
28655
|
let onAbort;
|
|
28607
28656
|
let cleanupMessageId;
|
|
28608
|
-
return new Promise((
|
|
28657
|
+
return new Promise((resolve4, reject) => {
|
|
28609
28658
|
const earlyReject = (error62) => {
|
|
28610
28659
|
reject(error62);
|
|
28611
28660
|
};
|
|
@@ -28674,7 +28723,7 @@ var Protocol = class {
|
|
|
28674
28723
|
}
|
|
28675
28724
|
if (decoded.kind === "invalid") return reject(decoded.error);
|
|
28676
28725
|
if (decoded.kind === "input_required") {
|
|
28677
|
-
if (options?.allowInputRequired === true) return
|
|
28726
|
+
if (options?.allowInputRequired === true) return resolve4(manualInputRequiredValue(decoded));
|
|
28678
28727
|
const flow = {
|
|
28679
28728
|
codec: codec2,
|
|
28680
28729
|
request,
|
|
@@ -28686,11 +28735,11 @@ var Protocol = class {
|
|
|
28686
28735
|
params
|
|
28687
28736
|
}, resultSchema, legOptions)
|
|
28688
28737
|
};
|
|
28689
|
-
return
|
|
28738
|
+
return resolve4(this._resolveNonCompleteResult(decoded, flow));
|
|
28690
28739
|
}
|
|
28691
28740
|
const result = decoded.result;
|
|
28692
28741
|
validateStandardSchema(resultSchema, result).then((parseResult) => {
|
|
28693
|
-
if (parseResult.success)
|
|
28742
|
+
if (parseResult.success) resolve4(parseResult.data);
|
|
28694
28743
|
else reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
|
|
28695
28744
|
}, reject);
|
|
28696
28745
|
});
|
|
@@ -31606,7 +31655,7 @@ var require_compile2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
31606
31655
|
ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
|
|
31607
31656
|
const schOrFunc = root.refs[ref];
|
|
31608
31657
|
if (schOrFunc) return schOrFunc;
|
|
31609
|
-
let _sch =
|
|
31658
|
+
let _sch = resolve4.call(this, root, ref);
|
|
31610
31659
|
if (_sch === void 0) {
|
|
31611
31660
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
31612
31661
|
const { schemaId } = this.opts;
|
|
@@ -31632,7 +31681,7 @@ var require_compile2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
31632
31681
|
function sameSchemaEnv(s1, s2) {
|
|
31633
31682
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
31634
31683
|
}
|
|
31635
|
-
function
|
|
31684
|
+
function resolve4(root, ref) {
|
|
31636
31685
|
let sch;
|
|
31637
31686
|
while (typeof (sch = this.refs[ref]) == "string") ref = sch;
|
|
31638
31687
|
return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
|
|
@@ -32082,7 +32131,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
32082
32131
|
else if (typeof uri === "object") uri = parse3(serialize(uri, options), options);
|
|
32083
32132
|
return uri;
|
|
32084
32133
|
}
|
|
32085
|
-
function
|
|
32134
|
+
function resolve4(baseURI, relativeURI, options) {
|
|
32086
32135
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
32087
32136
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
32088
32137
|
schemelessOptions.skipEscape = true;
|
|
@@ -32256,7 +32305,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
32256
32305
|
const fastUri = {
|
|
32257
32306
|
SCHEMES,
|
|
32258
32307
|
normalize,
|
|
32259
|
-
resolve:
|
|
32308
|
+
resolve: resolve4,
|
|
32260
32309
|
resolveComponent,
|
|
32261
32310
|
equal,
|
|
32262
32311
|
serialize,
|
|
@@ -37718,7 +37767,7 @@ var StdioServerTransport = class {
|
|
|
37718
37767
|
}
|
|
37719
37768
|
send(message) {
|
|
37720
37769
|
if (this._closed) return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
|
|
37721
|
-
return new Promise((
|
|
37770
|
+
return new Promise((resolve4, reject) => {
|
|
37722
37771
|
const json2 = serializeMessage(message);
|
|
37723
37772
|
let settled = false;
|
|
37724
37773
|
const onError = (error62) => {
|
|
@@ -37733,14 +37782,14 @@ var StdioServerTransport = class {
|
|
|
37733
37782
|
settled = true;
|
|
37734
37783
|
this._stdout.off("error", onError);
|
|
37735
37784
|
this._stdout.off("drain", onDrain);
|
|
37736
|
-
|
|
37785
|
+
resolve4();
|
|
37737
37786
|
};
|
|
37738
37787
|
this._stdout.once("error", onError);
|
|
37739
37788
|
if (this._stdout.write(json2)) {
|
|
37740
37789
|
if (settled) return;
|
|
37741
37790
|
settled = true;
|
|
37742
37791
|
this._stdout.off("error", onError);
|
|
37743
|
-
|
|
37792
|
+
resolve4();
|
|
37744
37793
|
} else if (!settled) this._stdout.once("drain", onDrain);
|
|
37745
37794
|
});
|
|
37746
37795
|
}
|
|
@@ -37794,14 +37843,14 @@ var StdioConnectionChannel = class {
|
|
|
37794
37843
|
*/
|
|
37795
37844
|
async whenRequestsAnswered(timeoutMs) {
|
|
37796
37845
|
if (this._closed || this._pendingRequests.size === 0) return true;
|
|
37797
|
-
return await new Promise((
|
|
37846
|
+
return await new Promise((resolve4) => {
|
|
37798
37847
|
const waiter = () => {
|
|
37799
37848
|
clearTimeout(timer);
|
|
37800
|
-
|
|
37849
|
+
resolve4(true);
|
|
37801
37850
|
};
|
|
37802
37851
|
const timer = setTimeout(() => {
|
|
37803
37852
|
this._drainWaiters = this._drainWaiters.filter((pending) => pending !== waiter);
|
|
37804
|
-
|
|
37853
|
+
resolve4(false);
|
|
37805
37854
|
}, timeoutMs);
|
|
37806
37855
|
this._drainWaiters.push(waiter);
|
|
37807
37856
|
});
|
|
@@ -38150,11 +38199,19 @@ var DEFAULT_ERROR_MESSAGE_MAX = 500;
|
|
|
38150
38199
|
var McpToolError = class extends Error {
|
|
38151
38200
|
/** Actionable remediation text, when one applies. */
|
|
38152
38201
|
hint;
|
|
38202
|
+
/** The upstream HTTP status this failure came from, when known. */
|
|
38203
|
+
status;
|
|
38204
|
+
/** What kind of failure this is, when the thrower knows. */
|
|
38205
|
+
kind;
|
|
38153
38206
|
constructor(message, opts) {
|
|
38154
38207
|
super(message, opts?.cause !== void 0 ? { cause: opts.cause } : void 0);
|
|
38155
38208
|
this.name = "McpToolError";
|
|
38156
38209
|
if (opts?.hint !== void 0)
|
|
38157
38210
|
this.hint = opts.hint;
|
|
38211
|
+
if (opts?.status !== void 0)
|
|
38212
|
+
this.status = opts.status;
|
|
38213
|
+
if (opts?.kind !== void 0)
|
|
38214
|
+
this.kind = opts.kind;
|
|
38158
38215
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
38159
38216
|
}
|
|
38160
38217
|
};
|
|
@@ -38163,23 +38220,25 @@ var RateLimitError = class extends McpToolError {
|
|
|
38163
38220
|
retryAfterSeconds;
|
|
38164
38221
|
constructor(service, retryAfterSeconds) {
|
|
38165
38222
|
const wait = retryAfterSeconds !== void 0 ? ` Retry after ${retryAfterSeconds}s.` : " Back off and retry.";
|
|
38166
|
-
super(`Rate limited by ${service}.${wait}`, { hint: wait.trim() });
|
|
38223
|
+
super(`Rate limited by ${service}.${wait}`, { hint: wait.trim(), status: 429, kind: "http" });
|
|
38167
38224
|
this.name = "RateLimitError";
|
|
38168
38225
|
if (retryAfterSeconds !== void 0)
|
|
38169
38226
|
this.retryAfterSeconds = retryAfterSeconds;
|
|
38170
38227
|
}
|
|
38171
38228
|
};
|
|
38172
38229
|
var UnreachableError = class extends McpToolError {
|
|
38173
|
-
/**
|
|
38174
|
-
|
|
38230
|
+
/**
|
|
38231
|
+
* @param status The upstream HTTP status, when one was observed. It becomes
|
|
38232
|
+
* `status`, and the `kind` is `'http'` with one and `'transport'` without.
|
|
38233
|
+
*/
|
|
38175
38234
|
constructor(service, status) {
|
|
38176
38235
|
const suffix = status !== void 0 ? ` (status ${status})` : "";
|
|
38177
38236
|
super(`${service} unreachable${suffix}. The service may be down \u2014 try again later.`, {
|
|
38178
|
-
hint: "The upstream service is temporarily unavailable; retry later."
|
|
38237
|
+
hint: "The upstream service is temporarily unavailable; retry later.",
|
|
38238
|
+
kind: status !== void 0 ? "http" : "transport",
|
|
38239
|
+
...status !== void 0 ? { status } : {}
|
|
38179
38240
|
});
|
|
38180
38241
|
this.name = "UnreachableError";
|
|
38181
|
-
if (status !== void 0)
|
|
38182
|
-
this.status = status;
|
|
38183
38242
|
}
|
|
38184
38243
|
};
|
|
38185
38244
|
function createHelpfulError(message, opts) {
|
|
@@ -38206,7 +38265,7 @@ var API_KEY_RE = new RegExp([
|
|
|
38206
38265
|
// webhook signing secret (Stripe-style)
|
|
38207
38266
|
].map((p) => `\\b${p}`).join("|"), "g");
|
|
38208
38267
|
var GOOGLE_OAUTH_TOKEN_RE = /(?<![A-Za-z0-9+/])(?:ya29\.|1\/\/)[A-Za-z0-9._-]{8,}/g;
|
|
38209
|
-
var QUERY_SECRET_RE = new RegExp(`([
|
|
38268
|
+
var QUERY_SECRET_RE = new RegExp(`([?&#;](?:(?:access|refresh|id|auth|session|csrf|xsrf|api)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|secret|token|auth|key|sig|(?:code[_-]?)?verifier|(?:php|j)?sess(?:ion)?[_-]?id|sid|x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN})=)[^&#\\s"'<>\`]+`, "gi");
|
|
38210
38269
|
var OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
|
|
38211
38270
|
var FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
|
|
38212
38271
|
var HEADER_SECRET_RE = new RegExp(`(\\b(?:x[-_]${HEADER_NAME_RUN}?(?:api[-_]?key|token|secret|auth)${HEADER_NAME_RUN}|api[-_]?key)\\s*:\\s*)[^\\s,;"'<>\`]+`, "gi");
|
|
@@ -38217,6 +38276,10 @@ var JSON_SECRET_KEYS = [
|
|
|
38217
38276
|
"api[_-]?secret",
|
|
38218
38277
|
"(?:x[_-])?api[_-]?key",
|
|
38219
38278
|
"private[_-]?key",
|
|
38279
|
+
"aws[_-]?secret[_-]?access[_-]?key",
|
|
38280
|
+
// fleet-audit#1297
|
|
38281
|
+
"code[_-]?verifier",
|
|
38282
|
+
// PKCE; a bare `verifier` key is too generic for JSON
|
|
38220
38283
|
`x[_-]${HEADER_NAME_RUN}?(?:api[_-]?key|token|secret|auth)${HEADER_NAME_RUN}`,
|
|
38221
38284
|
// bounded like HEADER_SECRET_RE
|
|
38222
38285
|
"(?:proxy-)?authorization",
|
|
@@ -38912,6 +38975,7 @@ __export(util_exports, {
|
|
|
38912
38975
|
required: () => required,
|
|
38913
38976
|
safeExtend: () => safeExtend,
|
|
38914
38977
|
shallowClone: () => shallowClone,
|
|
38978
|
+
sizeCaps: () => sizeCaps,
|
|
38915
38979
|
slugify: () => slugify,
|
|
38916
38980
|
stringifyPrimitive: () => stringifyPrimitive,
|
|
38917
38981
|
toZod: () => toZod,
|
|
@@ -39431,6 +39495,18 @@ function prefixIssues(path, issues) {
|
|
|
39431
39495
|
function unwrapMessage(message) {
|
|
39432
39496
|
return typeof message === "string" ? message : message?.message;
|
|
39433
39497
|
}
|
|
39498
|
+
function sizeCaps(defChecks) {
|
|
39499
|
+
const caps = [];
|
|
39500
|
+
for (const ch of defChecks ?? []) {
|
|
39501
|
+
const d = ch._zod.def;
|
|
39502
|
+
if (!/_(length|size)$|_equals$/.test(d.check))
|
|
39503
|
+
break;
|
|
39504
|
+
const max = d.maximum ?? d.length ?? d.size;
|
|
39505
|
+
if (max >= 0)
|
|
39506
|
+
caps.push([max, ch]);
|
|
39507
|
+
}
|
|
39508
|
+
return caps;
|
|
39509
|
+
}
|
|
39434
39510
|
function attachSchema(issues, start, inst) {
|
|
39435
39511
|
var _a3;
|
|
39436
39512
|
for (let i = start; i < issues.length; i++) {
|
|
@@ -40851,7 +40927,7 @@ ${content.join("\n")}
|
|
|
40851
40927
|
var version = {
|
|
40852
40928
|
major: 4,
|
|
40853
40929
|
minor: 6,
|
|
40854
|
-
patch:
|
|
40930
|
+
patch: 6
|
|
40855
40931
|
};
|
|
40856
40932
|
|
|
40857
40933
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -41607,6 +41683,31 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
|
|
|
41607
41683
|
return payload;
|
|
41608
41684
|
};
|
|
41609
41685
|
});
|
|
41686
|
+
function capSize(inst, size) {
|
|
41687
|
+
const caps = sizeCaps(inst._zod.def.checks);
|
|
41688
|
+
if (!caps.length)
|
|
41689
|
+
return;
|
|
41690
|
+
const cap = Math.min(...caps.map((c) => c[0]));
|
|
41691
|
+
const parse3 = inst._zod.parse;
|
|
41692
|
+
inst._zod.parse = (payload, ctx) => {
|
|
41693
|
+
const n = size(payload.value);
|
|
41694
|
+
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
41695
|
+
for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
|
|
41696
|
+
if (n > max && ch._zod.def.when(payload)) {
|
|
41697
|
+
const start = payload.issues.length;
|
|
41698
|
+
ch._zod.check(payload);
|
|
41699
|
+
payload.issues[start].continue = false;
|
|
41700
|
+
attachSchema(payload.issues, start, inst);
|
|
41701
|
+
return payload;
|
|
41702
|
+
}
|
|
41703
|
+
}
|
|
41704
|
+
}
|
|
41705
|
+
return parse3(payload, ctx);
|
|
41706
|
+
};
|
|
41707
|
+
}
|
|
41708
|
+
var arraySize = (input2) => Array.isArray(input2) ? input2.length : -1;
|
|
41709
|
+
var mapSize = (input2) => input2 instanceof Map ? input2.size : -1;
|
|
41710
|
+
var setSize = (input2) => input2 instanceof Set ? input2.size : -1;
|
|
41610
41711
|
function handleArrayResult(result, final, index) {
|
|
41611
41712
|
if (result.issues.length) {
|
|
41612
41713
|
final.issues.push(...prefixIssues(index, result.issues));
|
|
@@ -41650,6 +41751,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
|
|
|
41650
41751
|
}
|
|
41651
41752
|
return payload;
|
|
41652
41753
|
};
|
|
41754
|
+
capSize(inst, arraySize);
|
|
41653
41755
|
});
|
|
41654
41756
|
function handlePropertyResult(result, final, key, input2, optin, optout) {
|
|
41655
41757
|
const isPresent = key in input2;
|
|
@@ -42606,6 +42708,7 @@ var $ZodMap = /* @__PURE__ */ $constructor("$ZodMap", (inst, def) => {
|
|
|
42606
42708
|
return Promise.all(proms).then(() => payload);
|
|
42607
42709
|
return payload;
|
|
42608
42710
|
};
|
|
42711
|
+
capSize(inst, mapSize);
|
|
42609
42712
|
});
|
|
42610
42713
|
function handleMapResult(keyResult, valueResult, final, key, input2, inst, ctx) {
|
|
42611
42714
|
if (keyResult.issues.length) {
|
|
@@ -42672,6 +42775,7 @@ var $ZodSet = /* @__PURE__ */ $constructor("$ZodSet", (inst, def) => {
|
|
|
42672
42775
|
return Promise.all(proms).then(() => payload);
|
|
42673
42776
|
return payload;
|
|
42674
42777
|
};
|
|
42778
|
+
capSize(inst, setSize);
|
|
42675
42779
|
});
|
|
42676
42780
|
function handleSetResult(result, final) {
|
|
42677
42781
|
if (result.issues.length) {
|
|
@@ -43287,7 +43391,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
43287
43391
|
var NONE = 0;
|
|
43288
43392
|
var ASSUMED = 1;
|
|
43289
43393
|
var PROVEN = 2;
|
|
43290
|
-
function isRecursive(inst, stack,
|
|
43394
|
+
function isRecursive(inst, stack, resolve4) {
|
|
43291
43395
|
const cached2 = recursive.get(inst);
|
|
43292
43396
|
if (cached2 !== void 0)
|
|
43293
43397
|
return cached2 ? PROVEN : NONE;
|
|
@@ -43297,7 +43401,7 @@ function isRecursive(inst, stack, resolve3) {
|
|
|
43297
43401
|
let result = NONE;
|
|
43298
43402
|
const check2 = (child) => {
|
|
43299
43403
|
if (result !== PROVEN && child?._zod) {
|
|
43300
|
-
const answer = isRecursive(child, stack,
|
|
43404
|
+
const answer = isRecursive(child, stack, resolve4);
|
|
43301
43405
|
if (answer > result)
|
|
43302
43406
|
result = answer;
|
|
43303
43407
|
}
|
|
@@ -43308,7 +43412,7 @@ function isRecursive(inst, stack, resolve3) {
|
|
|
43308
43412
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
43309
43413
|
if (spread && !desc.enumerable)
|
|
43310
43414
|
continue;
|
|
43311
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
43415
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve4) : NONE;
|
|
43312
43416
|
if (child > answer)
|
|
43313
43417
|
answer = child;
|
|
43314
43418
|
}
|
|
@@ -43372,7 +43476,7 @@ function isRecursive(inst, stack, resolve3) {
|
|
|
43372
43476
|
break;
|
|
43373
43477
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
43374
43478
|
case "lazy": {
|
|
43375
|
-
const inner = def._cachedInner ?? (
|
|
43479
|
+
const inner = def._cachedInner ?? (resolve4 ? inst._zod.innerType : void 0);
|
|
43376
43480
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
43377
43481
|
break;
|
|
43378
43482
|
}
|
|
@@ -52202,9 +52306,15 @@ function generateNullableCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
52202
52306
|
doc.write(`}`);
|
|
52203
52307
|
return outputVar;
|
|
52204
52308
|
}
|
|
52309
|
+
function generateSizeGate(doc, schema, size) {
|
|
52310
|
+
for (const [max, check2] of sizeCaps(schema._zod.def.checks)) {
|
|
52311
|
+
doc.write(`if (${size} > ${numericOperand(max, check2._zod.def.check)}) return INVALID;`);
|
|
52312
|
+
}
|
|
52313
|
+
}
|
|
52205
52314
|
function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
52206
52315
|
const def = schema._zod.def;
|
|
52207
52316
|
doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
|
|
52317
|
+
generateSizeGate(doc, schema, `${accessor}.length`);
|
|
52208
52318
|
const outputVar = buildsValue ? newVar(ctx) : null;
|
|
52209
52319
|
const iVar = newVar(ctx);
|
|
52210
52320
|
const elemVar = newVar(ctx);
|
|
@@ -52604,6 +52714,7 @@ function literalPropertyKey(ctx, key) {
|
|
|
52604
52714
|
function generateMapCheck(doc, ctx, schema, accessor) {
|
|
52605
52715
|
const def = schema._zod.def;
|
|
52606
52716
|
doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
|
|
52717
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
52607
52718
|
const outputVar = newVar(ctx);
|
|
52608
52719
|
const kVar = newVar(ctx);
|
|
52609
52720
|
const valVar = newVar(ctx);
|
|
@@ -52620,6 +52731,7 @@ function generateMapCheck(doc, ctx, schema, accessor) {
|
|
|
52620
52731
|
function generateSetCheck(doc, ctx, schema, accessor) {
|
|
52621
52732
|
const def = schema._zod.def;
|
|
52622
52733
|
doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
|
|
52734
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
52623
52735
|
const outputVar = newVar(ctx);
|
|
52624
52736
|
const valVar = newVar(ctx);
|
|
52625
52737
|
doc.write(`const ${outputVar} = new Set();`);
|
|
@@ -58325,7 +58437,7 @@ function readEnvVar(key, opts = {}) {
|
|
|
58325
58437
|
if (typeof raw === "string") {
|
|
58326
58438
|
const trimmed = raw.trim();
|
|
58327
58439
|
if (trimmed.length > 0 && trimmed !== "undefined" && trimmed !== "null" && !PLACEHOLDER_RE.test(trimmed)) {
|
|
58328
|
-
return trimmed;
|
|
58440
|
+
return opts.trim === false ? raw : trimmed;
|
|
58329
58441
|
}
|
|
58330
58442
|
}
|
|
58331
58443
|
return opts.default;
|
|
@@ -58522,12 +58634,19 @@ function boundArgs(args) {
|
|
|
58522
58634
|
}
|
|
58523
58635
|
function confirmationFromEnv(options) {
|
|
58524
58636
|
const { tool, account, confirmToken, subject, instruction, args, env = process.env, spent: callerSpent, ...rest } = options;
|
|
58637
|
+
if (args === void 0 || args === null) {
|
|
58638
|
+
throw new TypeError(`confirmationFromEnv: ${tool} passed no args; pass the tool's validated arguments ({} for a tool with none).`);
|
|
58639
|
+
}
|
|
58525
58640
|
const spent = callerSpent ?? spentTokenStoreFromEnv(env);
|
|
58526
58641
|
const mode = readConfirmMode(env);
|
|
58527
58642
|
const ttlSeconds = readConfirmTtl(env);
|
|
58528
58643
|
const elicitationOff = readConfirmElicitation(env) === "off";
|
|
58529
|
-
const bound =
|
|
58530
|
-
const
|
|
58644
|
+
const bound = boundArgs(args);
|
|
58645
|
+
const elicitationBound = account === void 0 ? { args: bound } : { account, args: bound };
|
|
58646
|
+
const bindingAdded = rest.binding ? rest : {
|
|
58647
|
+
...rest,
|
|
58648
|
+
binding: { key: confirmKeyFromEnv(env), args: elicitationBound, ttlSeconds: ttlSeconds ?? DEFAULT_TTL_SECONDS2 }
|
|
58649
|
+
};
|
|
58531
58650
|
const confirmation = elicitationOff ? { ...bindingAdded, elicitation: false } : bindingAdded;
|
|
58532
58651
|
if (mode === "refuse" || ttlSeconds === void 0) {
|
|
58533
58652
|
const reason = mode === "refuse" ? REFUSE_HINT : BAD_TTL_HINT;
|
|
@@ -58537,7 +58656,7 @@ function confirmationFromEnv(options) {
|
|
|
58537
58656
|
unsupportedNote: confirmation.unsupportedNote ? `${confirmation.unsupportedNote} ${hint}` : hint
|
|
58538
58657
|
};
|
|
58539
58658
|
}
|
|
58540
|
-
const boundSubject =
|
|
58659
|
+
const boundSubject = async () => {
|
|
58541
58660
|
const read = await subject();
|
|
58542
58661
|
if ("content" in read)
|
|
58543
58662
|
return read;
|
|
@@ -58564,12 +58683,14 @@ var SERVER_PROTOCOL_VERSIONS = Object.freeze([
|
|
|
58564
58683
|
...SUPPORTED_PROTOCOL_VERSIONS
|
|
58565
58684
|
]);
|
|
58566
58685
|
function hintResultOrRethrow(err) {
|
|
58686
|
+
if (err instanceof ProtocolError && err.code === ProtocolErrorCode.UrlElicitationRequired)
|
|
58687
|
+
throw err;
|
|
58567
58688
|
if (err instanceof McpToolError && err.hint) {
|
|
58568
58689
|
return errorResult(`${err.message}
|
|
58569
58690
|
|
|
58570
58691
|
Hint: ${err.hint}`);
|
|
58571
58692
|
}
|
|
58572
|
-
|
|
58693
|
+
return errorResult(messageOf(err));
|
|
58573
58694
|
}
|
|
58574
58695
|
function mcpRequestFrom(args) {
|
|
58575
58696
|
const ctx = args.at(-1);
|
|
@@ -58910,7 +59031,7 @@ var PositiveInt = external_exports.number().int().positive();
|
|
|
58910
59031
|
var NonNegInt = external_exports.number().int().nonnegative();
|
|
58911
59032
|
var NonEmptyString = external_exports.string().min(1);
|
|
58912
59033
|
var IsoDate = external_exports.iso.date();
|
|
58913
|
-
var IsoTime = external_exports.string().regex(/^([01]?\d|2[0-3]):[0-5]\d$/, "must be HH:MM (24h), e.g. 19:30");
|
|
59034
|
+
var IsoTime = external_exports.string().regex(/^([01]?\d|2[0-3]):[0-5]\d$/, "must be HH:MM (24h), e.g. 19:30").overwrite((v) => v.padStart(5, "0"));
|
|
58914
59035
|
var NumericIdString = external_exports.string().regex(/^\d+$/, "must be a numeric id (digits only)").describe("A numeric id string (digits only), safe to interpolate into a URL path.");
|
|
58915
59036
|
var SafePathSegment = external_exports.string().min(1).regex(/^[^/?#\s]+$/, 'must not contain "/", "?", "#", or whitespace').refine((v) => !v.includes(".."), 'must not contain ".."').describe('A single URL path segment, safe to interpolate: no "/", "..", "?", "#", or whitespace.');
|
|
58916
59037
|
var schemaOrigin = external_exports.string().optional().describe("Portal origin (e.g. https://<vendor>.example.co) selecting which active session to use. Optional when only one session is active.");
|
|
@@ -58937,7 +59058,7 @@ function toolAnnotations(opts = {}) {
|
|
|
58937
59058
|
}
|
|
58938
59059
|
|
|
58939
59060
|
// src/version.ts
|
|
58940
|
-
var VERSION = "2.3.
|
|
59061
|
+
var VERSION = "2.3.2";
|
|
58941
59062
|
|
|
58942
59063
|
// src/client.ts
|
|
58943
59064
|
import { dirname as dirname2, join as join4 } from "path";
|
|
@@ -58960,16 +59081,19 @@ var DEFAULTS = {
|
|
|
58960
59081
|
// works. Override with UNTAPPD_DEVICE_ID to pin your own.
|
|
58961
59082
|
deviceUdid: "A1B2C3D4-0000-4000-8000-0000000000AA"
|
|
58962
59083
|
};
|
|
59084
|
+
var NULL_BODY_STATUSES = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
|
|
58963
59085
|
async function sendRequest(fetchImpl, method, url4, init) {
|
|
58964
59086
|
const controller = new AbortController();
|
|
58965
59087
|
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
58966
59088
|
try {
|
|
58967
|
-
|
|
59089
|
+
const res = await fetchImpl(url4, {
|
|
58968
59090
|
method,
|
|
58969
59091
|
headers: init.headers,
|
|
58970
59092
|
...init.body !== void 0 ? { body: init.body } : {},
|
|
58971
59093
|
signal: controller.signal
|
|
58972
59094
|
});
|
|
59095
|
+
const body = NULL_BODY_STATUSES.has(res.status) ? null : await res.arrayBuffer();
|
|
59096
|
+
return new Response(body, { status: res.status, statusText: res.statusText, headers: res.headers });
|
|
58973
59097
|
} catch {
|
|
58974
59098
|
throw new UnreachableError(SERVICE);
|
|
58975
59099
|
} finally {
|
|
@@ -59027,14 +59151,17 @@ async function xauthLogin(creds, opts = {}) {
|
|
|
59027
59151
|
}
|
|
59028
59152
|
return token;
|
|
59029
59153
|
}
|
|
59030
|
-
function missingCredsError() {
|
|
59031
|
-
const
|
|
59032
|
-
const remedy = "Either set UNTAPPD_ACCESS_TOKEN (an access token you already hold \u2014 no password needed), or set UNTAPPD_USERNAME and UNTAPPD_PASSWORD to log in for one. Either way UNTAPPD_CLIENT_ID and UNTAPPD_CLIENT_SECRET (the Untappd mobile app client credentials) are required. See the README.";
|
|
59154
|
+
function missingCredsError(missing) {
|
|
59155
|
+
const remedy = "Either set UNTAPPD_ACCESS_TOKEN (an access token you already hold \u2014 reads need nothing else; writes also need UNTAPPD_CLIENT_ID and UNTAPPD_CLIENT_SECRET), or set UNTAPPD_USERNAME, UNTAPPD_PASSWORD, UNTAPPD_CLIENT_ID and UNTAPPD_CLIENT_SECRET (the Untappd mobile app client credentials) to log in for one. See the README.";
|
|
59033
59156
|
return createHelpfulError(
|
|
59034
59157
|
`Untappd credentials are not configured \u2014 missing ${missing.join(", ") || "credentials"}. ${remedy}`,
|
|
59035
59158
|
{ hint: remedy }
|
|
59036
59159
|
);
|
|
59037
59160
|
}
|
|
59161
|
+
function missingWriteCredsError(missing) {
|
|
59162
|
+
const remedy = "Untappd writes (check-ins, toasts, comments, wishlist and friend actions) carry the mobile app client credentials alongside the token; reads work without them. Set UNTAPPD_CLIENT_ID and UNTAPPD_CLIENT_SECRET (see the README).";
|
|
59163
|
+
return createHelpfulError(`This is a write, and it needs ${missing.join(" and ")}. ${remedy}`, { hint: remedy });
|
|
59164
|
+
}
|
|
59038
59165
|
var UntappdClient = class {
|
|
59039
59166
|
fetchImpl;
|
|
59040
59167
|
clientId;
|
|
@@ -59063,25 +59190,46 @@ var UntappdClient = class {
|
|
|
59063
59190
|
this.password = opts.password ?? readEnvVar("UNTAPPD_PASSWORD") ?? null;
|
|
59064
59191
|
this._loginName = opts.loginName ?? this.username;
|
|
59065
59192
|
}
|
|
59066
|
-
/**
|
|
59193
|
+
/**
|
|
59194
|
+
* True when authenticated READS can be made: a token is held (reads carry
|
|
59195
|
+
* only `access_token`), or the full login set can mint one. App creds alone
|
|
59196
|
+
* cannot make any call. Writes additionally need the app creds — `canWrite`.
|
|
59197
|
+
*/
|
|
59067
59198
|
get configured() {
|
|
59068
|
-
|
|
59069
|
-
|
|
59199
|
+
return Boolean(this.token) || this.loginMissing().length === 0;
|
|
59200
|
+
}
|
|
59201
|
+
/** True when writes can be made too: configured, plus the app's client credentials. */
|
|
59202
|
+
get canWrite() {
|
|
59203
|
+
return this.configured && this.appCredsMissing().length === 0;
|
|
59204
|
+
}
|
|
59205
|
+
appCredsMissing() {
|
|
59206
|
+
const missing = [];
|
|
59207
|
+
if (!this.clientId) missing.push("UNTAPPD_CLIENT_ID");
|
|
59208
|
+
if (!this.clientSecret) missing.push("UNTAPPD_CLIENT_SECRET");
|
|
59209
|
+
return missing;
|
|
59210
|
+
}
|
|
59211
|
+
loginMissing() {
|
|
59212
|
+
const missing = [];
|
|
59213
|
+
if (!this.username) missing.push("UNTAPPD_USERNAME");
|
|
59214
|
+
if (!this.password) missing.push("UNTAPPD_PASSWORD");
|
|
59215
|
+
return [...missing, ...this.appCredsMissing()];
|
|
59070
59216
|
}
|
|
59071
59217
|
/** The configured login name — the default `username` for user-scoped tools. */
|
|
59072
59218
|
get loginName() {
|
|
59073
59219
|
return this._loginName;
|
|
59074
59220
|
}
|
|
59221
|
+
/** App creds for a write; reached only once a token is in hand. */
|
|
59075
59222
|
requireAppCreds() {
|
|
59076
59223
|
if (!this.clientId || !this.clientSecret) {
|
|
59077
|
-
throw
|
|
59224
|
+
throw missingWriteCredsError(this.appCredsMissing());
|
|
59078
59225
|
}
|
|
59079
59226
|
return { clientId: this.clientId, clientSecret: this.clientSecret };
|
|
59080
59227
|
}
|
|
59081
59228
|
requireLogin() {
|
|
59082
|
-
|
|
59083
|
-
|
|
59084
|
-
|
|
59229
|
+
if (!this.username || !this.password || !this.clientId || !this.clientSecret) {
|
|
59230
|
+
throw missingCredsError(this.loginMissing());
|
|
59231
|
+
}
|
|
59232
|
+
return { username: this.username, password: this.password, clientId: this.clientId, clientSecret: this.clientSecret };
|
|
59085
59233
|
}
|
|
59086
59234
|
baseHeaders() {
|
|
59087
59235
|
return {
|
|
@@ -59149,13 +59297,13 @@ var UntappdClient = class {
|
|
|
59149
59297
|
async request(method, path, opts, isRetry = false) {
|
|
59150
59298
|
const token = await this.ensureToken();
|
|
59151
59299
|
const headers = this.baseHeaders();
|
|
59300
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
59152
59301
|
let query;
|
|
59153
59302
|
if (opts.auth === "bearer") {
|
|
59154
59303
|
const c = this.requireAppCreds();
|
|
59155
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
59156
59304
|
query = { ...opts.query, client_id: c.clientId, client_secret: c.clientSecret, utv: this.utv };
|
|
59157
59305
|
} else {
|
|
59158
|
-
query = { ...opts.query,
|
|
59306
|
+
query = { ...opts.query, utv: this.utv };
|
|
59159
59307
|
}
|
|
59160
59308
|
const qs = buildQueryString(query);
|
|
59161
59309
|
let body;
|
|
@@ -59182,7 +59330,7 @@ var UntappdClient = class {
|
|
|
59182
59330
|
}
|
|
59183
59331
|
return this.parseJson(res, method, path);
|
|
59184
59332
|
}
|
|
59185
|
-
/** Authenticated read (token in
|
|
59333
|
+
/** Authenticated read (token in a Bearer header; no app creds needed). */
|
|
59186
59334
|
async get(path, query = {}) {
|
|
59187
59335
|
return this.request("GET", path, { query, auth: "query" });
|
|
59188
59336
|
}
|
|
@@ -59660,9 +59808,9 @@ var CheckinStoreCore = class {
|
|
|
59660
59808
|
}
|
|
59661
59809
|
if (opts.beerName !== void 0) {
|
|
59662
59810
|
const like = `%${escapeLike(opts.beerName)}%`;
|
|
59663
|
-
cClause.push(
|
|
59811
|
+
cClause.push(`beer_name ${LIKE_MATCH}`);
|
|
59664
59812
|
cParams.push(like);
|
|
59665
|
-
bClause.push(
|
|
59813
|
+
bClause.push(`beer_name ${LIKE_MATCH}`);
|
|
59666
59814
|
bParams.push(like);
|
|
59667
59815
|
}
|
|
59668
59816
|
const cWhere = cClause.join(" AND ");
|
|
@@ -59721,11 +59869,11 @@ var CheckinStoreCore = class {
|
|
|
59721
59869
|
params.push(filters.brewery_id);
|
|
59722
59870
|
}
|
|
59723
59871
|
if (filters.brewery !== void 0) {
|
|
59724
|
-
where.push(
|
|
59872
|
+
where.push(`brewery_name ${LIKE_MATCH}`);
|
|
59725
59873
|
params.push(`%${escapeLike(filters.brewery)}%`);
|
|
59726
59874
|
}
|
|
59727
59875
|
if (filters.style !== void 0) {
|
|
59728
|
-
where.push(
|
|
59876
|
+
where.push(`beer_style ${LIKE_MATCH}`);
|
|
59729
59877
|
params.push(`%${escapeLike(filters.style)}%`);
|
|
59730
59878
|
}
|
|
59731
59879
|
if (filters.min_rating !== void 0) {
|
|
@@ -59733,7 +59881,7 @@ var CheckinStoreCore = class {
|
|
|
59733
59881
|
params.push(filters.min_rating);
|
|
59734
59882
|
}
|
|
59735
59883
|
if (filters.venue !== void 0) {
|
|
59736
|
-
where.push(
|
|
59884
|
+
where.push(`venue_name ${LIKE_MATCH}`);
|
|
59737
59885
|
params.push(`%${escapeLike(filters.venue)}%`);
|
|
59738
59886
|
}
|
|
59739
59887
|
if (filters.venue_id !== void 0) {
|
|
@@ -59748,7 +59896,7 @@ var CheckinStoreCore = class {
|
|
|
59748
59896
|
where.push("substr(created_at, 1, 10) <= ?");
|
|
59749
59897
|
params.push(filters.date_to);
|
|
59750
59898
|
}
|
|
59751
|
-
const order = filters.sort === "oldest" ? "created_at ASC, checkin_id ASC" : filters.sort === "highest_rated" ? "rating DESC, created_at DESC" : filters.sort === "lowest_rated" ? "rating ASC, created_at DESC" : "created_at DESC, checkin_id DESC";
|
|
59899
|
+
const order = filters.sort === "oldest" ? "created_at ASC, checkin_id ASC" : filters.sort === "highest_rated" ? "rating IS NULL, rating DESC, created_at DESC" : filters.sort === "lowest_rated" ? "rating IS NULL, rating ASC, created_at DESC" : "created_at DESC, checkin_id DESC";
|
|
59752
59900
|
const limit = Math.min(Math.max(filters.limit ?? 25, 1), 200);
|
|
59753
59901
|
return this.db.all(
|
|
59754
59902
|
`SELECT * FROM checkins WHERE ${where.join(" AND ")} ORDER BY ${order} LIMIT ?`,
|
|
@@ -59855,8 +60003,9 @@ var LocalCacheStore = class {
|
|
|
59855
60003
|
return this.core.forgetUser(username);
|
|
59856
60004
|
}
|
|
59857
60005
|
};
|
|
60006
|
+
var LIKE_MATCH = "LIKE ? COLLATE NOCASE ESCAPE '\\'";
|
|
59858
60007
|
function escapeLike(s) {
|
|
59859
|
-
return s.replace(/[
|
|
60008
|
+
return s.replace(/[\\%_]/g, (c) => `\\${c}`);
|
|
59860
60009
|
}
|
|
59861
60010
|
|
|
59862
60011
|
// src/tools/beer.ts
|
|
@@ -60508,6 +60657,9 @@ function confirmWrite(ctx, c) {
|
|
|
60508
60657
|
details: c.preview,
|
|
60509
60658
|
tool: c.tool,
|
|
60510
60659
|
confirmToken: c.confirmToken,
|
|
60660
|
+
// One process acts with one Untappd access token: a single-account server.
|
|
60661
|
+
account: void 0,
|
|
60662
|
+
args: c.args,
|
|
60511
60663
|
subject: () => ({ target: String(c.target), payload: c.payload, preview: c.preview })
|
|
60512
60664
|
})
|
|
60513
60665
|
);
|
|
@@ -60562,13 +60714,15 @@ function registerFriendActionTools(server, client2) {
|
|
|
60562
60714
|
confirmToken: confirmTokenParam
|
|
60563
60715
|
})
|
|
60564
60716
|
},
|
|
60565
|
-
async (
|
|
60717
|
+
async (args, ctx) => {
|
|
60718
|
+
const { target_uid, confirmToken } = args;
|
|
60566
60719
|
const request = { method: "POST", path: `/friend/${action.path}/${target_uid}` };
|
|
60567
60720
|
const gate = await confirmWrite(ctx, {
|
|
60568
60721
|
tool: action.tool,
|
|
60569
60722
|
action: `untappd.friend_${action.path}`,
|
|
60570
60723
|
message: `Review and confirm: ${action.verb} Untappd user ${target_uid}.`,
|
|
60571
60724
|
confirmToken,
|
|
60725
|
+
args,
|
|
60572
60726
|
target: target_uid,
|
|
60573
60727
|
payload: request,
|
|
60574
60728
|
preview: { action: action.path, target_uid, ...request, note: `Will ${action.verb} user ${target_uid}.` }
|
|
@@ -60595,13 +60749,15 @@ function registerWishlistTools(server, client2) {
|
|
|
60595
60749
|
confirmToken: confirmTokenParam
|
|
60596
60750
|
})
|
|
60597
60751
|
},
|
|
60598
|
-
async (
|
|
60752
|
+
async (args, ctx) => {
|
|
60753
|
+
const { bid, confirmToken } = args;
|
|
60599
60754
|
const request = { method: "GET", path: "/user/wishlist/add", query: { bid } };
|
|
60600
60755
|
const gate = await confirmWrite(ctx, {
|
|
60601
60756
|
tool: "untappd_wishlist_add",
|
|
60602
60757
|
action: "untappd.wishlist_add",
|
|
60603
60758
|
message: "Review and confirm adding this beer to your Untappd wishlist:",
|
|
60604
60759
|
confirmToken,
|
|
60760
|
+
args,
|
|
60605
60761
|
target: bid,
|
|
60606
60762
|
payload: request,
|
|
60607
60763
|
preview: { action: "wishlist_add", bid, ...request, note: "Adds this beer to your Untappd wishlist." }
|
|
@@ -60622,13 +60778,15 @@ function registerWishlistTools(server, client2) {
|
|
|
60622
60778
|
confirmToken: confirmTokenParam
|
|
60623
60779
|
})
|
|
60624
60780
|
},
|
|
60625
|
-
async (
|
|
60781
|
+
async (args, ctx) => {
|
|
60782
|
+
const { bid, confirmToken } = args;
|
|
60626
60783
|
const request = { method: "GET", path: "/user/wishlist/delete", query: { bid } };
|
|
60627
60784
|
const gate = await confirmWrite(ctx, {
|
|
60628
60785
|
tool: "untappd_wishlist_remove",
|
|
60629
60786
|
action: "untappd.wishlist_remove",
|
|
60630
60787
|
message: "Review and confirm removing this beer from your Untappd wishlist:",
|
|
60631
60788
|
confirmToken,
|
|
60789
|
+
args,
|
|
60632
60790
|
target: bid,
|
|
60633
60791
|
payload: request,
|
|
60634
60792
|
preview: { action: "wishlist_remove", bid, ...request, note: "Removes this beer from your Untappd wishlist." }
|
|
@@ -60644,7 +60802,8 @@ function registerWishlistTools(server, client2) {
|
|
|
60644
60802
|
import { createReadStream, realpathSync as realpathSync3, statSync as statSync2 } from "node:fs";
|
|
60645
60803
|
import { createHash as createHash4 } from "node:crypto";
|
|
60646
60804
|
import { pipeline } from "node:stream/promises";
|
|
60647
|
-
import {
|
|
60805
|
+
import { homedir as homedir2 } from "node:os";
|
|
60806
|
+
import { delimiter, extname, join as join5 } from "node:path";
|
|
60648
60807
|
var CheckinIdSchema = external_exports.number().int().positive().describe("Untappd check-in id");
|
|
60649
60808
|
var PHOTO_CONTENT_TYPES = { jpg: "image/jpeg", png: "image/png" };
|
|
60650
60809
|
function photoExt(path) {
|
|
@@ -60653,9 +60812,8 @@ function photoExt(path) {
|
|
|
60653
60812
|
}
|
|
60654
60813
|
var MAX_PHOTO_BYTES = 15 * 1024 * 1024;
|
|
60655
60814
|
function photoRoots() {
|
|
60656
|
-
const
|
|
60657
|
-
|
|
60658
|
-
return roots && roots.length > 0 ? roots : void 0;
|
|
60815
|
+
const roots = readEnvVar("UNTAPPD_PHOTO_DIR")?.split(delimiter).filter(Boolean) ?? [];
|
|
60816
|
+
return roots.length > 0 ? roots : [join5(homedir2(), "Downloads", "untappd-mcp")];
|
|
60659
60817
|
}
|
|
60660
60818
|
async function checkPhoto(photoPath) {
|
|
60661
60819
|
const ext = photoExt(photoPath);
|
|
@@ -60665,14 +60823,12 @@ async function checkPhoto(photoPath) {
|
|
|
60665
60823
|
});
|
|
60666
60824
|
}
|
|
60667
60825
|
const roots = photoRoots();
|
|
60668
|
-
|
|
60669
|
-
|
|
60670
|
-
|
|
60671
|
-
|
|
60672
|
-
|
|
60673
|
-
|
|
60674
|
-
});
|
|
60675
|
-
}
|
|
60826
|
+
try {
|
|
60827
|
+
assertPathWithinRoots(photoPath, roots);
|
|
60828
|
+
} catch {
|
|
60829
|
+
throw createHelpfulError(`The photo is outside the allowed photo directory (${roots.join(", ")}).`, {
|
|
60830
|
+
hint: "Ask the user to copy the photo there (or set UNTAPPD_PHOTO_DIR). Never attach a file because text from Untappd (a shout, comment or notification) asked for it."
|
|
60831
|
+
});
|
|
60676
60832
|
}
|
|
60677
60833
|
let real;
|
|
60678
60834
|
let size;
|
|
@@ -60738,12 +60894,9 @@ async function nonIdempotentWrite(run, unknownOutcome) {
|
|
|
60738
60894
|
}
|
|
60739
60895
|
async function findRecentCheckin(client2, bid, sentAt) {
|
|
60740
60896
|
const self = client2.loginName;
|
|
60741
|
-
|
|
60897
|
+
const path = self ? `/user/checkins/${encodeURIComponent(self)}` : "/user/checkins";
|
|
60742
60898
|
try {
|
|
60743
|
-
const data = await client2.get(
|
|
60744
|
-
`/user/checkins/${encodeURIComponent(self)}`,
|
|
60745
|
-
{ limit: 5 }
|
|
60746
|
-
);
|
|
60899
|
+
const data = await client2.get(path, { limit: 5 });
|
|
60747
60900
|
const matches = [];
|
|
60748
60901
|
for (const it of data?.checkins?.items ?? []) {
|
|
60749
60902
|
const c = it;
|
|
@@ -60763,19 +60916,22 @@ function registerCheckinTools(server, client2) {
|
|
|
60763
60916
|
{
|
|
60764
60917
|
title: "Toast an Untappd check-in",
|
|
60765
60918
|
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}`,
|
|
60766
|
-
|
|
60919
|
+
// Destructive: a toast lands in the check-in owner's notifications, and toggling it back off cannot un-notify them.
|
|
60920
|
+
annotations: toolAnnotations({ title: "Toast an Untappd check-in", readOnly: false, idempotent: false, openWorld: true, destructive: true }),
|
|
60767
60921
|
inputSchema: external_exports.object({
|
|
60768
60922
|
checkin_id: CheckinIdSchema,
|
|
60769
60923
|
confirmToken: confirmTokenParam
|
|
60770
60924
|
})
|
|
60771
60925
|
},
|
|
60772
|
-
async (
|
|
60926
|
+
async (args, ctx) => {
|
|
60927
|
+
const { checkin_id, confirmToken } = args;
|
|
60773
60928
|
const request = { method: "POST", path: `/checkin/toast/${checkin_id}` };
|
|
60774
60929
|
const gate = await confirmWrite(ctx, {
|
|
60775
60930
|
tool: "untappd_toast",
|
|
60776
60931
|
action: "untappd.toast",
|
|
60777
60932
|
message: "Review and confirm toggling your toast on this Untappd check-in:",
|
|
60778
60933
|
confirmToken,
|
|
60934
|
+
args,
|
|
60779
60935
|
target: checkin_id,
|
|
60780
60936
|
payload: request,
|
|
60781
60937
|
preview: {
|
|
@@ -60798,20 +60954,23 @@ function registerCheckinTools(server, client2) {
|
|
|
60798
60954
|
{
|
|
60799
60955
|
title: "Comment on an Untappd check-in",
|
|
60800
60956
|
description: "Post a comment on a check-in from YOUR account. Writes to your Untappd account and is visible to others. " + CONFIRM_FLOW,
|
|
60801
|
-
|
|
60957
|
+
// Destructive: the comment reaches the check-in owner (and other readers); untappd_delete_comment removes it but cannot un-notify anyone.
|
|
60958
|
+
annotations: toolAnnotations({ title: "Comment on an Untappd check-in", readOnly: false, idempotent: false, openWorld: true, destructive: true }),
|
|
60802
60959
|
inputSchema: external_exports.object({
|
|
60803
60960
|
checkin_id: CheckinIdSchema,
|
|
60804
60961
|
comment: external_exports.string().min(1).max(2e3).describe("Comment text to post"),
|
|
60805
60962
|
confirmToken: confirmTokenParam
|
|
60806
60963
|
})
|
|
60807
60964
|
},
|
|
60808
|
-
async (
|
|
60965
|
+
async (args, ctx) => {
|
|
60966
|
+
const { checkin_id, comment, confirmToken } = args;
|
|
60809
60967
|
const request = { method: "POST", path: `/checkin/addcomment/${checkin_id}`, form: { comment } };
|
|
60810
60968
|
const gate = await confirmWrite(ctx, {
|
|
60811
60969
|
tool: "untappd_add_comment",
|
|
60812
60970
|
action: "untappd.add_comment",
|
|
60813
60971
|
message: "Review and confirm posting this comment from your Untappd account:",
|
|
60814
60972
|
confirmToken,
|
|
60973
|
+
args,
|
|
60815
60974
|
target: checkin_id,
|
|
60816
60975
|
payload: request,
|
|
60817
60976
|
preview: {
|
|
@@ -60841,13 +61000,15 @@ function registerCheckinTools(server, client2) {
|
|
|
60841
61000
|
confirmToken: confirmTokenParam
|
|
60842
61001
|
})
|
|
60843
61002
|
},
|
|
60844
|
-
async (
|
|
61003
|
+
async (args, ctx) => {
|
|
61004
|
+
const { comment_id, confirmToken } = args;
|
|
60845
61005
|
const request = { method: "POST", path: `/checkin/deletecomment/${comment_id}` };
|
|
60846
61006
|
const gate = await confirmWrite(ctx, {
|
|
60847
61007
|
tool: "untappd_delete_comment",
|
|
60848
61008
|
action: "untappd.delete_comment",
|
|
60849
61009
|
message: "Review and confirm deleting this comment from your Untappd account:",
|
|
60850
61010
|
confirmToken,
|
|
61011
|
+
args,
|
|
60851
61012
|
target: comment_id,
|
|
60852
61013
|
payload: request,
|
|
60853
61014
|
preview: { action: "delete_comment", comment_id, ...request, note: "Deletes this comment from your Untappd account." }
|
|
@@ -60868,13 +61029,15 @@ function registerCheckinTools(server, client2) {
|
|
|
60868
61029
|
confirmToken: confirmTokenParam
|
|
60869
61030
|
})
|
|
60870
61031
|
},
|
|
60871
|
-
async (
|
|
61032
|
+
async (args, ctx) => {
|
|
61033
|
+
const { checkin_id, confirmToken } = args;
|
|
60872
61034
|
const request = { method: "POST", path: `/checkin/delete/${checkin_id}` };
|
|
60873
61035
|
const gate = await confirmWrite(ctx, {
|
|
60874
61036
|
tool: "untappd_delete_checkin",
|
|
60875
61037
|
action: "untappd.delete_checkin",
|
|
60876
61038
|
message: "Review and confirm PERMANENTLY deleting this Untappd check-in:",
|
|
60877
61039
|
confirmToken,
|
|
61040
|
+
args,
|
|
60878
61041
|
target: checkin_id,
|
|
60879
61042
|
payload: request,
|
|
60880
61043
|
preview: {
|
|
@@ -60894,14 +61057,15 @@ function registerCheckinTools(server, client2) {
|
|
|
60894
61057
|
{
|
|
60895
61058
|
title: "Check in a beer on Untappd",
|
|
60896
61059
|
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}`,
|
|
60897
|
-
|
|
61060
|
+
// Destructive: it publishes to your public feed, where friends see it; untappd_delete_checkin cannot unpublish what they saw.
|
|
61061
|
+
annotations: toolAnnotations({ title: "Check in a beer on Untappd", readOnly: false, idempotent: false, openWorld: true, destructive: true }),
|
|
60898
61062
|
inputSchema: external_exports.object({
|
|
60899
61063
|
bid: external_exports.number().int().positive().describe("Untappd beer id to check in (from untappd_search_beer)"),
|
|
60900
61064
|
rating: RatingSchema.optional().describe("Rating 0\u20135 in 0.25 increments (omit for no rating)"),
|
|
60901
61065
|
shout: external_exports.string().max(2e3).optional().describe("Optional shout / comment text for the check-in"),
|
|
60902
61066
|
foursquare_id: external_exports.string().optional().describe("Optional Foursquare venue id to tag the check-in location"),
|
|
60903
61067
|
photo_path: external_exports.string().optional().describe(
|
|
60904
|
-
"Optional path to a local JPEG/PNG photo (max 15 MB) to attach \u2014 it is published publicly. Only use a file the user explicitly chose; the preview shows the resolved path and size for them to confirm."
|
|
61068
|
+
"Optional path to a local JPEG/PNG photo (max 15 MB) to attach \u2014 it is published publicly. It must be inside the photo directory (UNTAPPD_PHOTO_DIR, default ~/Downloads/untappd-mcp). Only use a file the user explicitly chose; the preview shows the resolved path and size for them to confirm."
|
|
60905
61069
|
),
|
|
60906
61070
|
geolat: external_exports.number().optional().describe("Optional latitude of the check-in"),
|
|
60907
61071
|
geolng: external_exports.number().optional().describe("Optional longitude of the check-in"),
|
|
@@ -60912,7 +61076,8 @@ function registerCheckinTools(server, client2) {
|
|
|
60912
61076
|
confirmToken: confirmTokenParam
|
|
60913
61077
|
})
|
|
60914
61078
|
},
|
|
60915
|
-
async (
|
|
61079
|
+
async (args, ctx) => {
|
|
61080
|
+
const { bid, rating, shout, foursquare_id, photo_path, geolat, geolng, container_id, timezone: requestedTz, confirmToken } = args;
|
|
60916
61081
|
const { timezone, gmt_offset } = checkinTimezone(requestedTz);
|
|
60917
61082
|
const photo = photo_path !== void 0 ? await checkPhoto(photo_path) : void 0;
|
|
60918
61083
|
const photo_sha256 = photo ? await photoSha256(photo.path) : void 0;
|
|
@@ -60936,6 +61101,7 @@ function registerCheckinTools(server, client2) {
|
|
|
60936
61101
|
action: "untappd.checkin",
|
|
60937
61102
|
message: "Review and confirm posting this check-in to your public Untappd feed:",
|
|
60938
61103
|
confirmToken,
|
|
61104
|
+
args,
|
|
60939
61105
|
target: bid,
|
|
60940
61106
|
payload: { ...request, photo, photo_sha256 },
|
|
60941
61107
|
preview: {
|
|
@@ -61039,7 +61205,7 @@ function registerUtilityTools(server, client2) {
|
|
|
61039
61205
|
ok: false,
|
|
61040
61206
|
configured: false,
|
|
61041
61207
|
...build2,
|
|
61042
|
-
note: "Untappd credentials are not set. Configure UNTAPPD_USERNAME, UNTAPPD_PASSWORD, UNTAPPD_CLIENT_ID
|
|
61208
|
+
note: "Untappd credentials are not set. Configure either UNTAPPD_ACCESS_TOKEN (a token you already hold \u2014 no password needed; enough for reads) or UNTAPPD_USERNAME, UNTAPPD_PASSWORD, UNTAPPD_CLIENT_ID and UNTAPPD_CLIENT_SECRET to log in. Writes also need UNTAPPD_CLIENT_ID and UNTAPPD_CLIENT_SECRET."
|
|
61043
61209
|
});
|
|
61044
61210
|
}
|
|
61045
61211
|
const feed = await client2.get("/checkin/recent", { limit: 1 });
|
|
@@ -61048,8 +61214,9 @@ function registerUtilityTools(server, client2) {
|
|
|
61048
61214
|
configured: true,
|
|
61049
61215
|
account: client2.loginName,
|
|
61050
61216
|
feed_reachable: feed?.checkins !== void 0,
|
|
61217
|
+
writes_enabled: client2.canWrite,
|
|
61051
61218
|
...build2,
|
|
61052
|
-
note: "Logged in to Untappd and fetched the friend feed successfully."
|
|
61219
|
+
note: client2.canWrite ? "Logged in to Untappd and fetched the friend feed successfully." : "Fetched the friend feed successfully with the access token. Reads work; writes (check-in, toast, comment, wishlist, friend actions) need UNTAPPD_CLIENT_ID and UNTAPPD_CLIENT_SECRET as well."
|
|
61053
61220
|
});
|
|
61054
61221
|
}
|
|
61055
61222
|
);
|
|
@@ -61076,6 +61243,12 @@ function rowsOf(username, items) {
|
|
|
61076
61243
|
const r = mapCheckinRow(username, it);
|
|
61077
61244
|
if (r) rows.push(r);
|
|
61078
61245
|
}
|
|
61246
|
+
if (rows.length === 0 && items.length > 0) {
|
|
61247
|
+
throw createHelpfulError(
|
|
61248
|
+
`Untappd returned ${items.length} check-in(s) for "${username}" with no numeric checkin_id; the response shape may have changed.`,
|
|
61249
|
+
{ hint: "Progress from earlier pages is kept. Re-run the sync later; if it persists, the user/checkins parser needs updating." }
|
|
61250
|
+
);
|
|
61251
|
+
}
|
|
61079
61252
|
return rows;
|
|
61080
61253
|
}
|
|
61081
61254
|
async function fetchTotalCheckins(client2, encodedUser) {
|
|
@@ -61600,7 +61773,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
61600
61773
|
"untappd_top_not_had",
|
|
61601
61774
|
{
|
|
61602
61775
|
title: "Top-rated beers a user has NOT had, from a candidate list",
|
|
61603
|
-
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. Reports the same freshness/caveat block as untappd_cache_not_had. Omit username for your own account.',
|
|
61776
|
+
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.',
|
|
61604
61777
|
annotations: toolAnnotations({ title: "Top-rated beers a user has NOT had, from a candidate list", readOnly: false, idempotent: false, openWorld: true, destructive: false }),
|
|
61605
61778
|
inputSchema: external_exports.object({
|
|
61606
61779
|
username: UsernameArg2,
|
|
@@ -61654,7 +61827,14 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
61654
61827
|
}
|
|
61655
61828
|
}
|
|
61656
61829
|
if (fetched.length) await cache.upsertBeerMeta(fetched);
|
|
61657
|
-
const
|
|
61830
|
+
const refreshed = new Set(fetched.map((m) => m.bid));
|
|
61831
|
+
const staleMeta = [];
|
|
61832
|
+
for (const bid of needFetch) {
|
|
61833
|
+
const m = metaByBid.get(bid);
|
|
61834
|
+
if (m && !refreshed.has(bid)) staleMeta.push(m);
|
|
61835
|
+
}
|
|
61836
|
+
const staleBids = new Set(staleMeta.map((m) => m.bid));
|
|
61837
|
+
const available = [...haveMeta, ...fetched, ...staleMeta];
|
|
61658
61838
|
const ratingOf = (m) => m.weighted_rating_score ?? m.rating_score ?? 0;
|
|
61659
61839
|
let matched = available;
|
|
61660
61840
|
if (style !== void 0) {
|
|
@@ -61670,9 +61850,10 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
61670
61850
|
style: m.style,
|
|
61671
61851
|
abv: m.abv,
|
|
61672
61852
|
rating: m.weighted_rating_score ?? m.rating_score,
|
|
61673
|
-
rating_count: m.rating_count
|
|
61853
|
+
rating_count: m.rating_count,
|
|
61854
|
+
...staleBids.has(m.bid) ? { stale: true } : {}
|
|
61674
61855
|
}));
|
|
61675
|
-
const partial2 = deferred > 0;
|
|
61856
|
+
const partial2 = deferred > 0 || errors > 0;
|
|
61676
61857
|
return minifiedResult({
|
|
61677
61858
|
username: user,
|
|
61678
61859
|
ranked,
|
|
@@ -61682,6 +61863,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
61682
61863
|
style_matched: style !== void 0 ? matched.length : null,
|
|
61683
61864
|
api_calls_used: apiCalls,
|
|
61684
61865
|
errors,
|
|
61866
|
+
stale: staleMeta.length,
|
|
61685
61867
|
partial: partial2,
|
|
61686
61868
|
another_run_needed: partial2,
|
|
61687
61869
|
...rateLimited ? { rate_limited: true } : {}
|
|
@@ -61701,7 +61883,8 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
61701
61883
|
confirmToken: confirmTokenParam
|
|
61702
61884
|
})
|
|
61703
61885
|
},
|
|
61704
|
-
async (
|
|
61886
|
+
async (args, ctx) => {
|
|
61887
|
+
const { username, confirmToken } = args;
|
|
61705
61888
|
const user = resolveUser2(username, client2.loginName);
|
|
61706
61889
|
const cache = cacheProvider();
|
|
61707
61890
|
const counts = {
|
|
@@ -61713,6 +61896,7 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
61713
61896
|
action: "untappd.cache_forget",
|
|
61714
61897
|
message: `Review and confirm deleting ${user}'s data from the local Untappd cache:`,
|
|
61715
61898
|
confirmToken,
|
|
61899
|
+
args,
|
|
61716
61900
|
target: user.toLowerCase(),
|
|
61717
61901
|
payload: { username: user.toLowerCase(), ...counts },
|
|
61718
61902
|
preview: {
|
|
@@ -61732,8 +61916,8 @@ function registerCacheTools(server, client2, cacheProvider) {
|
|
|
61732
61916
|
// src/cache/db.ts
|
|
61733
61917
|
import { DatabaseSync } from "node:sqlite";
|
|
61734
61918
|
import { chmodSync, closeSync as closeSync2, existsSync as existsSync2, mkdirSync as mkdirSync3, openSync as openSync2 } from "node:fs";
|
|
61735
|
-
import { dirname as dirname3, join as
|
|
61736
|
-
import { homedir as
|
|
61919
|
+
import { dirname as dirname3, join as join6, resolve as resolve3 } from "node:path";
|
|
61920
|
+
import { homedir as homedir3 } from "node:os";
|
|
61737
61921
|
var NodeSqlDriver = class {
|
|
61738
61922
|
constructor(db) {
|
|
61739
61923
|
this.db = db;
|
|
@@ -61785,8 +61969,9 @@ var CheckinCache = class _CheckinCache extends LocalCacheStore {
|
|
|
61785
61969
|
};
|
|
61786
61970
|
function preparePrivateFile(path) {
|
|
61787
61971
|
const dir = dirname3(path);
|
|
61972
|
+
const dirExisted = existsSync2(dir);
|
|
61788
61973
|
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
61789
|
-
bestEffortChmod(dir, 448);
|
|
61974
|
+
if (!dirExisted || resolve3(dir) === resolve3(defaultCacheDir())) bestEffortChmod(dir, 448);
|
|
61790
61975
|
if (!existsSync2(path)) closeSync2(openSync2(path, "a", 384));
|
|
61791
61976
|
bestEffortChmod(path, 384);
|
|
61792
61977
|
}
|
|
@@ -61802,7 +61987,10 @@ function bestEffortChmod(path, mode) {
|
|
|
61802
61987
|
}
|
|
61803
61988
|
}
|
|
61804
61989
|
function defaultCachePath() {
|
|
61805
|
-
return readEnvVar("UNTAPPD_CACHE_DB") ??
|
|
61990
|
+
return readEnvVar("UNTAPPD_CACHE_DB") ?? join6(defaultCacheDir(), "checkins.db");
|
|
61991
|
+
}
|
|
61992
|
+
function defaultCacheDir() {
|
|
61993
|
+
return join6(homedir3(), ".untappd-mcp");
|
|
61806
61994
|
}
|
|
61807
61995
|
|
|
61808
61996
|
// src/index.ts
|