untappd-mcp 1.1.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
@@ -31184,13 +31184,15 @@ function toolAnnotations(opts = {}) {
31184
31184
  }
31185
31185
 
31186
31186
  // src/version.ts
31187
- var VERSION = "1.1.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,7 +31444,6 @@ var UntappdClient = class {
31420
31444
  }
31421
31445
  }
31422
31446
  };
31423
- var client = new UntappdClient();
31424
31447
 
31425
31448
  // src/compact.ts
31426
31449
  function asDict(v) {
@@ -31500,7 +31523,7 @@ var compactUserBeers = (resp) => projectItems(resp, "beers", compactUserBeer);
31500
31523
  // src/tools/beer.ts
31501
31524
  var BidSchema = external_exports.number().int().positive().describe("Untappd beer id (bid)");
31502
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)");
31503
- function registerBeerTools(server) {
31526
+ function registerBeerTools(server, client2) {
31504
31527
  server.registerTool(
31505
31528
  "untappd_search_beer",
31506
31529
  {
@@ -31516,7 +31539,7 @@ function registerBeerTools(server) {
31516
31539
  }
31517
31540
  },
31518
31541
  async ({ query, limit, offset, sort, compact }) => {
31519
- const data = await client.get("/search/beer", { q: query, limit, offset, sort });
31542
+ const data = await client2.get("/search/beer", { q: query, limit, offset, sort });
31520
31543
  return textResult(compact ? compactBeerSearch(data) : data);
31521
31544
  }
31522
31545
  );
@@ -31532,7 +31555,7 @@ function registerBeerTools(server) {
31532
31555
  }
31533
31556
  },
31534
31557
  async ({ bid, compact }) => {
31535
- 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 });
31536
31559
  return textResult(data);
31537
31560
  }
31538
31561
  );
@@ -31550,14 +31573,14 @@ function registerBeerTools(server) {
31550
31573
  }
31551
31574
  },
31552
31575
  async ({ bid, limit, max_id, compact }) => {
31553
- const data = await client.get(`/beer/checkins/${bid}`, { limit, max_id });
31576
+ const data = await client2.get(`/beer/checkins/${bid}`, { limit, max_id });
31554
31577
  return textResult(compact ? compactCheckins(data) : data);
31555
31578
  }
31556
31579
  );
31557
31580
  }
31558
31581
 
31559
31582
  // src/tools/brewery.ts
31560
- function registerBreweryTools(server) {
31583
+ function registerBreweryTools(server, client2) {
31561
31584
  server.registerTool(
31562
31585
  "untappd_search_brewery",
31563
31586
  {
@@ -31571,7 +31594,7 @@ function registerBreweryTools(server) {
31571
31594
  }
31572
31595
  },
31573
31596
  async ({ query, limit, offset }) => {
31574
- const data = await client.get("/search/brewery", { q: query, limit, offset });
31597
+ const data = await client2.get("/search/brewery", { q: query, limit, offset });
31575
31598
  return textResult(data);
31576
31599
  }
31577
31600
  );
@@ -31587,7 +31610,7 @@ function registerBreweryTools(server) {
31587
31610
  }
31588
31611
  },
31589
31612
  async ({ brewery_id, compact }) => {
31590
- 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 });
31591
31614
  return textResult(data);
31592
31615
  }
31593
31616
  );
@@ -31605,14 +31628,14 @@ function registerBreweryTools(server) {
31605
31628
  }
31606
31629
  },
31607
31630
  async ({ brewery_id, limit, offset, sort }) => {
31608
- 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 });
31609
31632
  return textResult(data);
31610
31633
  }
31611
31634
  );
31612
31635
  }
31613
31636
 
31614
31637
  // src/tools/venue.ts
31615
- function registerVenueTools(server) {
31638
+ function registerVenueTools(server, client2) {
31616
31639
  server.registerTool(
31617
31640
  "untappd_search_venue",
31618
31641
  {
@@ -31625,7 +31648,7 @@ function registerVenueTools(server) {
31625
31648
  }
31626
31649
  },
31627
31650
  async ({ query, limit }) => {
31628
- const data = await client.get("/search/venue", { q: query, limit });
31651
+ const data = await client2.get("/search/venue", { q: query, limit });
31629
31652
  return textResult(data);
31630
31653
  }
31631
31654
  );
@@ -31641,7 +31664,7 @@ function registerVenueTools(server) {
31641
31664
  }
31642
31665
  },
31643
31666
  async ({ venue_id, compact }) => {
31644
- 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 });
31645
31668
  return textResult(data);
