picnic-mcp 0.3.0 → 0.5.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 (44) hide show
  1. package/README.md +40 -15
  2. package/dist/cli.js +51 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/errors.d.ts +8 -0
  5. package/dist/errors.js +16 -0
  6. package/dist/errors.js.map +1 -1
  7. package/dist/format/deliveries.d.ts +3 -2
  8. package/dist/format/deliveries.js +5 -4
  9. package/dist/format/deliveries.js.map +1 -1
  10. package/dist/http.d.ts +34 -0
  11. package/dist/http.js +113 -0
  12. package/dist/http.js.map +1 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +1 -0
  15. package/dist/index.js.map +1 -1
  16. package/dist/recipe-link.d.ts +7 -0
  17. package/dist/recipe-link.js +55 -0
  18. package/dist/recipe-link.js.map +1 -0
  19. package/dist/tools/cart.d.ts +1 -1
  20. package/dist/tools/cart.js +12 -6
  21. package/dist/tools/cart.js.map +1 -1
  22. package/dist/tools/context.d.ts +5 -0
  23. package/dist/tools/context.js +9 -0
  24. package/dist/tools/context.js.map +1 -1
  25. package/dist/tools/deliveries.js +22 -10
  26. package/dist/tools/deliveries.js.map +1 -1
  27. package/dist/tools/groups.js +1 -1
  28. package/dist/tools/groups.js.map +1 -1
  29. package/dist/tools/product.d.ts +1 -1
  30. package/dist/tools/product.js +5 -1
  31. package/dist/tools/product.js.map +1 -1
  32. package/dist/tools/recipes.js +21 -8
  33. package/dist/tools/recipes.js.map +1 -1
  34. package/dist/tools/replacements.d.ts +6 -2
  35. package/dist/tools/replacements.js +14 -7
  36. package/dist/tools/replacements.js.map +1 -1
  37. package/dist/tools/result.d.ts +17 -4
  38. package/dist/tools/result.js +22 -7
  39. package/dist/tools/result.js.map +1 -1
  40. package/dist/tools/search-products.js +12 -8
  41. package/dist/tools/search-products.js.map +1 -1
  42. package/dist/tools/slots.js +1 -1
  43. package/dist/tools/slots.js.map +1 -1
  44. package/package.json +2 -2
package/README.md CHANGED
@@ -8,20 +8,20 @@ Unofficial: not made or supported by Picnic. Built on [picnic-api](https://githu
8
8
 
9
9
  The tools come in groups. `cart`, `slots` and `product` are on by default; turn groups on or off with `PICNIC_MCP_TOOLS` (see Setup).
10
10
 
11
- | Group | Tool | What it does |
12
- | ------------ | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
13
- | `cart` | `search_products` | Searches products. Returns a `bestMatch` when one product fits (usually one you bought before), or 2 or 3 `choices` to ask you about. |
14
- | | `add_to_cart` | Adds a product by its id. Returns the quantity in the cart and the cart total, and replacements when the product can't be delivered. |
15
- | | `remove_from_cart` | Removes a product by its id. |
16
- | | `get_cart` | Shows the cart: products, quantities, prices, total and delivery window, and replacements for products that can't be delivered. |
17
- | `slots` | `get_delivery_slots` | Lists the delivery slots, two days at a time or for one date. |
18
- | | `select_delivery_slot` | Chooses the delivery slot of the cart. |
19
- | `product` | `get_product` | Shows a product's ingredients, allergens, nutrition, storage info, promotion and price. |
20
- | `deliveries` | `get_deliveries` | Shows upcoming deliveries: window, number of products, total, and the expected arrival while the driver is on the way. Optionally the products. |
21
- | `recipes` | `search_recipes` | Searches Picnic's recipes. |
22
- | | `get_recipe` | Shows a recipe's ingredients, allergens and steps. |
23
- | | `add_recipe_to_cart` | Adds a recipe's ingredients to the cart for a number of portions. |
24
- | | `remove_recipe_from_cart` | Removes a recipe and its ingredients from the cart. |
11
+ | Group | Tool | What it does |
12
+ | ------------ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
13
+ | `cart` | `search_products` | Searches products. Returns a `bestMatch` when one product fits (usually one you bought before), or 2 or 3 `choices` to ask you about. |
14
+ | | `add_to_cart` | Adds a product by its id. Returns the quantity in the cart and the cart total, and replacements when the product can't be delivered. |
15
+ | | `remove_from_cart` | Removes a product by its id. |
16
+ | | `get_cart` | Shows the cart: products, quantities, prices, total and delivery window, and replacements for products that can't be delivered. |
17
+ | `slots` | `get_delivery_slots` | Lists the delivery slots, two days at a time or for one date. |
18
+ | | `select_delivery_slot` | Chooses the delivery slot of the cart. |
19
+ | `product` | `get_product` | Shows a product's ingredients, allergens, nutrition, storage info, promotion and price. |
20
+ | `deliveries` | `get_deliveries` | Shows upcoming deliveries (window, number of products, total, and the expected arrival while the driver is on the way) or the last past ones. Optionally the products, e.g. to order the same again. |
21
+ | `recipes` | `search_recipes` | Searches Picnic's recipes. |
22
+ | | `get_recipe` | Shows a recipe's ingredients, allergens and steps. The recipe tools also take a recipe link shared from the Picnic app. |
23
+ | | `add_recipe_to_cart` | Adds a recipe's ingredients to the cart for a number of portions. |
24
+ | | `remove_recipe_from_cart` | Removes a recipe and its ingredients from the cart. |
25
25
 
26
26
  Output is compact JSON with only what a model needs. It leaves out your name, address, payment details, order numbers and other account details. Keep in mind that whatever the tools return is sent to the model your MCP client uses.
27
27
 
@@ -56,10 +56,31 @@ Most clients take a configuration like this:
56
56
  }
57
57
  ```
58
58
 
59
- `PICNIC_COUNTRY_CODE` is `NL`, `DE` or `FR` (default `NL`). To choose the tool groups, add `PICNIC_MCP_TOOLS` with a comma-separated list, e.g. `"cart,slots,product,deliveries,recipes"` (default `cart,slots,product`). The server runs over stdio.
59
+ `PICNIC_COUNTRY_CODE` is `NL`, `DE` or `FR` (default `NL`). To choose the tool groups, add `PICNIC_MCP_TOOLS` with a comma-separated list, e.g. `"cart,slots,product,deliveries,recipes"` (default `cart,slots,product`). The server runs over stdio; see [HTTP](#http) to reach it over the network.
60
60
 
61
61
  When the key expires, the tools say so; run `npx picnic-mcp login` again.
62
62
 
63
+ ## HTTP
64
+
65
+ `picnic-mcp http` serves the same tools over HTTP at `/mcp`, for clients that connect to a URL. It needs a token that clients send as `Authorization: Bearer <token>`; generate one with:
66
+
67
+ ```bash
68
+ openssl rand -hex 32
69
+ ```
70
+
71
+ ```bash
72
+ PICNIC_AUTH_KEY=<auth key> PICNIC_MCP_HTTP_TOKEN=<token> npx picnic-mcp http
73
+ ```
74
+
75
+ | Variable | |
76
+ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
77
+ | `PICNIC_MCP_HTTP_TOKEN` | Required, at least 32 characters. Anyone with it can change your cart, so keep it secret. |
78
+ | `PICNIC_MCP_HTTP_HOST` | Address to listen on. Default `127.0.0.1`, so only this machine can connect. |
79
+ | `PICNIC_MCP_HTTP_PORT` | Default `3000`. |
80
+ | `PICNIC_MCP_HTTP_ALLOWED_HOSTS` | Host names allowed besides localhost, comma-separated, e.g. the name of a reverse proxy in front of it. Requests with another Host or Origin header are refused. |
81
+
82
+ The server doesn't do HTTPS. To use it from another machine, put it behind a reverse proxy with HTTPS, and add the proxy's host name to `PICNIC_MCP_HTTP_ALLOWED_HOSTS`.
83
+
63
84
  ## Use it in your own code
64
85
 
65
86
  `createServer` returns the MCP server without reading environment variables or files, so you can run it in your own process and connect it to any MCP transport:
@@ -72,3 +93,7 @@ await server.connect(transport);
72
93
  ```
73
94
 
74
95
  Pass `tools` to choose the tool groups (e.g. `tools: ["cart", "recipes"]`), `client` instead of `authKey` to use a picnic-api client you already have, and `onError` to handle errors from Picnic yourself (by default they're logged to stderr).
96
+
97
+ ### Product images
98
+
99
+ Results that list products (search, the cart, an added product, product details) carry the image URLs of those products in `_meta["picnic-mcp/images"]`, by product id. MCP clients don't pass `_meta` to the model, so the images cost no tokens; your own code can use them, e.g. to show product photos. The images don't need the auth key. The key is exported as `IMAGES_META_KEY`.
package/dist/cli.js CHANGED
@@ -3,6 +3,7 @@ import { serveStdio } from "@modelcontextprotocol/server/stdio";
3
3
  import { createInterface } from "node:readline/promises";
4
4
  import { Writable } from "node:stream";
5
5
  import PicnicClient from "picnic-api";
6
+ import { MIN_TOKEN_LENGTH, startHttpServer } from "./http.js";
6
7
  import { createServer } from "./server.js";
7
8
  import { DEFAULT_TOOL_GROUPS, TOOL_GROUPS, parseToolGroups } from "./tools/groups.js";
8
9
  import { COUNTRY_CODES } from "./types.js";
@@ -13,6 +14,13 @@ const USAGE = `Usage:
13
14
  PICNIC_MCP_TOOLS tool groups, comma-separated, from
14
15
  ${TOOL_GROUPS.join(", ")}
15
16
  (default ${DEFAULT_TOOL_GROUPS.join(",")})
17
+ picnic-mcp http Serve it over HTTP at /mcp instead, with the same variables and:
18
+ PICNIC_MCP_HTTP_TOKEN bearer token clients send, at least
19
+ ${MIN_TOKEN_LENGTH} characters (required)
20
+ PICNIC_MCP_HTTP_HOST address to listen on (default 127.0.0.1)
21
+ PICNIC_MCP_HTTP_PORT port (default 3000)
22
+ PICNIC_MCP_HTTP_ALLOWED_HOSTS host names allowed besides localhost,
23
+ comma-separated, e.g. a reverse proxy's
16
24
  picnic-mcp login Sign in to Picnic and print an auth key for PICNIC_AUTH_KEY.`;
17
25
  function parseCountryCode(value) {
18
26
  const code = (value ?? "NL").trim().toUpperCase();
@@ -21,7 +29,8 @@ function parseCountryCode(value) {
21
29
  }
22
30
  return code;
23
31
  }
24
- async function serve() {
32
+ /** Reads the Picnic settings from the environment, or exits with a message. */
33
+ function picnicSettings() {
25
34
  const authKey = process.env.PICNIC_AUTH_KEY?.trim();
26
35
  if (!authKey) {
27
36
  console.error("PICNIC_AUTH_KEY is not set. Get an auth key with `npx picnic-mcp login`.\n\n" + USAGE);
@@ -30,7 +39,45 @@ async function serve() {
30
39
  const countryCode = parseCountryCode(process.env.PICNIC_COUNTRY_CODE);
31
40
  const toolsEnv = process.env.PICNIC_MCP_TOOLS?.trim();
32
41
  const tools = toolsEnv ? parseToolGroups(toolsEnv) : DEFAULT_TOOL_GROUPS;
33
- serveStdio(() => createServer({ authKey, countryCode, tools }));
42
+ return { authKey, countryCode, tools };
43
+ }
44
+ async function serve() {
45
+ const settings = picnicSettings();
46
+ serveStdio(() => createServer(settings));
47
+ }
48
+ async function serveHttp() {
49
+ const { authKey, countryCode, tools } = picnicSettings();
50
+ const token = process.env.PICNIC_MCP_HTTP_TOKEN?.trim() ?? "";
51
+ if (token.length < MIN_TOKEN_LENGTH) {
52
+ console.error(`PICNIC_MCP_HTTP_TOKEN must be set to a random value of at least ${MIN_TOKEN_LENGTH} characters, e.g. from \`openssl rand -hex 32\`. Clients send it as "Authorization: Bearer <token>".`);
53
+ process.exit(1);
54
+ }
55
+ const host = process.env.PICNIC_MCP_HTTP_HOST?.trim() || "127.0.0.1";
56
+ const port = Number(process.env.PICNIC_MCP_HTTP_PORT?.trim() || 3000);
57
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
58
+ throw new Error(`PICNIC_MCP_HTTP_PORT must be a port number, not "${process.env.PICNIC_MCP_HTTP_PORT}".`);
59
+ }
60
+ const allowedHosts = (process.env.PICNIC_MCP_HTTP_ALLOWED_HOSTS ?? "")
61
+ .split(",")
62
+ .map((name) => name.trim())
63
+ .filter(Boolean);
64
+ // One Picnic client for all requests; each request gets its own MCP server.
65
+ const client = new PicnicClient({ authKey, countryCode });
66
+ const server = await startHttpServer({
67
+ token,
68
+ host,
69
+ port,
70
+ allowedHosts,
71
+ createServer: () => createServer({ client, countryCode, tools }),
72
+ onError: (error) => console.error("[picnic-mcp]", error),
73
+ });
74
+ console.error(`picnic-mcp listening on ${server.url}`);
75
+ if (!["127.0.0.1", "localhost", "::1"].includes(host)) {
76
+ console.error("This address can be reached from other machines, and the server doesn't use HTTPS. Put it behind an HTTPS reverse proxy.");
77
+ }
78
+ for (const signal of ["SIGINT", "SIGTERM"]) {
79
+ process.once(signal, () => void server.close().then(() => process.exit(0)));
80
+ }
34
81
  }
35
82
  /** Asks for the login details on the terminal and prints the auth key. Stores nothing. */
36
83
  async function login() {
@@ -78,6 +125,8 @@ async function main() {
78
125
  return serve();
79
126
  if (command === "login")
80
127
  return login();
128
+ if (command === "http")
129
+ return serveHttp();
81
130
  console.error(command === "--help" || command === "-h" ? USAGE : `Unknown command "${command}".\n\n${USAGE}`);
82
131
  process.exit(command === "--help" || command === "-h" ? 0 : 1);
83
132
  }
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/errors.d.ts CHANGED
@@ -3,3 +3,11 @@
3
3
  * they can be long HTML pages or contain account data.
4
4
  */
5
5
  export declare function describeError(error: unknown): string;
6
+ /** Added to an error of a tool that changes the cart, when the change may have happened anyway. */
7
+ export declare const CART_MAY_HAVE_CHANGED = "The cart may have changed anyway: check get_cart before trying again.";
8
+ /**
9
+ * Whether a failed cart change may still have gone through: after a timeout, a network error or a
10
+ * server error the request may have reached Picnic. Not when Picnic rejected it (a 4xx, the auth
11
+ * key, rate limiting) or it failed before anything was sent.
12
+ */
13
+ export declare function mayHaveChangedCart(error: unknown): boolean;
package/dist/errors.js CHANGED
@@ -25,4 +25,20 @@ export function describeError(error) {
25
25
  }
26
26
  return "Could not reach Picnic. Try again later.";
27
27
  }
28
+ /** Added to an error of a tool that changes the cart, when the change may have happened anyway. */
29
+ export const CART_MAY_HAVE_CHANGED = "The cart may have changed anyway: check get_cart before trying again.";
30
+ /**
31
+ * Whether a failed cart change may still have gone through: after a timeout, a network error or a
32
+ * server error the request may have reached Picnic. Not when Picnic rejected it (a 4xx, the auth
33
+ * key, rate limiting) or it failed before anything was sent.
34
+ */
35
+ export function mayHaveChangedCart(error) {
36
+ if (PicnicClient.isPicnicApiError(error))
37
+ return error.status >= 500 && !/<html[\s>]/i.test(error.body);
38
+ if (PicnicClient.isProductNotFoundError(error) ||
39
+ PicnicClient.isUnexpectedPageFormatError(error)) {
40
+ return false;
41
+ }
42
+ return true;
43
+ }
28
44
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,YAAY,CAAC;AAEtC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,YAAY,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QACzC,sFAAsF;QACtF,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,OAAO,kEAAkE,CAAC;QAC5E,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACjD,OAAO,kHAAkH,CAAC;QAC5H,CAAC;QACD,OAAO,6BAA6B,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;IAC7F,CAAC;IACD,IAAI,YAAY,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,WAAW,KAAK,CAAC,SAAS,oFAAoF,CAAC;IACxH,CAAC;IACD,IAAI,YAAY,CAAC,2BAA2B,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,uHAAuH,CAAC;IACjI,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QAC5D,OAAO,2CAA2C,CAAC;IACrD,CAAC;IACD,OAAO,0CAA0C,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,YAAY,CAAC;AAEtC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,YAAY,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QACzC,sFAAsF;QACtF,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,OAAO,kEAAkE,CAAC;QAC5E,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACjD,OAAO,kHAAkH,CAAC;QAC5H,CAAC;QACD,OAAO,6BAA6B,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;IAC7F,CAAC;IACD,IAAI,YAAY,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,WAAW,KAAK,CAAC,SAAS,oFAAoF,CAAC;IACxH,CAAC;IACD,IAAI,YAAY,CAAC,2BAA2B,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,uHAAuH,CAAC;IACjI,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QAC5D,OAAO,2CAA2C,CAAC;IACrD,CAAC;IACD,OAAO,0CAA0C,CAAC;AACpD,CAAC;AAED,mGAAmG;AACnG,MAAM,CAAC,MAAM,qBAAqB,GAChC,uEAAuE,CAAC;AAE1E;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,YAAY,CAAC,gBAAgB,CAAC,KAAK,CAAC;QACtC,OAAO,KAAK,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChE,IACE,YAAY,CAAC,sBAAsB,CAAC,KAAK,CAAC;QAC1C,YAAY,CAAC,2BAA2B,CAAC,KAAK,CAAC,EAC/C,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,13 +1,14 @@
1
1
  import type { CountryCode, Delivery, DeliveryPosition, DeliverySummary } from "../types.js";
2
2
  import { type CartLineOutput } from "./cart.js";
3
3
  /**
4
- * An upcoming delivery as a tool shows it to a model. Delivery and order ids, payment details, the
4
+ * A delivery as a tool shows it to a model. Delivery and order ids, payment details, the
5
5
  * driver, the address and the route stay out.
6
6
  */
7
7
  export type DeliveryOutput = {
8
8
  /** The delivery window, e.g. "Thu 23 Oct, 18:00-19:00". */
9
9
  window?: string;
10
- itemCount: number;
10
+ /** Left out when unknown: Picnic's summary of a past delivery has 0. */
11
+ itemCount?: number;
11
12
  total: string;
12
13
  /** Set when the driver is on the way. */
13
14
  onTheWay?: true;
@@ -3,15 +3,16 @@ import { formatPrice, formatTime, formatWindow } from "./prices.js";
3
3
  export function formatDelivery(summary, position, delivery, countryCode) {
4
4
  const window = formatWindow(summary.windowStart, summary.windowEnd);
5
5
  const arrival = position?.inProgress ? expectedArrival(position) : null;
6
+ const items = delivery?.orders.flatMap((order) => order.items);
7
+ // Counted from the products when they're loaded, since the summary of a past delivery has 0.
8
+ const itemCount = items ? items.reduce((sum, item) => sum + item.quantity, 0) : summary.itemCount;
6
9
  return {
7
10
  ...(window && { window }),
8
- itemCount: summary.itemCount,
11
+ ...(itemCount > 0 && { itemCount }),
9
12
  total: formatPrice(summary.totalPrice, countryCode),
10
13
  ...(position?.inProgress && { onTheWay: true }),
11
14
  ...(arrival && { expectedArrival: arrival }),
12
- ...(delivery && {
13
- products: delivery.orders.flatMap((order) => order.items.map((item) => formatCartLine(item, countryCode))),
14
- }),
15
+ ...(items && { products: items.map((item) => formatCartLine(item, countryCode)) }),
15
16
  };
16
17
  }
17
18
  function expectedArrival(position) {
@@ -1 +1 @@
1
- {"version":3,"file":"deliveries.js","sourceRoot":"","sources":["../../src/format/deliveries.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,cAAc,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkBpE,MAAM,UAAU,cAAc,CAC5B,OAAwB,EACxB,QAAiC,EACjC,QAAyB,EACzB,WAAwB;IAExB,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxE,OAAO;QACL,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;QACzB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,EAAE,WAAW,CAAC;QACnD,GAAG,CAAC,QAAQ,EAAE,UAAU,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC/C,GAAG,CAAC,OAAO,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC;QAC5C,GAAG,CAAC,QAAQ,IAAI;YACd,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAC1C,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAC7D;SACF,CAAC;KACH,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,QAA0B;IACjD,IAAI,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QACrD,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;IACvF,CAAC;IACD,OAAO,QAAQ,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AACzF,CAAC"}
1
+ {"version":3,"file":"deliveries.js","sourceRoot":"","sources":["../../src/format/deliveries.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,cAAc,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAmBpE,MAAM,UAAU,cAAc,CAC5B,OAAwB,EACxB,QAAiC,EACjC,QAAyB,EACzB,WAAwB;IAExB,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxE,MAAM,KAAK,GAAG,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/D,6FAA6F;IAC7F,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IAClG,OAAO;QACL,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;QACzB,GAAG,CAAC,SAAS,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;QACnC,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,EAAE,WAAW,CAAC;QACnD,GAAG,CAAC,QAAQ,EAAE,UAAU,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC/C,GAAG,CAAC,OAAO,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC;QAC5C,GAAG,CAAC,KAAK,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC;KACnF,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,QAA0B;IACjD,IAAI,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QACrD,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;IACvF,CAAC;IACD,OAAO,QAAQ,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AACzF,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ import { type McpServer } from "@modelcontextprotocol/server";
2
+ /** The only path the server answers on. */
3
+ export declare const MCP_PATH = "/mcp";
4
+ /** The shortest token the server accepts, so a weak token can't be guessed. */
5
+ export declare const MIN_TOKEN_LENGTH = 32;
6
+ export type HttpOptions = {
7
+ /** Clients send it as `Authorization: Bearer <token>`. At least 32 characters. */
8
+ token: string;
9
+ /** Host names allowed in the Host and Origin headers besides localhost, e.g. a reverse proxy's. */
10
+ allowedHosts?: string[];
11
+ /** Called once per request: the MCP server that handles it. */
12
+ createServer: () => McpServer;
13
+ onError?: (error: Error) => void;
14
+ };
15
+ /**
16
+ * The HTTP handler of the MCP endpoint, in fetch terms. Before a request reaches the MCP server it
17
+ * must be for `/mcp`, have an allowed Host and Origin (against DNS rebinding) and carry the token.
18
+ */
19
+ export declare function createHttpHandler(options: HttpOptions): {
20
+ fetch: (request: Request) => Promise<Response>;
21
+ close: () => Promise<void>;
22
+ };
23
+ export type HttpServerOptions = HttpOptions & {
24
+ host: string;
25
+ port: number;
26
+ };
27
+ /**
28
+ * Serves the MCP endpoint over HTTP with Node's own server. It doesn't do TLS: when it isn't on
29
+ * localhost, put it behind an HTTPS reverse proxy.
30
+ */
31
+ export declare function startHttpServer(options: HttpServerOptions): Promise<{
32
+ url: string;
33
+ close: () => Promise<void>;
34
+ }>;
package/dist/http.js ADDED
@@ -0,0 +1,113 @@
1
+ import { createMcpHandler, hostHeaderValidationResponse, localhostAllowedHostnames, localhostAllowedOrigins, originValidationResponse, } from "@modelcontextprotocol/server";
2
+ import { createHash, timingSafeEqual } from "node:crypto";
3
+ import { createServer as createHttpServer, } from "node:http";
4
+ import { Readable } from "node:stream";
5
+ import { pipeline } from "node:stream/promises";
6
+ /** The only path the server answers on. */
7
+ export const MCP_PATH = "/mcp";
8
+ /** The shortest token the server accepts, so a weak token can't be guessed. */
9
+ export const MIN_TOKEN_LENGTH = 32;
10
+ /** Requests to the MCP endpoint are small JSON-RPC messages. */
11
+ const MAX_REQUEST_BODY_SIZE = 1024 * 1024;
12
+ /**
13
+ * The HTTP handler of the MCP endpoint, in fetch terms. Before a request reaches the MCP server it
14
+ * must be for `/mcp`, have an allowed Host and Origin (against DNS rebinding) and carry the token.
15
+ */
16
+ export function createHttpHandler(options) {
17
+ if (options.token.length < MIN_TOKEN_LENGTH) {
18
+ throw new Error(`The HTTP token must be at least ${MIN_TOKEN_LENGTH} characters.`);
19
+ }
20
+ const extraHosts = (options.allowedHosts ?? []).map((host) => host.toLowerCase());
21
+ const hosts = [...localhostAllowedHostnames(), ...extraHosts];
22
+ const origins = [...localhostAllowedOrigins(), ...extraHosts];
23
+ const expected = digest(options.token);
24
+ const mcp = createMcpHandler(() => options.createServer(), {
25
+ maxRequestBodySize: MAX_REQUEST_BODY_SIZE,
26
+ onerror: options.onError,
27
+ });
28
+ return {
29
+ async fetch(request) {
30
+ if (new URL(request.url).pathname !== MCP_PATH)
31
+ return new Response("Not Found", { status: 404 });
32
+ const rejected = hostHeaderValidationResponse(request, hosts) ?? originValidationResponse(request, origins);
33
+ if (rejected)
34
+ return rejected;
35
+ if (!hasToken(request, expected)) {
36
+ return new Response("Unauthorized", {
37
+ status: 401,
38
+ headers: { "WWW-Authenticate": 'Bearer realm="picnic-mcp"' },
39
+ });
40
+ }
41
+ return mcp.fetch(request);
42
+ },
43
+ close: () => mcp.close(),
44
+ };
45
+ }
46
+ /** Compares the request's bearer token in constant time, through digests of equal length. */
47
+ function hasToken(request, expected) {
48
+ const match = /^Bearer\s+(.+)$/i.exec(request.headers.get("authorization") ?? "");
49
+ return match !== null && timingSafeEqual(digest(match[1].trim()), expected);
50
+ }
51
+ function digest(value) {
52
+ return createHash("sha256").update(value).digest();
53
+ }
54
+ /**
55
+ * Serves the MCP endpoint over HTTP with Node's own server. It doesn't do TLS: when it isn't on
56
+ * localhost, put it behind an HTTPS reverse proxy.
57
+ */
58
+ export async function startHttpServer(options) {
59
+ const handler = createHttpHandler(options);
60
+ const server = createHttpServer((req, res) => {
61
+ serve(req, res, handler.fetch).catch((error) => {
62
+ options.onError?.(error instanceof Error ? error : new Error(String(error)));
63
+ if (!res.headersSent)
64
+ res.writeHead(500);
65
+ res.end();
66
+ });
67
+ });
68
+ await new Promise((resolve, reject) => {
69
+ server.once("error", reject);
70
+ server.listen(options.port, options.host, () => resolve());
71
+ });
72
+ const address = server.address();
73
+ const port = typeof address === "object" && address ? address.port : options.port;
74
+ const host = options.host.includes(":") ? `[${options.host}]` : options.host;
75
+ return {
76
+ url: `http://${host}:${port}${MCP_PATH}`,
77
+ close: async () => {
78
+ // Stop accepting connections, then end the open ones (keep-alive, SSE), which close waits for.
79
+ const closed = new Promise((resolve) => server.close(() => resolve()));
80
+ server.closeAllConnections();
81
+ await Promise.all([closed, handler.close()]);
82
+ },
83
+ };
84
+ }
85
+ /** Turns a Node request into a fetch Request and streams the Response back, so SSE keeps working. */
86
+ async function serve(req, res, handle) {
87
+ const controller = new AbortController();
88
+ res.on("close", () => controller.abort());
89
+ const headers = new Headers();
90
+ for (const [name, value] of Object.entries(req.headers)) {
91
+ for (const item of Array.isArray(value) ? value : value === undefined ? [] : [value]) {
92
+ headers.append(name, item);
93
+ }
94
+ }
95
+ const hasBody = req.method !== "GET" && req.method !== "HEAD";
96
+ // The Host header is checked by the handler; the URL only needs a valid origin to parse.
97
+ const request = new Request(new URL(req.url ?? "/", "http://localhost"), {
98
+ method: req.method,
99
+ headers,
100
+ signal: controller.signal,
101
+ ...(hasBody && { body: Readable.toWeb(req), duplex: "half" }),
102
+ });
103
+ const response = await handle(request);
104
+ res.writeHead(response.status, Object.fromEntries(response.headers));
105
+ if (!response.body) {
106
+ res.end();
107
+ return;
108
+ }
109
+ await pipeline(Readable.fromWeb(response.body), res).catch(() => {
110
+ // The client disconnected while the response was streaming.
111
+ });
112
+ }
113
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -3,3 +3,4 @@ export type { CountryCode } from "./types.js";
3
3
  export type { ProductOutput } from "./format/products.js";
4
4
  export type { CartOutput, CartLineOutput, CartSummaryOutput } from "./format/cart.js";
5
5
  export { DEFAULT_TOOL_GROUPS, TOOL_GROUPS, type ToolGroup } from "./tools/groups.js";
6
+ export { IMAGES_META_KEY, type ImageUrls } from "./tools/result.js";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { createServer } from "./server.js";
2
2
  export { DEFAULT_TOOL_GROUPS, TOOL_GROUPS } from "./tools/groups.js";
3
+ export { IMAGES_META_KEY } from "./tools/result.js";
3
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA4B,MAAM,aAAa,CAAC;AAIrE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA4B,MAAM,aAAa,CAAC;AAIrE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,eAAe,EAAkB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Turns the recipeId a tool got into a recipe id. Text that isn't a URL is passed on as an id. A
3
+ * picnic.app link, as the app's share button makes them (`https://picnic.app/nl/go/<code>`), is
4
+ * followed through its redirects to the recipe page, whose URL has the id; this needs no auth key,
5
+ * so none is sent. Returns null for a link to another site or to a page that isn't a recipe.
6
+ */
7
+ export declare function resolveRecipeLink(input: string, fetchImpl?: typeof fetch): Promise<string | null>;
@@ -0,0 +1,55 @@
1
+ /** Picnic's web hosts. A shared recipe link starts and ends on one of them. */
2
+ const PICNIC_HOSTS = ["picnic.app", "www.picnic.app"];
3
+ /** A recipe id: 24 hex characters for Picnic's recipes, 32 for the user's own. */
4
+ const RECIPE_ID = "[a-f0-9]{24}(?:[a-f0-9]{8})?";
5
+ /** The recipe id in a recipe page URL: in the path (`/nl/recepten/<id>`) or a `selling_group_id=` parameter. */
6
+ const ID_IN_URL = new RegExp(`(?:/|selling_group_id=)(${RECIPE_ID})(?=$|[/?#,&;])`, "i");
7
+ /** Following a short link takes a few redirects; give up when Picnic doesn't answer in time. */
8
+ const TIMEOUT_MS = 10_000;
9
+ /**
10
+ * Turns the recipeId a tool got into a recipe id. Text that isn't a URL is passed on as an id. A
11
+ * picnic.app link, as the app's share button makes them (`https://picnic.app/nl/go/<code>`), is
12
+ * followed through its redirects to the recipe page, whose URL has the id; this needs no auth key,
13
+ * so none is sent. Returns null for a link to another site or to a page that isn't a recipe.
14
+ */
15
+ export async function resolveRecipeLink(input, fetchImpl = fetch) {
16
+ const text = input.trim();
17
+ const url = parseUrl(text);
18
+ if (!url)
19
+ return text;
20
+ if (!isPicnicUrl(url))
21
+ return null;
22
+ const direct = recipeIdIn(url);
23
+ if (direct)
24
+ return direct;
25
+ const response = await fetchImpl(url, {
26
+ redirect: "follow",
27
+ signal: AbortSignal.timeout(TIMEOUT_MS),
28
+ });
29
+ await response.body?.cancel();
30
+ const final = parseUrl(response.url);
31
+ return final && isPicnicUrl(final) ? recipeIdIn(final) : null;
32
+ }
33
+ function parseUrl(text) {
34
+ try {
35
+ return new URL(text);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ function isPicnicUrl(url) {
42
+ return url.protocol === "https:" && PICNIC_HOSTS.includes(url.hostname.toLowerCase());
43
+ }
44
+ function recipeIdIn(url) {
45
+ let href = url.href;
46
+ try {
47
+ // The page's deeplink comes percent-encoded in a query parameter.
48
+ href = decodeURIComponent(href);
49
+ }
50
+ catch {
51
+ // Keep the URL as it is when it isn't valid percent-encoding.
52
+ }
53
+ return ID_IN_URL.exec(href)?.[1].toLowerCase() ?? null;
54
+ }
55
+ //# sourceMappingURL=recipe-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-link.js","sourceRoot":"","sources":["../src/recipe-link.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,YAAY,GAAG,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAC;AAEtD,kFAAkF;AAClF,MAAM,SAAS,GAAG,8BAA8B,CAAC;AAEjD,gHAAgH;AAChH,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,2BAA2B,SAAS,iBAAiB,EAAE,GAAG,CAAC,CAAC;AAEzF,gGAAgG;AAChG,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAa,EACb,SAAS,GAAiB,KAAK;IAE/B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1B,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;QACpC,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;KACxC,CAAC,CAAC;IACH,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACrC,OAAO,KAAK,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChE,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAQ;IAC3B,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;AACxF,CAAC;AAED,SAAS,UAAU,CAAC,GAAQ;IAC1B,IAAI,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACpB,IAAI,CAAC;QACH,kEAAkE;QAClE,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;IACD,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC;AACzD,CAAC"}
@@ -1,3 +1,3 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/server";
2
- import type { ToolContext } from "./context.js";
2
+ import { type ToolContext } from "./context.js";
3
3
  export declare function registerCartTools(server: McpServer, context: ToolContext): void;
@@ -1,5 +1,6 @@
1
1
  import * as z from "zod";
2
2
  import { formatCart, formatCartSummary, formatUnavailability } from "../format/cart.js";
3
+ import { imageUrls } from "./context.js";
3
4
  import { productId, quantity } from "./inputs.js";
4
5
  import { loadReplacements } from "./replacements.js";
5
6
  import { errorResult, jsonResult, runTool } from "./result.js";
@@ -12,7 +13,11 @@ export function registerCartTools(server, context) {
12
13
  annotations: { readOnlyHint: true, openWorldHint: true },
13
14
  }, () => runTool(async () => {
14
15
  const cart = await client.cart.getCart();
15
- return jsonResult(formatCart(cart, countryCode, await loadReplacements(context, cart)));
16
+ const replacements = await loadReplacements(context, cart);
17
+ return jsonResult(formatCart(cart, countryCode, replacements.byProduct), {
18
+ ...imageUrls(context, cart.items),
19
+ ...replacements.images,
20
+ });
16
21
  }, onError));
17
22
  server.registerTool("add_to_cart", {
18
23
  title: "Add to cart",
@@ -30,7 +35,7 @@ export function registerCartTools(server, context) {
30
35
  }
31
36
  const unavailableLine = lines.find((line) => line.isUnavailable);
32
37
  const replacements = unavailableLine
33
- ? (await loadReplacements(context, cart, [productId])).get(productId)
38
+ ? await loadReplacements(context, cart, [productId])
34
39
  : undefined;
35
40
  return jsonResult({
36
41
  ...(unavailableLine && {
@@ -40,11 +45,12 @@ export function registerCartTools(server, context) {
40
45
  productId,
41
46
  name: lines[0].name,
42
47
  quantityInCart: quantityOf(lines),
43
- ...(unavailableLine && formatUnavailability(unavailableLine, replacements)),
48
+ ...(unavailableLine &&
49
+ formatUnavailability(unavailableLine, replacements?.byProduct.get(productId))),
44
50
  },
45
51
  cart: formatCartSummary(cart, countryCode),
46
- });
47
- }, onError));
52
+ }, { ...imageUrls(context, lines.slice(0, 1)), ...replacements?.images });
53
+ }, onError, { changesCart: true }));
48
54
  server.registerTool("remove_from_cart", {
49
55
  title: "Remove from cart",
50
56
  description: "Remove a product from the user's Picnic shopping cart. Get the product id from get_cart. " +
@@ -57,7 +63,7 @@ export function registerCartTools(server, context) {
57
63
  removed: { productId, quantityInCart: quantityOf(linesOf(cart, productId)) },
58
64
  cart: formatCartSummary(cart, countryCode),
59
65
  });
60
- }, onError));
66
+ }, onError, { changesCart: true }));
61
67
  }
62
68
  /** The lines of a product: more than one when it was also added for a recipe. */
63
69
  function linesOf(cart, productId) {
@@ -1 +1 @@
1
- {"version":3,"file":"cart.js","sourceRoot":"","sources":["../../src/tools/cart.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cart.js","sourceRoot":"","sources":["../../src/tools/cart.ts"],"names":[],"mappings":"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"}
@@ -8,3 +8,8 @@ export type ToolContext = {
8
8
  };
9
9
  /** The language Picnic's search expects in each country. */
10
10
  export declare const SEARCH_LANGUAGES: Record<CountryCode, string>;
11
+ /** The image URLs of products, by product id, leaving out products without an image. */
12
+ export declare function imageUrls({ client }: ToolContext, products: Iterable<{
13
+ productId: string;
14
+ imageId: string | null;
15
+ }>): Record<string, string>;
@@ -4,4 +4,13 @@ export const SEARCH_LANGUAGES = {
4
4
  DE: "German",
5
5
  FR: "French",
6
6
  };
7
+ /** The image URLs of products, by product id, leaving out products without an image. */
8
+ export function imageUrls({ client }, products) {
9
+ const urls = {};
10
+ for (const { productId, imageId } of products) {
11
+ if (imageId)
12
+ urls[productId] = client.catalog.getImageUrl(imageId, "large");
13
+ }
14
+ return urls;
15
+ }
7
16
  //# sourceMappingURL=context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/tools/context.ts"],"names":[],"mappings":"AAWA,4DAA4D;AAC5D,MAAM,CAAC,MAAM,gBAAgB,GAAgC;IAC3D,EAAE,EAAE,OAAO;IACX,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,QAAQ;CACb,CAAC"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/tools/context.ts"],"names":[],"mappings":"AAWA,4DAA4D;AAC5D,MAAM,CAAC,MAAM,gBAAgB,GAAgC;IAC3D,EAAE,EAAE,OAAO;IACX,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,QAAQ;CACb,CAAC;AAEF,wFAAwF;AACxF,MAAM,UAAU,SAAS,CACvB,EAAE,MAAM,EAAe,EACvB,QAAiE;IAEjE,MAAM,IAAI,GAA2B,EAAE,CAAC;IACxC,KAAK,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC9C,IAAI,OAAO;YAAE,IAAI,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,32 +1,44 @@
1
1
  import * as z from "zod";
2
2
  import { formatDelivery } from "../format/deliveries.js";
3
3
  import { jsonResult, runTool } from "./result.js";
4
- /** At most this many upcoming deliveries are shown, each costing one or two requests. */
4
+ /** At most this many deliveries are shown, each costing one or two requests. */
5
5
  const MAX_DELIVERIES = 3;
6
6
  export function registerDeliveryTools(server, context) {
7
7
  const { client, countryCode, onError } = context;
8
8
  server.registerTool("get_deliveries", {
9
- title: "Get upcoming deliveries",
10
- description: "Show the user's upcoming Picnic deliveries: the delivery window, the number of products and the total, " +
11
- "and the expected arrival time when the driver is on the way. Set includeProducts to also list what was ordered.",
9
+ title: "Get deliveries",
10
+ description: 'Show the user\'s Picnic deliveries: upcoming ones (the default), or the last past ones with when: "past". ' +
11
+ "Each has the delivery window, the number of products and the total; an upcoming one also has the expected arrival while the driver is on the way. " +
12
+ "Set includeProducts to list what was ordered, e.g. to order the same again: show the products and add the ones the user wants with add_to_cart.",
12
13
  inputSchema: z.object({
14
+ when: z
15
+ .enum(["upcoming", "past"])
16
+ .default("upcoming")
17
+ .describe('"upcoming" for deliveries still to come, "past" for the last delivered ones. Default upcoming.'),
13
18
  includeProducts: z
14
19
  .boolean()
15
20
  .default(false)
16
21
  .describe("Also list the products of each delivery. Default false."),
17
22
  }),
18
23
  annotations: { readOnlyHint: true, openWorldHint: true },
19
- }, ({ includeProducts }) => runTool(async () => {
20
- const summaries = (await client.delivery.getDeliverySummaries(["CURRENT"]))
21
- .sort(byWindowStart)
24
+ }, ({ when, includeProducts }) => runTool(async () => {
25
+ const past = when === "past";
26
+ const summaries = (await client.delivery.getDeliverySummaries([past ? "COMPLETED" : "CURRENT"]))
27
+ .sort((a, b) => (past ? byWindowStart(b, a) : byWindowStart(a, b)))
22
28
  .slice(0, MAX_DELIVERIES);
23
29
  if (summaries.length === 0) {
24
- return jsonResult({ next: "The user has no upcoming deliveries.", deliveries: [] });
30
+ return jsonResult({
31
+ next: past
32
+ ? "The user has no past deliveries."
33
+ : "The user has no upcoming deliveries.",
34
+ deliveries: [],
35
+ });
25
36
  }
26
37
  const deliveries = await Promise.all(summaries.map(async (summary) => {
27
38
  const [position, delivery] = await Promise.all([
28
- // A delivery that hasn't left yet may have no position; that only hides the arrival time.
29
- client.delivery.getDeliveryPosition(summary.id).catch(() => null),
39
+ // Only an upcoming delivery has a position. One that hasn't left yet may have none;
40
+ // that only hides the arrival time.
41
+ past ? null : client.delivery.getDeliveryPosition(summary.id).catch(() => null),
30
42
  includeProducts ? client.delivery.getDelivery(summary.id) : null,
31
43
  ]);
32
44
  return formatDelivery(summary, position, delivery, countryCode);
@@ -1 +1 @@
1
- {"version":3,"file":"deliveries.js","sourceRoot":"","sources":["../../src/tools/deliveries.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGzD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAElD,yFAAyF;AACzF,MAAM,cAAc,GAAG,CAAC,CAAC;AAEzB,MAAM,UAAU,qBAAqB,CAAC,MAAiB,EAAE,OAAoB;IAC3E,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAEjD,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;QACE,KAAK,EAAE,yBAAyB;QAChC,WAAW,EACT,yGAAyG;YACzG,iHAAiH;QACnH,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,eAAe,EAAE,CAAC;iBACf,OAAO,EAAE;iBACT,OAAO,CAAC,KAAK,CAAC;iBACd,QAAQ,CAAC,yDAAyD,CAAC;SACvE,CAAC;QACF,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;KACzD,EACD,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE,CACtB,OAAO,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,SAAS,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;aACxE,IAAI,CAAC,aAAa,CAAC;aACnB,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;QAC5B,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,UAAU,CAAC,EAAE,IAAI,EAAE,sCAAsC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YAC9B,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAC7C,0FAA0F;gBAC1F,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;gBACjE,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;aACjE,CAAC,CAAC;YACH,OAAO,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;QAClE,CAAC,CAAC,CACH,CAAC;QACF,OAAO,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;IACpC,CAAC,EAAE,OAAO,CAAC,CACd,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,CAAkB,EAAE,CAAkB;IAC3D,OAAO,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;AAClE,CAAC"}
1
+ {"version":3,"file":"deliveries.js","sourceRoot":"","sources":["../../src/tools/deliveries.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@ const GROUPS = {
23
23
  },
24
24
  deliveries: {
25
25
  register: registerDeliveryTools,
26
- instructions: () => "For orders that are on their way: get_deliveries.",
26
+ instructions: () => 'For orders on their way: get_deliveries. To order the same as before: get_deliveries with when "past" and includeProducts.',
27
27
  },
28
28
  recipes: {
29
29
  register: registerRecipeTools,
@@ -1 +1 @@
1
- {"version":3,"file":"groups.js","sourceRoot":"","sources":["../../src/tools/groups.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"groups.js","sourceRoot":"","sources":["../../src/tools/groups.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/server";
2
- import type { ToolContext } from "./context.js";
2
+ import { type ToolContext } from "./context.js";
3
3
  export declare function registerProductTools(server: McpServer, context: ToolContext): void;
@@ -1,5 +1,6 @@
1
1
  import * as z from "zod";
2
2
  import { formatProductDetails } from "../format/product.js";
3
+ import { imageUrls } from "./context.js";
3
4
  import { productId } from "./inputs.js";
4
5
  import { jsonResult, runTool } from "./result.js";
5
6
  export function registerProductTools(server, context) {
@@ -10,6 +11,9 @@ export function registerProductTools(server, context) {
10
11
  "Use it for questions such as whether a product contains gluten. Get the product id from search_products or get_cart.",
11
12
  inputSchema: z.object({ productId }),
12
13
  annotations: { readOnlyHint: true, openWorldHint: true },
13
- }, ({ productId }) => runTool(async () => jsonResult(formatProductDetails(await client.catalog.getProduct(productId), countryCode)), onError));
14
+ }, ({ productId }) => runTool(async () => {
15
+ const product = await client.catalog.getProduct(productId);
16
+ return jsonResult(formatProductDetails(product, countryCode), imageUrls(context, [{ productId: product.id, imageId: product.imageIds[0] ?? null }]));
17
+ }, onError));
14
18
  }
15
19
  //# sourceMappingURL=product.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"product.js","sourceRoot":"","sources":["../../src/tools/product.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,OAAoB;IAC1E,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAEjD,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EACT,6GAA6G;YAC7G,sHAAsH;QACxH,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;QACpC,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;KACzD,EACD,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAChB,OAAO,CACL,KAAK,IAAI,EAAE,CACT,UAAU,CAAC,oBAAoB,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC,EAC3F,OAAO,CACR,CACJ,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"product.js","sourceRoot":"","sources":["../../src/tools/product.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,OAAoB;IAC1E,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAEjD,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EACT,6GAA6G;YAC7G,sHAAsH;QACxH,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;QACpC,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;KACzD,EACD,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAChB,OAAO,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAC3D,OAAO,UAAU,CACf,oBAAoB,CAAC,OAAO,EAAE,WAAW,CAAC,EAC1C,SAAS,CAAC,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CACtF,CAAC;IACJ,CAAC,EAAE,OAAO,CAAC,CACd,CAAC;AACJ,CAAC"}
@@ -1,15 +1,17 @@
1
1
  import * as z from "zod";
2
2
  import { formatCartSummary } from "../format/cart.js";
3
3
  import { formatRecipe, formatRecipeSummary } from "../format/recipes.js";
4
+ import { resolveRecipeLink } from "../recipe-link.js";
4
5
  import { SEARCH_LANGUAGES } from "./context.js";
5
- import { jsonResult, runTool } from "./result.js";
6
+ import { errorResult, jsonResult, runTool } from "./result.js";
6
7
  /** At most this many recipes in search results. */
7
8
  const MAX_RECIPES = 8;
8
9
  const recipeId = z
9
10
  .string()
10
11
  .trim()
11
12
  .min(1)
12
- .describe("The recipeId from search_recipes or get_recipe.");
13
+ .describe("The recipeId from search_recipes or get_recipe, or a Picnic recipe link the user shared (https://picnic.app/...).");
14
+ const NOT_A_RECIPE = "That isn't a Picnic recipe or recipe link. Use search_recipes to find the recipe and its recipeId.";
13
15
  const portions = z
14
16
  .number()
15
17
  .int()
@@ -38,18 +40,26 @@ export function registerRecipeTools(server, context) {
38
40
  }, onError));
39
41
  server.registerTool("get_recipe", {
40
42
  title: "Get recipe",
41
- description: "Get a Picnic recipe: its ingredients with the products that are added for them, allergens and steps. " +
43
+ description: "Get a Picnic recipe, by recipeId or a recipe link the user shared: its ingredients with the products that are added for them, allergens and steps. " +
42
44
  "Ingredients with `added: false` aren't added to the cart by default; `cupboard` ones are things most people have at home.",
43
45
  inputSchema: z.object({ recipeId, portions }),
44
46
  annotations: { readOnlyHint: true, openWorldHint: true },
45
- }, ({ recipeId, portions }) => runTool(async () => jsonResult(formatRecipe(await client.recipe.getRecipe(recipeId, { portions }), countryCode)), onError));
47
+ }, ({ recipeId: link, portions }) => runTool(async () => {
48
+ const recipeId = await resolveRecipeLink(link);
49
+ if (!recipeId)
50
+ return errorResult(NOT_A_RECIPE);
51
+ return jsonResult(formatRecipe(await client.recipe.getRecipe(recipeId, { portions }), countryCode));
52
+ }, onError));
46
53
  server.registerTool("add_recipe_to_cart", {
47
54
  title: "Add recipe to cart",
48
55
  description: "Add the ingredients of a Picnic recipe to the user's cart, for the given number of portions. " +
49
56
  "Only after the user chose the recipe. Returns the portions and the cart total. This does not place an order.",
50
57
  inputSchema: z.object({ recipeId, portions }),
51
58
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
52
- }, ({ recipeId, portions }) => runTool(async () => {
59
+ }, ({ recipeId: link, portions }) => runTool(async () => {
60
+ const recipeId = await resolveRecipeLink(link);
61
+ if (!recipeId)
62
+ return errorResult(NOT_A_RECIPE);
53
63
  const result = await client.recipe.addRecipeToCart(recipeId, { portions });
54
64
  const cart = await client.cart.getCart();
55
65
  const recipe = cart.recipes.find((item) => item.id === recipeId);
@@ -64,14 +74,17 @@ export function registerRecipeTools(server, context) {
64
74
  },
65
75
  cart: formatCartSummary(cart, countryCode),
66
76
  });
67
- }, onError));
77
+ }, onError, { changesCart: true }));
68
78
  server.registerTool("remove_recipe_from_cart", {
69
79
  title: "Remove recipe from cart",
70
80
  description: "Remove a recipe and the ingredients that were added for it from the user's cart. " +
71
81
  "Returns the number of products removed and the cart total.",
72
82
  inputSchema: z.object({ recipeId }),
73
83
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
74
- }, ({ recipeId }) => runTool(async () => {
84
+ }, ({ recipeId: link }) => runTool(async () => {
85
+ const recipeId = await resolveRecipeLink(link);
86
+ if (!recipeId)
87
+ return errorResult(NOT_A_RECIPE);
75
88
  const result = await client.recipe.removeRecipeFromCart(recipeId);
76
89
  const cart = await client.cart.getCart();
77
90
  const removedCount = result.SURemoved.reduce((sum, item) => sum + item.quantity, 0);
@@ -79,6 +92,6 @@ export function registerRecipeTools(server, context) {
79
92
  removed: { recipeId, productsRemoved: removedCount },
80
93
  cart: formatCartSummary(cart, countryCode),
81
94
  });
82
- }, onError));
95
+ }, onError, { changesCart: true }));
83
96
  }
84
97
  //# sourceMappingURL=recipes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"recipes.js","sourceRoot":"","sources":["../../src/tools/recipes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recipes.js","sourceRoot":"","sources":["../../src/tools/recipes.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,13 @@
1
1
  import { type ProductOutput } from "../format/products.js";
2
2
  import type { Cart } from "../types.js";
3
- import type { ToolContext } from "./context.js";
3
+ import { type ToolContext } from "./context.js";
4
+ import type { ImageUrls } from "./result.js";
4
5
  /**
5
6
  * Reads the replacements of the given products that can't be delivered, as products to offer. A
6
7
  * product whose replacements can't be read is left out: the model still sees why it can't be
7
8
  * delivered.
8
9
  */
9
- export declare function loadReplacements({ client, countryCode, onError }: ToolContext, cart: Cart, productIds?: readonly string[]): Promise<Map<string, ProductOutput[]>>;
10
+ export declare function loadReplacements(context: ToolContext, cart: Cart, productIds?: readonly string[]): Promise<{
11
+ byProduct: Map<string, ProductOutput[]>;
12
+ images: ImageUrls;
13
+ }>;
@@ -1,4 +1,5 @@
1
1
  import { formatProduct } from "../format/products.js";
2
+ import { imageUrls } from "./context.js";
2
3
  /** At most this many products' replacements are read, each one request. */
