All files / src/tools promotions.ts

95.83% Statements 23/24
53.33% Branches 16/30
100% Functions 6/6
95.83% Lines 23/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131          2x 11x                                 3x 3x 2x   3x 1x     1x       1x                         1x         11x                         2x 2x   1x                         1x         11x                           2x 2x 1x   2x       1x       1x                       1x          
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import * as z from "zod/v4";
import type { BolClient } from "../bol-client.js";
import { toTextResult, toErrorResult } from "../tool-result.js";
 
export const registerPromotionTools = (server: McpServer, client: BolClient): void => {
  server.registerTool(
    "list_promotions",
    {
      title: "List Promotions",
      description:
        "List available promotions on bol.com. " +
        "Filter by promotion type: AWARENESS (visibility promotions) or PRICE_OFF (discount promotions).",
      annotations: { readOnlyHint: true, openWorldHint: true },
 
      inputSchema: z.object({
        promotionType: z
          .enum(["AWARENESS", "PRICE_OFF"])
          .describe("Type of promotion: AWARENESS (visibility) or PRICE_OFF (discount)."),
        page: z.number().int().min(1).default(1).describe("Page number (1-based)."),
      }),
    },
    async ({ promotionType, page }) => {
      try {
        const response = await client.getPromotions(promotionType, page);
        const promotions = response.promotions ?? [];
 
        if (promotions.length === 0) {
          return toTextResult("No promotions found.");
        }
 
        return toTextResult(
          [
            `Promotions (page ${page}): ${promotions.length} results`,
            ...promotions.map((p) =>
              [
                `  - ${p.promotionId}: ${p.title ?? "Untitled"}`,
                p.startDateTime ? `    Start: ${p.startDateTime}` : null,
                p.endDateTime ? `    End: ${p.endDateTime}` : null,
                p.type ? `    Type: ${p.type}` : null,
              ]
                .filter(Boolean)
                .join("\n"),
            ),
          ].join("\n"),
          { promotions } as Record<string, unknown>,
        );
      } catch (error) {
        return toErrorResult(error);
      }
    },
  );
 
  server.registerTool(
    "get_promotion",
    {
      title: "Get Promotion",
      description:
        "Get detailed information about a specific promotion including its type, dates, and conditions.",
      annotations: { readOnlyHint: true, openWorldHint: true },
 
      inputSchema: z.object({
        promotionId: z.string().min(1).describe("The bol.com promotion ID."),
      }),
    },
    async ({ promotionId }) => {
      try {
        const promotion = await client.getPromotion(promotionId);
 
        return toTextResult(
          [
            `Promotion: ${promotion.promotionId}`,
            promotion.title ? `Title: ${promotion.title}` : null,
            promotion.type ? `Type: ${promotion.type}` : null,
            promotion.startDateTime ? `Start: ${promotion.startDateTime}` : null,
            promotion.endDateTime ? `End: ${promotion.endDateTime}` : null,
          ]
            .filter(Boolean)
            .join("\n"),
          promotion as Record<string, unknown>,
        );
      } catch (error) {
        return toErrorResult(error);
      }
    },
  );
 
  server.registerTool(
    "get_promotion_products",
    {
      title: "Get Promotion Products",
      description:
        "Get the list of products participating in a specific promotion.",
      annotations: { readOnlyHint: true, openWorldHint: true },
 
      inputSchema: z.object({
        promotionId: z.string().min(1).describe("The bol.com promotion ID."),
        page: z.number().int().min(1).default(1).describe("Page number (1-based)."),
      }),
    },
    async ({ promotionId, page }) => {
      try {
        const response = await client.getPromotionProducts(promotionId, page);
        const products = response.products ?? [];
 
        Iif (products.length === 0) {
          return toTextResult("No products found for this promotion.");
        }
 
        return toTextResult(
          [
            `Promotion ${promotionId} products (page ${page}): ${products.length} results`,
            ...products.map((p) =>
              [
                `  - EAN: ${p.ean ?? "unknown"}`,
                p.title ? `    Title: ${p.title}` : null,
                p.unitPrice !== undefined ? `    Price: ${p.unitPrice}` : null,
              ]
                .filter(Boolean)
                .join("\n"),
            ),
          ].join("\n"),
          { products } as Record<string, unknown>,
        );
      } catch (error) {
        return toErrorResult(error);
      }
    },
  );
};