31646
31669
  }
31647
31670
  );
@@ -31656,7 +31679,7 @@ function registerVenueTools(server) {
31656
31679
  }
31657
31680
  },
31658
31681
  async ({ foursquare_id }) => {
31659
- const data = await client.get(`/venue/foursquare_lookup/${encodeURIComponent(foursquare_id)}`);
31682
+ const data = await client2.get(`/venue/foursquare_lookup/${encodeURIComponent(foursquare_id)}`);
31660
31683
  return textResult(data);
31661
31684
  }
31662
31685
  );
@@ -31674,7 +31697,7 @@ function registerVenueTools(server) {
31674
31697
  }
31675
31698
  },
31676
31699
  async ({ venue_id, limit, max_id, compact }) => {
31677
- const data = await client.get(`/venue/checkins/${venue_id}`, { limit, max_id });
31700
+ const data = await client2.get(`/venue/checkins/${venue_id}`, { limit, max_id });
31678
31701
  return textResult(compact ? compactCheckins(data) : data);
31679
31702
  }
31680
31703
  );
@@ -31682,8 +31705,8 @@ function registerVenueTools(server) {
31682
31705
 
31683
31706
  // src/tools/user.ts
31684
31707
  var UsernameArg = external_exports.string().min(1).optional().describe("Untappd username. Omit to use your own configured account (UNTAPPD_USERNAME).");
31685
- function resolveUser(username) {
31686
- const u = username ?? client.loginName;
31708
+ function resolveUser(username, loginName) {
31709
+ const u = username ?? loginName;
31687
31710
  if (!u) {
31688
31711
  throw createHelpfulError("No username given and no configured account to fall back to.", {
31689
31712
  hint: "Pass `username`, or set UNTAPPD_USERNAME so user tools default to your own account."
@@ -31691,7 +31714,7 @@ function resolveUser(username) {
31691
31714
  }
31692
31715
  return encodeURIComponent(u);
31693
31716
  }
31694
- function registerUserTools(server) {
31717
+ function registerUserTools(server, client2) {
31695
31718
  server.registerTool(
31696
31719
  "untappd_user_info",
31697
31720
  {
@@ -31704,7 +31727,7 @@ function registerUserTools(server) {
31704
31727
  }
31705
31728
  },
31706
31729
  async ({ username, compact }) => {
31707
- 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 });
31708
31731
  return textResult(data);
31709
31732
  }
31710
31733
  );
@@ -31722,7 +31745,7 @@ function registerUserTools(server) {
31722
31745
  }
31723
31746
  },
31724
31747
  async ({ username, limit, max_id, compact }) => {
31725
- const data = await client.get(`/user/checkins/${resolveUser(username)}`, { limit, max_id });
31748
+ const data = await client2.get(`/user/checkins/${resolveUser(username, client2.loginName)}`, { limit, max_id });
31726
31749
  return textResult(compact ? compactCheckins(data) : data);
31727
31750
  }
31728
31751
  );
@@ -31741,7 +31764,7 @@ function registerUserTools(server) {
31741
31764
  }
31742
31765
  },
31743
31766
  async ({ username, limit, offset, sort, compact }) => {
31744
- const data = await client.get(`/user/wishlist/${resolveUser(username)}`, { limit, offset, sort });
31767
+ const data = await client2.get(`/user/wishlist/${resolveUser(username, client2.loginName)}`, { limit, offset, sort });
31745
31768
  return textResult(compact ? compactWishlist(data) : data);
31746
31769
  }
31747
31770
  );
@@ -31760,7 +31783,7 @@ function registerUserTools(server) {
31760
31783
  }
31761
31784
  },
31762
31785
  async ({ username, limit, offset, sort, compact }) => {
31763
- const data = await client.get(`/user/beers/${resolveUser(username)}`, { limit, offset, sort });
31786
+ const data = await client2.get(`/user/beers/${resolveUser(username, client2.loginName)}`, { limit, offset, sort });
31764
31787
  return textResult(compact ? compactUserBeers(data) : data);
31765
31788
  }
31766
31789
  );