3
4
  const MAX_PRODUCTS = 3;
4
5
  /** At most this many replacements per product. */
@@ -8,7 +9,8 @@ const MAX_REPLACEMENTS = 3;
8
9
  * product whose replacements can't be read is left out: the model still sees why it can't be
9
10
  * delivered.
10
11
  */
11
- export async function loadReplacements({ client, countryCode, onError }, cart, productIds = cart.items.map((item) => item.productId)) {
12
+ export async function loadReplacements(context, cart, productIds = cart.items.map((item) => item.productId)) {
13
+ const { client, countryCode, onError } = context;
12
14
  const unavailable = [
13
15
  ...new Set(cart.items
14
16
  .filter((item) => item.isUnavailable && productIds.includes(item.productId))
@@ -17,17 +19,22 @@ export async function loadReplacements({ client, countryCode, onError }, cart, p
17
19
  const entries = await Promise.all(unavailable.map(async (productId) => {
18
20
  try {
19
21
  const { replacements } = await client.cart.getReplacements(productId);
20
- const products = replacements
21
- .filter((tile) => !tile.isUnavailable)
22
- .slice(0, MAX_REPLACEMENTS)
23
- .map((tile) => formatProduct({ tile, boughtBefore: false }, countryCode));
24
- return [productId, products];
22
+ const tiles = replacements.filter((tile) => !tile.isUnavailable).slice(0, MAX_REPLACEMENTS);
23
+ return {
24
+ productId,
25
+ products: tiles.map((tile) => formatProduct({ tile, boughtBefore: false }, countryCode)),
26
+ images: imageUrls(context, tiles.map((tile) => ({ productId: tile.id, imageId: tile.imageId }))),
27
+ };
25
28
  }
26
29
  catch (error) {
27
30
  onError(error);
28
31
  return null;
29
32
  }
30
33
  }));
31
- return new Map(entries.filter((entry) => entry !== null));
34
+ const found = entries.filter((entry) => entry !== null);
35
+ return {
36
+ byProduct: new Map(found.map((entry) => [entry.productId, entry.products])),
37
+ images: Object.assign({}, ...found.map((entry) => entry.images)),
38
+ };
32
39
  }
33
40
  //# sourceMappingURL=replacements.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"replacements.js","sourceRoot":"","sources":["../../src/tools/replacements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI1E,2EAA2E;AAC3E,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,kDAAkD;AAClD,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAe,EAC7C,IAAU,EACV,UAAU,GAAsB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;IAExE,MAAM,WAAW,GAAG;QAClB,GAAG,IAAI,GAAG,CACR,IAAI,CAAC,KAAK;aACP,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;aAC3E,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CACjC;KACF,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;IAEzB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,EAA6C,EAAE;QAC7E,IAAI,CAAC;YACH,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;YACtE,MAAM,QAAQ,GAAG,YAAY;iBAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC;iBACrC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC;iBAC1B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;YAC5E,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IACF,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC"}
1
+ {"version":3,"file":"replacements.js","sourceRoot":"","sources":["../../src/tools/replacements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE1E,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAG3D,2EAA2E;AAC3E,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,kDAAkD;AAClD,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAoB,EACpB,IAAU,EACV,UAAU,GAAsB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;IAExE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACjD,MAAM,WAAW,GAAG;QAClB,GAAG,IAAI,GAAG,CACR,IAAI,CAAC,KAAK;aACP,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;aAC3E,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CACjC;KACF,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;IAEzB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;YACtE,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;YAC5F,OAAO;gBACL,SAAS;gBACT,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,WAAW,CAAC,CAAC;gBACxF,MAAM,EAAE,SAAS,CACf,OAAO,EACP,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CACrE;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IACF,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;IACxD,OAAO;QACL,SAAS,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC3E,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;KACjE,CAAC;AACJ,CAAC"}
@@ -1,6 +1,19 @@
1
1
  import type { CallToolResult } from "@modelcontextprotocol/server";
2
- /** Returns data as compact JSON text: models read the text, and indentation only costs tokens. */
3
- export declare function jsonResult(data: unknown): CallToolResult;
2
+ /** The `_meta` key with the image URLs of the products in a result, by product id. */
3
+ export declare const IMAGES_META_KEY = "picnic-mcp/images";
4
+ /** Image URLs by product id. */
5
+ export type ImageUrls = Record<string, string>;
6
+ /**
7
+ * Returns data as compact JSON text: models read the text, and indentation only costs tokens.
8
+ * Image URLs go in `_meta`, which clients keep from the model, so they cost no tokens.
9
+ */
10
+ export declare function jsonResult(data: unknown, images?: ImageUrls): CallToolResult;
4
11
  export declare function errorResult(message: string): CallToolResult;
5
- /** Runs a tool handler and turns a thrown error into a short tool error. */
6
- export declare function runTool(run: () => Promise<CallToolResult>, onError: (error: unknown) => void): Promise<CallToolResult>;
12
+ /**
13
+ * Runs a tool handler and turns a thrown error into a short tool error. For a tool that changes
14
+ * the cart, the error says when the change may have gone through anyway, so the model checks the
15
+ * cart instead of retrying blindly.
16
+ */
17
+ export declare function runTool(run: () => Promise<CallToolResult>, onError: (error: unknown) => void, options?: {
18
+ changesCart?: boolean;
19
+ }): Promise<CallToolResult>;
@@ -1,19 +1,34 @@
1
- import { describeError } from "../errors.js";
2
- /** Returns data as compact JSON text: models read the text, and indentation only costs tokens. */
3
- export function jsonResult(data) {
4
- return { content: [{ type: "text", text: JSON.stringify(data) }] };
1
+ import { CART_MAY_HAVE_CHANGED, describeError, mayHaveChangedCart } from "../errors.js";
2
+ /** The `_meta` key with the image URLs of the products in a result, by product id. */
3
+ export const IMAGES_META_KEY = "picnic-mcp/images";
4
+ /**
5
+ * Returns data as compact JSON text: models read the text, and indentation only costs tokens.
6
+ * Image URLs go in `_meta`, which clients keep from the model, so they cost no tokens.
7
+ */
8
+ export function jsonResult(data, images = {}) {
9
+ return {
10
+ content: [{ type: "text", text: JSON.stringify(data) }],
11
+ ...(Object.keys(images).length > 0 && { _meta: { [IMAGES_META_KEY]: images } }),
12
+ };
5
13
  }
6
14
  export function errorResult(message) {
7
15
  return { content: [{ type: "text", text: message }], isError: true };
8
16
  }
9
- /** Runs a tool handler and turns a thrown error into a short tool error. */
10
- export async function runTool(run, onError) {
17
+ /**
18
+ * Runs a tool handler and turns a thrown error into a short tool error. For a tool that changes
19
+ * the cart, the error says when the change may have gone through anyway, so the model checks the
20
+ * cart instead of retrying blindly.
21
+ */
22
+ export async function runTool(run, onError, options = {}) {
11
23
  try {
12
24
  return await run();
13
25
  }
14
26
  catch (error) {
15
27
  onError(error);
16
- return errorResult(describeError(error));
28
+ const message = describeError(error);
29
+ return errorResult(options.changesCart && mayHaveChangedCart(error)
30
+ ? `${message} ${CART_MAY_HAVE_CHANGED}`
31
+ : message);
17
32
  }
18
33
  }
19
34
  //# sourceMappingURL=result.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/tools/result.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,kGAAkG;AAClG,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAkC,EAClC,OAAiC;IAEjC,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,CAAC;QACf,OAAO,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/tools/result.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAExF,sFAAsF;AACtF,MAAM,CAAC,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAKnD;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,IAAa,EAAE,MAAM,GAAc,EAAE;IAC9D,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;KAChF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAkC,EAClC,OAAiC,EACjC,OAAO,GAA8B,EAAE;IAEvC,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,CAAC;QACf,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACrC,OAAO,WAAW,CAChB,OAAO,CAAC,WAAW,IAAI,kBAAkB,CAAC,KAAK,CAAC;YAC9C,CAAC,CAAC,GAAG,OAAO,IAAI,qBAAqB,EAAE;YACvC,CAAC,CAAC,OAAO,CACZ,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import * as z from "zod";
2
2
  import { formatProduct } from "../format/products.js";
3
3
  import { listCandidates, matchProducts } from "../match.js";
4
- import { SEARCH_LANGUAGES } from "./context.js";
4
+ import { SEARCH_LANGUAGES, imageUrls } from "./context.js";
5
5
  import { jsonResult, runTool } from "./result.js";
6
6
  /** At most this many products in the output, to keep it short. */
7
7
  const MAX_PRODUCTS = 8;
@@ -24,29 +24,33 @@ export function registerSearchProducts(server, context) {
24
24
  const corrected = results.correctedQuery && results.correctedQuery !== query
25
25
  ? { searchedFor: results.correctedQuery }
26
26
  : {};
27
+ const images = (shown) => imageUrls(context, shown.map(({ tile }) => ({ productId: tile.id, imageId: tile.imageId })));
27
28
  if (match.type === "best") {
29
+ const others = match.others.slice(0, MAX_PRODUCTS - 1);
28
30
  return jsonResult({
29
31
  ...corrected,
30
32
  next: "Add bestMatch with add_to_cart, unless the user asked for something else.",
31
33
  bestMatch: formatProduct(match.best, context.countryCode),
32
- otherResults: format(match.others.slice(0, MAX_PRODUCTS - 1)),
33
- });
34
+ otherResults: format(others),
35
+ }, images([match.best, ...others]));
34
36
  }
35
37
  if (match.type === "choose") {
38
+ const others = match.others.slice(0, MAX_PRODUCTS - match.choices.length);
36
39
  return jsonResult({
37
40
  ...corrected,
38
41
  next: "No clear match. Ask the user which of the choices they want, then add it with add_to_cart.",
39
42
  choices: format(match.choices),
40
- otherResults: format(match.others.slice(0, MAX_PRODUCTS - match.choices.length)),
41
- });
43
+ otherResults: format(others),
44
+ }, images([...match.choices, ...others]));
42
45
  }
46
+ const others = match.others.slice(0, MAX_PRODUCTS);
43
47
  return jsonResult({
44
48
  ...corrected,
45
- next: match.others.length > 0
49
+ next: others.length > 0
46
50
  ? "None of the results can be ordered right now. Tell the user, or try another search term."
47
51
  : "Nothing found. Try another search term, or tell the user.",
48
- otherResults: format(match.others.slice(0, MAX_PRODUCTS)),
49
- });
52
+ otherResults: format(others),
53
+ }, images(others));
50
54
  }, context.onError));
51
55
  }
52
56
  //# sourceMappingURL=search-products.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-products.js","sourceRoot":"","sources":["../../src/tools/search-products.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search-products.js","sourceRoot":"","sources":["../../src/tools/search-products.ts"],"names":[],"mappings":"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"}
@@ -56,6 +56,6 @@ export function registerSlotTools(server, context) {
56
56
  return errorResult(`Picnic did not select slot ${slotId}. Check the slotId with get_delivery_slots.`);
57
57
  }
58
58
  return jsonResult({ selected: true, cart: formatCartSummary(cart, countryCode) });
59
- }, onError));
59
+ }, onError, { changesCart: true }));
60
60
  }
61
61
  //# sourceMappingURL=slots.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"slots.js","sourceRoot":"","sources":["../../src/tools/slots.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"slots.js","sourceRoot":"","sources":["../../src/tools/slots.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picnic-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "MCP server for the online supermarket Picnic: search products and manage the cart",
5
5
  "keywords": [
6
6
  "picnic",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@modelcontextprotocol/server": "^2.3.1",
50
- "picnic-api": "^5.1.0",
50
+ "picnic-api": "^5.2.0",
51
51
  "zod": "^4.6.7"
52
52
  },
53
53
  "devDependencies": {