picnic-mcp 0.2.0 → 0.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 (54) hide show
  1. package/README.md +20 -10
  2. package/dist/cli.js +10 -3
  3. package/dist/cli.js.map +1 -1
  4. package/dist/errors.js +3 -0
  5. package/dist/errors.js.map +1 -1
  6. package/dist/format/cart.d.ts +14 -14
  7. package/dist/format/cart.js +8 -15
  8. package/dist/format/cart.js.map +1 -1
  9. package/dist/format/deliveries.d.ts +18 -0
  10. package/dist/format/deliveries.js +23 -0
  11. package/dist/format/deliveries.js.map +1 -0
  12. package/dist/format/prices.d.ts +8 -0
  13. package/dist/format/prices.js +30 -12
  14. package/dist/format/prices.js.map +1 -1
  15. package/dist/format/product.d.ts +29 -0
  16. package/dist/format/product.js +49 -0
  17. package/dist/format/product.js.map +1 -0
  18. package/dist/format/recipes.d.ts +40 -0
  19. package/dist/format/recipes.js +37 -0
  20. package/dist/format/recipes.js.map +1 -0
  21. package/dist/format/slots.d.ts +29 -0
  22. package/dist/format/slots.js +40 -0
  23. package/dist/format/slots.js.map +1 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +1 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/server.d.ts +6 -3
  28. package/dist/server.js +8 -12
  29. package/dist/server.js.map +1 -1
  30. package/dist/tools/cart.js +10 -8
  31. package/dist/tools/cart.js.map +1 -1
  32. package/dist/tools/deliveries.d.ts +3 -0
  33. package/dist/tools/deliveries.js +40 -0
  34. package/dist/tools/deliveries.js.map +1 -0
  35. package/dist/tools/groups.d.ts +14 -0
  36. package/dist/tools/groups.js +64 -0
  37. package/dist/tools/groups.js.map +1 -0
  38. package/dist/tools/inputs.d.ts +4 -0
  39. package/dist/tools/inputs.js +9 -0
  40. package/dist/tools/inputs.js.map +1 -0
  41. package/dist/tools/product.d.ts +3 -0
  42. package/dist/tools/product.js +15 -0
  43. package/dist/tools/product.js.map +1 -0
  44. package/dist/tools/recipes.d.ts +3 -0
  45. package/dist/tools/recipes.js +84 -0
  46. package/dist/tools/recipes.js.map +1 -0
  47. package/dist/tools/replacements.d.ts +9 -0
  48. package/dist/tools/replacements.js +33 -0
  49. package/dist/tools/replacements.js.map +1 -0
  50. package/dist/tools/slots.d.ts +3 -0
  51. package/dist/tools/slots.js +61 -0
  52. package/dist/tools/slots.js.map +1 -0
  53. package/dist/types.d.ts +8 -0
  54. package/package.json +2 -2
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@ export { createServer, type CreateServerOptions } from "./server.js";
2
2
  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
+ export { DEFAULT_TOOL_GROUPS, TOOL_GROUPS, type ToolGroup } from "./tools/groups.js";
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { createServer } from "./server.js";
2
+ export { DEFAULT_TOOL_GROUPS, TOOL_GROUPS } from "./tools/groups.js";
2
3
  //# 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"}
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"}
package/dist/server.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { McpServer } from "@modelcontextprotocol/server";
2
2
  import PicnicClient from "picnic-api";
3
+ import { type ToolGroup } from "./tools/groups.js";
3
4
  import type { CountryCode } from "./types.js";
