untappd-mcp 2.1.1 → 2.1.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.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "MCP server for Untappd — beers, breweries, venues, check-ins, wishlists, and your friend feed",
10
- "version": "2.1.1"
10
+ "version": "2.1.2"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "Untappd",
16
16
  "source": "./",
17
17
  "description": "MCP server for Untappd — search beers/breweries/venues, read profiles/check-ins/wishlists, and post check-ins, toasts, and comments",
18
- "version": "2.1.1",
18
+ "version": "2.1.2",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "untappd-mcp",
3
3
  "displayName": "Untappd",
4
- "version": "2.1.1",
4
+ "version": "2.1.2",
5
5
  "description": "MCP server for Untappd — search beers/breweries/venues, read check-ins and wishlists, and post check-ins, toasts, and comments",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/dist/bundle.js CHANGED
@@ -2282,7 +2282,7 @@ var require_versions = __commonJS({
2282
2282
  exports.version = {
2283
2283
  major: 4,
2284
2284
  minor: 6,
2285
- patch: 4
2285
+ patch: 5
2286
2286
  };
2287
2287
  (function() {
2288
2288
  var keys = Object.getOwnPropertyNames(exports);
@@ -4851,7 +4851,7 @@ var require_memoizer = __commonJS({
4851
4851
  var NONE2 = 0;
4852
4852
  var ASSUMED2 = 1;
4853
4853
  var PROVEN2 = 2;
4854
- function isRecursive2(inst, stack, resolve) {
4854
+ function isRecursive2(inst, stack, resolve3) {
4855
4855
  const cached2 = recursive2.get(inst);
4856
4856
  if (cached2 !== void 0)
4857
4857
  return cached2 ? PROVEN2 : NONE2;
@@ -4861,7 +4861,7 @@ var require_memoizer = __commonJS({
4861
4861
  let result = NONE2;
4862
4862
  const check2 = (child) => {
4863
4863
  if (result !== PROVEN2 && child?._zod) {
4864
- const answer = isRecursive2(child, stack, resolve);
4864
+ const answer = isRecursive2(child, stack, resolve3);
4865
4865
  if (answer > result)
4866
4866
  result = answer;
4867
4867
  }
@@ -4872,7 +4872,7 @@ var require_memoizer = __commonJS({
4872
4872
  const desc = Object.getOwnPropertyDescriptor(sh, key);
4873
4873
  if (spread && !desc.enumerable)
4874
4874
  continue;
4875
- const child = desc.get ? ASSUMED2 : desc.value?._zod ? isRecursive2(desc.value, stack, resolve) : NONE2;
4875
+ const child = desc.get ? ASSUMED2 : desc.value?._zod ? isRecursive2(desc.value, stack, resolve3) : NONE2;
4876
4876
  if (child > answer)
4877
4877
  answer = child;
4878
4878
  }
@@ -4936,7 +4936,7 @@ var require_memoizer = __commonJS({
4936
4936
  break;
4937
4937
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
4938
4938
  case "lazy": {
4939
- const inner = def._cachedInner ?? (resolve ? inst._zod.innerType : void 0);
4939
+ const inner = def._cachedInner ?? (resolve3 ? inst._zod.innerType : void 0);
4940
4940
  merge2(inner ? isRecursive2(inner, stack, false) : ASSUMED2);
4941
4941
  break;
4942
4942
  }
@@ -27718,14 +27718,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
27718
27718
  return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
27719
27719
  }
27720
27720
  function sleep(ms, signal) {
27721
- return new Promise((resolve, reject) => {
27721
+ return new Promise((resolve3, reject) => {
27722
27722
  if (signal?.aborted) {
27723
27723
  reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
27724
27724
  return;
27725
27725
  }
27726
27726
  const timer = setTimeout(() => {
27727
27727
  signal?.removeEventListener("abort", onAbort);
27728
- resolve();
27728
+ resolve3();
27729
27729
  }, ms);
27730
27730
  const onAbort = () => {
27731
27731
  clearTimeout(timer);
@@ -28519,7 +28519,7 @@ var Protocol = class {
28519
28519
  const flowStartedAt = Date.now();
28520
28520
  let onAbort;
28521
28521
  let cleanupMessageId;
28522
- return new Promise((resolve, reject) => {
28522
+ return new Promise((resolve3, reject) => {
28523
28523
  const earlyReject = (error62) => {
28524
28524
  reject(error62);
28525
28525
  };
@@ -28587,7 +28587,7 @@ var Protocol = class {
28587
28587
  }
28588
28588
  if (decoded.kind === "invalid") return reject(decoded.error);
28589
28589
  if (decoded.kind === "input_required") {
28590
- if (options?.allowInputRequired === true) return resolve(manualInputRequiredValue(decoded));
28590
+ if (options?.allowInputRequired === true) return resolve3(manualInputRequiredValue(decoded));
28591
28591
  const flow = {
28592
28592
  codec: codec2,
28593
28593
  request,
@@ -28599,11 +28599,11 @@ var Protocol = class {
28599
28599
  params
28600
28600
  }, resultSchema, legOptions)
28601
28601
  };
28602
- return resolve(this._resolveNonCompleteResult(decoded, flow));
28602
+ return resolve3(this._resolveNonCompleteResult(decoded, flow));
28603
28603
  }
28604
28604
  const result = decoded.result;
28605
28605
  validateStandardSchema(resultSchema, result).then((parseResult) => {
28606
- if (parseResult.success) resolve(parseResult.data);
28606
+ if (parseResult.success) resolve3(parseResult.data);
28607
28607
  else reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
28608
28608
  }, reject);
28609
28609
  });
@@ -31515,7 +31515,7 @@ var require_compile2 = /* @__PURE__ */ __commonJSMin(((exports) => {
31515
31515
  ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref);
31516
31516
  const schOrFunc = root.refs[ref];
31517
31517
  if (schOrFunc) return schOrFunc;
31518
- let _sch = resolve.call(this, root, ref);
31518
+ let _sch = resolve3.call(this, root, ref);
31519
31519
  if (_sch === void 0) {
31520
31520
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
31521
31521
  const { schemaId } = this.opts;
@@ -31541,7 +31541,7 @@ var require_compile2 = /* @__PURE__ */ __commonJSMin(((exports) => {
31541
31541
  function sameSchemaEnv(s1, s2) {
31542
31542
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
31543
31543
  }
31544
- function resolve(root, ref) {
31544
+ function resolve3(root, ref) {
31545
31545
  let sch;
31546
31546
  while (typeof (sch = this.refs[ref]) == "string") ref = sch;
31547
31547
  return sch || this.schemas[ref] || resolveSchema.call(this, root, ref);
@@ -31991,47 +31991,47 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
31991
31991
  else if (typeof uri === "object") uri = parse3(serialize(uri, options), options);
31992
31992
  return uri;
31993
31993
  }
31994
- function resolve(baseURI, relativeURI, options) {
31994
+ function resolve3(baseURI, relativeURI, options) {
31995
31995
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
31996
31996
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
31997
31997
  schemelessOptions.skipEscape = true;
31998
31998
  return serialize(resolved, schemelessOptions);
31999
31999
  }
32000
- function resolveComponent(base, relative, options, skipNormalization) {
32000
+ function resolveComponent(base, relative2, options, skipNormalization) {
32001
32001
  const target = {};
32002
32002
  if (!skipNormalization) {
32003
32003
  base = parse3(serialize(base, options), options);
32004
- relative = parse3(serialize(relative, options), options);
32004
+ relative2 = parse3(serialize(relative2, options), options);
32005
32005
  }
32006
32006
  options = options || {};
32007
- if (!options.tolerant && relative.scheme) {
32008
- target.scheme = relative.scheme;
32009
- target.userinfo = relative.userinfo;
32010
- target.host = relative.host;
32011
- target.port = relative.port;
32012
- target.path = removeDotSegments(relative.path || "");
32013
- target.query = relative.query;
32007
+ if (!options.tolerant && relative2.scheme) {
32008
+ target.scheme = relative2.scheme;
32009
+ target.userinfo = relative2.userinfo;
32010
+ target.host = relative2.host;
32011
+ target.port = relative2.port;
32012
+ target.path = removeDotSegments(relative2.path || "");
32013
+ target.query = relative2.query;
32014
32014
  } else {
32015
- if (relative.userinfo !== void 0 || relative.host !== void 0 || relative.port !== void 0) {
32016
- target.userinfo = relative.userinfo;
32017
- target.host = relative.host;
32018
- target.port = relative.port;
32019
- target.path = removeDotSegments(relative.path || "");
32020
- target.query = relative.query;
32015
+ if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
32016
+ target.userinfo = relative2.userinfo;
32017
+ target.host = relative2.host;
32018
+ target.port = relative2.port;
32019
+ target.path = removeDotSegments(relative2.path || "");
32020
+ target.query = relative2.query;
32021
32021
  } else {
32022
- if (!relative.path) {
32022
+ if (!relative2.path) {
32023
32023
  target.path = base.path;
32024
- if (relative.query !== void 0) target.query = relative.query;
32024
+ if (relative2.query !== void 0) target.query = relative2.query;
32025
32025
  else target.query = base.query;
32026
32026
  } else {
32027
- if (relative.path[0] === "/") target.path = removeDotSegments(relative.path);
32027
+ if (relative2.path[0] === "/") target.path = removeDotSegments(relative2.path);
32028
32028
  else {
32029
- if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) target.path = "/" + relative.path;
32030
- else if (!base.path) target.path = relative.path;
32031
- else target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path;
32029
+ if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) target.path = "/" + relative2.path;
32030
+ else if (!base.path) target.path = relative2.path;
32031
+ else target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
32032
32032
  target.path = removeDotSegments(target.path);
32033
32033
  }
32034
- target.query = relative.query;
32034
+ target.query = relative2.query;
32035
32035
  }
32036
32036
  target.userinfo = base.userinfo;
32037
32037
  target.host = base.host;
@@ -32039,7 +32039,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
32039
32039
  }
32040
32040
  target.scheme = base.scheme;
32041
32041
  }
32042
- target.fragment = relative.fragment;
32042
+ target.fragment = relative2.fragment;
32043
32043
  return target;
32044
32044
  }
32045
32045
  function equal(uriA, uriB, options) {
@@ -32165,7 +32165,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
32165
32165
  const fastUri = {
32166
32166
  SCHEMES,
32167
32167
  normalize,
32168
- resolve,
32168
+ resolve: resolve3,
32169
32169
  resolveComponent,
32170
32170
  equal,
32171
32171
  serialize,
@@ -37538,7 +37538,7 @@ var StdioServerTransport = class {
37538
37538
  }
37539
37539
  send(message) {
37540
37540
  if (this._closed) return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
37541
- return new Promise((resolve, reject) => {
37541
+ return new Promise((resolve3, reject) => {
37542
37542
  const json2 = serializeMessage(message);
37543
37543
  let settled = false;
37544
37544
  const onError = (error62) => {
@@ -37553,14 +37553,14 @@ var StdioServerTransport = class {
37553
37553
  settled = true;
37554
37554
  this._stdout.off("error", onError);
37555
37555
  this._stdout.off("drain", onDrain);
37556
- resolve();
37556
+ resolve3();
37557
37557
  };
37558
37558
  this._stdout.once("error", onError);
37559
37559
  if (this._stdout.write(json2)) {
37560
37560
  if (settled) return;
37561
37561
  settled = true;
37562
37562
  this._stdout.off("error", onError);
37563
- resolve();
37563
+ resolve3();
37564
37564
  } else if (!settled) this._stdout.once("drain", onDrain);
37565
37565
  });
37566
37566
  }
@@ -37614,14 +37614,14 @@ var StdioConnectionChannel = class {
37614
37614
  */
37615
37615
  async whenRequestsAnswered(timeoutMs) {
37616
37616
  if (this._closed || this._pendingRequests.size === 0) return true;
37617
- return await new Promise((resolve) => {
37617
+ return await new Promise((resolve3) => {
37618
37618
  const waiter = () => {
37619
37619
  clearTimeout(timer);
37620
- resolve(true);
37620
+ resolve3(true);
37621
37621
  };
37622
37622
  const timer = setTimeout(() => {
37623
37623
  this._drainWaiters = this._drainWaiters.filter((pending) => pending !== waiter);
37624
- resolve(false);
37624
+ resolve3(false);
37625
37625
  }, timeoutMs);
37626
37626
  this._drainWaiters.push(waiter);
37627
37627
  });
@@ -38019,13 +38019,28 @@ var API_KEY_RE = new RegExp([
38019
38019
  "whsec_[A-Za-z0-9]{16,}\\b"
38020
38020
  // webhook signing secret (Stripe-style)
38021
38021
  ].map((p) => `\\b${p}`).join("|"), "g");
38022
- var QUERY_SECRET_RE = /([?&](?:access_token|refresh_token|client_secret|api_?key|signature|token|key|sig)=)[^&#\s"'<>`]+/gi;
38022
+ var QUERY_SECRET_RE = /([?&](?:(?:access|refresh|id|auth|session|csrf|xsrf)[_-]?token|client[_-]?secret|api[_-]?secret|api[_-]?key|password|passwd|signature|token|key|sig)=)[^&#\s"'<>`]+/gi;
38023
+ var OAUTH_CODE_RE = /([?&]code=)(?=[^&#\s"'<>`]{16,})[^&#\s"'<>`]+/gi;
38024
+ var FORM_PASSWORD_RE = /((?:^|[\s,;:"'(\[{])(?:password|passwd)=)[^&#\s"'<>`]+/gim;
38025
+ var HEADER_SECRET_RE = /(\b(?:x[-_][a-z0-9_-]*?(?:api[-_]?key|token|secret|auth)[a-z0-9_-]*|api[-_]?key)\s*:\s*)[^\s,;"'<>`]+/gi;
38023
38026
  var AWS_SIGV4_RE = /([?&]X-Amz-(?:Signature|Security-Token|Credential)=)[^&#\s"'<>`]+/gi;
38024
- var JSON_SECRET_KEYS = "access_token|refresh_token|client_secret|api_?key|password|passwd|secret|token";
38025
- var JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")[^"]*(")`, "gi");
38026
- var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')[^']*(')`, "gi");
38027
+ var JSON_SECRET_KEYS = [
38028
+ "(?:access|refresh|id|auth|session|bearer|csrf|xsrf)[_-]?token",
38029
+ "client[_-]?secret",
38030
+ "api[_-]?secret",
38031
+ "(?:x[_-])?api[_-]?key",
38032
+ "private[_-]?key",
38033
+ "x[_-][a-z0-9_-]*?(?:api[_-]?key|token|secret|auth)[a-z0-9_-]*",
38034
+ "(?:proxy-)?authorization",
38035
+ "password",
38036
+ "passwd",
38037
+ "secret",
38038
+ "token"
38039
+ ].join("|");
38040
+ var JSON_SECRET_DQ_RE = new RegExp(`("(?:${JSON_SECRET_KEYS})"\\s*:\\s*")(?:[^"\\\\]|\\\\.)*(")`, "gi");
38041
+ var JSON_SECRET_SQ_RE = new RegExp(`('(?:${JSON_SECRET_KEYS})'\\s*:\\s*')(?:[^'\\\\]|\\\\.)*(')`, "gi");
38027
38042
  function redactSecrets(text) {
38028
- return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JWT_RE, "[REDACTED]");
38043
+ return text.replace(BEARER_RE, "$1[REDACTED]").replace(BASIC_AUTH_RE, "$1[REDACTED]").replace(SET_COOKIE_RE, "$1$2=[REDACTED]").replace(COOKIE_HEADER_RE, (_m, prefix, pairs) => `${prefix}${pairs.replace(/=[^;,\s]*/g, "=[REDACTED]")}`).replace(API_KEY_RE, "[REDACTED]").replace(HEADER_SECRET_RE, "$1[REDACTED]").replace(QUERY_SECRET_RE, "$1[REDACTED]").replace(OAUTH_CODE_RE, "$1[REDACTED]").replace(FORM_PASSWORD_RE, "$1[REDACTED]").replace(AWS_SIGV4_RE, "$1[REDACTED]").replace(JSON_SECRET_DQ_RE, "$1[REDACTED]$2").replace(JSON_SECRET_SQ_RE, "$1[REDACTED]$2").replace(JWT_RE, "[REDACTED]");
38029
38044
  }
38030
38045
  function truncateErrorMessage(text, max = DEFAULT_ERROR_MESSAGE_MAX) {
38031
38046
  const str2 = text === null || text === void 0 ? "" : String(text);
@@ -40630,7 +40645,7 @@ ${content.join("\n")}
40630
40645
  var version = {
40631
40646
  major: 4,
40632
40647
  minor: 6,
40633
- patch: 4
40648
+ patch: 5
40634
40649
  };
40635
40650
 
40636
40651
  // node_modules/zod/v4/core/schemas.js
@@ -43066,7 +43081,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
43066
43081
  var NONE = 0;
43067
43082
  var ASSUMED = 1;
43068
43083
  var PROVEN = 2;
43069
- function isRecursive(inst, stack, resolve) {
43084
+ function isRecursive(inst, stack, resolve3) {
43070
43085
  const cached2 = recursive.get(inst);
43071
43086
  if (cached2 !== void 0)
43072
43087
  return cached2 ? PROVEN : NONE;
@@ -43076,7 +43091,7 @@ function isRecursive(inst, stack, resolve) {
43076
43091
  let result = NONE;
43077
43092
  const check2 = (child) => {
43078
43093
  if (result !== PROVEN && child?._zod) {
43079
- const answer = isRecursive(child, stack, resolve);
43094
+ const answer = isRecursive(child, stack, resolve3);
43080
43095
  if (answer > result)
43081
43096
  result = answer;
43082
43097
  }
@@ -43087,7 +43102,7 @@ function isRecursive(inst, stack, resolve) {
43087
43102
  const desc = Object.getOwnPropertyDescriptor(sh, key);
43088
43103
  if (spread && !desc.enumerable)
43089
43104
  continue;
43090
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve) : NONE;
43105
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve3) : NONE;
43091
43106
  if (child > answer)
43092
43107
  answer = child;
43093
43108
  }
@@ -43151,7 +43166,7 @@ function isRecursive(inst, stack, resolve) {
43151
43166
  break;
43152
43167
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
43153
43168
  case "lazy": {
43154
- const inner = def._cachedInner ?? (resolve ? inst._zod.innerType : void 0);
43169
+ const inner = def._cachedInner ?? (resolve3 ? inst._zod.innerType : void 0);
43155
43170
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
43156
43171
  break;
43157
43172
  }
@@ -57844,13 +57859,47 @@ async function createMcpServer(opts) {
57844
57859
  }
57845
57860
  return server;
57846
57861
  }
57862
+ var DEFAULT_SHUTDOWN_TIMEOUT_MS = 5e3;
57863
+ var DEFAULT_REPEAT_SIGNAL_GRACE_MS = 500;
57847
57864
  function withGracefulShutdown(target, opts = {}) {
57848
57865
  const shouldExit = opts.exit ?? true;
57866
+ if (opts.timeoutMs !== void 0 && !(Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0)) {
57867
+ throw new RangeError("withGracefulShutdown: timeoutMs must be a finite number greater than 0.");
57868
+ }
57869
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;
57870
+ if (opts.repeatSignalGraceMs !== void 0 && !(Number.isFinite(opts.repeatSignalGraceMs) && opts.repeatSignalGraceMs >= 0)) {
57871
+ throw new RangeError("withGracefulShutdown: repeatSignalGraceMs must be a finite number >= 0.");
57872
+ }
57873
+ const repeatGraceMs = opts.repeatSignalGraceMs ?? DEFAULT_REPEAT_SIGNAL_GRACE_MS;
57849
57874
  let shuttingDown = false;
57875
+ let firstSignalAt = 0;
57876
+ let exited = false;
57877
+ const exitOnce = () => {
57878
+ if (exited)
57879
+ return;
57880
+ exited = true;
57881
+ process.exit(0);
57882
+ };
57850
57883
  const handler = (signal) => {
57851
- if (shuttingDown)
57884
+ if (shuttingDown) {
57885
+ if (Date.now() - firstSignalAt < repeatGraceMs)
57886
+ return;
57887
+ if (shouldExit) {
57888
+ console.error(`[mcp-utils] second ${signal} during shutdown \u2014 exiting now`);
57889
+ exitOnce();
57890
+ }
57852
57891
  return;
57892
+ }
57853
57893
  shuttingDown = true;
57894
+ firstSignalAt = Date.now();
57895
+ let timer;
57896
+ if (shouldExit) {
57897
+ timer = setTimeout(() => {
57898
+ console.error(`[mcp-utils] graceful shutdown on ${signal} did not finish in ${timeoutMs}ms \u2014 exiting`);
57899
+ exitOnce();
57900
+ }, timeoutMs);
57901
+ timer.unref?.();
57902
+ }
57854
57903
  void (async () => {
57855
57904
  try {
57856
57905
  if (opts.onSignal)
@@ -57859,8 +57908,10 @@ function withGracefulShutdown(target, opts = {}) {
57859
57908
  } catch (err) {
57860
57909
  console.error(`[mcp-utils] error during graceful shutdown on ${signal}: ${err instanceof Error ? err.message : String(err)}`);
57861
57910
  } finally {
57911
+ if (timer !== void 0)
57912
+ clearTimeout(timer);
57862
57913
  if (shouldExit)
57863
- process.exit(0);
57914
+ exitOnce();
57864
57915
  }
57865
57916
  })();
57866
57917
  };
@@ -57890,6 +57941,8 @@ function runMcp(opts) {
57890
57941
  }
57891
57942
 
57892
57943
  // node_modules/@chrischall/mcp-utils/dist/config/index.js
57944
+ import { homedir } from "node:os";
57945
+ import { isAbsolute, join, resolve } from "node:path";
57893
57946
  var PLACEHOLDER_RE = /^\$\{[^}]*\}$/;
57894
57947
  function readEnvVar(key, opts = {}) {
57895
57948
  const env = opts.env ?? process.env;
@@ -57902,6 +57955,15 @@ function readEnvVar(key, opts = {}) {
57902
57955
  }
57903
57956
  return opts.default;
57904
57957
  }
57958
+ function expandPath(p) {
57959
+ let expanded = p;
57960
+ if (p === "~") {
57961
+ expanded = homedir();
57962
+ } else if (p.startsWith("~/")) {
57963
+ expanded = join(homedir(), p.slice(2));
57964
+ }
57965
+ return isAbsolute(expanded) ? expanded : resolve(expanded);
57966
+ }
57905
57967
  async function loadDotenvSafely(opts = {}) {
57906
57968
  try {
57907
57969
  const mod = await import(
@@ -57921,10 +57983,48 @@ async function loadDotenvSafely(opts = {}) {
57921
57983
 
57922
57984
  // node_modules/@chrischall/mcp-utils/dist/fs/index.js
57923
57985
  import { openAsBlob } from "node:fs";
57986
+
57987
+ // node_modules/@chrischall/mcp-utils/dist/fs/confine.js
57988
+ import { existsSync, realpathSync } from "node:fs";
57989
+ import { dirname, isAbsolute as isAbsolute2, relative, resolve as resolve2, sep } from "node:path";
57990
+ function isInside(root, target) {
57991
+ const rel = relative(root, target);
57992
+ return rel === "" || !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute2(rel);
57993
+ }
57994
+ function realPathAllowingMissing(p) {
57995
+ const abs = resolve2(p);
57996
+ let existing = abs;
57997
+ const tail = [];
57998
+ while (!existsSync(existing)) {
57999
+ const parent = dirname(existing);
58000
+ if (parent === existing)
58001
+ break;
58002
+ tail.unshift(existing.slice(parent.length).replace(/^[/\\]+/, ""));
58003
+ existing = parent;
58004
+ }
58005
+ let real;
58006
+ try {
58007
+ real = realpathSync(existing);
58008
+ } catch {
58009
+ real = existing;
58010
+ }
58011
+ return tail.length > 0 ? resolve2(real, ...tail) : real;
58012
+ }
58013
+ function assertPathWithinRoots(path, roots) {
58014
+ const target = realPathAllowingMissing(expandPath(path));
58015
+ for (const root of roots) {
58016
+ if (isInside(realPathAllowingMissing(expandPath(root)), target))
58017
+ return target;
58018
+ }
58019
+ throw new Error(`Path is outside the allowed directories: ${path}`);
58020
+ }
58021
+
58022
+ // node_modules/@chrischall/mcp-utils/dist/fs/index.js
57924
58023
  async function fileBlob(path, opts = {}) {
58024
+ const target = opts.allowedRoots ? assertPathWithinRoots(path, opts.allowedRoots) : path;
57925
58025
  let blob;
57926
58026
  try {
57927
- blob = await openAsBlob(path, opts.type !== void 0 ? { type: opts.type } : void 0);
58027
+ blob = await openAsBlob(target, opts.type !== void 0 ? { type: opts.type } : void 0);
57928
58028
  } catch {
57929
58029
  throw new Error(`Cannot read file for upload: ${path}`);
57930
58030
  }
@@ -57978,9 +58078,12 @@ var pageSchema = {
57978
58078
  page_size: external_exports.number().int().min(1).max(200).default(50).describe("Number of items per page (1-200).")
57979
58079
  };
57980
58080
  function toolAnnotations(opts = {}) {
58081
+ if (opts.readOnly === true && opts.destructive === true) {
58082
+ throw new Error("toolAnnotations: readOnly: true contradicts destructive: true \u2014 a destructive tool is not read-only.");
58083
+ }
57981
58084
  return {
57982
58085
  ...opts.title !== void 0 ? { title: opts.title } : {},
57983
- readOnlyHint: opts.readOnly ?? true,
58086
+ readOnlyHint: opts.readOnly ?? opts.destructive === void 0,
57984
58087
  ...opts.idempotent !== void 0 ? { idempotentHint: opts.idempotent } : {},
57985
58088
  ...opts.destructive !== void 0 ? { destructiveHint: opts.destructive } : {},
57986
58089
  ...opts.openWorld !== void 0 ? { openWorldHint: opts.openWorld } : {}
@@ -57988,14 +58091,14 @@ function toolAnnotations(opts = {}) {
57988
58091
  }
57989
58092
 
57990
58093
  // src/version.ts
57991
- var VERSION = "2.1.1";
58094
+ var VERSION = "2.1.2";
57992
58095
 
57993
58096
  // src/client.ts
57994
- import { dirname, join } from "path";
58097
+ import { dirname as dirname2, join as join2 } from "path";
57995
58098
  import { fileURLToPath } from "url";
57996
58099
  if (import.meta.url) {
57997
- const __dirname = dirname(fileURLToPath(import.meta.url));
57998
- await loadDotenvSafely({ path: join(__dirname, "..", ".env"), override: false });
58100
+ const __dirname = dirname2(fileURLToPath(import.meta.url));
58101
+ await loadDotenvSafely({ path: join2(__dirname, "..", ".env"), override: false });
57999
58102
  }
58000
58103
  var BASE_URL = "https://api.untappd.com/v4";
58001
58104
  var SERVICE = "Untappd";
@@ -59637,7 +59740,7 @@ function registerWishlistTools(server, client2) {
59637
59740
  }
59638
59741
 
59639
59742
  // src/tools/checkin.ts
59640
- import { existsSync } from "node:fs";
59743
+ import { existsSync as existsSync2 } from "node:fs";
59641
59744
  import { extname } from "node:path";
59642
59745
  var CheckinIdSchema = external_exports.number().int().positive().describe("Untappd check-in id");
59643
59746
  var PHOTO_CONTENT_TYPES = { jpg: "image/jpeg", png: "image/png" };
@@ -59804,7 +59907,7 @@ function registerCheckinTools(server, client2) {
59804
59907
  }
59805
59908
  let blob;
59806
59909
  if (photo_path !== void 0) {
59807
- if (!existsSync(photo_path)) throw new McpToolError(`Photo file not found: ${photo_path}`);
59910
+ if (!existsSync2(photo_path)) throw new McpToolError(`Photo file not found: ${photo_path}`);
59808
59911
  blob = await fileBlob(photo_path);
59809
59912
  }
59810
59913
  const data = await client2.write("POST", "/checkin/add", { form });
@@ -60518,8 +60621,8 @@ function registerCacheTools(server, client2, cacheProvider) {
60518
60621
  // src/cache/db.ts
60519
60622
  import { DatabaseSync } from "node:sqlite";
60520
60623
  import { mkdirSync } from "node:fs";
60521
- import { dirname as dirname2, join as join2 } from "node:path";
60522
- import { homedir } from "node:os";
60624
+ import { dirname as dirname3, join as join3 } from "node:path";
60625
+ import { homedir as homedir2 } from "node:os";
60523
60626
  var NodeSqlDriver = class {
60524
60627
  constructor(db) {
60525
60628
  this.db = db;
@@ -60556,7 +60659,7 @@ var CheckinCache = class _CheckinCache extends LocalCacheStore {
60556
60659
  db;
60557
60660
  /** Open (creating parent dirs) a file-backed cache, or `:memory:` for tests. */
60558
60661
  static open(path) {
60559
- if (path !== ":memory:") mkdirSync(dirname2(path), { recursive: true });
60662
+ if (path !== ":memory:") mkdirSync(dirname3(path), { recursive: true });
60560
60663
  return new _CheckinCache(new DatabaseSync(path));
60561
60664
  }
60562
60665
  close() {
@@ -60564,7 +60667,7 @@ var CheckinCache = class _CheckinCache extends LocalCacheStore {
60564
60667
  }
60565
60668
  };
60566
60669
  function defaultCachePath() {
60567
- return readEnvVar("UNTAPPD_CACHE_DB") ?? join2(homedir(), ".untappd-mcp", "checkins.db");
60670
+ return readEnvVar("UNTAPPD_CACHE_DB") ?? join3(homedir2(), ".untappd-mcp", "checkins.db");
60568
60671
  }
60569
60672
 
60570
60673
  // src/index.ts
package/dist/version.js CHANGED
@@ -3,4 +3,4 @@
3
3
  // json's `extra-files`), and `versionSyncTest` guards that it stays equal to
4
4
  // package.json. Import VERSION wherever the version is needed rather than
5
5
  // re-declaring it.
6
- export const VERSION = '2.1.1'; // x-release-please-version
6
+ export const VERSION = '2.1.2'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "untappd-mcp",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "mcpName": "io.github.chrischall/untappd-mcp",
5
5
  "description": "Untappd MCP server for Claude — developed and maintained by AI (Claude Code)",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
@@ -45,11 +45,11 @@
45
45
  "typecheck": "tsc -p tsconfig.json --noEmit"
46
46
  },
47
47
  "dependencies": {
48
- "@chrischall/mcp-utils": "^2.2.0",
48
+ "@chrischall/mcp-utils": "^2.4.0",
49
49
  "@modelcontextprotocol/server": "^2.0.0",
50
50
  "ai": "^7.0.19",
51
- "dotenv": "^17.4.0",
52
- "zod": "^4.6.2"
51
+ "dotenv": "^18.0.1",
52
+ "zod": "^4.6.5"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@modelcontextprotocol/client": "^2.0.0",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/untappd-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "2.1.1",
9
+ "version": "2.1.2",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "untappd-mcp",
14
- "version": "2.1.1",
14
+ "version": "2.1.2",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },