albert-heijn-mcp 1.3.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.
Files changed (71) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +376 -0
  3. package/assets/icon.png +0 -0
  4. package/deploy/albert-heijn-mcp.service +42 -0
  5. package/dist/ahapi/auth.js +18 -0
  6. package/dist/ahapi/auth.js.map +1 -0
  7. package/dist/ahapi/bonus.js +53 -0
  8. package/dist/ahapi/bonus.js.map +1 -0
  9. package/dist/ahapi/client.js +115 -0
  10. package/dist/ahapi/client.js.map +1 -0
  11. package/dist/ahapi/favorites.js +67 -0
  12. package/dist/ahapi/favorites.js.map +1 -0
  13. package/dist/ahapi/index.js +12 -0
  14. package/dist/ahapi/index.js.map +1 -0
  15. package/dist/ahapi/member.js +23 -0
  16. package/dist/ahapi/member.js.map +1 -0
  17. package/dist/ahapi/orders.js +85 -0
  18. package/dist/ahapi/orders.js.map +1 -0
  19. package/dist/ahapi/products.js +103 -0
  20. package/dist/ahapi/products.js.map +1 -0
  21. package/dist/ahapi/receipts.js +37 -0
  22. package/dist/ahapi/receipts.js.map +1 -0
  23. package/dist/ahapi/recipes.js +57 -0
  24. package/dist/ahapi/recipes.js.map +1 -0
  25. package/dist/ahapi/shoppingList.js +58 -0
  26. package/dist/ahapi/shoppingList.js.map +1 -0
  27. package/dist/ahapi/stores.js +35 -0
  28. package/dist/ahapi/stores.js.map +1 -0
  29. package/dist/auth/login.js +15 -0
  30. package/dist/auth/login.js.map +1 -0
  31. package/dist/auth/session.js +125 -0
  32. package/dist/auth/session.js.map +1 -0
  33. package/dist/auth/tokens.js +132 -0
  34. package/dist/auth/tokens.js.map +1 -0
  35. package/dist/config.js +61 -0
  36. package/dist/config.js.map +1 -0
  37. package/dist/index.js +70 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/log.js +70 -0
  40. package/dist/log.js.map +1 -0
  41. package/dist/server/http.js +99 -0
  42. package/dist/server/http.js.map +1 -0
  43. package/dist/tools/account.js +126 -0
  44. package/dist/tools/account.js.map +1 -0
  45. package/dist/tools/bonus.js +151 -0
  46. package/dist/tools/bonus.js.map +1 -0
  47. package/dist/tools/cache.js +44 -0
  48. package/dist/tools/cache.js.map +1 -0
  49. package/dist/tools/cart.js +127 -0
  50. package/dist/tools/cart.js.map +1 -0
  51. package/dist/tools/common.js +173 -0
  52. package/dist/tools/common.js.map +1 -0
  53. package/dist/tools/favorites.js +103 -0
  54. package/dist/tools/favorites.js.map +1 -0
  55. package/dist/tools/index.js +24 -0
  56. package/dist/tools/index.js.map +1 -0
  57. package/dist/tools/orders.js +141 -0
  58. package/dist/tools/orders.js.map +1 -0
  59. package/dist/tools/products.js +165 -0
  60. package/dist/tools/products.js.map +1 -0
  61. package/dist/tools/receipts.js +52 -0
  62. package/dist/tools/receipts.js.map +1 -0
  63. package/dist/tools/recipes.js +261 -0
  64. package/dist/tools/recipes.js.map +1 -0
  65. package/dist/tools/shoppingList.js +149 -0
  66. package/dist/tools/shoppingList.js.map +1 -0
  67. package/dist/tools/stores.js +108 -0
  68. package/dist/tools/stores.js.map +1 -0
  69. package/dist/tools/views.js +100 -0
  70. package/dist/tools/views.js.map +1 -0
  71. package/package.json +53 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"products.js","sourceRoot":"","sources":["../../src/tools/products.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { z } from "zod";
2
+ import { getReceipt, getReceipts } from "../ahapi/index.js";
3
+ import { addAuthedTool, formatDate, orDefault, structured } from "./common.js";
4
+ export function registerReceiptTools(server, ctx) {
5
+ addAuthedTool(server, ctx, {
6
+ name: "ah_get_receipts",
7
+ title: "Albert Heijn: Receipts",
8
+ kind: "readOnly",
9
+ output: { receipts: z.array(z.object({ id: z.string(), date: z.string(), total_amount: z.number() })) },
10
+ description: "List recent Albert Heijn in-store receipts (kassabonnen). " +
11
+ "Returns receipt id, date, and total amount. " +
12
+ "Use ah_get_receipt_details with the id to see individual items.",
13
+ input: { limit: z.number().int().optional().describe("Maximum number of results to return (default 10)") },
14
+ }, async (c, { limit }) => {
15
+ const receipts = await getReceipts(c, Math.min(orDefault(limit, 10), 50));
16
+ return structured({
17
+ receipts: receipts.map((r) => ({ id: r.id, date: formatDate(r.dateTime, true), total_amount: r.total })),
18
+ });
19
+ });
20
+ addAuthedTool(server, ctx, {
21
+ name: "ah_get_receipt_details",
22
+ title: "Albert Heijn: Receipt Details",
23
+ kind: "readOnly",
24
+ output: {
25
+ id: z.string(),
26
+ items: z.array(z.object({ name: z.string(), quantity: z.number().optional(), unit_price: z.number().optional(), total: z.number() })),
27
+ discounts: z.array(z.object({ name: z.string(), amount: z.number() })).optional(),
28
+ payments: z.array(z.object({ method: z.string(), amount: z.number() })).optional(),
29
+ },
30
+ description: "Get full details of a single Albert Heijn in-store receipt (kassabon) by its id. " +
31
+ "Returns all purchased items with name, quantity, unit price and line total, " +
32
+ "plus any discounts and payment method. " +
33
+ "Get the id from ah_get_receipts first.",
34
+ input: { id: z.string().describe("Receipt transaction ID from ah_get_receipts") },
35
+ }, async (c, { id }) => {
36
+ if (!id)
37
+ throw new Error("id is required");
38
+ const r = await getReceipt(c, id);
39
+ return structured({
40
+ id: r.id,
41
+ items: r.items.map((it) => ({
42
+ name: it.name,
43
+ quantity: it.quantity || undefined,
44
+ unit_price: it.unitPrice || undefined,
45
+ total: it.total,
46
+ })),
47
+ discounts: r.discounts.length ? r.discounts : undefined,
48
+ payments: r.payments.length ? r.payments : undefined,
49
+ });
50
+ });
51
+ }
52
+ //# sourceMappingURL=receipts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipts.js","sourceRoot":"","sources":["../../src/tools/receipts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,261 @@
1
+ import { z } from "zod";
2
+ import { addProductsToShoppingList, getRecipe, recipeUrl, searchRecipes } from "../ahapi/index.js";
3
+ import { addAuthedTool, nonFatal, orDefault, parallel, structured, wrapError } from "./common.js";
4
+ import { search } from "./products.js";
5
+ import { listCall } from "./shoppingList.js";
6
+ import { productSummary, summarizeProduct } from "./views.js";
7
+ const num = z.number().optional();
8
+ const str = z.string().optional();
9
+ const strs = z.array(z.string()).optional();
10
+ /** Words that mark an ingredient nobody buys: "kokend water", "500 ml water", not "waterkers" or "kokoswater". */
11
+ const TAP_WATER = new Set(["water", "kraanwater", "ijswater"]);
12
+ /** Search results scored per ingredient: AH ranks flavoured products (apple yoghurt) above plain ones (apples). */
13
+ const CANDIDATES = 20;
14
+ /** Words that say nothing about what the product is: labels, packaging and filler. */
15
+ const NEUTRAL = new Set(["ah", "biologisch", "biologische", "bio", "excellent", "basic", "terra", "vers", "verse", "stuk", "stuks", "per"].concat(["uit", "in", "met", "van", "en", "of", "de", "het", "een"], ["blik", "pot", "potje", "pak", "zak", "fles", "schaal", "net", "doos", "doosje", "bak", "bakje", "tros"]));
16
+ /** Product types that make a title a flavoured product ("knijpyoghurt appel", "crackers zout") unless the ingredient names them. */
17
+ const PRODUCT_TYPES = ["yoghurt", "kwark", "vla", "toetje", "sap", "drank", "limonade", "smoothie", "thee"].concat(["crackers", "chips", "pretzels", "popcorn", "koek", "koekjes", "wafels", "reep", "snoep", "zoutjes", "ijs"]);
18
+ /** Dutch adjective forms: "rode paprika" is AH's "Paprika rood". */
19
+ const BASE = { rode: "rood", gele: "geel", groene: "groen", witte: "wit", zwarte: "zwart", zoete: "zoet" };
20
+ /** Lowercase words without accents or neutral ones, e.g. for a search query. */
21
+ const plainWords = (s) => s
22
+ .normalize("NFKD")
23
+ .replace(/[\u0300-\u036f]/g, "")
24
+ .toLowerCase()
25
+ .split(/[^a-z]+/)
26
+ .filter((w) => w.length > 1 && !NEUTRAL.has(w));
27
+ /** plainWords with adjectives in their base form, for comparing. */
28
+ const words = (s) => plainWords(s).map((w) => BASE[w] ?? w);
29
+ /** Regular plural and diminutive endings: "uien", "appeltjes". Irregular ones like "eieren" are not covered. */
30
+ const ENDINGS = ["s", "en", "n", "tje", "tjes"];
31
+ const sameWord = (a, b) => a === b || ENDINGS.some((e) => b === a + e || a === b + e);
32
+ /**
33
+ * How well a product title names an ingredient: matched ingredient words, minus 0.1 per other title
34
+ * word, which ready meals and mixes have many of, and 0.3 more per product type (yoghurt, crackers) the
35
+ * ingredient doesn't name. A word or its plural counts 1. Dutch compounds end in
36
+ * their noun, so a shared end counts 0.75 ("tarwebloem" is flour) and a shared start only 0.25
37
+ * ("bloemkool" is not); both need 4+ letters in common.
38
+ */
39
+ function matchScore(ingredient, title) {
40
+ const want = words(ingredient);
41
+ const have = words(title);
42
+ if (want.length === 0)
43
+ return 0;
44
+ const used = new Set();
45
+ let matched = 0;
46
+ for (const w of want) {
47
+ let best = 0;
48
+ let bestWord = "";
49
+ for (const t of have) {
50
+ const long = Math.min(t.length, w.length) >= 4;
51
+ const score = sameWord(w, t)
52
+ ? 1
53
+ : long && (t.endsWith(w) || w.endsWith(t))
54
+ ? 0.75
55
+ : long && (t.startsWith(w) || w.startsWith(t))
56
+ ? 0.25
57
+ : 0;
58
+ if (score > best)
59
+ [best, bestWord] = [score, t];
60
+ }
61
+ matched += best;
62
+ if (bestWord)
63
+ used.add(bestWord);
64
+ }
65
+ if (matched === 0)
66
+ return 0;
67
+ const extra = have.filter((t) => !used.has(t)).length;
68
+ // Matched words count too: "zoutjes" (a snack) is not "zout".
69
+ const flavoured = have.filter((t) => !want.includes(t) && PRODUCT_TYPES.some((type) => t.endsWith(type))).length;
70
+ return matched / want.length - 0.1 * extra - 0.3 * flavoured;
71
+ }
72
+ /** The best-named product for an ingredient; among equally good ones, one on bonus if preferBonus. */
73
+ function pickProduct(ingredient, products, preferBonus) {
74
+ const scored = products.map((p) => ({ p, score: matchScore(ingredient, p.title) })).filter((x) => x.score > 0);
75
+ if (scored.length === 0)
76
+ return undefined;
77
+ const best = Math.max(...scored.map((x) => x.score));
78
+ const top = scored.filter((x) => x.score === best).map((x) => x.p);
79
+ return (preferBonus && top.find((p) => p.isBonus)) || top[0];
80
+ }
81
+ export function registerRecipeTools(server, ctx) {
82
+ addAuthedTool(server, ctx, {
83
+ name: "ah_search_recipes",
84
+ title: "Albert Heijn: Search Recipes",
85
+ kind: "readOnly",
86
+ description: "Search Allerhande, Albert Heijn's recipe collection. Dutch search terms work best, " +
87
+ "e.g. 'pasta', 'vegetarisch', 'stamppot', 'kip curry'. " +
88
+ "Returns id, title, url, cook_minutes, servings, courses, nutri_score, rating. " +
89
+ "Use the id with ah_get_recipe for ingredients and steps.",
90
+ input: {
91
+ query: z.string().describe("Search text, e.g. 'pasta zalm'"),
92
+ limit: z.number().int().optional().describe("Maximum number of results (default 10, max 30)"),
93
+ },
94
+ output: {
95
+ total: z.number(),
96
+ recipes: z.array(z.object({
97
+ id: z.number(),
98
+ title: z.string(),
99
+ url: z.string(),
100
+ cook_minutes: num,
101
+ oven_minutes: num,
102
+ wait_minutes: num,
103
+ servings: str,
104
+ courses: strs,
105
+ nutri_score: str,
106
+ rating: str,
107
+ })),
108
+ },
109
+ }, async (c, args) => {
110
+ if (!args.query.trim())
111
+ throw new Error("query is required");
112
+ const { total, recipes } = await searchRecipes(c, args.query, Math.min(orDefault(args.limit, 10), 30));
113
+ const message = recipes.length ? undefined : `No recipes found for "${args.query}".`;
114
+ return structured({
115
+ total,
116
+ recipes: recipes.map((r) => ({
117
+ id: r.id,
118
+ title: r.title,
119
+ url: recipeUrl(r.id, r.slug),
120
+ cook_minutes: r.time?.cook || undefined,
121
+ oven_minutes: r.time?.oven || undefined,
122
+ wait_minutes: r.time?.wait || undefined,
123
+ servings: r.serving ? `${r.serving.number} ${r.serving.type}` : undefined,
124
+ courses: r.courses?.length ? r.courses : undefined,
125
+ nutri_score: r.nutriScore || undefined,
126
+ rating: rating(r.rating),
127
+ })),
128
+ }, message);
129
+ });
130
+ addAuthedTool(server, ctx, {
131
+ name: "ah_get_recipe",
132
+ title: "Albert Heijn: Recipe",
133
+ kind: "readOnly",
134
+ description: "Get an Allerhande recipe by id: ingredients, preparation steps, and nutrition per serving. " +
135
+ "Set servings to scale the ingredient quantities. " +
136
+ "To shop for it, search the ingredients with ah_search_products and add them with ah_add_to_shopping_list.",
137
+ input: {
138
+ recipe_id: z.number().int().describe("Recipe id from ah_search_recipes"),
139
+ servings: z.number().int().optional().describe("Number of servings to scale the ingredients to"),
140
+ },
141
+ output: {
142
+ id: z.number(),
143
+ title: z.string(),
144
+ url: z.string(),
145
+ description: str,
146
+ cook_minutes: num,
147
+ servings: z.string(),
148
+ courses: strs,
149
+ cuisines: strs,
150
+ nutri_score: str,
151
+ rating: str,
152
+ ingredients: z.array(z.string()),
153
+ steps: z.array(z.string()),
154
+ nutrition_per_serving: z.record(z.string(), z.string()).optional(),
155
+ },
156
+ }, async (c, args) => {
157
+ if (args.recipe_id <= 0)
158
+ throw new Error("recipe_id is required");
159
+ const servings = args.servings && args.servings > 0 ? args.servings : undefined;
160
+ const r = await getRecipe(c, args.recipe_id, servings).catch((err) => {
161
+ // AH redacts the reason, which is usually an unknown id. The cause keeps a 401 visible.
162
+ throw wrapError(`could not get recipe ${args.recipe_id}; check the id`, err);
163
+ });
164
+ const n = r.nutritions;
165
+ return structured({
166
+ id: r.id,
167
+ title: r.title,
168
+ url: `https://www.ah.nl${r.href}`,
169
+ description: r.description || undefined,
170
+ cook_minutes: r.cookTime || undefined,
171
+ servings: `${servings ?? r.servings.number} ${r.servings.type}`,
172
+ courses: r.courses?.length ? r.courses : undefined,
173
+ cuisines: r.cuisines?.length ? r.cuisines : undefined,
174
+ nutri_score: r.nutriScore || undefined,
175
+ rating: rating(r.rating),
176
+ ingredients: r.ingredients.map((i) => i.text),
177
+ steps: r.preparation?.steps ?? [],
178
+ nutrition_per_serving: n
179
+ ? Object.fromEntries(Object.values(n)
180
+ .filter((x) => x != null)
181
+ .map((x) => [x.name, `${x.value} ${x.unit}`]))
182
+ : undefined,
183
+ });
184
+ });
185
+ addAuthedTool(server, ctx, {
186
+ name: "ah_add_recipe_to_shopping_list",
187
+ title: "Albert Heijn: Add Recipe to Shopping List",
188
+ kind: "additive",
189
+ description: "Put the ingredients of an Allerhande recipe on the shopping list in one step. " +
190
+ "Each ingredient is matched to the AH product whose name fits it best (among equally good matches, " +
191
+ "one on bonus unless prefer_bonus=false); " +
192
+ "one unit of each product is added. Water is always skipped; pass skip for anything the user already has " +
193
+ "(e.g. olive oil, salt). Set dry_run=true to show the matches without adding them. " +
194
+ "Returns the added, skipped and unmatched ingredients, and any whose search failed (worth retrying).",
195
+ input: {
196
+ recipe_id: z.number().int().describe("Recipe id from ah_search_recipes"),
197
+ servings: z.number().int().optional().describe("Number of servings, as in ah_get_recipe"),
198
+ skip: z.array(z.string()).optional().describe('Ingredients to leave out, matched by name, e.g. ["olijfolie", "zout"]'),
199
+ prefer_bonus: z.boolean().optional().describe("Prefer a matching product on bonus (default true)"),
200
+ dry_run: z.boolean().optional().describe("Only show the matches; don't add anything"),
201
+ },
202
+ output: {
203
+ recipe: z.string(),
204
+ added: z.array(z.object({ ingredient: z.string(), product: productSummary })),
205
+ skipped: z.array(z.string()),
206
+ not_found: z.array(z.string()),
207
+ /** Searches that failed (e.g. AH unavailable); unlike not_found, these may exist. */
208
+ failed: z.array(z.string()),
209
+ dry_run: z.boolean(),
210
+ },
211
+ }, async (c, args) => {
212
+ if (args.recipe_id <= 0)
213
+ throw new Error("recipe_id is required");
214
+ const servings = args.servings && args.servings > 0 ? args.servings : undefined;
215
+ const recipe = await getRecipe(c, args.recipe_id, servings).catch((err) => {
216
+ throw wrapError(`could not get recipe ${args.recipe_id}; check the id`, err);
217
+ });
218
+ // A skip entry matches an ingredient containing all its words, so "zout" skips "zout en peper" but not "ongezouten boter".
219
+ const skips = (args.skip ?? []).map(words).filter((w) => w.length > 0);
220
+ const isSkipped = (name) => {
221
+ const have = words(name);
222
+ return have.some((w) => TAP_WATER.has(w)) || skips.some((s) => s.every((w) => have.some((t) => sameWord(w, t))));
223
+ };
224
+ const wanted = recipe.ingredients.filter((i) => !isSkipped(i.name));
225
+ const matches = new Array(wanted.length);
226
+ const errors = new Array(wanted.length);
227
+ await parallel(wanted.length, 3, async (i) => {
228
+ const name = wanted[i].name;
229
+ // Packaging and filler words ("uit blik") only confuse AH's search.
230
+ const query = plainWords(name).join(" ") || name;
231
+ const products = await nonFatal(() => search(ctx, c, query, CANDIDATES, { sort: "RELEVANCE" }), (err) => {
232
+ errors[i] = err;
233
+ return [];
234
+ });
235
+ matches[i] = pickProduct(name, products, args.prefer_bonus !== false);
236
+ });
237
+ if (wanted.length > 0 && errors.filter(Boolean).length === wanted.length) {
238
+ throw wrapError("could not search for the ingredients", errors[0]);
239
+ }
240
+ const added = wanted.flatMap((ing, i) => {
241
+ const p = matches[i];
242
+ return p ? [{ ingredient: ing.text, product: summarizeProduct(p) }] : [];
243
+ });
244
+ const quantities = new Map(added.map((a) => [a.product.id, 1]));
245
+ if (!args.dry_run && quantities.size > 0) {
246
+ await listCall("failed to add the ingredients", () => addProductsToShoppingList(c, quantities));
247
+ }
248
+ return structured({
249
+ recipe: recipe.title,
250
+ added,
251
+ skipped: recipe.ingredients.filter((i) => isSkipped(i.name)).map((i) => i.text),
252
+ not_found: wanted.filter((_, i) => !matches[i] && !errors[i]).map((i) => i.text),
253
+ failed: wanted.filter((_, i) => errors[i]).map((i) => i.text),
254
+ dry_run: Boolean(args.dry_run),
255
+ });
256
+ });
257
+ }
258
+ function rating(r) {
259
+ return r?.average && r.count ? `${r.average.toFixed(1)}/5 (${r.count})` : undefined;
260
+ }
261
+ //# sourceMappingURL=recipes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipes.js","sourceRoot":"","sources":["../../src/tools/recipes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,149 @@
1
+ import { z } from "zod";
2
+ import { addProductsToShoppingList, addTextToShoppingList, getShoppingList, isOrderMode, productsByIds, productUrl, removeFromShoppingList, } from "../ahapi/index.js";
3
+ import { addAuthedTool, structured, text, wrapError } from "./common.js";
4
+ /** Replaces AH's "Server in order mode" error. */
5
+ const ORDER_MODE_MESSAGE = "the shopping list is not available while a delivery order is active: " +
6
+ "AH moved its products into the order when the delivery slot was chosen. " +
7
+ "Use ah_get_cart, ah_update_cart_item and ah_remove_from_cart to view or change the order";
8
+ /** Runs a shopping-list call; errors are prefixed with action, or replaced with ORDER_MODE_MESSAGE. */
9
+ export async function listCall(action, fn) {
10
+ try {
11
+ return await fn();
12
+ }
13
+ catch (err) {
14
+ throw isOrderMode(err) ? new Error(ORDER_MODE_MESSAGE) : wrapError(action, err);
15
+ }
16
+ }
17
+ export const confirmInput = { confirm: z.string().describe('Must be "yes" to confirm') };
18
+ export function registerShoppingListTools(server, ctx) {
19
+ addAuthedTool(server, ctx, {
20
+ name: "ah_get_shopping_list",
21
+ title: "Albert Heijn: View Shopping List",
22
+ kind: "readOnly",
23
+ output: {
24
+ items: z.array(z.object({
25
+ position: z.number(),
26
+ name: z.string(),
27
+ product_id: z.number().optional(),
28
+ url: z.string().optional(),
29
+ quantity: z.number(),
30
+ checked: z.boolean().optional(),
31
+ })),
32
+ },
33
+ description: "Get the contents of the Albert Heijn shopping list (boodschappenlijst). " +
34
+ "In the AH app this is the basket the user fills while shopping; choosing a delivery slot moves its products " +
35
+ "into an order, after which the list is unavailable until the order is done (use ah_get_cart then). " +
36
+ "Returns item names, quantities, and product IDs.",
37
+ }, async (c) => {
38
+ const items = await listCall("failed to get shopping list", () => getShoppingList(c));
39
+ return structured({
40
+ items: items.map((it) => ({
41
+ position: it.position,
42
+ name: it.name,
43
+ product_id: it.productId || undefined,
44
+ url: it.productId ? productUrl(it.productId, it.name) : undefined,
45
+ quantity: it.quantity,
46
+ checked: it.checked || undefined,
47
+ })),
48
+ }, items.length ? undefined : "Your shopping list is empty.");
49
+ });
50
+ addAuthedTool(server, ctx, {
51
+ name: "ah_add_to_shopping_list",
52
+ title: "Albert Heijn: Add to Shopping List",
53
+ kind: "additive",
54
+ description: "Add one or more products to your Albert Heijn shopping list. " +
55
+ "Pass an array of items, each with product_id (int) and quantity (int). " +
56
+ "Returns confirmation listing the names of successfully added products.",
57
+ input: {
58
+ items: z
59
+ .array(z.object({
60
+ product_id: z.number().int().describe("numeric product ID"),
61
+ quantity: z.number().int().describe("number of units"),
62
+ }))
63
+ .describe('Items to add, e.g. [{"product_id": 123456, "quantity": 2}]'),
64
+ },
65
+ }, async (c, { items }) => {
66
+ const quantities = new Map();
67
+ for (const it of items) {
68
+ if (it.product_id > 0 && it.quantity > 0) {
69
+ quantities.set(it.product_id, (quantities.get(it.product_id) ?? 0) + it.quantity);
70
+ }
71
+ }
72
+ if (quantities.size === 0) {
73
+ throw new Error("no valid items provided (each item needs product_id > 0 and quantity > 0)");
74
+ }
75
+ await listCall("failed to add items", () => addProductsToShoppingList(c, quantities));
76
+ // Product names for the confirmation; IDs if the lookup fails.
77
+ const names = new Map();
78
+ try {
79
+ for (const p of await productsByIds(c, [...quantities.keys()]))
80
+ names.set(p.webshopId, p.title);
81
+ }
82
+ catch {
83
+ // Names are cosmetic.
84
+ }
85
+ const lines = [...quantities].map(([id, qty]) => `${names.get(id) ?? `Product ${id}`} (x${qty})`);
86
+ return text(`Added to shopping list:\n- ${lines.join("\n- ")}`);
87
+ });
88
+ addAuthedTool(server, ctx, {
89
+ name: "ah_add_free_text_to_shopping_list",
90
+ title: "Albert Heijn: Add Free-Text to Shopping List",
91
+ kind: "additive",
92
+ description: "Add a free-text item to the Albert Heijn shopping list (no product ID needed). " +
93
+ "Use for reminders like 'verse bloemen', 'any good wine', or items not found in search. " +
94
+ "Free-text items have no quantity: put amounts in the text, e.g. '2 bossen bloemen'.",
95
+ input: {
96
+ name: z.string().describe("Free-text item description, e.g. 'verse bloemen', 'goede rode wijn'"),
97
+ },
98
+ }, async (c, args) => {
99
+ if (!args.name)
100
+ throw new Error("name is required");
101
+ await listCall("failed to add free-text item", () => addTextToShoppingList(c, args.name));
102
+ return text(`Added '${args.name}' to shopping list.`);
103
+ });
104
+ addAuthedTool(server, ctx, {
105
+ name: "ah_remove_from_shopping_list",
106
+ title: "Albert Heijn: Remove from Shopping List",
107
+ kind: "destructive",
108
+ description: "Remove one or more items from the Albert Heijn Boodschappenlijst. " +
109
+ "For product items pass product_ids; for free-text items pass names. " +
110
+ "Get product_ids from ah_get_shopping_list.",
111
+ input: {
112
+ product_ids: z
113
+ .array(z.number().int())
114
+ .optional()
115
+ .describe("Product IDs to remove, e.g. [123456, 789012]. Use for product items."),
116
+ names: z
117
+ .array(z.string())
118
+ .optional()
119
+ .describe('Free-text item names to remove, e.g. ["verse bloemen"]. Use for items without a product ID.'),
120
+ },
121
+ }, async (c, args) => {
122
+ const ids = new Set((args.product_ids ?? []).filter((id) => id > 0));
123
+ const names = new Set((args.names ?? []).filter(Boolean).map((n) => n.toLowerCase()));
124
+ if (ids.size === 0 && names.size === 0)
125
+ throw new Error("provide at least one product_id or name to remove");
126
+ const items = await listCall("failed to read shopping list", () => getShoppingList(c));
127
+ const matches = items.filter((it) => ids.has(it.productId) || (it.productId === 0 && names.has(it.name.toLowerCase())));
128
+ if (matches.length === 0)
129
+ throw new Error("no matching items found on the shopping list");
130
+ await listCall("failed to update shopping list", () => removeFromShoppingList(c, matches));
131
+ return text(`Removed from shopping list: ${matches.map((it) => it.name).join(", ")}`);
132
+ });
133
+ addAuthedTool(server, ctx, {
134
+ name: "ah_clear_shopping_list",
135
+ title: "Albert Heijn: Clear Shopping List",
136
+ kind: "destructive",
137
+ description: 'Remove ALL items from the Albert Heijn shopping list. Irreversible — requires confirm="yes".',
138
+ input: confirmInput,
139
+ }, async (c, { confirm }) => {
140
+ if (confirm !== "yes")
141
+ throw new Error('confirm must be "yes" to clear the shopping list');
142
+ const items = await listCall("failed to read shopping list", () => getShoppingList(c));
143
+ if (items.length === 0)
144
+ return text("Shopping list is already empty.");
145
+ await listCall("failed to clear shopping list", () => removeFromShoppingList(c, items));
146
+ return text("Shopping list cleared.");
147
+ });
148
+ }
149
+ //# sourceMappingURL=shoppingList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shoppingList.js","sourceRoot":"","sources":["../../src/tools/shoppingList.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ import { z } from "zod";
2
+ import { getBargains, productUrl, searchStores } from "../ahapi/index.js";
3
+ import { addAuthedTool, cachedMember, formatDate, orDefault, structured, wrapError } from "./common.js";
4
+ const str = z.string().optional();
5
+ export function registerStoreTools(server, ctx) {
6
+ addAuthedTool(server, ctx, {
7
+ name: "ah_search_stores",
8
+ title: "Albert Heijn: Search Stores",
9
+ kind: "readOnly",
10
+ output: {
11
+ stores: z.array(z.object({ id: z.number(), name: z.string(), type: str, street: str, city: str, postal_code: str })),
12
+ },
13
+ description: "Find Albert Heijn stores near a Dutch postal code. " +
14
+ "If no postal_code is given, automatically uses the address from the member profile. " +
15
+ "Returns store id (use this for ah_get_last_chance_items), name, type, and address.",
16
+ input: {
17
+ postal_code: z
18
+ .string()
19
+ .optional()
20
+ .describe("Dutch postal code, e.g. '1234AB'. Optional — falls back to member address."),
21
+ },
22
+ }, async (c, { postal_code }) => {
23
+ const postalCode = postal_code || (await memberPostalCode(ctx, c));
24
+ if (!postalCode)
25
+ throw new Error("no postal_code provided and member profile has no address on file");
26
+ const stores = await searchStores(c, postalCode);
27
+ return structured({
28
+ stores: stores.map((s) => ({
29
+ id: s.id,
30
+ name: s.name,
31
+ type: s.storeType || undefined,
32
+ street: [s.address?.street, s.address?.houseNumber].filter(Boolean).join(" ") || undefined,
33
+ city: s.address?.city || undefined,
34
+ postal_code: s.address?.postalCode || undefined,
35
+ })),
36
+ }, stores.length ? undefined : `No AH stores found near ${postalCode}.`);
37
+ });
38
+ addAuthedTool(server, ctx, {
39
+ name: "ah_get_last_chance_items",
40
+ title: "Albert Heijn: Last-Chance Items",
41
+ kind: "readOnly",
42
+ output: {
43
+ items: z.array(z.object({
44
+ id: z.number(),
45
+ title: z.string(),
46
+ url: z.string(),
47
+ brand: str,
48
+ category: str,
49
+ markdown_type: str,
50
+ discount_percentage: z.number().optional(),
51
+ expiration_date: str,
52
+ stock: z.number().optional(),
53
+ price_was: str,
54
+ price_now: str,
55
+ })),
56
+ },
57
+ description: "Get last-chance / vandaag-af / clearance items from an Albert Heijn store. " +
58
+ "Requires a store_id (use ah_search_stores to find stores, or provide postal_code to find the nearest store). " +
59
+ "Uses the dedicated bargainItems GraphQL endpoint which returns today-only markdown deals.",
60
+ input: {
61
+ limit: z.number().int().optional().describe("Maximum number of results to return (default 20)"),
62
+ store_id: z.number().int().optional().describe("AH store ID. Required to retrieve bargain items."),
63
+ postal_code: z
64
+ .string()
65
+ .optional()
66
+ .describe("Dutch postal code (e.g. '1234AB') to find the nearest store when store_id is not known."),
67
+ },
68
+ }, async (c, args) => {
69
+ const storeId = args.store_id || (await nearestStoreId(ctx, c, args.postal_code));
70
+ if (!storeId) {
71
+ throw new Error("cannot retrieve last-chance items without a store. Please provide store_id or postal_code. " +
72
+ 'Example: {"store_id": 1234} or {"postal_code": "1011AB"}');
73
+ }
74
+ const bargains = (await getBargains(c, storeId)).slice(0, orDefault(args.limit, 20));
75
+ return structured({
76
+ items: bargains.map((b) => ({
77
+ id: b.productId,
78
+ title: b.title,
79
+ url: productUrl(b.productId, b.title),
80
+ brand: b.brand || undefined,
81
+ category: b.category || undefined,
82
+ markdown_type: b.markdownType || undefined,
83
+ discount_percentage: b.discount || undefined,
84
+ expiration_date: formatDate(b.expirationDate, false) || undefined,
85
+ stock: b.stock || undefined,
86
+ price_was: b.priceWas || undefined,
87
+ price_now: b.priceNow ?? undefined,
88
+ })),
89
+ });
90
+ });
91
+ }
92
+ /** The member's postal code, or "" if none is on file. */
93
+ async function memberPostalCode(ctx, c) {
94
+ try {
95
+ return (await cachedMember(ctx, c)).postalCode;
96
+ }
97
+ catch (err) {
98
+ throw wrapError("no postal_code provided and could not fetch member address", err);
99
+ }
100
+ }
101
+ /** Nearest store to postalCode (default: member address), or 0 if there is no address or store. */
102
+ async function nearestStoreId(ctx, c, postalCode) {
103
+ const code = postalCode || (await memberPostalCode(ctx, c));
104
+ if (!code)
105
+ return 0;
106
+ return (await searchStores(c, code))[0]?.id ?? 0;
107
+ }
108
+ //# sourceMappingURL=stores.js.map