@@ -31777,7 +31800,7 @@ function registerUserTools(server) {
31777
31800
  }
31778
31801
  },
31779
31802
  async ({ username, limit, offset }) => {
31780
- 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 });
31781
31804
  return textResult(data);
31782
31805
  }
31783
31806
  );
@@ -31794,7 +31817,7 @@ function registerUserTools(server) {
31794
31817
  }
31795
31818
  },
31796
31819
  async ({ username, limit, offset }) => {
31797
- 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 });
31798
31821
  return textResult(data);
31799
31822
  }
31800
31823
  );
@@ -31812,7 +31835,7 @@ function registerUserTools(server) {
31812
31835
  }
31813
31836
  },
31814
31837
  async ({ username, limit, offset, sort }) => {
31815
- const data = await client.get(`/user/venues/${resolveUser(username)}`, { limit, offset, sort });
31838
+ const data = await client2.get(`/user/venues/${resolveUser(username, client2.loginName)}`, { limit, offset, sort });
31816
31839
  return textResult(data);
31817
31840
  }
31818
31841
  );
@@ -31828,14 +31851,14 @@ function registerUserTools(server) {
31828
31851
  }
31829
31852
  },
31830
31853
  async ({ limit, offset }) => {
31831
- const data = await client.get("/user/pending", { limit, offset });
31854
+ const data = await client2.get("/user/pending", { limit, offset });
31832
31855
  return textResult(data);
31833
31856
  }
31834
31857
  );
31835
31858
  }
31836
31859
 
31837
31860
  // src/tools/feed.ts
31838
- function registerFeedTools(server) {
31861
+ function registerFeedTools(server, client2) {
31839
31862
  server.registerTool(
31840
31863
  "untappd_activity_feed",
31841
31864
  {
@@ -31849,7 +31872,7 @@ function registerFeedTools(server) {
31849
31872
  }
31850
31873
  },
31851
31874
  async ({ limit, max_id, compact }) => {
31852
- const data = await client.get("/checkin/recent", { limit, max_id });
31875
+ const data = await client2.get("/checkin/recent", { limit, max_id });
31853
31876
  return textResult(compact ? compactCheckins(data) : data);
31854
31877
  }
31855
31878
  );
@@ -31864,7 +31887,7 @@ function registerFeedTools(server) {
31864
31887
  }
31865
31888
  },
31866
31889
  async ({ checkin_id }) => {
31867
- const data = await client.get(`/checkin/view/${checkin_id}`);
31890
+ const data = await client2.get(`/checkin/view/${checkin_id}`);
31868
31891
  return textResult(data);
31869
31892
  }
31870
31893
  );
@@ -31899,7 +31922,7 @@ function parseUntappdUrl(input) {
31899
31922
  }
31900
31923
  return null;
31901
31924
  }
