All files / src/tools invoices.ts

80% Statements 16/20
57.14% Branches 8/14
80% Functions 4/5
80% Lines 16/20

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          2x 10x                                         3x 3x 2x   3x 1x     1x         1x                                       1x         10x                       2x 2x   1x         1x         10x                                                        
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 registerInvoiceTools = (server: McpServer, client: BolClient): void => {
  server.registerTool(
    "list_invoices",
    {
      title: "List Invoices",
      description:
        "List invoices from bol.com. Optionally filter by date range using period start and end dates (format: YYYY-MM-DD). " +
        "The date range must not exceed 31 days.",
      annotations: { readOnlyHint: true, openWorldHint: true },
 
      inputSchema: z.object({
        periodStartDate: z
          .string()
          .optional()
          .describe("Start date for the invoice period (YYYY-MM-DD)."),
        periodEndDate: z
          .string()
          .optional()
          .describe("End date for the invoice period (YYYY-MM-DD)."),
      }),
    },
    async ({ periodStartDate, periodEndDate }) => {
      try {
        const response = await client.getInvoices(periodStartDate, periodEndDate);
        const invoices = response.invoiceListItems ?? [];
 
        if (invoices.length === 0) {
          return toTextResult("No invoices found for the specified period.");
        }
 
        return toTextResult(
          [
            `Invoices: ${invoices.length} results`,
            response.period ? `Period: ${response.period}` : null,
            ...invoices.map((inv) =>
              [
                `  - Invoice ${inv.invoiceId}`,
                inv.invoiceType ? `    Type: ${inv.invoiceType}` : null,
                inv.issueDate ? `    Issued: ${inv.issueDate}` : null,
                inv.invoicePeriod
                  ? `    Period: ${inv.invoicePeriod.startDate} to ${inv.invoicePeriod.endDate}`
                  : null,
                inv.legalMonetaryTotal?.payableAmount !== undefined
                  ? `    Payable: ${inv.legalMonetaryTotal.payableAmount}`
                  : null,
              ]
                .filter(Boolean)
                .join("\n"),
            ),
          ]
            .filter(Boolean)
            .join("\n"),
          { invoices } as Record<string, unknown>,
        );
      } catch (error) {
        return toErrorResult(error);
      }
    },
  );
 
  server.registerTool(
    "get_invoice",
    {
      title: "Get Invoice Details",
      description: "Get detailed information about a specific invoice.",
      annotations: { readOnlyHint: true, openWorldHint: true },
 
      inputSchema: z.object({
        invoiceId: z.string().min(1).describe("The bol.com invoice ID."),
      }),
    },
    async ({ invoiceId }) => {
      try {
        const invoice = await client.getInvoice(invoiceId);
 
        return toTextResult(
          `Invoice ${invoiceId} details retrieved.`,
          invoice as Record<string, unknown>,
        );
      } catch (error) {
        return toErrorResult(error);
      }
    },
  );
 
  server.registerTool(
    "get_invoice_specification",
    {
      title: "Get Invoice Specification",
      description:
        "Get an invoice specification with a paginated list of its transactions. " +
        "The specification contains detailed line items for the invoice.",
      annotations: { readOnlyHint: true, openWorldHint: true },
 
      inputSchema: z.object({
        invoiceId: z.string().min(1).describe("The bol.com invoice ID."),
        page: z.number().int().min(1).optional().describe("Page number (max 25,000 lines per page)."),
      }),
    },
    async ({ invoiceId, page }) => {
      try {
        const specification = await client.getInvoiceSpecification(invoiceId, page);
 
        return toTextResult(
          `Invoice specification for ${invoiceId} retrieved.`,
          specification as Record<string, unknown>,
        );
      } catch (error) {
        return toErrorResult(error);
      }
    },
  );
};