4
5
  export type CreateServerOptions = ({
5
6
  /** The Picnic auth key of the account, from `picnic-mcp login`. */
@@ -12,12 +13,14 @@ export type CreateServerOptions = ({
12
13
  /** The country of the client's account. Default: "NL". */
13
14
  countryCode?: CountryCode;
14
15
  }) & {
16
+ /** The tool groups to turn on. Default: `["cart", "slots", "product"]`. */
17
+ tools?: readonly ToolGroup[];
15
18
  /** Called with errors from Picnic before the tool returns a short message. Default: log to stderr. */
16
19
  onError?: (error: unknown) => void;
17
20
  };
18
21
  /**
19
- * Creates an MCP server with tools to search Picnic's products and change the cart. It reads no
20
- * environment variables or files, so it can run in-process: connect it to a transport with
21
- * `server.connect(transport)`.
22
+ * Creates an MCP server with tools to search Picnic's products and change the cart, plus the
23
+ * other tool groups that are turned on. It reads no environment variables or files, so it can run
24
+ * in-process: connect it to a transport with `server.connect(transport)`.
22
25
  */
23
26
  export declare function createServer(options: CreateServerOptions): McpServer;
package/dist/server.js CHANGED
@@ -1,17 +1,16 @@
1
1
  import { McpServer } from "@modelcontextprotocol/server";
2
2
  import { createRequire } from "node:module";
3
3
  import PicnicClient from "picnic-api";
4
- import { registerCartTools } from "./tools/cart.js";
5
- import { SEARCH_LANGUAGES } from "./tools/context.js";
6
- import { registerSearchProducts } from "./tools/search-products.js";
4
+ import { DEFAULT_TOOL_GROUPS, registerToolGroups, toolGroupInstructions, } from "./tools/groups.js";
7
5
  const { version } = createRequire(import.meta.url)("../package.json");
8
6
  /**
9
- * Creates an MCP server with tools to search Picnic's products and change the cart. It reads no
10
- * environment variables or files, so it can run in-process: connect it to a transport with
11
- * `server.connect(transport)`.
7
+ * Creates an MCP server with tools to search Picnic's products and change the cart, plus the
8
+ * other tool groups that are turned on. It reads no environment variables or files, so it can run
9
+ * in-process: connect it to a transport with `server.connect(transport)`.
12
10
  */
13
11
  export function createServer(options) {
14
12
  const countryCode = options.countryCode ?? "NL";
13
+ const groups = options.tools ?? DEFAULT_TOOL_GROUPS;
15
14
  const context = {
16
15
  client: "client" in options
17
16
  ? options.client
@@ -22,13 +21,10 @@ export function createServer(options) {
22
21
  const server = new McpServer({ name: "picnic-mcp", version }, {
23
22
  capabilities: { tools: {} },
24
23
  instructions: "Tools for the user's account at the online supermarket Picnic. " +
25
- "To add a product: search_products (query in " +
26
- SEARCH_LANGUAGES[countryCode] +
27
- "), then add_to_cart with the product id. If the search returns choices, ask the user first. " +
28
- "These tools can change the cart but cannot place an order.",
24
+ toolGroupInstructions(context, groups) +
25
+ " These tools can change the cart but cannot place an order.",
29
26
  });
30
- registerSearchProducts(server, context);
31
- registerCartTools(server, context);
27
+ registerToolGroups(server, context, groups);
32
28
  return server;
33
29
  }
34
30
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,YAAY,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAoB,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAGpE,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAwB,CAAC;AAoB7F;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC;IAChD,MAAM,OAAO,GAAgB;QAC3B,MAAM,EACJ,QAAQ,IAAI,OAAO;YACjB,CAAC,CAAC,OAAO,CAAC,MAAM;YAChB,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC;QACjE,WAAW;QACX,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;KAC9E,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,EAC/B;QACE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3B,YAAY,EACV,iEAAiE;YACjE,8CAA8C;YAC9C,gBAAgB,CAAC,WAAW,CAAC;YAC7B,8FAA8F;YAC9F,4DAA4D;KAC/D,CACF,CAAC;IACF,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,YAAY,MAAM,YAAY,CAAC;AAGtC,OAAO,EACL,mBAAmB,EAEnB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAwB,CAAC;AAsB7F;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,mBAAmB,CAAC;IACpD,MAAM,OAAO,GAAgB;QAC3B,MAAM,EACJ,QAAQ,IAAI,OAAO;YACjB,CAAC,CAAC,OAAO,CAAC,MAAM;YAChB,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC;QACjE,WAAW;QACX,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;KAC9E,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,EAC/B;QACE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3B,YAAY,EACV,iEAAiE;YACjE,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC;YACtC,6DAA6D;KAChE,CACF,CAAC;IACF,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC5C,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1,12 +1,8 @@
1
1
  import * as z from "zod";
2
2
  import { formatCart, formatCartSummary, formatUnavailability } from "../format/cart.js";
3
+ import { productId, quantity } from "./inputs.js";
4
+ import { loadReplacements } from "./replacements.js";
3
5
  import { errorResult, jsonResult, runTool } from "./result.js";
4
- const productId = z
5
- .string()
6
- .trim()
7
- .regex(/^s\d+$/, "Use a product id from search_products or get_cart, e.g. s1004201.")
8
- .describe("The product id from search_products or get_cart, e.g. s1004201.");
9
- const quantity = (what) => z.number().int().min(1).max(50).default(1).describe(`How many to ${what}. Default 1.`);
10
6
  export function registerCartTools(server, context) {
11
7
  const { client, countryCode, onError } = context;
12
8
  server.registerTool("get_cart", {
@@ -14,7 +10,10 @@ export function registerCartTools(server, context) {
14
10
  description: "Show the user's Picnic shopping cart: the products with their quantity and price, the total, and the delivery window. " +
15
11
  "A product that can't be delivered in that window has `unavailable` with the reason, and `replacements` Picnic offers instead.",
16
12
  annotations: { readOnlyHint: true, openWorldHint: true },
17
- }, () => runTool(async () => jsonResult(formatCart(await client.cart.getCart(), countryCode)), onError));
13
+ }, () => runTool(async () => {
14
+ const cart = await client.cart.getCart();
15
+ return jsonResult(formatCart(cart, countryCode, await loadReplacements(context, cart)));
16
+ }, onError));
18
17
  server.registerTool("add_to_cart", {
19
18
  title: "Add to cart",
20
19
  description: "Add a product to the user's Picnic shopping cart. Get the product id from search_products first. " +
@@ -30,6 +29,9 @@ export function registerCartTools(server, context) {
30
29
  return errorResult(`Picnic did not add ${productId}. Check the product id with search_products.`);
31
30
  }
32
31
  const unavailableLine = lines.find((line) => line.isUnavailable);
32
+ const replacements = unavailableLine
33
+ ? (await loadReplacements(context, cart, [productId])).get(productId)
34
+ : undefined;
33
35
  return jsonResult({
34
36
  ...(unavailableLine && {
35
37
  next: "The product is in the cart, but it can't be delivered in the selected delivery window. Tell the user, and offer the replacements if there are any.",
@@ -38,7 +40,7 @@ export function registerCartTools(server, context) {
38
40
  productId,
39
41
  name: lines[0].name,
40
42
  quantityInCart: quantityOf(lines),
41
- ...(unavailableLine && formatUnavailability(unavailableLine, countryCode)),
43
+ ...(unavailableLine && formatUnavailability(unavailableLine, replacements)),
42
44
  },
43
45
  cart: formatCartSummary(cart, countryCode),
44
46
  });
@@ -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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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { ToolContext } from "./context.js";
3
+ export declare function registerDeliveryTools(server: McpServer, context: ToolContext): void;
@@ -0,0 +1,40 @@
1
+ import * as z from "zod";
2
+ import { formatDelivery } from "../format/deliveries.js";
3
+ import { jsonResult, runTool } from "./result.js";
4
+ /** At most this many upcoming deliveries are shown, each costing one or two requests. */
5
+ const MAX_DELIVERIES = 3;
6
+ export function registerDeliveryTools(server, context) {
7
+ const { client, countryCode, onError } = context;
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.",
12
+ inputSchema: z.object({
13
+ includeProducts: z
14
+ .boolean()
15
+ .default(false)
16
+ .describe("Also list the products of each delivery. Default false."),
17
+ }),
18
+ annotations: { readOnlyHint: true, openWorldHint: true },
19
+ }, ({ includeProducts }) => runTool(async () => {
20
+ const summaries = (await client.delivery.getDeliverySummaries(["CURRENT"]))
21
+ .sort(byWindowStart)
22
+ .slice(0, MAX_DELIVERIES);
23
+ if (summaries.length === 0) {
24
+ return jsonResult({ next: "The user has no upcoming deliveries.", deliveries: [] });
25
+ }
26
+ const deliveries = await Promise.all(summaries.map(async (summary) => {
27
+ 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),
30
+ includeProducts ? client.delivery.getDelivery(summary.id) : null,
31
+ ]);
32
+ return formatDelivery(summary, position, delivery, countryCode);
33
+ }));
34
+ return jsonResult({ deliveries });
35
+ }, onError));
36
+ }
37
+ function byWindowStart(a, b) {
38
+ return (a.windowStart ?? "").localeCompare(b.windowStart ?? "");
39
+ }
40
+ //# sourceMappingURL=deliveries.js.map
@@ -0,0 +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"}
@@ -0,0 +1,14 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import { type ToolContext } from "./context.js";
3
+ /** A set of tools that can be turned on together. */
4
+ export type ToolGroup = "cart" | "slots" | "product" | "deliveries" | "recipes";
5
+ /** Every group, in the order the tools are registered. */
6
+ export declare const TOOL_GROUPS: ToolGroup[];
7
+ /** The groups that are on when none are given: the cart flow and read-only product details. */
8
+ export declare const DEFAULT_TOOL_GROUPS: readonly ToolGroup[];
9
+ /** Reads a comma-separated list of groups, e.g. "cart,recipes". */
10
+ export declare function parseToolGroups(value: string): ToolGroup[];
11
+ /** Registers the tools of the given groups. */
12
+ export declare function registerToolGroups(server: McpServer, context: ToolContext, groups: readonly ToolGroup[]): void;
13
+ /** How to use the tools of the given groups, for the server instructions. */
14
+ export declare function toolGroupInstructions(context: ToolContext, groups: readonly ToolGroup[]): string;
@@ -0,0 +1,64 @@
1
+ import { registerCartTools } from "./cart.js";
2
+ import { SEARCH_LANGUAGES } from "./context.js";
3
+ import { registerDeliveryTools } from "./deliveries.js";
4
+ import { registerProductTools } from "./product.js";
5
+ import { registerRecipeTools } from "./recipes.js";
6
+ import { registerSearchProducts } from "./search-products.js";
7
+ import { registerSlotTools } from "./slots.js";
8
+ const GROUPS = {
9
+ cart: {
10
+ register: (server, context) => {
11
+ registerSearchProducts(server, context);
12
+ registerCartTools(server, context);
13
+ },
14
+ instructions: ({ countryCode }) => `To add a product: search_products (query in ${SEARCH_LANGUAGES[countryCode]}), then add_to_cart with the product id. If the search returns choices, ask the user first.`,
15
+ },
16
+ slots: {
17
+ register: registerSlotTools,
18
+ instructions: () => "To choose when the order is delivered: get_delivery_slots, then select_delivery_slot once the user picked one.",
19
+ },
20
+ product: {
21
+ register: registerProductTools,
22
+ instructions: () => "For a product's ingredients, allergens or nutrition: get_product.",
23
+ },
24
+ deliveries: {
25
+ register: registerDeliveryTools,
26
+ instructions: () => "For orders that are on their way: get_deliveries.",
27
+ },
28
+ recipes: {
29
+ register: registerRecipeTools,
30
+ instructions: () => "For recipes: search_recipes, get_recipe, then add_recipe_to_cart once the user picked one.",
31
+ },
32
+ };
33
+ /** Every group, in the order the tools are registered. */
34
+ export const TOOL_GROUPS = Object.keys(GROUPS);
35
+ /** The groups that are on when none are given: the cart flow and read-only product details. */
36
+ export const DEFAULT_TOOL_GROUPS = ["cart", "slots", "product"];
37
+ /** Reads a comma-separated list of groups, e.g. "cart,recipes". */
38
+ export function parseToolGroups(value) {
39
+ const groups = value
40
+ .split(",")
41
+ .map((name) => name.trim().toLowerCase())
42
+ .filter(Boolean);
43
+ const unknown = groups.filter((name) => !TOOL_GROUPS.includes(name));
44
+ if (unknown.length > 0) {
45
+ throw new Error(`Unknown tool group ${unknown.map((name) => `"${name}"`).join(", ")}. Use any of ${TOOL_GROUPS.join(", ")}.`);
46
+ }
47
+ if (groups.length === 0)
48
+ throw new Error(`No tool groups given. Use any of ${TOOL_GROUPS.join(", ")}.`);
49
+ return groups;
50
+ }
51
+ /** Registers the tools of the given groups. */
52
+ export function registerToolGroups(server, context, groups) {
53
+ for (const group of TOOL_GROUPS) {
54
+ if (groups.includes(group))
55
+ GROUPS[group].register(server, context);
56
+ }
57
+ }
58
+ /** How to use the tools of the given groups, for the server instructions. */
59
+ export function toolGroupInstructions(context, groups) {
60
+ return TOOL_GROUPS.filter((group) => groups.includes(group))
61
+ .map((group) => GROUPS[group].instructions(context))
62
+ .join(" ");
63
+ }
64
+ //# sourceMappingURL=groups.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.js","sourceRoot":"","sources":["../../src/tools/groups.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import * as z from "zod";
2
+ /** A product (selling unit) id. Rejects a product name before anything is sent to Picnic. */
3
+ export declare const productId: z.ZodString;
4
+ export declare const quantity: (what: string) => z.ZodDefault<z.ZodNumber>;
@@ -0,0 +1,9 @@
1
+ import * as z from "zod";
2
+ /** A product (selling unit) id. Rejects a product name before anything is sent to Picnic. */
3
+ export const productId = z
4
+ .string()
5
+ .trim()
6
+ .regex(/^s\d+$/, "Use a product id from search_products or get_cart, e.g. s1004201.")
7
+ .describe("The product id from search_products or get_cart, e.g. s1004201.");
8
+ export const quantity = (what) => z.number().int().min(1).max(50).default(1).describe(`How many to ${what}. Default 1.`);
9
+ //# sourceMappingURL=inputs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputs.js","sourceRoot":"","sources":["../../src/tools/inputs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,6FAA6F;AAC7F,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC;KACvB,MAAM,EAAE;KACR,IAAI,EAAE;KACN,KAAK,CAAC,QAAQ,EAAE,mEAAmE,CAAC;KACpF,QAAQ,CAAC,iEAAiE,CAAC,CAAC;AAE/E,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CACvC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,IAAI,cAAc,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { ToolContext } from "./context.js";
3
+ export declare function registerProductTools(server: McpServer, context: ToolContext): void;
@@ -0,0 +1,15 @@
1
+ import * as z from "zod";
2
+ import { formatProductDetails } from "../format/product.js";
3
+ import { productId } from "./inputs.js";
4
+ import { jsonResult, runTool } from "./result.js";
5
+ export function registerProductTools(server, context) {
6
+ const { client, countryCode, onError } = context;
7
+ server.registerTool("get_product", {
8
+ title: "Get product details",
9
+ description: "Get the details of a Picnic product: ingredients, allergens, nutrition, storage info, promotion and price. " +
10
+ "Use it for questions such as whether a product contains gluten. Get the product id from search_products or get_cart.",
11
+ inputSchema: z.object({ productId }),
12
+ annotations: { readOnlyHint: true, openWorldHint: true },
13
+ }, ({ productId }) => runTool(async () => jsonResult(formatProductDetails(await client.catalog.getProduct(productId), countryCode)), onError));
14
+ }
15
+ //# sourceMappingURL=product.js.map
@@ -0,0 +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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import { type ToolContext } from "./context.js";
3
+ export declare function registerRecipeTools(server: McpServer, context: ToolContext): void;
@@ -0,0 +1,84 @@
1
+ import * as z from "zod";
2
+ import { formatCartSummary } from "../format/cart.js";
3
+ import { formatRecipe, formatRecipeSummary } from "../format/recipes.js";
4
+ import { SEARCH_LANGUAGES } from "./context.js";
5
+ import { jsonResult, runTool } from "./result.js";
6
+ /** At most this many recipes in search results. */
7
+ const MAX_RECIPES = 8;
8
+ const recipeId = z
9
+ .string()
10
+ .trim()
11
+ .min(1)
12
+ .describe("The recipeId from search_recipes or get_recipe.");
13
+ const portions = z
14
+ .number()
15
+ .int()
16
+ .min(1)
17
+ .max(20)
18
+ .optional()
19
+ .describe("The number of portions. Leave out for the recipe's default.");
20
+ export function registerRecipeTools(server, context) {
21
+ const { client, countryCode, onError } = context;
22
+ const language = SEARCH_LANGUAGES[countryCode];
23
+ server.registerTool("search_recipes", {
24
+ title: "Search recipes",
25
+ description: `Search Picnic's recipes. Write the query in ${language}, e.g. a dish or an ingredient. ` +
26
+ "Returns recipe ids for get_recipe and add_recipe_to_cart.",
27
+ inputSchema: z.object({
28
+ query: z.string().trim().min(1).describe(`What to search for, in ${language}.`),
29
+ }),
30
+ annotations: { readOnlyHint: true, openWorldHint: true },
31
+ }, ({ query }) => runTool(async () => {
32
+ const results = await client.catalog.getSearchResults(query, { context: "MEALS" });
33
+ const recipes = results.recipes.slice(0, MAX_RECIPES).map(formatRecipeSummary);
34
+ return jsonResult({
35
+ ...(recipes.length === 0 && { next: "No recipes found. Try another search term." }),
36
+ recipes,
37
+ });
38
+ }, onError));
39
+ server.registerTool("get_recipe", {
40
+ title: "Get recipe",
41
+ description: "Get a Picnic recipe: its ingredients with the products that are added for them, allergens and steps. " +
42
+ "Ingredients with `added: false` aren't added to the cart by default; `cupboard` ones are things most people have at home.",
43
+ inputSchema: z.object({ recipeId, portions }),
44
+ annotations: { readOnlyHint: true, openWorldHint: true },
45
+ }, ({ recipeId, portions }) => runTool(async () => jsonResult(formatRecipe(await client.recipe.getRecipe(recipeId, { portions }), countryCode)), onError));
46
+ server.registerTool("add_recipe_to_cart", {
47
+ title: "Add recipe to cart",
48
+ description: "Add the ingredients of a Picnic recipe to the user's cart, for the given number of portions. " +
49
+ "Only after the user chose the recipe. Returns the portions and the cart total. This does not place an order.",
50
+ inputSchema: z.object({ recipeId, portions }),
51
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
52
+ }, ({ recipeId, portions }) => runTool(async () => {
53
+ const result = await client.recipe.addRecipeToCart(recipeId, { portions });
54
+ const cart = await client.cart.getCart();
55
+ const recipe = cart.recipes.find((item) => item.id === recipeId);
56
+ return jsonResult({
57
+ ...(result.anyUnavailableIngredient && {
58
+ next: "Some ingredients can't be delivered. Tell the user; get_cart shows which ones and their replacements.",
59
+ }),
60
+ added: {
61
+ recipeId,
62
+ ...(recipe && { name: recipe.title }),
63
+ portions: result.assignedNumberOfPortions,
64
+ },
65
+ cart: formatCartSummary(cart, countryCode),
66
+ });
67
+ }, onError));
68
+ server.registerTool("remove_recipe_from_cart", {
69
+ title: "Remove recipe from cart",
70
+ description: "Remove a recipe and the ingredients that were added for it from the user's cart. " +
71
+ "Returns the number of products removed and the cart total.",
72
+ inputSchema: z.object({ recipeId }),
73
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
74
+ }, ({ recipeId }) => runTool(async () => {
75
+ const result = await client.recipe.removeRecipeFromCart(recipeId);
76
+ const cart = await client.cart.getCart();
77
+ const removedCount = result.SURemoved.reduce((sum, item) => sum + item.quantity, 0);
78
+ return jsonResult({
79
+ removed: { recipeId, productsRemoved: removedCount },
80
+ cart: formatCartSummary(cart, countryCode),
81
+ });
82
+ }, onError));
83
+ }
84
+ //# 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,9 @@
1
+ import { type ProductOutput } from "../format/products.js";
2
+ import type { Cart } from "../types.js";
3
+ import type { ToolContext } from "./context.js";
4
+ /**
5
+ * Reads the replacements of the given products that can't be delivered, as products to offer. A
6
+ * product whose replacements can't be read is left out: the model still sees why it can't be
7
+ * delivered.
8
+ */
9
+ export declare function loadReplacements({ client, countryCode, onError }: ToolContext, cart: Cart, productIds?: readonly string[]): Promise<Map<string, ProductOutput[]>>;
@@ -0,0 +1,33 @@
1
+ import { formatProduct } from "../format/products.js";
2
+ /** At most this many products' replacements are read, each one request. */
3
+ const MAX_PRODUCTS = 3;
4
+ /** At most this many replacements per product. */
5
+ const MAX_REPLACEMENTS = 3;
6
+ /**
7
+ * Reads the replacements of the given products that can't be delivered, as products to offer. A
8
+ * product whose replacements can't be read is left out: the model still sees why it can't be
9
+ * delivered.
10
+ */
11
+ export async function loadReplacements({ client, countryCode, onError }, cart, productIds = cart.items.map((item) => item.productId)) {
12
+ const unavailable = [
13
+ ...new Set(cart.items
14
+ .filter((item) => item.isUnavailable && productIds.includes(item.productId))
15
+ .map((item) => item.productId)),
16
+ ].slice(0, MAX_PRODUCTS);
17
+ const entries = await Promise.all(unavailable.map(async (productId) => {
18
+ try {
19
+ 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];
25
+ }
26
+ catch (error) {
27
+ onError(error);
28
+ return null;
29
+ }
30
+ }));
31
+ return new Map(entries.filter((entry) => entry !== null));
32
+ }
33
+ //# sourceMappingURL=replacements.js.map
@@ -0,0 +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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { ToolContext } from "./context.js";
3
+ export declare function registerSlotTools(server: McpServer, context: ToolContext): void;
@@ -0,0 +1,61 @@
1
+ import * as z from "zod";
2
+ import { formatCartSummary } from "../format/cart.js";
3
+ import { formatWindow } from "../format/prices.js";
4
+ import { slotDays } from "../format/slots.js";
5
+ import { errorResult, jsonResult, runTool } from "./result.js";
6
+ /** Without a date, the slots of this many days are listed. */
7
+ const DAYS_WITHOUT_DATE = 2;
8
+ export function registerSlotTools(server, context) {
9
+ const { client, countryCode, onError } = context;
10
+ server.registerTool("get_delivery_slots", {
11
+ title: "Get delivery slots",
12
+ description: "List the delivery slots the user can choose for the cart. Without a date it lists the first 2 days and the dates of the other days. " +
13
+ "Pass a date from that list to see its slots. Use select_delivery_slot once the user picked a slot.",
14
+ inputSchema: z.object({
15
+ date: z
16
+ .string()
17
+ .regex(/^\d{4}-\d{2}-\d{2}$/, "Use a date like 2026-10-23.")
18
+ .optional()
19
+ .describe("The day to list, as YYYY-MM-DD. Leave out for the first days."),
20
+ }),
21
+ annotations: { readOnlyHint: true, openWorldHint: true },
22
+ }, ({ date }) => runTool(async () => {
23
+ const slots = await client.cart.getDeliverySlots();
24
+ const days = slotDays(slots, countryCode);
25
+ const shown = date
26
+ ? days.filter((day) => day.date === date)
27
+ : days.slice(0, DAYS_WITHOUT_DATE);
28
+ const otherDays = days
29
+ .filter((day) => !shown.includes(day))
30
+ .map((day) => `${day.date} (${day.day})`);
31
+ const selected = formatWindow(slots.selectedSlot?.windowStart ?? null, slots.selectedSlot?.windowEnd ?? null);
32
+ return jsonResult({
33
+ ...(date &&
34
+ shown.length === 0 && { next: `No slots on ${date}. Pick one of otherDays.` }),
35
+ ...(selected && { selectedWindow: selected }),
36
+ days: shown,
37
+ ...(otherDays.length > 0 && { otherDays }),
38
+ });
39
+ }, onError));
40
+ server.registerTool("select_delivery_slot", {
41
+ title: "Select delivery slot",
42
+ description: "Choose when the cart is delivered. Only use a slotId from get_delivery_slots, after the user picked the slot. " +
43
+ "Returns the selected delivery window and the cart total. This does not place an order.",
44
+ inputSchema: z.object({
45
+ slotId: z.string().trim().min(1).describe("The slotId from get_delivery_slots."),
46
+ }),
47
+ annotations: {
48
+ readOnlyHint: false,
49
+ destructiveHint: false,
50
+ idempotentHint: true,
51
+ openWorldHint: true,
52
+ },
53
+ }, ({ slotId }) => runTool(async () => {
54
+ const cart = await client.cart.setDeliverySlot(slotId);
55
+ if (cart.selectedSlot?.slotId !== slotId) {
56
+ return errorResult(`Picnic did not select slot ${slotId}. Check the slotId with get_delivery_slots.`);
57
+ }
58
+ return jsonResult({ selected: true, cart: formatCartSummary(cart, countryCode) });
59
+ }, onError));
60
+ }
61
+ //# sourceMappingURL=slots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slots.js","sourceRoot":"","sources":["../../src/tools/slots.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE/D,8DAA8D;AAC9D,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B,MAAM,UAAU,iBAAiB,CAAC,MAAiB,EAAE,OAAoB;IACvE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAEjD,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,sIAAsI;YACtI,oGAAoG;QACtG,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,KAAK,CAAC,qBAAqB,EAAE,6BAA6B,CAAC;iBAC3D,QAAQ,EAAE;iBACV,QAAQ,CAAC,+DAA+D,CAAC;SAC7E,CAAC;QACF,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;KACzD,EACD,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CACX,OAAO,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI;YAChB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC;YACzC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI;aACnB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;aACrC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,YAAY,CAC3B,KAAK,CAAC,YAAY,EAAE,WAAW,IAAI,IAAI,EACvC,KAAK,CAAC,YAAY,EAAE,SAAS,IAAI,IAAI,CACtC,CAAC;QACF,OAAO,UAAU,CAAC;YAChB,GAAG,CAAC,IAAI;gBACN,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,eAAe,IAAI,0BAA0B,EAAE,CAAC;YAChF,GAAG,CAAC,QAAQ,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC;YAC7C,IAAI,EAAE,KAAK;YACX,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;SAC3C,CAAC,CAAC;IACL,CAAC,EAAE,OAAO,CAAC,CACd,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EACT,gHAAgH;YAChH,wFAAwF;QAC1F,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,qCAAqC,CAAC;SACjF,CAAC;QACF,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CACb,OAAO,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,IAAI,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;YACzC,OAAO,WAAW,CAChB,8BAA8B,MAAM,6CAA6C,CAClF,CAAC;QACJ,CAAC;QACD,OAAO,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,iBAAiB,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACpF,CAAC,EAAE,OAAO,CAAC,CACd,CAAC;AACJ,CAAC"}
package/dist/types.d.ts CHANGED
@@ -3,5 +3,13 @@ export type CountryCode = "NL" | "DE" | "FR";
3
3
  export declare const COUNTRY_CODES: readonly CountryCode[];
4
4
  export type SearchResults = PicnicClient.Types.catalog.SearchResults;
5
5
  export type ProductTile = PicnicClient.Types.catalog.ProductTile;
6
+ export type SearchRecipe = PicnicClient.Types.catalog.SearchRecipe;
7
+ export type Product = PicnicClient.Types.catalog.Product;
6
8
  export type Cart = PicnicClient.Types.cart.Cart;
7
9
  export type CartLineItem = PicnicClient.Types.cart.CartLineItem;
10
+ export type DeliverySlots = PicnicClient.Types.cart.DeliverySlots;
11
+ export type DeliverySlot = PicnicClient.Types.cart.DeliverySlot;
12
+ export type DeliverySummary = PicnicClient.Types.delivery.DeliverySummary;
13
+ export type Delivery = PicnicClient.Types.delivery.Delivery;
14
+ export type DeliveryPosition = PicnicClient.Types.delivery.DeliveryPosition;
15
+ export type Recipe = PicnicClient.Types.recipe.Recipe;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picnic-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.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.0.0",
50
+ "picnic-api": "^5.1.0",
51
51
  "zod": "^4.6.7"
52
52
  },
53
53
  "devDependencies": {