31902
- function registerResolveTools(server) {
31925
+ function registerResolveTools(server, client2) {
31903
31926
  server.registerTool(
31904
31927
  "untappd_resolve",
31905
31928
  {
@@ -31938,14 +31961,14 @@ function registerResolveTools(server) {
31938
31961
  });
31939
31962
  }
31940
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)}`;
31941
- const detail = await client.get(path);
31964
+ const detail = await client2.get(path);
31942
31965
  return textResult({ resolved, detail });
31943
31966
  }
31944
31967
  );
31945
31968
  }
31946
31969
 
31947
31970
  // src/tools/discover.ts
31948
- function registerDiscoverTools(server) {
31971
+ function registerDiscoverTools(server, client2) {
31949
31972
  server.registerTool(
31950
31973
  "untappd_trending",
31951
31974
  {
@@ -31955,7 +31978,7 @@ function registerDiscoverTools(server) {
31955
31978
  inputSchema: {}
31956
31979
  },
31957
31980
  async () => {
31958
- const data = await client.get("/beer/trending");
31981
+ const data = await client2.get("/beer/trending");
31959
31982
  return textResult(data);
31960
31983
  }
31961
31984
  );
@@ -31971,7 +31994,7 @@ function registerDiscoverTools(server) {
31971
31994
  }
31972
31995
  },
31973
31996
  async ({ limit, offset }) => {
31974
- const data = await client.get("/notifications", { limit, offset });
31997
+ const data = await client2.get("/notifications", { limit, offset });
31975
31998
  return textResult(data);
31976
31999
  }
31977
32000
  );
@@ -31990,65 +32013,12 @@ function registerDiscoverTools(server) {
31990
32013
  }
31991
32014
  },
31992
32015
  async ({ lat, lng, limit, radius, compact }) => {
31993
- const data = await client.get("/thepub/local", { lat, lng, limit, radius });
32016
+ const data = await client2.get("/thepub/local", { lat, lng, limit, radius });
31994
32017
  return textResult(compact ? compactCheckins(data) : data);
31995
32018
  }
31996
32019
  );
31997
32020
  }
31998
32021
 
31999
- // src/tools/wishlist.ts
32000
- var BidSchema2 = external_exports.number().int().positive().describe("Untappd beer id (bid) \u2014 from untappd_search_beer");
32001
- function registerWishlistTools(server) {
32002
- server.registerTool(
32003
- "untappd_wishlist_add",
32004
- {
32005
- title: "Add a beer to your wishlist",
32006
- description: "Add a beer to YOUR Untappd wishlist by its bid. Without confirm: true it returns a dry-run preview and makes NO network call; with confirm: true it adds. Writes to your account.",
32007
- annotations: toolAnnotations({ title: "Add a beer to your wishlist", readOnly: false, idempotent: true, openWorld: true }),
32008
- inputSchema: {
32009
- bid: BidSchema2,
32010
- confirm: schemaConfirm
32011
- }
32012
- },
32013
- async ({ bid, confirm }) => {
32014
- if (confirm !== true) {
32015
- return textResult({
32016
- dryRun: true,
32017
- action: "wishlist_add",
32018
- bid,
32019
- note: "Dry run \u2014 re-run with confirm: true to add this beer to your wishlist."
32020
- });
32021
- }
32022
- const data = await client.write("GET", "/user/wishlist/add", { query: { bid } });
32023
- return textResult({ added: true, bid, result: data?.result });
32024
- }
32025
- );
32026
- server.registerTool(
32027
- "untappd_wishlist_remove",
32028
- {
32029
- title: "Remove a beer from your wishlist",
32030
- description: "Remove a beer from YOUR Untappd wishlist by its bid. Without confirm: true it returns a dry-run preview and makes NO network call; with confirm: true it removes. Writes to your account.",
32031
- annotations: toolAnnotations({ title: "Remove a beer from your wishlist", readOnly: false, idempotent: true, openWorld: true }),
32032
- inputSchema: {
32033
- bid: BidSchema2,
32034
- confirm: schemaConfirm
32035
- }
32036
- },
32037
- async ({ bid, confirm }) => {
32038
- if (confirm !== true) {
32039
- return textResult({
32040
- dryRun: true,
32041
- action: "wishlist_remove",
32042
- bid,
32043
- note: "Dry run \u2014 re-run with confirm: true to remove this beer from your wishlist."
32044
- });
32045
- }
32046
- const data = await client.write("GET", "/user/wishlist/delete", { query: { bid } });
32047
- return textResult({ removed: true, bid, result: data?.result });
32048
- }
32049
- );
32050
- }
32051
-
32052
32022
  // src/tools/friends.ts
32053
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");
32054
32024
  var ACTIONS = [
@@ -32081,7 +32051,7 @@ var ACTIONS = [
32081
32051
  detail: "Removes an existing friend, or cancels a friend request you sent."
32082
32052
  }
32083
32053
  ];
32084
- function registerFriendActionTools(server) {
32054
+ function registerFriendActionTools(server, client2) {
32085
32055
  for (const action of ACTIONS) {
32086
32056
  server.registerTool(
32087
32057
  action.tool,
@@ -32103,13 +32073,66 @@ function registerFriendActionTools(server) {
32103
32073
  note: `Dry run \u2014 re-run with confirm: true to ${action.verb} user ${target_uid}.`
32104
32074
  });
32105
32075
  }
32106
- const data = await client.write("POST", `/friend/${action.path}/${target_uid}`);
32076
+ const data = await client2.write("POST", `/friend/${action.path}/${target_uid}`);
32107
32077
  return textResult({ done: true, action: action.path, target_uid, result: data?.result });
32108
32078
  }
32109
32079
  );
32110
32080
  }
32111
32081
  }
32112
32082
 
32083
+ // src/tools/wishlist.ts
32084
+ var BidSchema2 = external_exports.number().int().positive().describe("Untappd beer id (bid) \u2014 from untappd_search_beer");
32085
+ function registerWishlistTools(server, client2) {
32086
+ server.registerTool(
32087
+ "untappd_wishlist_add",
32088
+ {
32089
+ title: "Add a beer to your wishlist",
32090
+ description: "Add a beer to YOUR Untappd wishlist by its bid. Without confirm: true it returns a dry-run preview and makes NO network call; with confirm: true it adds. Writes to your account.",
32091
+ annotations: toolAnnotations({ title: "Add a beer to your wishlist", readOnly: false, idempotent: true, openWorld: true }),
32092
+ inputSchema: {
32093
+ bid: BidSchema2,
32094
+ confirm: schemaConfirm
32095
+ }
32096
+ },
32097
+ async ({ bid, confirm }) => {
32098
+ if (confirm !== true) {
32099
+ return textResult({
32100
+ dryRun: true,
32101
+ action: "wishlist_add",
32102
+ bid,
32103
+ note: "Dry run \u2014 re-run with confirm: true to add this beer to your wishlist."
32104
+ });
32105
+ }
32106
+ const data = await client2.write("GET", "/user/wishlist/add", { query: { bid } });
32107
+ return textResult({ added: true, bid, result: data?.result });
32108
+ }
32109
+ );
32110
+ server.registerTool(
32111
+ "untappd_wishlist_remove",
32112
+ {
32113
+ title: "Remove a beer from your wishlist",
32114
+ description: "Remove a beer from YOUR Untappd wishlist by its bid. Without confirm: true it returns a dry-run preview and makes NO network call; with confirm: true it removes. Writes to your account.",
32115
+ annotations: toolAnnotations({ title: "Remove a beer from your wishlist", readOnly: false, idempotent: true, openWorld: true }),
32116
+ inputSchema: {
32117
+ bid: BidSchema2,
32118
+ confirm: schemaConfirm
32119
+ }
32120
+ },
32121
+ async ({ bid, confirm }) => {
32122
+ if (confirm !== true) {
32123
+ return textResult({
32124
+ dryRun: true,
32125
+ action: "wishlist_remove",
32126
+ bid,
32127
+ note: "Dry run \u2014 re-run with confirm: true to remove this beer from your wishlist."
32128
+ });
32129
+ }
32130
+ const data = await client2.write("GET", "/user/wishlist/delete", { query: { bid } });
32131
+ return textResult({ removed: true, bid, result: data?.result });
32132
+ }
32133
+ );
32134
+ }
32135
+
32113
32136
  // src/tools/checkin.ts
32114
32137
  import { existsSync } from "node:fs";
32115
32138
  import { extname } from "node:path";
@@ -32125,7 +32148,7 @@ function localTimezone() {
32125
32148
  const gmt_offset = -(/* @__PURE__ */ new Date()).getTimezoneOffset() / 60;
32126
32149
  return { timezone, gmt_offset };
32127
32150
  }
32128
- function registerCheckinTools(server) {
32151
+ function registerCheckinTools(server, client2) {
32129
32152
  server.registerTool(
32130
32153
  "untappd_toast",
32131
32154
  {
@@ -32146,7 +32169,7 @@ function registerCheckinTools(server) {
32146
32169
  note: "Dry run \u2014 re-run with confirm: true to toggle your toast on this check-in."
32147
32170
  });
32148
32171
  }
32149
- const data = await client.write("POST", `/checkin/toast/${checkin_id}`);
32172
+ const data = await client2.write("POST", `/checkin/toast/${checkin_id}`);
32150
32173
  return textResult({ toggled: true, checkin_id, result: data?.result, like_type: data?.like_type });
32151
32174
  }
32152
32175
  );
@@ -32172,7 +32195,7 @@ function registerCheckinTools(server) {
32172
32195
  note: "Dry run \u2014 re-run with confirm: true to post this comment to your Untappd account."
32173
32196
  });
32174
32197
  }
32175
- 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 } });
32176
32199
  return textResult({ posted: true, checkin_id, response: data });
32177
32200
  }
32178
32201
  );
@@ -32196,7 +32219,7 @@ function registerCheckinTools(server) {
32196
32219
  note: "Dry run \u2014 re-run with confirm: true to delete this comment from your Untappd account."
32197
32220
  });
32198
32221
  }
32199
- const data = await client.write("POST", `/checkin/deletecomment/${comment_id}`);
32222
+ const data = await client2.write("POST", `/checkin/deletecomment/${comment_id}`);
32200
32223
  return textResult({ deleted: true, comment_id, result: data?.result });
32201
32224
  }
32202
32225
  );
@@ -32220,7 +32243,7 @@ function registerCheckinTools(server) {
32220
32243
  note: "Dry run \u2014 re-run with confirm: true to PERMANENTLY delete this check-in. This cannot be undone."
32221
32244
  });
32222
32245
  }
32223
- const data = await client.write("POST", `/checkin/delete/${checkin_id}`);
32246
+ const data = await client2.write("POST", `/checkin/delete/${checkin_id}`);
32224
32247
  return textResult({ deleted: true, checkin_id, result: data?.result });
32225
32248
  }
32226
32249
  );
@@ -32281,14 +32304,14 @@ function registerCheckinTools(server) {
32281
32304
  if (!existsSync(photo_path)) throw new McpToolError(`Photo file not found: ${photo_path}`);
32282
32305
  blob = await fileBlob(photo_path);
32283
32306
  }
32284
- const data = await client.write("POST", "/checkin/add", { form });
32307
+ const data = await client2.write("POST", "/checkin/add", { form });
32285
32308
  let photo_attached = false;
32286
32309
  let photo_error;
32287
32310
  if (photo_path !== void 0) {
32288
32311
  if (data?.photo_upload?.url && data.checkin_id) {
32289
32312
  try {
32290
- await client.putBinary(data.photo_upload.url, blob, PHOTO_CONTENT_TYPES[ext]);
32291
- 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", {
32292
32315
  form: { checkin_id: data.checkin_id, destination_url: data.photo_upload.destination_url }
32293
32316
  });
32294
32317
  photo_attached = true;
@@ -32311,7 +32334,7 @@ function registerCheckinTools(server) {
32311
32334
  }
32312
32335
 
32313
32336
  // src/tools/utilities.ts
32314
- function registerUtilityTools(server) {
32337
+ function registerUtilityTools(server, client2) {
32315
32338
  server.registerTool(
32316
32339
  "untappd_healthcheck",
32317
32340
  {
@@ -32321,18 +32344,18 @@ function registerUtilityTools(server) {
32321
32344
  inputSchema: {}
32322
32345
  },
32323
32346
  async () => {
32324
- if (!client.configured) {
32347
+ if (!client2.configured) {
32325
32348
  return textResult({
32326
32349
  ok: false,
32327
32350
  configured: false,
32328
32351
  note: "Untappd credentials are not set. Configure UNTAPPD_USERNAME, UNTAPPD_PASSWORD, UNTAPPD_CLIENT_ID, and UNTAPPD_CLIENT_SECRET."
32329
32352
  });
32330
32353
  }
32331
- const feed = await client.get("/checkin/recent", { limit: 1 });
32354
+ const feed = await client2.get("/checkin/recent", { limit: 1 });
32332
32355
  return textResult({
32333
32356
  ok: true,
32334
32357
  configured: true,
32335
- account: client.loginName,
32358
+ account: client2.loginName,
32336
32359
  feed_reachable: feed?.checkins !== void 0,
32337
32360
  note: "Logged in to Untappd and fetched the friend feed successfully."
32338
32361
  });
@@ -32341,21 +32364,22 @@ function registerUtilityTools(server) {
32341
32364
  }
32342
32365
 
32343
32366
  // src/index.ts
32367
+ var client = new UntappdClient();
32344
32368
  await runMcp({
32345
32369
  name: "untappd-mcp",
32346
32370
  version: VERSION,
32347
32371
  banner: "[untappd-mcp] This project was developed and is maintained by AI (Claude Opus 4.8). Use at your own discretion.",
32348
32372
  tools: [
32349
- registerBeerTools,
32350
- registerBreweryTools,
32351
- registerVenueTools,
32352
- registerUserTools,
32353
- registerFeedTools,
32354
- registerResolveTools,
32355
- registerDiscoverTools,
32356
- registerWishlistTools,
32357
- registerFriendActionTools,
32358
- registerCheckinTools,
32359
- 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)
32360
32384
  ]
32361
32385
  });