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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +15 -0
- package/dist/bundle.js +216 -192
- package/dist/client.js +126 -96
- package/dist/index.js +17 -17
- package/dist/tools/beer.js +1 -2
- package/dist/tools/brewery.js +1 -2
- package/dist/tools/checkin.js +1 -2
- package/dist/tools/discover.js +1 -2
- package/dist/tools/feed.js +1 -2
- package/dist/tools/friends.js +1 -2
- package/dist/tools/resolve.js +1 -2
- package/dist/tools/user.js +10 -11
- package/dist/tools/utilities.js +1 -2
- package/dist/tools/venue.js +1 -2
- package/dist/tools/wishlist.js +1 -2
- package/dist/version.js +1 -1
- package/package.json +11 -3
- package/server.json +2 -2
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.
|
|
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
|
-
|
|
31193
|
-
|
|
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
|
|
31209
|
-
const
|
|
31210
|
-
const
|
|
31211
|
-
|
|
31212
|
-
|
|
31213
|
-
|
|
31214
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31230
|
-
|
|
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
|
-
|
|
31245
|
-
|
|
31246
|
-
|
|
31247
|
-
|
|
31248
|
-
|
|
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
|
-
/**
|
|
31312
|
+
/** True when enough is configured to make authenticated calls. */
|
|
31254
31313
|
get configured() {
|
|
31255
|
-
return this.
|
|
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.
|
|
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
|
-
|
|
31262
|
-
|
|
31263
|
-
|
|
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
|
|
31274
|
-
//
|
|
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
|
-
|
|
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.
|
|
31307
|
-
const
|
|
31308
|
-
|
|
31309
|
-
|
|
31310
|
-
|
|
31311
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ??
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
32291
|
-
await
|
|
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 (!
|
|
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
|
|
32354
|
+
const feed = await client2.get("/checkin/recent", { limit: 1 });
|
|
32332
32355
|
return textResult({
|
|
32333
32356
|
ok: true,
|
|
32334
32357
|
configured: true,
|
|
32335
|
-
account:
|
|
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
|
-
|
|
32357
|
-
|
|
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
|
});
|