untappd-mcp 2.1.2 → 2.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.
@@ -1,46 +1,53 @@
1
1
  import { z } from 'zod';
2
- import { minifiedResult, schemaConfirm, toolAnnotations } from '@chrischall/mcp-utils';
2
+ import { confirmTokenParam, minifiedResult, toolAnnotations } from '@chrischall/mcp-utils';
3
+ import { CONFIRM_FLOW, confirmWrite } from './confirm.js';
3
4
  const BidSchema = z.number().int().positive().describe('Untappd beer id (bid) — from untappd_search_beer');
4
5
  export function registerWishlistTools(server, client) {
5
6
  server.registerTool('untappd_wishlist_add', {
6
7
  title: 'Add a beer to your wishlist',
7
- description: 'Add a beer to YOUR Untappd wishlist by its bid. Without confirm: true it returns a dry-run preview and ' +
8
- 'makes NO network call; with confirm: true it adds. Writes to your account.',
8
+ description: `Add a beer to YOUR Untappd wishlist by its bid. Writes to your account. ${CONFIRM_FLOW}`,
9
9
  annotations: toolAnnotations({ title: 'Add a beer to your wishlist', readOnly: false, idempotent: true, openWorld: true, destructive: false }),
10
10
  inputSchema: z.object({
11
11
  bid: BidSchema,
12
- confirm: schemaConfirm,
12
+ confirmToken: confirmTokenParam,
13
13
  }),
14
- }, async ({ bid, confirm }) => {
15
- if (confirm !== true) {
16
- return minifiedResult({
17
- dryRun: true,
18
- action: 'wishlist_add',
19
- bid,
20
- note: 'Dry run — re-run with confirm: true to add this beer to your wishlist.',
21
- });
22
- }
14
+ }, async ({ bid, confirmToken }, ctx) => {
15
+ const request = { method: 'GET', path: '/user/wishlist/add', query: { bid } };
16
+ const gate = await confirmWrite(ctx, {
17
+ tool: 'untappd_wishlist_add',
18
+ action: 'untappd.wishlist_add',
19
+ message: 'Review and confirm adding this beer to your Untappd wishlist:',
20
+ confirmToken,
21
+ target: bid,
22
+ payload: request,
23
+ preview: { action: 'wishlist_add', bid, ...request, note: 'Adds this beer to your Untappd wishlist.' },
24
+ });
25
+ if (gate)
26
+ return gate;
23
27
  const data = await client.write('GET', '/user/wishlist/add', { query: { bid } });
24
28
  return minifiedResult({ added: true, bid, result: data?.result });
25
29
  });
26
30
  server.registerTool('untappd_wishlist_remove', {
27
31
  title: 'Remove a beer from your wishlist',
28
- description: 'Remove a beer from YOUR Untappd wishlist by its bid. Without confirm: true it returns a dry-run preview ' +
29
- 'and makes NO network call; with confirm: true it removes. Writes to your account.',
32
+ description: `Remove a beer from YOUR Untappd wishlist by its bid. Writes to your account. ${CONFIRM_FLOW}`,
30
33
  annotations: toolAnnotations({ title: 'Remove a beer from your wishlist', readOnly: false, idempotent: true, openWorld: true, destructive: false }),
31
34
  inputSchema: z.object({
32
35
  bid: BidSchema,
33
- confirm: schemaConfirm,
36
+ confirmToken: confirmTokenParam,
34
37
  }),
35
- }, async ({ bid, confirm }) => {
36
- if (confirm !== true) {
37
- return minifiedResult({
38
- dryRun: true,
39
- action: 'wishlist_remove',
40
- bid,
41
- note: 'Dry run — re-run with confirm: true to remove this beer from your wishlist.',
42
- });
43
- }
38
+ }, async ({ bid, confirmToken }, ctx) => {
39
+ const request = { method: 'GET', path: '/user/wishlist/delete', query: { bid } };
40
+ const gate = await confirmWrite(ctx, {
41
+ tool: 'untappd_wishlist_remove',
42
+ action: 'untappd.wishlist_remove',
43
+ message: 'Review and confirm removing this beer from your Untappd wishlist:',
44
+ confirmToken,
45
+ target: bid,
46
+ payload: request,
47
+ preview: { action: 'wishlist_remove', bid, ...request, note: 'Removes this beer from your Untappd wishlist.' },
48
+ });
49
+ if (gate)
50
+ return gate;
44
51
  const data = await client.write('GET', '/user/wishlist/delete', { query: { bid } });
45
52
  return minifiedResult({ removed: true, bid, result: data?.result });
46
53
  });
package/dist/version.js CHANGED
@@ -3,4 +3,4 @@
3
3
  // json's `extra-files`), and `versionSyncTest` guards that it stays equal to
4
4
  // package.json. Import VERSION wherever the version is needed rather than
5
5
  // re-declaring it.
6
- export const VERSION = '2.1.2'; // x-release-please-version
6
+ export const VERSION = '2.2.0'; // x-release-please-version
package/mint.yaml CHANGED
@@ -26,6 +26,15 @@ env:
26
26
  required: true
27
27
  help: >-
28
28
  Untappd mobile app client secret.
29
+ - name: UNTAPPD_TIMEZONE
30
+ required: false
31
+ help: >-
32
+ IANA timezone (e.g. America/New_York) that check-ins are stamped with
33
+ when a call passes no timezone. Defaults to the server's own zone.
34
+ - name: UNTAPPD_PHOTO_DIR
35
+ required: false
36
+ help: >-
37
+ Restrict check-in photo uploads to files inside this directory.
29
38
  - name: UNTAPPD_CACHE_DB
30
39
  required: false
31
40
  help: >-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "untappd-mcp",
3
- "version": "2.1.2",
3
+ "version": "2.2.0",
4
4
  "mcpName": "io.github.chrischall/untappd-mcp",
5
5
  "description": "Untappd MCP server for Claude — developed and maintained by AI (Claude Code)",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
@@ -45,7 +45,7 @@
45
45
  "typecheck": "tsc -p tsconfig.json --noEmit"
46
46
  },
