untappd-mcp 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.js CHANGED
@@ -2220,20 +2220,20 @@ var require_resolve = __commonJS({
2220
2220
  return false;
2221
2221
  }
2222
2222
  function countKeys(schema) {
2223
- let count = 0;
2223
+ let count2 = 0;
2224
2224
  for (const key in schema) {
2225
2225
  if (key === "$ref")
2226
2226
  return Infinity;
2227
- count++;
2227
+ count2++;
2228
2228
  if (SIMPLE_INLINED.has(key))
2229
2229
  continue;
2230
2230
  if (typeof schema[key] == "object") {
2231
- (0, util_1.eachItem)(schema[key], (sch) => count += countKeys(sch));
2231
+ (0, util_1.eachItem)(schema[key], (sch) => count2 += countKeys(sch));
2232
2232
  }
2233
- if (count === Infinity)
2233
+ if (count2 === Infinity)
2234
2234
  return Infinity;
2235
2235
  }
2236
- return count;
2236
+ return count2;
2237
2237
  }
2238
2238
  function getFullPath(resolver, id = "", normalize) {
2239
2239
  if (normalize !== false)
@@ -5405,8 +5405,8 @@ var require_contains = __commonJS({
5405
5405
  cxt.result(valid, () => cxt.reset());
5406
5406
  function validateItemsWithCount() {
5407
5407
  const schValid = gen.name("_valid");
5408
- const count = gen.let("count", 0);
5409
- validateItems(schValid, () => gen.if(schValid, () => checkLimits(count)));
5408
+ const count2 = gen.let("count", 0);
5409
+ validateItems(schValid, () => gen.if(schValid, () => checkLimits(count2)));
5410
5410
  }
5411
5411
  function validateItems(_valid, block) {
5412
5412
  gen.forRange("i", 0, len, (i) => {
@@ -5419,16 +5419,16 @@ var require_contains = __commonJS({
5419
5419
  block();
5420
5420
  });
5421
5421
  }
5422
- function checkLimits(count) {
5423
- gen.code((0, codegen_1._)`${count}++`);
5422
+ function checkLimits(count2) {
5423
+ gen.code((0, codegen_1._)`${count2}++`);
5424
5424
  if (max === void 0) {
5425
- gen.if((0, codegen_1._)`${count} >= ${min}`, () => gen.assign(valid, true).break());
5425
+ gen.if((0, codegen_1._)`${count2} >= ${min}`, () => gen.assign(valid, true).break());
5426
5426
  } else {
5427
- gen.if((0, codegen_1._)`${count} > ${max}`, () => gen.assign(valid, false).break());
5427
+ gen.if((0, codegen_1._)`${count2} > ${max}`, () => gen.assign(valid, false).break());
5428
5428
  if (min === 1)
5429
5429
  gen.assign(valid, true);
5430
5430
  else
5431
- gen.if((0, codegen_1._)`${count} >= ${min}`, () => gen.assign(valid, true));
5431
+ gen.if((0, codegen_1._)`${count2} >= ${min}`, () => gen.assign(valid, true));
5432
5432
  }
5433
5433
  }
5434
5434
  }
@@ -15158,8 +15158,8 @@ function az_default() {
15158
15158
  }
15159
15159
 
15160
15160
  // node_modules/zod/v4/locales/be.js
15161
- function getBelarusianPlural(count, one, few, many) {
15162
- const absCount = Math.abs(count);
15161
+ function getBelarusianPlural(count2, one, few, many) {
15162
+ const absCount = Math.abs(count2);
15163
15163
  const lastDigit = absCount % 10;
15164
15164
  const lastTwoDigits = absCount % 100;
15165
15165
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -17237,8 +17237,8 @@ function hu_default() {
17237
17237
  }
17238
17238
 
17239
17239
  // node_modules/zod/v4/locales/hy.js
17240
- function getArmenianPlural(count, one, many) {
17241
- return Math.abs(count) === 1 ? one : many;
17240
+ function getArmenianPlural(count2, one, many) {
17241
+ return Math.abs(count2) === 1 ? one : many;
17242
17242
  }
17243
17243
  function withDefiniteArticle(word) {
17244
17244
  if (!word)
@@ -19366,8 +19366,8 @@ function ro_default() {
19366
19366
  }
19367
19367
 
19368
19368
  // node_modules/zod/v4/locales/ru.js
19369
- function getRussianPlural(count, one, few, many) {
19370
- const absCount = Math.abs(count);
19369
+ function getRussianPlural(count2, one, few, many) {
19370
+ const absCount = Math.abs(count2);
19371
19371
  const lastDigit = absCount % 10;
19372
19372
  const lastTwoDigits = absCount % 100;
19373
19373
  if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
@@ -31184,13 +31184,15 @@ function toolAnnotations(opts = {}) {
31184
31184
  }
31185
31185
 
31186
31186
  // src/version.ts
31187
- var VERSION = "1.0.0";
31187
+ var VERSION = "1.2.0";
31188
31188
 
31189
31189
  // src/client.ts
31190
31190
  import { dirname, join } from "path";
31191
31191
  import { fileURLToPath } from "url";
31192
- var __dirname = dirname(fileURLToPath(import.meta.url));
31193
- await loadDotenvSafely({ path: join(__dirname, "..", ".env"), override: false });
31192
+ if (import.meta.url) {
31193
+ const __dirname = dirname(fileURLToPath(import.meta.url));
31194
+ await loadDotenvSafely({ path: join(__dirname, "..", ".env"), override: false });
31195
+ }
31194
31196
  var BASE_URL = "https://api.untappd.com/v4";
31195
31197
  var SERVICE = "Untappd";
31196
31198
  var REQUEST_TIMEOUT_MS = 3e4;
@@ -31205,15 +31207,72 @@ var DEFAULTS = {
31205
31207
  // works. Override with UNTAPPD_DEVICE_ID to pin your own.
31206
31208
  deviceUdid: "A1B2C3D4-0000-4000-8000-0000000000AA"
31207
31209
  };
31208
- function readCredentials() {
31209
- const username = readEnvVar("UNTAPPD_USERNAME");
31210
- const password = readEnvVar("UNTAPPD_PASSWORD");
31211
- const clientId = readEnvVar("UNTAPPD_CLIENT_ID");
31212
- const clientSecret = readEnvVar("UNTAPPD_CLIENT_SECRET");
31213
- if (username && password && clientId && clientSecret) {
31214
- return { username, password, clientId, clientSecret };
31210
+ async function sendRequest(fetchImpl, method, url2, init) {
31211
+ const controller = new AbortController();
31212
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
31213
+ try {
31214
+ return await fetchImpl(url2, {
31215
+ method,
31216
+ headers: init.headers,
31217
+ ...init.body !== void 0 ? { body: init.body } : {},
31218
+ signal: controller.signal
31219
+ });
31220
+ } catch {
31221
+ throw new UnreachableError(SERVICE);
31222
+ } finally {
31223
+ clearTimeout(timer);
31215
31224
  }
31216
- return null;
31225
+ }
31226
+ function xauthHeaders(userAgent) {
31227
+ return {
31228
+ "User-Agent": userAgent,
31229
+ Accept: "application/json",
31230
+ "x-untappd-app": "ios",
31231
+ "x-untappd-app-version": DEFAULTS.appVersion
31232
+ };
31233
+ }
31234
+ async function xauthLogin(creds, opts = {}) {
31235
+ const fetchImpl = opts.fetchImpl ?? fetch;
31236
+ const utv = opts.utv ?? readEnvVar("UNTAPPD_UTV") ?? DEFAULTS.utv;
31237
+ const deviceUdid = opts.deviceUdid ?? readEnvVar("UNTAPPD_DEVICE_ID") ?? DEFAULTS.deviceUdid;
31238
+ const userAgent = opts.userAgent ?? readEnvVar("UNTAPPD_USER_AGENT") ?? DEFAULTS.userAgent;
31239
+ const qs = buildQueryString({ client_id: creds.clientId, client_secret: creds.clientSecret, utv });
31240
+ const form = new URLSearchParams({
31241
+ user_name: creds.username,
31242
+ user_password: creds.password,
31243
+ device_udid: deviceUdid,
31244
+ device_name: DEFAULTS.deviceName,
31245
+ device_version: DEFAULTS.deviceVersion,
31246
+ device_platform: DEFAULTS.devicePlatform,
31247
+ app_version: DEFAULTS.appVersion,
31248
+ multi_account: "true"
31249
+ });
31250
+ const res = await sendRequest(fetchImpl, "POST", `${BASE_URL}/xauth${qs}`, {
31251
+ headers: { ...xauthHeaders(userAgent), "Content-Type": "application/x-www-form-urlencoded" },
31252
+ body: form.toString()
31253
+ });
31254
+ const text = await res.text();
31255
+ if (!res.ok) {
31256
+ if (res.status === 401 || res.status === 400) {
31257
+ throw createHelpfulError(`Untappd login failed (${res.status}).`, {
31258
+ hint: "Check UNTAPPD_USERNAME / UNTAPPD_PASSWORD, and that UNTAPPD_CLIENT_ID / UNTAPPD_CLIENT_SECRET are the mobile app credentials."
31259
+ });
31260
+ }
31261
+ throw new McpToolError(formatApiError(res.status, "POST", "/xauth", text, { service: SERVICE }));
31262
+ }
31263
+ let data;
31264
+ try {
31265
+ data = JSON.parse(text);
31266
+ } catch {
31267
+ throw new McpToolError("Untappd login returned a non-JSON response.");
31268
+ }
31269
+ const token = data?.response?.access_token;
31270
+ if (!token) {
31271
+ throw createHelpfulError("Untappd login did not return an access token.", {
31272
+ hint: data?.response?.two_factor_enabled ? "This account has two-factor authentication enabled, which xauth login cannot satisfy." : "The credentials may be incorrect."
31273
+ });
31274
+ }
31275
+ return token;
31217
31276
  }
31218
31277
  function missingCredsError() {
31219
31278
  const missing = ["UNTAPPD_USERNAME", "UNTAPPD_PASSWORD", "UNTAPPD_CLIENT_ID", "UNTAPPD_CLIENT_SECRET"].filter((k) => !readEnvVar(k));
@@ -31226,8 +31285,11 @@ function missingCredsError() {
31226
31285
  }
31227
31286
  var UntappdClient = class {
31228
31287
  fetchImpl;
31229
- creds;
31230
- configError;
31288
+ clientId;
31289
+ clientSecret;
31290
+ username;
31291
+ password;
31292
+ _loginName;
31231
31293
  utv = readEnvVar("UNTAPPD_UTV") ?? DEFAULTS.utv;
31232
31294
  deviceUdid = readEnvVar("UNTAPPD_DEVICE_ID") ?? DEFAULTS.deviceUdid;
31233
31295
  userAgent = readEnvVar("UNTAPPD_USER_AGENT") ?? DEFAULTS.userAgent;
@@ -31241,26 +31303,31 @@ var UntappdClient = class {
31241
31303
  constructor(opts = {}) {
31242
31304
  this.fetchImpl = opts.fetchImpl ?? fetch;
31243
31305
  this.token = opts.token ?? null;
31244
- const creds = opts.credentials !== void 0 ? opts.credentials : readCredentials();
31245
- if (creds) {
31246
- this.creds = creds;
31247
- this.configError = null;
31248
- } else {
31249
- this.creds = null;
31250
- this.configError = missingCredsError();
31251
- }
31306
+ this.clientId = opts.clientId ?? readEnvVar("UNTAPPD_CLIENT_ID") ?? null;
31307
+ this.clientSecret = opts.clientSecret ?? readEnvVar("UNTAPPD_CLIENT_SECRET") ?? null;
31308
+ this.username = opts.username ?? readEnvVar("UNTAPPD_USERNAME") ?? null;
31309
+ this.password = opts.password ?? readEnvVar("UNTAPPD_PASSWORD") ?? null;
31310
+ this._loginName = opts.loginName ?? this.username;
31252
31311
  }
31253
- /** Whether credentials are configured (used by the healthcheck tool). */
31312
+ /** True when enough is configured to make authenticated calls. */
31254
31313
  get configured() {
31255
- return this.configError === null;
31314
+ if (this.token) return Boolean(this.clientId && this.clientSecret);
31315
+ return Boolean(this.clientId && this.clientSecret && this.username && this.password);
31256
31316
  }
31257
31317
  /** The configured login name — the default `username` for user-scoped tools. */
31258
31318
  get loginName() {
31259
- return this.creds?.username ?? null;
31319
+ return this._loginName;
31320
+ }
31321
+ requireAppCreds() {
31322
+ if (!this.clientId || !this.clientSecret) {
31323
+ throw missingCredsError();
31324
+ }
31325
+ return { clientId: this.clientId, clientSecret: this.clientSecret };
31260
31326
  }
31261
- requireCreds() {
31262
- if (this.configError) throw this.configError;
31263
- return this.creds;
31327
+ requireLogin() {
31328
+ const app = this.requireAppCreds();
31329
+ if (!this.username || !this.password) throw missingCredsError();
31330
+ return { ...app, username: this.username, password: this.password };
31264
31331
  }
31265
31332
  baseHeaders() {
31266
31333
  return {
@@ -31270,23 +31337,10 @@ var UntappdClient = class {
31270
31337
  "x-untappd-app-version": DEFAULTS.appVersion
31271
31338
  };
31272
31339
  }
31273
- // One HTTP attempt with a hard timeout. Network/timeout failures become an
31274
- // UnreachableError; HTTP status handling is left to the caller.
31340
+ // One HTTP attempt with a hard timeout, delegating to the module-level
31341
+ // helper shared with `xauthLogin()`.
31275
31342
  async send(method, url2, init) {
31276
- const controller = new AbortController();
31277
- const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
31278
- try {
31279
- return await this.fetchImpl(url2, {
31280
- method,
31281
- headers: init.headers,
31282
- ...init.body !== void 0 ? { body: init.body } : {},
31283
- signal: controller.signal
31284
- });
31285
- } catch {
31286
- throw new UnreachableError(SERVICE);
31287
- } finally {
31288
- clearTimeout(timer);
31289
- }
31343
+ return sendRequest(this.fetchImpl, method, url2, init);
31290
31344
  }
31291
31345
  /**
31292
31346
  * Obtain an access token, logging in via xauth (username/password) on demand.
@@ -31303,43 +31357,13 @@ var UntappdClient = class {
31303
31357
  return this.loginInFlight;
31304
31358
  }
31305
31359
  async login() {
31306
- const c = this.requireCreds();
31307
- const qs = buildQueryString({ client_id: c.clientId, client_secret: c.clientSecret, utv: this.utv });
31308
- const form = new URLSearchParams({
31309
- user_name: c.username,
31310
- user_password: c.password,
31311
- device_udid: this.deviceUdid,
31312
- device_name: DEFAULTS.deviceName,
31313
- device_version: DEFAULTS.deviceVersion,
31314
- device_platform: DEFAULTS.devicePlatform,
31315
- app_version: DEFAULTS.appVersion,
31316
- multi_account: "true"
31360
+ const c = this.requireLogin();
31361
+ const token = await xauthLogin(c, {
31362
+ fetchImpl: this.fetchImpl,
31363
+ utv: this.utv,
31364
+ deviceUdid: this.deviceUdid,
31365
+ userAgent: this.userAgent
31317
31366
  });
31318
- const res = await this.send("POST", `${BASE_URL}/xauth${qs}`, {
31319
- headers: { ...this.baseHeaders(), "Content-Type": "application/x-www-form-urlencoded" },
31320
- body: form.toString()
31321
- });
31322
- const text = await res.text();
31323
- if (!res.ok) {
31324
- if (res.status === 401 || res.status === 400) {
31325
- throw createHelpfulError(`Untappd login failed (${res.status}).`, {
31326
- hint: "Check UNTAPPD_USERNAME / UNTAPPD_PASSWORD, and that UNTAPPD_CLIENT_ID / UNTAPPD_CLIENT_SECRET are the mobile app credentials."
31327
- });
31328
- }
31329
- throw new McpToolError(formatApiError(res.status, "POST", "/xauth", text, { service: SERVICE }));
31330
- }
31331
- let data;
31332
- try {
31333
- data = JSON.parse(text);
31334
- } catch {
31335
- throw new McpToolError("Untappd login returned a non-JSON response.");
31336
- }
31337
- const token = data?.response?.access_token;
31338
- if (!token) {
31339
- throw createHelpfulError("Untappd login did not return an access token.", {
31340
- hint: data?.response?.two_factor_enabled ? "This account has two-factor authentication enabled, which xauth login cannot satisfy." : "The credentials may be incorrect."
31341
- });
31342
- }
31343
31367
  this.token = token;
31344
31368
  return token;
31345
31369
  }
@@ -31372,7 +31396,7 @@ var UntappdClient = class {
31372
31396
  const headers = this.baseHeaders();
31373
31397
  let query;
31374
31398
  if (opts.auth === "bearer") {
31375
- const c = this.requireCreds();
31399
+ const c = this.requireAppCreds();
31376
31400
  headers["Authorization"] = `Bearer ${token}`;
31377
31401
  query = { ...opts.query, client_id: c.clientId, client_secret: c.clientSecret, utv: this.utv };
31378
31402
  } else {
@@ -31420,11 +31444,86 @@ var UntappdClient = class {
31420
31444
  }
31421
31445
  }
31422
31446
  };
31423
- var client = new UntappdClient();
31447
+
31448
+ // src/compact.ts
31449
+ function asDict(v) {
31450
+ return v && typeof v === "object" ? v : void 0;
31451
+ }
31452
+ function count(v) {
31453
+ const d = asDict(v);
31454
+ if (!d) return void 0;
31455
+ return d.count ?? d.total_count;
31456
+ }
31457
+ function compactCheckin(item) {
31458
+ const c = asDict(item) ?? {};
31459
+ const beer = asDict(c.beer);
31460
+ const brewery = asDict(c.brewery);
31461
+ const venue = asDict(c.venue);
31462
+ const user = asDict(c.user);
31463
+ return {
31464
+ checkin_id: c.checkin_id,
31465
+ created_at: c.created_at,
31466
+ user: user?.user_name,
31467
+ rating: c.rating_score,
31468
+ comment: c.checkin_comment || void 0,
31469
+ beer: beer && { bid: beer.bid, name: beer.beer_name, style: beer.beer_style, abv: beer.beer_abv },
31470
+ brewery: brewery?.brewery_name,
31471
+ venue: venue?.venue_name || void 0,
31472
+ toasts: count(c.toasts),
31473
+ comments: count(c.comments),
31474
+ has_photo: (count(c.media) ?? 0) > 0
31475
+ };
31476
+ }
31477
+ function beerCore(item) {
31478
+ const beer = asDict(item.beer) ?? {};
31479
+ const brewery = asDict(item.brewery);
31480
+ return {
31481
+ bid: beer.bid,
31482
+ name: beer.beer_name,
31483
+ style: beer.beer_style,
31484
+ abv: beer.beer_abv,
31485
+ ibu: beer.beer_ibu,
31486
+ brewery: brewery?.brewery_name
31487
+ };
31488
+ }
31489
+ function compactBeerResult(item) {
31490
+ const i = asDict(item) ?? {};
31491
+ return { ...beerCore(i), checkin_count: i.checkin_count, have_had: i.have_had };
31492
+ }
31493
+ function compactWishlistBeer(item) {
31494
+ const i = asDict(item) ?? {};
31495
+ return { ...beerCore(i), added_at: i.created_at };
31496
+ }
31497
+ function compactUserBeer(item) {
31498
+ const i = asDict(item) ?? {};
31499
+ return {
31500
+ ...beerCore(i),
31501
+ your_count: i.count,
31502
+ your_rating: i.user_auth_rating_score || void 0,
31503
+ global_rating: i.rating_score || void 0,
31504
+ last_had: i.recent_created_at
31505
+ };
31506
+ }
31507
+ function projectItems(resp, container, mapFn) {
31508
+ const r = asDict(resp);
31509
+ const box = asDict(r?.[container]);
31510
+ const items = box?.items;
31511
+ if (!r || !box || !Array.isArray(items)) {
31512
+ process.stderr.write(`[untappd-mcp] compact: expected ${container}.items array; returning raw response
31513
+ `);
31514
+ return resp;
31515
+ }
31516
+ return { ...r, [container]: { ...box, items: items.map(mapFn) } };
31517
+ }
31518
+ var compactCheckins = (resp) => projectItems(resp, "checkins", compactCheckin);
31519
+ var compactBeerSearch = (resp) => projectItems(resp, "beers", compactBeerResult);
31520
+ var compactWishlist = (resp) => projectItems(resp, "beers", compactWishlistBeer);
31521
+ var compactUserBeers = (resp) => projectItems(resp, "beers", compactUserBeer);
31424
31522
 
31425
31523
  // src/tools/beer.ts
31426
31524
  var BidSchema = external_exports.number().int().positive().describe("Untappd beer id (bid)");
31427
- function registerBeerTools(server) {
31525
+ var CompactCheckins = external_exports.boolean().optional().describe("Project each check-in to a slim summary (id, user, beer, rating, comment, venue, toast/comment counts) to save context (default false)");
31526
+ function registerBeerTools(server, client2) {
31428
31527
  server.registerTool(
31429
31528
  "untappd_search_beer",
31430
31529
  {
@@ -31435,12 +31534,13 @@ function registerBeerTools(server) {
31435
31534
  query: external_exports.string().min(1).describe("Beer name to search for"),
31436
31535
  limit: external_exports.number().int().min(1).max(50).optional().describe("Max results (1\u201350, default 25)"),
31437
31536
  offset: external_exports.number().int().min(0).optional().describe("Result offset for paging (default 0)"),
31438
- sort: external_exports.enum(["checkin", "name", "count"]).optional().describe("Sort order: checkin (relevance, default), name, or count")
31537
+ sort: external_exports.enum(["checkin", "name", "count"]).optional().describe("Sort order: checkin (relevance, default), name, or count"),
31538
+ compact: external_exports.boolean().optional().describe("Project each result to a slim summary (bid, name, brewery, style, abv, ibu, counts) to save context (default false)")
31439
31539
  }
31440
31540
  },
31441
- async ({ query, limit, offset, sort }) => {
31442
- const data = await client.get("/search/beer", { q: query, limit, offset, sort });
31443
- return textResult(data);
31541
+ async ({ query, limit, offset, sort, compact }) => {
31542
+ const data = await client2.get("/search/beer", { q: query, limit, offset, sort });
31543
+ return textResult(compact ? compactBeerSearch(data) : data);
31444
31544
  }
31445
31545
  );
31446
31546
  server.registerTool(
@@ -31455,7 +31555,7 @@ function registerBeerTools(server) {
31455
31555
  }
31456
31556
  },
31457
31557
  async ({ bid, compact }) => {
31458
- const data = await client.get(`/beer/info/${bid}`, { compact: compact ? "true" : void 0 });
31558
+ const data = await client2.get(`/beer/info/${bid}`, { compact: compact ? "true" : void 0 });
31459
31559
  return textResult(data);
31460
31560
  }
31461
31561
  );
@@ -31468,18 +31568,19 @@ function registerBeerTools(server) {
31468
31568
  inputSchema: {
31469
31569
  bid: BidSchema,
31470
31570
  limit: external_exports.number().int().min(1).max(50).optional().describe("Max check-ins (1\u201350, default 25)"),
31471
- max_id: external_exports.number().int().positive().optional().describe("Return check-ins older than this id (for paging)")
31571
+ max_id: external_exports.number().int().positive().optional().describe("Return check-ins older than this id (for paging)"),
31572
+ compact: CompactCheckins
31472
31573
  }
31473
31574
  },
31474
- async ({ bid, limit, max_id }) => {
31475
- const data = await client.get(`/beer/checkins/${bid}`, { limit, max_id });
31476
- return textResult(data);
31575
+ async ({ bid, limit, max_id, compact }) => {
31576
+ const data = await client2.get(`/beer/checkins/${bid}`, { limit, max_id });
31577
+ return textResult(compact ? compactCheckins(data) : data);
31477
31578
  }
31478
31579
  );
31479
31580
  }
31480
31581
 
31481
31582
  // src/tools/brewery.ts
31482
- function registerBreweryTools(server) {
31583
+ function registerBreweryTools(server, client2) {
31483
31584
  server.registerTool(
31484
31585
  "untappd_search_brewery",
31485
31586
  {
@@ -31493,7 +31594,7 @@ function registerBreweryTools(server) {
31493
31594
  }
31494
31595
  },
31495
31596
  async ({ query, limit, offset }) => {
31496
- const data = await client.get("/search/brewery", { q: query, limit, offset });
31597
+ const data = await client2.get("/search/brewery", { q: query, limit, offset });
31497
31598
  return textResult(data);
31498
31599
  }
31499
31600
  );
@@ -31509,7 +31610,7 @@ function registerBreweryTools(server) {
31509
31610
  }
31510
31611
  },
31511
31612
  async ({ brewery_id, compact }) => {
31512
- const data = await client.get(`/brewery/info/${brewery_id}`, { compact: compact ? "true" : void 0 });
31613
+ const data = await client2.get(`/brewery/info/${brewery_id}`, { compact: compact ? "true" : void 0 });
31513
31614
  return textResult(data);
31514
31615
  }
31515
31616
  );
@@ -31527,14 +31628,14 @@ function registerBreweryTools(server) {
31527
31628
  }
31528
31629
  },
31529
31630
  async ({ brewery_id, limit, offset, sort }) => {
31530
- const data = await client.get(`/brewery/beer_list/${brewery_id}`, { limit, offset, sort });
31631
+ const data = await client2.get(`/brewery/beer_list/${brewery_id}`, { limit, offset, sort });
31531
31632
  return textResult(data);
31532
31633
  }
31533
31634
  );
31534
31635
  }
31535
31636
 
31536
31637
  // src/tools/venue.ts
31537
- function registerVenueTools(server) {
31638
+ function registerVenueTools(server, client2) {
31538
31639
  server.registerTool(
31539
31640
  "untappd_search_venue",
31540
31641
  {
@@ -31547,7 +31648,7 @@ function registerVenueTools(server) {
31547
31648
  }
31548
31649
  },
31549
31650
  async ({ query, limit }) => {
31550
- const data = await client.get("/search/venue", { q: query, limit });
31651
+ const data = await client2.get("/search/venue", { q: query, limit });
31551
31652
  return textResult(data);
31552
31653
  }
31553
31654
  );
@@ -31563,7 +31664,22 @@ function registerVenueTools(server) {
31563
31664
  }
31564
31665
  },
31565
31666
  async ({ venue_id, compact }) => {
31566
- const data = await client.get(`/venue/info/${venue_id}`, { compact: compact ? "true" : void 0 });
31667
+ const data = await client2.get(`/venue/info/${venue_id}`, { compact: compact ? "true" : void 0 });
31668
+ return textResult(data);
31669
+ }
31670
+ );
31671
+ server.registerTool(
31672
+ "untappd_venue_by_foursquare",
31673
+ {
31674
+ title: "Look up an Untappd venue by Foursquare id",
31675
+ description: "Resolve a Foursquare venue id to its Untappd venue. Useful to turn a foursquare_id (e.g. from a check-in) into an Untappd venue you can pass to untappd_venue_info / untappd_venue_activity. Read-only.",
31676
+ annotations: toolAnnotations({ title: "Look up an Untappd venue by Foursquare id", readOnly: true, idempotent: true, openWorld: true }),
31677
+ inputSchema: {
31678
+ foursquare_id: external_exports.string().min(1).describe("Foursquare venue id")
31679
+ }
31680
+ },
31681
+ async ({ foursquare_id }) => {
31682
+ const data = await client2.get(`/venue/foursquare_lookup/${encodeURIComponent(foursquare_id)}`);
31567
31683
  return textResult(data);
31568
31684
  }
31569
31685
  );
@@ -31576,20 +31692,21 @@ function registerVenueTools(server) {
31576
31692
  inputSchema: {
31577
31693
  venue_id: external_exports.number().int().positive().describe("Untappd venue id"),
31578
31694
  limit: external_exports.number().int().min(1).max(50).optional().describe("Max check-ins (1\u201350, default 25)"),
31579
- max_id: external_exports.number().int().positive().optional().describe("Return check-ins older than this id (for paging)")
31695
+ max_id: external_exports.number().int().positive().optional().describe("Return check-ins older than this id (for paging)"),
31696
+ compact: external_exports.boolean().optional().describe("Project each check-in to a slim summary to save context (default false)")
31580
31697
  }
31581
31698
  },
31582
- async ({ venue_id, limit, max_id }) => {
31583
- const data = await client.get(`/venue/checkins/${venue_id}`, { limit, max_id });
31584
- return textResult(data);
31699
+ async ({ venue_id, limit, max_id, compact }) => {
31700
+ const data = await client2.get(`/venue/checkins/${venue_id}`, { limit, max_id });
31701
+ return textResult(compact ? compactCheckins(data) : data);
31585
31702
  }
31586
31703
  );
31587
31704
  }
31588
31705
 
31589
31706
  // src/tools/user.ts
31590
31707
  var UsernameArg = external_exports.string().min(1).optional().describe("Untappd username. Omit to use your own configured account (UNTAPPD_USERNAME).");
31591
- function resolveUser(username) {
31592
- const u = username ?? client.loginName;
31708
+ function resolveUser(username, loginName) {
31709
+ const u = username ?? loginName;
31593
31710
  if (!u) {
31594
31711
  throw createHelpfulError("No username given and no configured account to fall back to.", {
31595
31712
  hint: "Pass `username`, or set UNTAPPD_USERNAME so user tools default to your own account."
@@ -31597,7 +31714,7 @@ function resolveUser(username) {
31597
31714
  }
31598
31715
  return encodeURIComponent(u);
31599
31716
  }
31600
- function registerUserTools(server) {
31717
+ function registerUserTools(server, client2) {
31601
31718
  server.registerTool(
31602
31719
  "untappd_user_info",
31603
31720
  {
@@ -31610,7 +31727,7 @@ function registerUserTools(server) {
31610
31727
  }
31611
31728
  },
31612
31729
  async ({ username, compact }) => {
31613
- const data = await client.get(`/user/info/${resolveUser(username)}`, { compact: compact ? "true" : void 0 });
31730
+ const data = await client2.get(`/user/info/${resolveUser(username, client2.loginName)}`, { compact: compact ? "true" : void 0 });
31614
31731
  return textResult(data);
31615
31732
  }
31616
31733
  );
@@ -31623,12 +31740,13 @@ function registerUserTools(server) {
31623
31740
  inputSchema: {
31624
31741
  username: UsernameArg,
31625
31742
  limit: external_exports.number().int().min(1).max(50).optional().describe("Max check-ins (1\u201350, default 25)"),
31626
- max_id: external_exports.number().int().positive().optional().describe("Return check-ins older than this id (for paging)")
31743
+ max_id: external_exports.number().int().positive().optional().describe("Return check-ins older than this id (for paging)"),
31744
+ compact: external_exports.boolean().optional().describe("Project each check-in to a slim summary to save context (default false)")
31627
31745
  }
31628
31746
  },
31629
- async ({ username, limit, max_id }) => {
31630
- const data = await client.get(`/user/checkins/${resolveUser(username)}`, { limit, max_id });
31631
- return textResult(data);
31747
+ async ({ username, limit, max_id, compact }) => {
31748
+ const data = await client2.get(`/user/checkins/${resolveUser(username, client2.loginName)}`, { limit, max_id });
31749
+ return textResult(compact ? compactCheckins(data) : data);
31632
31750
  }
31633
31751
  );
31634
31752
  server.registerTool(
@@ -31641,12 +31759,13 @@ function registerUserTools(server) {
31641
31759
  username: UsernameArg,
31642
31760
  limit: external_exports.number().int().min(1).max(50).optional().describe("Max beers (1\u201350, default 25)"),
31643
31761
  offset: external_exports.number().int().min(0).optional().describe("Result offset for paging (default 0)"),
31644
- sort: external_exports.enum(["date", "name", "brewery", "style", "rating", "abv"]).optional().describe("Sort order (default date added, newest first)")
31762
+ sort: external_exports.enum(["date", "name", "brewery", "style", "rating", "abv"]).optional().describe("Sort order (default date added, newest first)"),
31763
+ compact: external_exports.boolean().optional().describe("Project each beer to a slim summary (bid, name, brewery, style, abv, added_at) to save context (default false)")
31645
31764
  }
31646
31765
  },
31647
- async ({ username, limit, offset, sort }) => {
31648
- const data = await client.get(`/user/wishlist/${resolveUser(username)}`, { limit, offset, sort });
31649
- return textResult(data);
31766
+ async ({ username, limit, offset, sort, compact }) => {
31767
+ const data = await client2.get(`/user/wishlist/${resolveUser(username, client2.loginName)}`, { limit, offset, sort });
31768
+ return textResult(compact ? compactWishlist(data) : data);
31650
31769
  }
31651
31770
  );
31652
31771
  server.registerTool(
@@ -31659,12 +31778,13 @@ function registerUserTools(server) {
31659
31778
  username: UsernameArg,
31660
31779
  limit: external_exports.number().int().min(1).max(50).optional().describe("Max beers (1\u201350, default 25)"),
31661
31780
  offset: external_exports.number().int().min(0).optional().describe("Result offset for paging (default 0)"),
31662
- sort: external_exports.enum(["date", "checkin", "highest_rated", "lowest_rated", "name", "this_month", "highest_abv"]).optional().describe("Sort order (default date, most recent first)")
31781
+ sort: external_exports.enum(["date", "checkin", "highest_rated", "lowest_rated", "name", "this_month", "highest_abv"]).optional().describe("Sort order (default date, most recent first)"),
31782
+ compact: external_exports.boolean().optional().describe("Project each beer to a slim summary (bid, name, brewery, style, abv, ibu, your_count, your_rating, global_rating, last_had) to save context (default false)")
31663
31783
  }
31664
31784
  },
31665
- async ({ username, limit, offset, sort }) => {
31666
- const data = await client.get(`/user/beers/${resolveUser(username)}`, { limit, offset, sort });
31667
- return textResult(data);
31785
+ async ({ username, limit, offset, sort, compact }) => {
31786
+ const data = await client2.get(`/user/beers/${resolveUser(username, client2.loginName)}`, { limit, offset, sort });
31787
+ return textResult(compact ? compactUserBeers(data) : data);
31668
31788
  }
31669
31789
  );
31670
31790
  server.registerTool(
@@ -31680,7 +31800,7 @@ function registerUserTools(server) {
31680
31800
  }
31681
31801
  },
31682
31802
  async ({ username, limit, offset }) => {
31683
- const data = await client.get(`/user/badges/${resolveUser(username)}`, { limit, offset });
31803
+ const data = await client2.get(`/user/badges/${resolveUser(username, client2.loginName)}`, { limit, offset });
31684
31804
  return textResult(data);
31685
31805
  }
31686
31806
  );
@@ -31697,14 +31817,48 @@ function registerUserTools(server) {
31697
31817
  }
31698
31818
  },
31699
31819
  async ({ username, limit, offset }) => {
31700
- const data = await client.get(`/user/friends/${resolveUser(username)}`, { limit, offset });
31820
+ const data = await client2.get(`/user/friends/${resolveUser(username, client2.loginName)}`, { limit, offset });
31821
+ return textResult(data);
31822
+ }
31823
+ );
31824
+ server.registerTool(
31825
+ "untappd_user_venues",
31826
+ {
31827
+ title: "Get venues a user has checked in at",
31828
+ description: "Get the venues a user has checked in at, most recent first, with per-venue check-in counts. Supports sorting and paging. Omit username for your own account. Read-only.",
31829
+ annotations: toolAnnotations({ title: "Get venues a user has checked in at", readOnly: true, idempotent: true, openWorld: true }),
31830
+ inputSchema: {
31831
+ username: UsernameArg,
31832
+ limit: external_exports.number().int().min(1).max(50).optional().describe("Max venues (1\u201350, default 25)"),
31833
+ offset: external_exports.number().int().min(0).optional().describe("Result offset for paging (default 0)"),
31834
+ sort: external_exports.enum(["date", "name", "checkin", "highest_rated"]).optional().describe("Sort order (default most recent)")
31835
+ }
31836
+ },
31837
+ async ({ username, limit, offset, sort }) => {
31838
+ const data = await client2.get(`/user/venues/${resolveUser(username, client2.loginName)}`, { limit, offset, sort });
31839
+ return textResult(data);
31840
+ }
31841
+ );
31842
+ server.registerTool(
31843
+ "untappd_pending_friends",
31844
+ {
31845
+ title: "Get your pending friend requests",
31846
+ description: "Get the incoming friend requests waiting on YOUR account \u2014 the users who have requested to be your friend. Read-only.",
31847
+ annotations: toolAnnotations({ title: "Get your pending friend requests", readOnly: true, idempotent: true, openWorld: true }),
31848
+ inputSchema: {
31849
+ limit: external_exports.number().int().min(1).max(50).optional().describe("Max requests (1\u201350, default 25)"),
31850
+ offset: external_exports.number().int().min(0).optional().describe("Result offset for paging (default 0)")
31851
+ }
31852
+ },
31853
+ async ({ limit, offset }) => {
31854
+ const data = await client2.get("/user/pending", { limit, offset });
31701
31855
  return textResult(data);
31702
31856
  }
31703
31857
  );
31704
31858
  }
31705
31859
 
31706
31860
  // src/tools/feed.ts
31707
- function registerFeedTools(server) {
31861
+ function registerFeedTools(server, client2) {
31708
31862
  server.registerTool(
31709
31863
  "untappd_activity_feed",
31710
31864
  {
@@ -31713,12 +31867,13 @@ function registerFeedTools(server) {
31713
31867
  annotations: toolAnnotations({ title: "Get Untappd friend activity feed", readOnly: true, idempotent: false, openWorld: true }),
31714
31868
  inputSchema: {
31715
31869
  limit: external_exports.number().int().min(1).max(50).optional().describe("Max check-ins (1\u201350, default 25)"),
31716
- max_id: external_exports.number().int().positive().optional().describe("Return check-ins older than this id (for paging)")
31870
+ max_id: external_exports.number().int().positive().optional().describe("Return check-ins older than this id (for paging)"),
31871
+ compact: external_exports.boolean().optional().describe("Project each check-in to a slim summary to save context (default false)")
31717
31872
  }
31718
31873
  },
31719
- async ({ limit, max_id }) => {
31720
- const data = await client.get("/checkin/recent", { limit, max_id });
31721
- return textResult(data);
31874
+ async ({ limit, max_id, compact }) => {
31875
+ const data = await client2.get("/checkin/recent", { limit, max_id });
31876
+ return textResult(compact ? compactCheckins(data) : data);
31722
31877
  }
31723
31878
  );
31724
31879
  server.registerTool(
@@ -31732,14 +31887,88 @@ function registerFeedTools(server) {
31732
31887
  }
31733
31888
  },
31734
31889
  async ({ checkin_id }) => {
31735
- const data = await client.get(`/checkin/view/${checkin_id}`);
31890
+ const data = await client2.get(`/checkin/view/${checkin_id}`);
31736
31891
  return textResult(data);
31737
31892
  }
31738
31893
  );
31739
31894
  }
31740
31895
 
31896
+ // src/tools/resolve.ts
31897
+ function parseUntappdUrl(input) {
31898
+ let url2;
31899
+ try {
31900
+ url2 = new URL(input.trim());
31901
+ } catch {
31902
+ return null;
31903
+ }
31904
+ if (!/(^|\.)untappd\.com$/i.test(url2.hostname)) return null;
31905
+ const segs = url2.pathname.split("/").filter(Boolean);
31906
+ if (segs[0] === "user" && segs[2] === "checkin" && /^\d+$/.test(segs[3] ?? "")) {
31907
+ return { type: "checkin", id: Number(segs[3]), username: segs[1], tool: "untappd_checkin_info" };
31908
+ }
31909
+ if (segs[0] === "user" && segs[1]) {
31910
+ return { type: "user", username: segs[1], tool: "untappd_user_info" };
31911
+ }
31912
+ const short = {
31913
+ b: { type: "beer", tool: "untappd_beer_info" },
31914
+ w: { type: "brewery", tool: "untappd_brewery_info" },
31915
+ v: { type: "venue", tool: "untappd_venue_info" }
31916
+ };
31917
+ if (segs[0] && short[segs[0]]) {
31918
+ const last = segs[segs.length - 1];
31919
+ if (/^\d+$/.test(last)) {
31920
+ return { ...short[segs[0]], id: Number(last) };
31921
+ }
31922
+ }
31923
+ return null;
31924
+ }
31925
+ function registerResolveTools(server, client2) {
31926
+ server.registerTool(
31927
+ "untappd_resolve",
31928
+ {
31929
+ title: "Resolve an Untappd URL",
31930
+ description: "Parse an untappd.com URL (a beer /b/, brewery /w/, venue /v/, user /user/, or check-in link) into its entity type and id, and name the tool to call next. Pure local parsing \u2014 no network. Read-only.",
31931
+ annotations: toolAnnotations({ title: "Resolve an Untappd URL", readOnly: true, idempotent: true, openWorld: false }),
31932
+ inputSchema: {
31933
+ url: external_exports.string().min(1).describe("An untappd.com URL to resolve")
31934
+ }
31935
+ },
31936
+ async ({ url: url2 }) => {
31937
+ const resolved = parseUntappdUrl(url2);
31938
+ if (!resolved) {
31939
+ throw createHelpfulError(`Not a recognised Untappd entity URL: ${url2}`, {
31940
+ hint: "Expected an untappd.com link like /b/<slug>/<bid>, /w/<slug>/<id>, /v/<slug>/<id>, or /user/<name>."
31941
+ });
31942
+ }
31943
+ return textResult(resolved);
31944
+ }
31945
+ );
31946
+ server.registerTool(
31947
+ "untappd_open_url",
31948
+ {
31949
+ title: "Open an Untappd URL (resolve + fetch)",
31950
+ description: "Resolve an untappd.com URL AND fetch the entity detail in one call \u2014 the convenience combination of untappd_resolve + the matching info tool. Returns { resolved, detail }. Read-only.",
31951
+ annotations: toolAnnotations({ title: "Open an Untappd URL", readOnly: true, idempotent: true, openWorld: true }),
31952
+ inputSchema: {
31953
+ url: external_exports.string().min(1).describe("An untappd.com URL to resolve and fetch")
31954
+ }
31955
+ },
31956
+ async ({ url: url2 }) => {
31957
+ const resolved = parseUntappdUrl(url2);
31958
+ if (!resolved) {
31959
+ throw createHelpfulError(`Not a recognised Untappd entity URL: ${url2}`, {
31960
+ hint: "Expected an untappd.com link like /b/<slug>/<bid>, /w/<slug>/<id>, /v/<slug>/<id>, or /user/<name>."
31961
+ });
31962
+ }
31963
+ const path = resolved.type === "beer" ? `/beer/info/${resolved.id}` : resolved.type === "brewery" ? `/brewery/info/${resolved.id}` : resolved.type === "venue" ? `/venue/info/${resolved.id}` : resolved.type === "checkin" ? `/checkin/view/${resolved.id}` : `/user/info/${encodeURIComponent(resolved.username)}`;
31964
+ const detail = await client2.get(path);
31965
+ return textResult({ resolved, detail });
31966
+ }
31967
+ );
31968
+ }
31969
+
31741
31970
  // src/tools/discover.ts
31742
- function registerDiscoverTools(server) {
31971
+ function registerDiscoverTools(server, client2) {
31743
31972
  server.registerTool(
31744
31973
  "untappd_trending",
31745
31974
  {
@@ -31749,7 +31978,7 @@ function registerDiscoverTools(server) {
31749
31978
  inputSchema: {}
31750
31979
  },
31751
31980
  async () => {
31752
- const data = await client.get("/beer/trending");
31981
+ const data = await client2.get("/beer/trending");
31753
31982
  return textResult(data);
31754
31983
  }
31755
31984
  );
@@ -31765,7 +31994,7 @@ function registerDiscoverTools(server) {
31765
31994
  }
31766
31995
  },
31767
31996
  async ({ limit, offset }) => {
31768
- const data = await client.get("/notifications", { limit, offset });
31997
+ const data = await client2.get("/notifications", { limit, offset });
31769
31998
  return textResult(data);
31770
31999
  }
31771
32000
  );
@@ -31779,19 +32008,81 @@ function registerDiscoverTools(server) {
31779
32008
  lat: external_exports.number().min(-90).max(90).describe("Latitude of the location"),
31780
32009
  lng: external_exports.number().min(-180).max(180).describe("Longitude of the location"),
31781
32010
  limit: external_exports.number().int().min(1).max(50).optional().describe("Max check-ins (1\u201350, default 25)"),
31782
- radius: external_exports.number().int().min(1).max(25).optional().describe("Search radius (default per Untappd)")
32011
+ radius: external_exports.number().int().min(1).max(25).optional().describe("Search radius (default per Untappd)"),
32012
+ compact: external_exports.boolean().optional().describe("Project each check-in to a slim summary to save context (default false)")
31783
32013
  }
31784
32014
  },
31785
- async ({ lat, lng, limit, radius }) => {
31786
- const data = await client.get("/thepub/local", { lat, lng, limit, radius });
31787
- return textResult(data);
32015
+ async ({ lat, lng, limit, radius, compact }) => {
32016
+ const data = await client2.get("/thepub/local", { lat, lng, limit, radius });
32017
+ return textResult(compact ? compactCheckins(data) : data);
31788
32018
  }
31789
32019
  );
31790
32020
  }
31791
32021
 
32022
+ // src/tools/friends.ts
32023
+ var TargetUidSchema = external_exports.number().int().positive().describe("The target user id (uid) \u2014 from a user profile (response.user.uid) or a friends-list item");
32024
+ var ACTIONS = [
32025
+ {
32026
+ tool: "untappd_add_friend",
32027
+ path: "request",
32028
+ title: "Send an Untappd friend request",
32029
+ verb: "send a friend request to",
32030
+ detail: "Sends a friend request to another user, who will be notified."
32031
+ },
32032
+ {
32033
+ tool: "untappd_accept_friend",
32034
+ path: "accept",
32035
+ title: "Accept an Untappd friend request",
32036
+ verb: "accept the incoming friend request from",
32037
+ detail: "Accepts an incoming friend request (see untappd_pending_friends for pending uids)."
32038
+ },
32039
+ {
32040
+ tool: "untappd_reject_friend",
32041
+ path: "reject",
32042
+ title: "Reject an Untappd friend request",
32043
+ verb: "reject the incoming friend request from",
32044
+ detail: "Rejects/ignores an incoming friend request (see untappd_pending_friends for pending uids)."
32045
+ },
32046
+ {
32047
+ tool: "untappd_remove_friend",
32048
+ path: "remove",
32049
+ title: "Remove an Untappd friend",
32050
+ verb: "remove your friendship with",
32051
+ detail: "Removes an existing friend, or cancels a friend request you sent."
32052
+ }
32053
+ ];
32054
+ function registerFriendActionTools(server, client2) {
32055
+ for (const action of ACTIONS) {
32056
+ server.registerTool(
32057
+ action.tool,
32058
+ {
32059
+ title: action.title,
32060
+ description: `${action.detail} Acts on YOUR account and affects a real relationship with another person. Without confirm: true it returns a dry-run preview and makes NO network call; with confirm: true it performs the action. Note: this endpoint path is taken from the Untappd app but is not otherwise independently verified.`,
32061
+ annotations: toolAnnotations({ title: action.title, readOnly: false, idempotent: true, openWorld: true }),
32062
+ inputSchema: {
32063
+ target_uid: TargetUidSchema,
32064
+ confirm: schemaConfirm
32065
+ }
32066
+ },
32067
+ async ({ target_uid, confirm }) => {
32068
+ if (confirm !== true) {
32069
+ return textResult({
32070
+ dryRun: true,
32071
+ action: action.path,
32072
+ target_uid,
32073
+ note: `Dry run \u2014 re-run with confirm: true to ${action.verb} user ${target_uid}.`
32074
+ });
32075
+ }
32076
+ const data = await client2.write("POST", `/friend/${action.path}/${target_uid}`);
32077
+ return textResult({ done: true, action: action.path, target_uid, result: data?.result });
32078
+ }
32079
+ );
32080
+ }
32081
+ }
32082
+
31792
32083
  // src/tools/wishlist.ts
31793
32084
  var BidSchema2 = external_exports.number().int().positive().describe("Untappd beer id (bid) \u2014 from untappd_search_beer");
31794
- function registerWishlistTools(server) {
32085
+ function registerWishlistTools(server, client2) {
31795
32086
  server.registerTool(
31796
32087
  "untappd_wishlist_add",
31797
32088
  {
@@ -31812,7 +32103,7 @@ function registerWishlistTools(server) {
31812
32103
  note: "Dry run \u2014 re-run with confirm: true to add this beer to your wishlist."
31813
32104
  });
31814
32105
  }
31815
- const data = await client.write("GET", "/user/wishlist/add", { query: { bid } });
32106
+ const data = await client2.write("GET", "/user/wishlist/add", { query: { bid } });
31816
32107
  return textResult({ added: true, bid, result: data?.result });
31817
32108
  }
31818
32109
  );
@@ -31836,7 +32127,7 @@ function registerWishlistTools(server) {
31836
32127
  note: "Dry run \u2014 re-run with confirm: true to remove this beer from your wishlist."
31837
32128
  });
31838
32129
  }
31839
- const data = await client.write("GET", "/user/wishlist/delete", { query: { bid } });
32130
+ const data = await client2.write("GET", "/user/wishlist/delete", { query: { bid } });
31840
32131
  return textResult({ removed: true, bid, result: data?.result });
31841
32132
  }
31842
32133
  );
@@ -31857,7 +32148,7 @@ function localTimezone() {
31857
32148
  const gmt_offset = -(/* @__PURE__ */ new Date()).getTimezoneOffset() / 60;
31858
32149
  return { timezone, gmt_offset };
31859
32150
  }
31860
- function registerCheckinTools(server) {
32151
+ function registerCheckinTools(server, client2) {
31861
32152
  server.registerTool(
31862
32153
  "untappd_toast",
31863
32154
  {
@@ -31878,7 +32169,7 @@ function registerCheckinTools(server) {
31878
32169
  note: "Dry run \u2014 re-run with confirm: true to toggle your toast on this check-in."
31879
32170
  });
31880
32171
  }
31881
- const data = await client.write("POST", `/checkin/toast/${checkin_id}`);
32172
+ const data = await client2.write("POST", `/checkin/toast/${checkin_id}`);
31882
32173
  return textResult({ toggled: true, checkin_id, result: data?.result, like_type: data?.like_type });
31883
32174
  }
31884
32175
  );
@@ -31904,7 +32195,7 @@ function registerCheckinTools(server) {
31904
32195
  note: "Dry run \u2014 re-run with confirm: true to post this comment to your Untappd account."
31905
32196
  });
31906
32197
  }
31907
- const data = await client.write("POST", `/checkin/addcomment/${checkin_id}`, { form: { comment } });
32198
+ const data = await client2.write("POST", `/checkin/addcomment/${checkin_id}`, { form: { comment } });
31908
32199
  return textResult({ posted: true, checkin_id, response: data });
31909
32200
  }
31910
32201
  );
@@ -31928,7 +32219,7 @@ function registerCheckinTools(server) {
31928
32219
  note: "Dry run \u2014 re-run with confirm: true to delete this comment from your Untappd account."
31929
32220
  });
31930
32221
  }
31931
- const data = await client.write("POST", `/checkin/deletecomment/${comment_id}`);
32222
+ const data = await client2.write("POST", `/checkin/deletecomment/${comment_id}`);
31932
32223
  return textResult({ deleted: true, comment_id, result: data?.result });
31933
32224
  }
31934
32225
  );
@@ -31952,7 +32243,7 @@ function registerCheckinTools(server) {
31952
32243
  note: "Dry run \u2014 re-run with confirm: true to PERMANENTLY delete this check-in. This cannot be undone."
31953
32244
  });
31954
32245
  }
31955
- const data = await client.write("POST", `/checkin/delete/${checkin_id}`);
32246
+ const data = await client2.write("POST", `/checkin/delete/${checkin_id}`);
31956
32247
  return textResult({ deleted: true, checkin_id, result: data?.result });
31957
32248
  }
31958
32249
  );
@@ -32013,14 +32304,14 @@ function registerCheckinTools(server) {
32013
32304
  if (!existsSync(photo_path)) throw new McpToolError(`Photo file not found: ${photo_path}`);
32014
32305
  blob = await fileBlob(photo_path);
32015
32306
  }
32016
- const data = await client.write("POST", "/checkin/add", { form });
32307
+ const data = await client2.write("POST", "/checkin/add", { form });
32017
32308
  let photo_attached = false;
32018
32309
  let photo_error;
32019
32310
  if (photo_path !== void 0) {
32020
32311
  if (data?.photo_upload?.url && data.checkin_id) {
32021
32312
  try {
32022
- await client.putBinary(data.photo_upload.url, blob, PHOTO_CONTENT_TYPES[ext]);
32023
- await client.write("POST", "/photo/uploadComplete", {
32313
+ await client2.putBinary(data.photo_upload.url, blob, PHOTO_CONTENT_TYPES[ext]);
32314
+ await client2.write("POST", "/photo/uploadComplete", {
32024
32315
  form: { checkin_id: data.checkin_id, destination_url: data.photo_upload.destination_url }
32025
32316
  });
32026
32317
  photo_attached = true;
@@ -32043,7 +32334,7 @@ function registerCheckinTools(server) {
32043
32334
  }
32044
32335
 
32045
32336
  // src/tools/utilities.ts
32046
- function registerUtilityTools(server) {
32337
+ function registerUtilityTools(server, client2) {
32047
32338
  server.registerTool(
32048
32339
  "untappd_healthcheck",
32049
32340
  {
@@ -32053,18 +32344,18 @@ function registerUtilityTools(server) {
32053
32344
  inputSchema: {}
32054
32345
  },
32055
32346
  async () => {
32056
- if (!client.configured) {
32347
+ if (!client2.configured) {
32057
32348
  return textResult({
32058
32349
  ok: false,
32059
32350
  configured: false,
32060
32351
  note: "Untappd credentials are not set. Configure UNTAPPD_USERNAME, UNTAPPD_PASSWORD, UNTAPPD_CLIENT_ID, and UNTAPPD_CLIENT_SECRET."
32061
32352
  });
32062
32353
  }
32063
- const feed = await client.get("/checkin/recent", { limit: 1 });
32354
+ const feed = await client2.get("/checkin/recent", { limit: 1 });
32064
32355
  return textResult({
32065
32356
  ok: true,
32066
32357
  configured: true,
32067
- account: client.loginName,
32358
+ account: client2.loginName,
32068
32359
  feed_reachable: feed?.checkins !== void 0,
32069
32360
  note: "Logged in to Untappd and fetched the friend feed successfully."
32070
32361
  });
@@ -32073,19 +32364,22 @@ function registerUtilityTools(server) {
32073
32364
  }
32074
32365
 
32075
32366
  // src/index.ts
32367
+ var client = new UntappdClient();
32076
32368
  await runMcp({
32077
32369
  name: "untappd-mcp",
32078
32370
  version: VERSION,
32079
32371
  banner: "[untappd-mcp] This project was developed and is maintained by AI (Claude Opus 4.8). Use at your own discretion.",
32080
32372
  tools: [
32081
- registerBeerTools,
32082
- registerBreweryTools,
32083
- registerVenueTools,
32084
- registerUserTools,
32085
- registerFeedTools,
32086
- registerDiscoverTools,
32087
- registerWishlistTools,
32088
- registerCheckinTools,
32089
- registerUtilityTools
32373
+ (s) => registerBeerTools(s, client),
32374
+ (s) => registerBreweryTools(s, client),
32375
+ (s) => registerVenueTools(s, client),
32376
+ (s) => registerUserTools(s, client),
32377
+ (s) => registerFeedTools(s, client),
32378
+ (s) => registerResolveTools(s, client),
32379
+ (s) => registerDiscoverTools(s, client),
32380
+ (s) => registerFriendActionTools(s, client),
32381
+ (s) => registerWishlistTools(s, client),
32382
+ (s) => registerCheckinTools(s, client),
32383
+ (s) => registerUtilityTools(s, client)
32090
32384
  ]
32091
32385
  });