47
47
  "dependencies": {
48
- "@chrischall/mcp-utils": "^2.4.0",
48
+ "@chrischall/mcp-utils": "^2.6.0",
49
49
  "@modelcontextprotocol/server": "^2.0.0",
50
50
  "ai": "^7.0.19",
51
51
  "dotenv": "^18.0.1",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/untappd-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "2.1.2",
9
+ "version": "2.2.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "untappd-mcp",
14
- "version": "2.1.2",
14
+ "version": "2.2.0",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
@@ -109,9 +109,9 @@ one that exists.
109
109
  Each for its own reason — and none of them will tell you it ignored the
110
110
  parameter, because an undeclared key is dropped by zod without a warning:
111
111
 
112
- - **The 11 confirm-gated writes** (`untappd_checkin`, `untappd_toast`,
112
+ - **The 11 confirmation-gated writes** (`untappd_checkin`, `untappd_toast`,
113
113
  `untappd_add_comment`, the two deletes, the wishlist pair, the four friend
114
- actions) answer with a dry-run preview or a receipt. Nothing in a receipt is
114
+ actions) answer with a confirmation preview or a receipt. Nothing in a receipt is
115
115
  decoration.
116
116
  - **`untappd_sync_checkins` / `untappd_sync_user_beers`** answer with sync
117
117
  PROGRESS — pages walked, `another_run_needed`, `backfill_complete`. Slimming
@@ -140,10 +140,15 @@ parameter, because an undeclared key is dropped by zod without a warning:
140
140
  (#161) — it takes a `view` now, and its field shape is documented with the
141
141
  other projections above.
142
142
 
143
- ## Write tools (confirm-gated — these post to your public account)
143
+ ## Write tools (confirmation-gated — these post to your public account)
144
144
 
145
- Each returns a dry-run preview and makes NO network call unless called with
146
- `confirm: true`:
145
+ Each asks the user to confirm first. Where the client can show a confirmation
146
+ prompt, it does. Otherwise the first call makes NO network call and returns
147
+ `status: "confirmation-required"` with a `preview` of the exact request and a
148
+ `confirmToken`; show the preview to the user, and only after they approve call
149
+ the tool again with the same arguments plus `confirmToken`. A token works once
150
+ and only for those arguments (a change returns `DRAFT_CHANGED` with a fresh
151
+ preview). `MCP_CONFIRM_MODE` on the server can make this `auto` or `refuse`.
147
152
 
148
153
  - `untappd_toast` — toast/like a check-in (this endpoint is a toggle).
149
154
  - `untappd_add_comment` — comment on a check-in.