{
  "meta": {
    "attribution": "Anchor Terminal (https://www.anchorterminal.com)",
    "docs": "https://www.anchorterminal.com/docs/",
    "generatedAt": "2026-10-04",
    "license": "CC-BY-4.0",
    "method": "https://www.anchorterminal.com/benchmark/",
    "methodology": "0.3",
    "openapi": "https://www.anchorterminal.com/openapi.json",
    "preview": false,
    "run": "2026-10-01",
    "runLabel": "October 2026 research run"
  },
  "tool": {
    "category": "commerce",
    "endpoint": "https://mcp.kapruka.com/mcp",
    "jsonUrl": "https://www.anchorterminal.com/api/v1/tools/kapruka-mcp.json",
    "kind": "mcp",
    "listed": "indexed",
    "liveUrl": "https://www.anchorterminal.com/api/v1/live/kapruka-mcp.json",
    "markdownUrl": "https://www.anchorterminal.com/tools/kapruka-mcp.md",
    "mcpTools": {
      "check": {
        "checker": "anchor-check/1.0",
        "totalTokens": 9979,
        "counts": {
          "error": 0,
          "note": 9,
          "warn": 17
        },
        "findings": [
          {
            "rule": "TC07",
            "severity": "warn",
            "tool": "kapruka_check_delivery",
            "message": "the description is about 1,103 tokens",
            "fix": "Keep the description to what a model needs to choose and call the tool; move the manual to a resource or the docs."
          },
          {
            "rule": "TC07",
            "severity": "warn",
            "tool": "kapruka_create_order",
            "message": "the description is about 994 tokens",
            "fix": "Keep the description to what a model needs to choose and call the tool; move the manual to a resource or the docs."
          },
          {
            "rule": "TC07",
            "severity": "warn",
            "tool": "kapruka_get_product",
            "message": "the description is about 648 tokens",
            "fix": "Keep the description to what a model needs to choose and call the tool; move the manual to a resource or the docs."
          },
          {
            "rule": "TC07",
            "severity": "warn",
            "tool": "kapruka_search_products",
            "message": "the description is about 905 tokens",
            "fix": "Keep the description to what a model needs to choose and call the tool; move the manual to a resource or the docs."
          },
          {
            "rule": "TC07",
            "severity": "warn",
            "tool": "kapruka_track_order",
            "message": "the description is about 545 tokens",
            "fix": "Keep the description to what a model needs to choose and call the tool; move the manual to a resource or the docs."
          },
          {
            "rule": "TC15",
            "severity": "warn",
            "tool": "kapruka_check_delivery",
            "message": "inputSchema uses $ref/$defs",
            "fix": "Inline the referenced schemas. Pydantic and zod-to-json-schema emit $defs for nested models; most can be told not to."
          },
          {
            "rule": "TC15",
            "severity": "warn",
            "tool": "kapruka_create_order",
            "message": "inputSchema uses $ref/$defs",
            "fix": "Inline the referenced schemas. Pydantic and zod-to-json-schema emit $defs for nested models; most can be told not to."
          },
          {
            "rule": "TC15",
            "severity": "warn",
            "tool": "kapruka_get_product",
            "message": "inputSchema uses $ref/$defs",
            "fix": "Inline the referenced schemas. Pydantic and zod-to-json-schema emit $defs for nested models; most can be told not to."
          },
          {
            "rule": "TC15",
            "severity": "warn",
            "tool": "kapruka_list_categories",
            "message": "inputSchema uses $ref/$defs",
            "fix": "Inline the referenced schemas. Pydantic and zod-to-json-schema emit $defs for nested models; most can be told not to."
          },
          {
            "rule": "TC15",
            "severity": "warn",
            "tool": "kapruka_list_delivery_cities",
            "message": "inputSchema uses $ref/$defs",
            "fix": "Inline the referenced schemas. Pydantic and zod-to-json-schema emit $defs for nested models; most can be told not to."
          },
          {
            "rule": "TC15",
            "severity": "warn",
            "tool": "kapruka_render_options_card",
            "message": "inputSchema uses $ref/$defs",
            "fix": "Inline the referenced schemas. Pydantic and zod-to-json-schema emit $defs for nested models; most can be told not to."
          },
          {
            "rule": "TC15",
            "severity": "warn",
            "tool": "kapruka_search_products",
            "message": "inputSchema uses $ref/$defs",
            "fix": "Inline the referenced schemas. Pydantic and zod-to-json-schema emit $defs for nested models; most can be told not to."
          },
          {
            "rule": "TC15",
            "severity": "warn",
            "tool": "kapruka_track_order",
            "message": "inputSchema uses $ref/$defs",
            "fix": "Inline the referenced schemas. Pydantic and zod-to-json-schema emit $defs for nested models; most can be told not to."
          },
          {
            "rule": "TC18",
            "severity": "warn",
            "tool": "kapruka_track_order",
            "message": "readOnlyHint is true but the name says \"order\"",
            "fix": "If it changes anything, readOnlyHint must be false."
          },
          {
            "rule": "TC22",
            "severity": "warn",
            "tool": "kapruka_check_delivery",
            "message": "the definition is about 1,973 tokens",
            "fix": "Trim the description and parameter docs, or split the tool."
          },
          {
            "rule": "TC22",
            "severity": "warn",
            "tool": "kapruka_create_order",
            "message": "the definition is about 2,227 tokens",
            "fix": "Trim the description and parameter docs, or split the tool."
          },
          {
            "rule": "TC22",
            "severity": "warn",
            "tool": "kapruka_search_products",
            "message": "the definition is about 1,704 tokens",
            "fix": "Trim the description and parameter docs, or split the tool."
          },
          {
            "rule": "TC11",
            "severity": "note",
            "tool": "kapruka_check_delivery",
            "message": "a parameter is documented in the description but not in the schema: params",
            "fix": "Put each one in its property's description (in Python: Annotated[str, Field(description=...)])."
          },
          {
            "rule": "TC11",
            "severity": "note",
            "tool": "kapruka_create_order",
            "message": "a parameter is documented in the description but not in the schema: params",
            "fix": "Put each one in its property's description (in Python: Annotated[str, Field(description=...)])."
          },
          {
            "rule": "TC11",
            "severity": "note",
            "tool": "kapruka_get_product",
            "message": "a parameter is documented in the description but not in the schema: params",
            "fix": "Put each one in its property's description (in Python: Annotated[str, Field(description=...)])."
          },
          {
            "rule": "TC11",
            "severity": "note",
            "tool": "kapruka_list_categories",
            "message": "a parameter is documented in the description but not in the schema: params",
            "fix": "Put each one in its property's description (in Python: Annotated[str, Field(description=...)])."
          },
          {
            "rule": "TC11",
            "severity": "note",
            "tool": "kapruka_list_delivery_cities",
            "message": "a parameter is documented in the description but not in the schema: params",
            "fix": "Put each one in its property's description (in Python: Annotated[str, Field(description=...)])."
          },
          {
            "rule": "TC11",
            "severity": "note",
            "tool": "kapruka_render_options_card",
            "message": "a parameter is documented in the description but not in the schema: params",
            "fix": "Put each one in its property's description (in Python: Annotated[str, Field(description=...)])."
          },
          {
            "rule": "TC11",
            "severity": "note",
            "tool": "kapruka_search_products",
            "message": "a parameter is documented in the description but not in the schema: params",
            "fix": "Put each one in its property's description (in Python: Annotated[str, Field(description=...)])."
          },
          {
            "rule": "TC11",
            "severity": "note",
            "tool": "kapruka_track_order",
            "message": "a parameter is documented in the description but not in the schema: params",
            "fix": "Put each one in its property's description (in Python: Annotated[str, Field(description=...)])."
          },
          {
            "rule": "TC29",
            "severity": "note",
            "message": "about 503 tokens (5% of the definitions) are titles that repeat property or function names",
            "fix": "Strip \"title\" keys that only restate a name before listing tools."
          }
        ]
      },
      "checkedAt": "2026-10-04T22:23:08Z",
      "count": 8,
      "schemaTokens": 9979,
      "status": "ok",
      "tools": [
        {
          "name": "kapruka_list_categories",
          "description": "List top-level Kapruka product categories by name with browse URLs.\n\n    Returns the site's NAVIGATION categories plus the public Kapruka.com URL for each\n    category landing page — useful for sending a customer to a category to browse.\n\n    These names are NOT search filters. kapruka_search_products filters on search\n    facets, a different vocabulary (e.g. navigation 'Electronic' vs facet 'Electronics',\n    'Cakes' vs 'Kapruka Cakes'); every search response lists its valid facet names\n    under facets.categories. Use those for `category`, never these. Internal IDs and\n    product counts are not exposed. Results are cached for 30 minutes server-side.\n\n    Args:\n        params (ListCategoriesInput):\n            - depth (int): Sub-category levels to include, 1 or 2 (default 1)\n            - response_format (str): 'markdown' (default) or 'json'\n\n    Returns:\n        str: Category tree in the requested format.\n\n        JSON schema:\n        {\n          \"categories\": [\n            {\n              \"name\": str,\n              \"url\": str,                  # kapruka.com category landing page\n              \"children\": [{\"name\": str, \"url\": str, \"children\": [...]}]\n            }\n          ]\n        }\n\n        Error: \"Error: \u003cmessage\u003e\" on failure.\n    ",
          "inputSchema": {
            "$defs": {
              "ListCategoriesInput": {
                "additionalProperties": false,
                "properties": {
                  "depth": {
                    "default": 1,
                    "description": "How many levels of sub-categories to include (1 or 2). Default 1.",
                    "maximum": 2,
                    "minimum": 1,
                    "title": "Depth",
                    "type": "integer"
                  },
                  "response_format": {
                    "default": "markdown",
                    "description": "'markdown' for human-readable output, 'json' for raw structured data.",
                    "title": "Response Format",
                    "type": "string"
                  }
                },
                "title": "ListCategoriesInput",
                "type": "object"
              }
            },
            "properties": {
              "params": {
                "$ref": "#/$defs/ListCategoriesInput"
              }
            },
            "required": [
              "params"
            ],
            "title": "kapruka_list_categoriesArguments",
            "type": "object"
          },
          "outputSchema": {
            "properties": {
              "result": {
                "title": "Result",
                "type": "string"
              }
            },
            "required": [
              "result"
            ],
            "title": "kapruka_list_categoriesOutput",
            "type": "object"
          },
          "annotations": {
            "destructiveHint": false,
            "idempotentHint": true,
            "openWorldHint": true,
            "readOnlyHint": true,
            "title": "List Kapruka Product Categories"
          }
        },
        {
          "name": "kapruka_get_product",
          "description": "Fetch full details for a single Kapruka product by its product ID.\n\n    Returns name, description, price (with optional currency conversion), stock status,\n    images, variants, shipping info, delivery scope, and a direct product URL.\n\n    Delivery scope: most gifts ship island-wide, but restaurant food, hotel cakes\n    and liquor only reach a limited city set (typically the Colombo area). The\n    `delivery` object is the authority — search results do NOT carry it. When\n    `delivery.island_wide` is false, tell the customer up front that the item is\n    delivered only to selected cities, and confirm their city with\n    kapruka_check_delivery(city, product_id) before promising anything. If\n    `deliverable_city_count` exceeds the returned list, the list is truncated —\n    say \"and more\", don't treat it as complete.\n\n    Note: Some IDs starting with 'CATSYM' are category landing pages, not purchasable\n    products — this tool will flag those clearly.\n\n    Args:\n        params (GetProductInput):\n            - product_id (str): Kapruka product ID (e.g. 'cakeXX000000')\n            - currency (str): Price currency — LKR (default), USD, GBP, AUD, EUR\n            - type (Optional[str]): Optional type hint (e.g. 'specialgifts')\n            - response_format (str): 'markdown' (default) or 'json'\n\n    Returns:\n        str: Product details in the requested format.\n\n        JSON schema:\n        {\n          \"id\": str,\n          \"name\": str,\n          \"description\": str,\n          \"summary\": str,\n          \"price\": {\"amount\": float, \"currency\": str},\n          \"compare_at_price\": {\"amount\": float, \"currency\": str} | null,\n          \"in_stock\": bool,\n          \"stock_level\": str,           # \"low\" | \"medium\" | \"high\"\n          \"category\": {\"id\": str, \"name\": str, \"slug\": str, \"path\": str},\n          \"variants\": [{\"id\": str, \"name\": str, \"sku\": str, \"price\": {...},\n                        \"in_stock\": bool, \"stock_level\": str, \"attributes\": {...}}],\n          \"images\": [str],              # list of full-resolution image URLs\n          \"attributes\": {\"type\": str, \"subtype\": str, \"weight\": str, \"vendor\": str},\n          \"shipping\": {\"ships_from\": str, \"ships_internationally\": bool, \"restricted_countries\": [str]},\n          \"delivery\": {\n            \"island_wide\": bool,\n            \"deliverable_city_count\": int,   # only when island_wide=false; TRUE total\n            \"deliverable_cities\": [str]      # only when island_wide=false; capped at 60\n          },\n          \"rating\": null,\n          \"url\": str\n        }\n\n        Error: \"Error: \u003cmessage\u003e\" on failure.\n    ",
          "inputSchema": {
            "$defs": {
              "GetProductInput": {
                "additionalProperties": false,
                "properties": {
                  "currency": {
                    "default": "LKR",
                    "description": "Price currency. Supported: LKR, USD, GBP, AUD, EUR",
                    "title": "Currency",
                    "type": "string"
                  },
                  "product_id": {
                    "description": "Kapruka product ID (e.g. 'cakeXX000000', 'EF_PC_CHOC0V2774P00065')",
                    "maxLength": 80,
                    "minLength": 3,
                    "title": "Product Id",
                    "type": "string"
                  },
                  "response_format": {
                    "default": "markdown",
                    "description": "'markdown' for human-readable output, 'json' for raw structured data.",
                    "title": "Response Format",
                    "type": "string"
                  },
                  "type": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Optional product type hint passed to the API (e.g. 'specialgifts'). Rarely needed.",
                    "title": "Type"
                  }
                },
                "required": [
                  "product_id"
                ],
                "title": "GetProductInput",
                "type": "object"
              }
            },
            "properties": {
              "params": {
                "$ref": "#/$defs/GetProductInput"
              }
            },
            "required": [
              "params"
            ],
            "title": "kapruka_get_productArguments",
            "type": "object"
          },
          "outputSchema": {
            "properties": {
              "result": {
                "title": "Result",
                "type": "string"
              }
            },
            "required": [
              "result"
            ],
            "title": "kapruka_get_productOutput",
            "type": "object"
          },
          "annotations": {
            "destructiveHint": false,
            "idempotentHint": true,
            "openWorldHint": true,
            "readOnlyHint": true,
            "title": "Get Kapruka Product Details"
          }
        },
        {
          "name": "kapruka_search_products",
          "description": "Search for products on Kapruka.com by keyword, with optional category filter and pagination.\n\n    Returns a ranked list of matching products with prices, stock status, images, and URLs,\n    plus the category facets available for the query (facets.categories) — the names that\n    work in `category` to narrow the same search.\n    Supports cursor-based pagination — pass next_cursor from one response into the next call.\n    Pagination is capped at 3 pages per query to discourage catalog enumeration; for broader\n    discovery, refine the query or narrow with a facet instead.\n\n    Queries must be at least 3 characters and contain specific terms — pure stopword queries\n    (e.g. \"the\", \"a an\") are rejected.\n\n    Relevance: the API matches ANY single query word, so extra words add loosely related\n    results rather than narrowing. Search the thing the customer wants (the head noun) and\n    check the product names actually contain it before presenting them.\n\n    Prices and min_price/max_price are both in `currency` — pass the customer's own budget\n    in their own currency; do not convert it.\n\n    Search results carry NO delivery-scope information. Food, hotel cakes and liquor\n    are delivered only to selected cities — never infer deliverability from a search\n    hit. Before quoting delivery on a specific item, call kapruka_get_product (read\n    `delivery.island_wide`) or kapruka_check_delivery with `product_id`.\n\n    Args:\n        params (SearchProductsInput):\n            - q (str): Search query (e.g. 'birthday cake', 'roses', 'tea gift'). Min 3 chars.\n            - category (Optional[str]): A facet name from a previous search's facets.categories\n            - limit (int): Results per page, 1–50 (default 10)\n            - cursor (Optional[str]): Pagination cursor from previous response\n            - currency (str): LKR (default), USD, GBP, AUD, EUR\n            - min_price (Optional[float]): Min price (inclusive) in `currency`\n            - max_price (Optional[float]): Max price (inclusive) in `currency`\n            - in_stock_only (bool): Restrict to in-stock items (default false)\n            - sort (str): 'relevance' | 'price_asc' | 'price_desc' | 'newest' | 'bestseller'\n            - include_stubs (bool): Deprecated, no effect\n            - response_format (str): 'markdown' (default) or 'json'\n\n    Returns:\n        str: Search results in the requested format.\n\n        JSON schema:\n        {\n          \"results\": [\n            {\n              \"id\": str,\n              \"type\": \"product\",\n              \"name\": str,\n              \"summary\": str,\n              \"price\": {\"amount\": float | null, \"currency\": str},\n              \"compare_at_price\": {\"amount\": float, \"currency\": str} | null,\n              \"in_stock\": bool,\n              \"stock_level\": str,\n              \"image_url\": str | null,\n              \"category\": {\"id\": str, \"name\": str, \"slug\": str},\n              \"rating\": null,\n              \"ships_internationally\": bool,\n              \"url\": str\n            }\n          ],\n          \"next_cursor\": str | null,     # null after page 3 even if upstream has more\n          \"total_estimate\": int,         # index hits for the query words (any-word match)\n          \"applied_filters\": {\"q\": str, \"category\": str, \"currency\": str, ...},\n          \"facets\": {\"categories\": [{\"name\": str, \"count\": int}]},\n          \"category_filter_dropped\": str,   # only when `category` was not a facet here\n          \"valid_categories\": [str]         # ...and the facet names that are\n        }\n\n        Error: \"Error: \u003cmessage\u003e\" or \"No products found for '\u003cquery\u003e'\" on failure.\n    ",
          "inputSchema": {
            "$defs": {
              "SearchProductsInput": {
                "additionalProperties": false,
                "properties": {
                  "category": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Filter by a SEARCH FACET name. Every search response lists the valid ones for its words under facets.categories (e.g. 'Kapruka Cakes', 'Fresh Flowers', 'Electronics', 'Mobile Phones', 'Greeting Cards') — search once without a category, then narrow with one of those names. Department words ('Cakes', 'Flowers') and names from kapruka_list_categories (the site's navigation, a different vocabulary) are NOT facet names. Case and spacing don't matter. A name that isn't a facet for this query is dropped: you get results without it plus the list of valid names.",
                    "title": "Category"
                  },
                  "currency": {
                    "default": "LKR",
                    "description": "Price currency. Supported: LKR, USD, GBP, AUD, EUR",
                    "title": "Currency",
                    "type": "string"
                  },
                  "cursor": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Pagination cursor from a previous search response's 'next_cursor' field.",
                    "title": "Cursor"
                  },
                  "in_stock_only": {
                    "default": false,
                    "description": "If true, only return products currently in stock.",
                    "title": "In Stock Only",
                    "type": "boolean"
                  },
                  "include_stubs": {
                    "default": false,
                    "description": "Deprecated, has no effect. Kept so existing callers don't break: since 2026-09-26 the API never returns category landing pages from a search.",
                    "title": "Include Stubs",
                    "type": "boolean"
                  },
                  "limit": {
                    "default": 10,
                    "description": "Number of results to return (1–50).",
                    "maximum": 50,
                    "minimum": 1,
                    "title": "Limit",
                    "type": "integer"
                  },
                  "max_price": {
                    "anyOf": [
                      {
                        "minimum": 0,
                        "type": "number"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Maximum price (inclusive), in the requested currency.",
                    "title": "Max Price"
                  },
                  "min_price": {
                    "anyOf": [
                      {
                        "minimum": 0,
                        "type": "number"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Minimum price (inclusive), in the requested currency.",
                    "title": "Min Price"
                  },
                  "q": {
                    "description": "Search query (e.g. 'birthday cake', 'roses', 'chocolates for mom'). Min 3 characters, must contain specific terms (not stopwords only).",
                    "maxLength": 200,
                    "minLength": 3,
                    "title": "Q",
                    "type": "string"
                  },
                  "response_format": {
                    "default": "markdown",
                    "description": "'markdown' for human-readable output, 'json' for raw structured data.",
                    "title": "Response Format",
                    "type": "string"
                  },
                  "sort": {
                    "default": "relevance",
                    "description": "Sort order: 'relevance' (default), 'price_asc', 'price_desc', 'newest', 'bestseller'.",
                    "title": "Sort",
                    "type": "string"
                  }
                },
                "required": [
                  "q"
                ],
                "title": "SearchProductsInput",
                "type": "object"
              }
            },
            "properties": {
              "params": {
                "$ref": "#/$defs/SearchProductsInput"
              }
            },
            "required": [
              "params"
            ],
            "title": "kapruka_search_productsArguments",
            "type": "object"
          },
          "outputSchema": {
            "properties": {
              "result": {
                "title": "Result",
                "type": "string"
              }
            },
            "required": [
              "result"
            ],
            "title": "kapruka_search_productsOutput",
            "type": "object"
          },
          "annotations": {
            "destructiveHint": false,
            "idempotentHint": true,
            "openWorldHint": true,
            "readOnlyHint": true,
            "title": "Search Kapruka Products"
          }
        },
        {
          "name": "kapruka_create_order",
          "description": "Create a guest-checkout order on Kapruka and return a click-to-pay link.\n\n    Builds a Kapruka order from the supplied cart + recipient + delivery + sender,\n    then returns a checkout URL the customer opens in a browser to complete payment.\n    No Kapruka account is required. Prices are locked for the lifetime of the link\n    (60 minutes) — the customer pays exactly the quoted grand total even if the\n    catalog price changes meanwhile.\n\n    Free public tier limits: 30 orders per hour per client IP. Cart up to 30 items,\n    quantity up to 99 per item. A fresh idempotency key is generated per call so\n    retries on transient errors return the same checkout URL rather than duplicates.\n\n    Args:\n        params (CreateOrderInput):\n            - cart (list[CartItem]): 1–30 lines. Catalogue line: product_id, quantity (default 1), optional icing_text (cakes only).\n              Custom cake line: custom_cake_request_id + phone — orders the cake Kapruka staff quoted via\n              kapruka_custom_cake_status (status must be 'quoted'; quantity is always 1; never send a price).\n              Lines can be mixed in one order — one delivery fee covers everything; the whole cart must be\n              deliverable to delivery.city. Only place a custom cake order after the customer clearly\n              accepted the quoted total.\n            - recipient (Recipient): name + phone (E.164 +9477… or local 077…)\n            - delivery (Delivery): address, city (must be Kapruka-deliverable — use kapruka_list_delivery_cities), location_type (house/apartment/office/other, default house), date (YYYY-MM-DD, today-or-future Asia/Colombo), optional instructions\n            - sender (Sender): name + anonymous flag\n            - gift_message (Optional[str]): Up to 300 chars\n            - currency (str): LKR (default), USD, GBP, AUD, EUR\n            - response_format (str): 'markdown' (default) or 'json'\n\n    Returns:\n        str: Order confirmation with checkout URL.\n\n        JSON schema:\n        {\n          \"checkout_url\": str,           # Open in browser to pay (no login required)\n          \"order_ref\": str,              # e.g. \"ORD-20260520-7823\"\n          \"order_id\": str,               # id used by the bank-deposit flow\n          \"summary\": {\n            \"items_total\":   number,\n            \"delivery_fee\":  number,\n            \"addons_total\":  number,\n            \"grand_total\":   number,     # items_total + delivery_fee + addons_total\n            \"currency\":      str\n          },\n          \"expires_at\": str              # ISO 8601 — link stops working after this\n        }\n\n        Error: \"Error (\u003ccode\u003e): \u003cmessage\u003e\" on failure. Common codes:\n          empty_cart, missing_field, past_delivery_date, product_not_found,\n          product_out_of_stock, city_not_deliverable (city not in the network at\n          all), date_not_deliverable, city_not_deliverable_for_item.\n\n        Custom cake lines add: request_not_found (404 — wrong id/phone or staff\n        removed it), quote_not_ready (409 — staff haven't priced it; check\n        kapruka_custom_cake_status later), quote_expired (410 — submit a new\n        kapruka_custom_cake_request). summary.items_total may differ from the\n        quoted cake total by a few rupees (USD round-trip) — quote the summary\n        numbers when asking for payment.\n\n        city_not_deliverable_for_item (HTTP 422): at least one cart item (food /\n        hotel cake / liquor) cannot reach delivery.city. NOTHING is created — the\n        API never places a partial order and neither should you. The error text\n        names every blocking item and lists the cities the whole cart CAN go to.\n        Tell the customer which item blocks the order and offer to (a) change the\n        city to one of those, or (b) remove/replace that item. Never retry with\n        the same city. Avoid this entirely by calling kapruka_check_delivery with\n        `product_id` for each limited item before ordering.\n    ",
          "inputSchema": {
            "$defs": {
              "CartItem": {
                "additionalProperties": false,
                "description": "One cart line — EITHER a catalogue product OR a quoted custom cake.\n\nCatalogue line:   {\"product_id\": \"...\", \"quantity\": 1, \"icing_text\": \"...\"}\nCustom cake line: {\"custom_cake_request_id\": \"...\", \"phone\": \"+9477...\"}\nThe custom cake line is turned into the staff-quoted cake server-side\n(name, picture, price); quantity is always 1 and no price is ever sent.",
                "properties": {
                  "custom_cake_request_id": {
                    "anyOf": [
                      {
                        "maxLength": 80,
                        "minLength": 6,
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "request_id of a custom cake that kapruka_custom_cake_status reports as 'quoted'. Orders the staff-quoted cake (quantity 1). Requires `phone`.",
                    "title": "Custom Cake Request Id"
                  },
                  "icing_text": {
                    "anyOf": [
                      {
                        "maxLength": 120,
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Cake icing text. Silently ignored for non-cake products. Catalogue lines only.",
                    "title": "Icing Text"
                  },
                  "phone": {
                    "anyOf": [
                      {
                        "maxLength": 30,
                        "minLength": 7,
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "The customer.phone the custom cake request was made with (custom cake lines only).",
                    "title": "Phone"
                  },
                  "product_id": {
                    "anyOf": [
                      {
                        "maxLength": 80,
                        "minLength": 3,
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Kapruka product ID (e.g. 'cakeXX000000'). Catalogue line.",
                    "title": "Product Id"
                  },
                  "quantity": {
                    "anyOf": [
                      {
                        "maximum": 99,
                        "minimum": 1,
                        "type": "integer"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Quantity (1–99, default 1). Catalogue lines only.",
                    "title": "Quantity"
                  }
                },
                "title": "CartItem",
                "type": "object"
              },
              "CreateOrderInput": {
                "additionalProperties": false,
                "properties": {
                  "cart": {
                    "description": "1–30 items.",
                    "items": {
                      "$ref": "#/$defs/CartItem"
                    },
                    "maxItems": 30,
                    "minItems": 1,
                    "title": "Cart",
                    "type": "array"
                  },
                  "currency": {
                    "default": "LKR",
                    "description": "Pricing currency. Supported: LKR, USD, GBP, AUD, EUR.",
                    "title": "Currency",
                    "type": "string"
                  },
                  "delivery": {
                    "$ref": "#/$defs/Delivery"
                  },
                  "gift_message": {
                    "anyOf": [
                      {
                        "maxLength": 300,
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Optional gift card message.",
                    "title": "Gift Message"
                  },
                  "recipient": {
                    "$ref": "#/$defs/Recipient"
                  },
                  "response_format": {
                    "default": "markdown",
                    "description": "'markdown' (default) or 'json'.",
                    "title": "Response Format",
                    "type": "string"
                  },
                  "sender": {
                    "$ref": "#/$defs/Sender"
                  }
                },
                "required": [
                  "cart",
                  "recipient",
                  "delivery",
                  "sender"
                ],
                "title": "CreateOrderInput",
                "type": "object"
              },
              "Delivery": {
                "additionalProperties": false,
                "properties": {
                  "address": {
                    "description": "Street address.",
                    "maxLength": 250,
                    "minLength": 3,
                    "title": "Address",
                    "type": "string"
                  },
                  "city": {
                    "description": "Must be a Kapruka delivery city (canonical name from kapruka_list_delivery_cities) that EVERY cart item can reach. The order ships as one shipment, so food / hotel cake / liquor items restrict the whole cart to their city set — verify with kapruka_check_delivery(city, product_id) first; otherwise the order is rejected with city_not_deliverable_for_item.",
                    "maxLength": 100,
                    "minLength": 2,
                    "title": "City",
                    "type": "string"
                  },
                  "date": {
                    "description": "Delivery date in YYYY-MM-DD (Asia/Colombo). Must be today or future.",
                    "title": "Date",
                    "type": "string"
                  },
                  "instructions": {
                    "anyOf": [
                      {
                        "maxLength": 250,
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Free-form delivery instructions.",
                    "title": "Instructions"
                  },
                  "location_type": {
                    "default": "house",
                    "description": "One of: house, apartment, office, other.",
                    "title": "Location Type",
                    "type": "string"
                  }
                },
                "required": [
                  "address",
                  "city",
                  "date"
                ],
                "title": "Delivery",
                "type": "object"
              },
              "Recipient": {
                "additionalProperties": false,
                "properties": {
                  "name": {
                    "description": "Recipient name shown on the order.",
                    "maxLength": 80,
                    "minLength": 1,
                    "title": "Name",
                    "type": "string"
                  },
                  "phone": {
                    "description": "Recipient phone — E.164 (+9477…) or local SL (077…) format.",
                    "maxLength": 30,
                    "minLength": 7,
                    "title": "Phone",
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "phone"
                ],
                "title": "Recipient",
                "type": "object"
              },
              "Sender": {
                "additionalProperties": false,
                "properties": {
                  "anonymous": {
                    "default": false,
                    "description": "If true, gift card shows 'Anonymous' instead of the sender name.",
                    "title": "Anonymous",
                    "type": "boolean"
                  },
                  "name": {
                    "description": "Sender name on the gift card.",
                    "maxLength": 80,
                    "minLength": 1,
                    "title": "Name",
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ],
                "title": "Sender",
                "type": "object"
              }
            },
            "properties": {
              "params": {
                "$ref": "#/$defs/CreateOrderInput"
              }
            },
            "required": [
              "params"
            ],
            "title": "kapruka_create_orderArguments",
            "type": "object"
          },
          "outputSchema": {
            "properties": {
              "result": {
                "title": "Result",
                "type": "string"
              }
            },
            "required": [
              "result"
            ],
            "title": "kapruka_create_orderOutput",
            "type": "object"
          },
          "annotations": {
            "destructiveHint": false,
            "idempotentHint": true,
            "openWorldHint": true,
            "readOnlyHint": false,
            "title": "Create Kapruka Order (Guest Checkout)"
          }
        },
        {
          "name": "kapruka_track_order",
          "description": "Look up status and delivery progress for a Kapruka order by order number.\n\n    Returns current status (received / confirmed / out-for-delivery / delivered /\n    cancelled), the recipient and delivery details on file, a timestamped progress\n    timeline, the cart contents, and flags for whether a delivery photo or video is\n    available. Use this after a customer has placed and paid for an order and reads\n    back the order number from their confirmation email or the order complete page.\n\n    The order number is NOT the `order_ref` returned by kapruka_create_order\n    (which is the pre-payment checkout reference). Once the customer completes\n    payment in the browser, Kapruka emails them a separate order number — that\n    is what this tool expects.\n\n    Args:\n        params (TrackOrderInput):\n            - order_number (str): Kapruka order number (e.g. 'VIMP34456CB2')\n            - response_format (str): 'markdown' (default) or 'json'\n\n    Returns:\n        str: Order tracking details in the requested format.\n\n        JSON schema:\n        {\n          \"order_number\": str,\n          \"pnref\": str,                 # internal payment reference (numeric; not the same as order_number)\n          \"status\": str,                # received | confirmed | shipped | delivered | cancelled | ...\n          \"status_display\": str,        # human label\n          \"order_date\": str,            # human-formatted, Asia/Colombo\n          \"delivery_date\": str,         # human-formatted\n          \"shipped_date\": str | null,\n          \"amount\": str,                # LKR string (e.g. \"15500.00\")\n          \"payment_method\": str,\n          \"comments\": str | null,\n          \"recipient\": {\"name\": str, \"phone\": str, \"address\": str, \"city\": str},\n          \"greeting_message\": str | null,\n          \"special_instructions\": str | null,\n          \"progress\": [{\"step\": str, \"timestamp\": str}],\n          \"live_tracking_available\": bool,\n          \"has_delivery_video\": bool,\n          \"has_delivery_photo\": bool,\n          \"items\": [{\"product_id\": str, \"name\": str, \"quantity\": int, \"selling_price\": float}]\n        }\n\n        Error: \"Error: \u003cmessage\u003e\" on failure (e.g. order not found).\n    ",
          "inputSchema": {
            "$defs": {
              "TrackOrderInput": {
                "additionalProperties": false,
                "properties": {
                  "order_number": {
                    "description": "Order number from the customer's Kapruka order confirmation email or the order complete page on kapruka.com (e.g. 'VIMP34456CB2'). This is NOT the same as the order_ref returned by kapruka_create_order — the customer must complete payment first; the Kapruka order number is then emailed to them.",
                    "maxLength": 40,
                    "minLength": 4,
                    "title": "Order Number",
                    "type": "string"
                  },
                  "response_format": {
                    "default": "markdown",
                    "description": "'markdown' (default) or 'json'.",
                    "title": "Response Format",
                    "type": "string"
                  }
                },
                "required": [
                  "order_number"
                ],
                "title": "TrackOrderInput",
                "type": "object"
              }
            },
            "properties": {
              "params": {
                "$ref": "#/$defs/TrackOrderInput"
              }
            },
            "required": [
              "params"
            ],
            "title": "kapruka_track_orderArguments",
            "type": "object"
          },
          "outputSchema": {
            "properties": {
              "result": {
                "title": "Result",
                "type": "string"
              }
            },
            "required": [
              "result"
            ],
            "title": "kapruka_track_orderOutput",
            "type": "object"
          },
          "annotations": {
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true,
            "readOnlyHint": true,
            "title": "Track Kapruka Order"
          }
        },
        {
          "name": "kapruka_render_options_card",
          "description": "Render 1-4 products as ONE shareable JPEG \"menu\" card and return its URL.\n\n    The card shows each product's photo with a big numbered badge (the ref you\n    assign), and its name + price printed under the photo. Built for chat\n    commerce (WhatsApp): send the image, tell the customer \"reply 1, 2 or 3\",\n    and they pick without opening links. No AI is involved — the image is\n    server-composited from the live catalog data, so prices match what the\n    product tools return.\n\n    Ref numbering contract: refs are yours to assign — use sequential numbers\n    per conversation and NEVER reuse one (if the first card was 1-3, the next\n    card starts at 4). A number must keep meaning the same product for the whole\n    conversation.\n\n    Args:\n        params (RenderOptionsCardInput):\n            - items (list[CardProduct]): 1-4 of {product_id, ref}\n            - currency (str): LKR (default), USD, GBP, AUD, EUR\n            - courtesy ({currency, per_usd}, optional): home-currency figure printed\n              under each USD price (\"≈ JPY 2,544\"); the caller's rate, echoed back\n            - footer_note (str, optional): appended to the footer reply hint\n\n    Returns:\n        str: JSON:\n        {\n          \"card_url\": str,              # public JPEG URL — send this as the image\n          \"items\": [{\"ref\": int, \"product_id\": str, \"name\": str,\n                      \"price\": {\"amount\": float, \"currency\": str},\n                      \"price_note\": str | null, \"url\": str}],\n          \"courtesy\": {\"currency\": str, \"per_usd\": float} | null,  # what was printed, or null\n          \"unavailable\": [str]          # product_ids that failed to load (omitted from card)\n        }\n\n        Error: \"Error: \u003cmessage\u003e\" when no product could be loaded.\n    ",
          "inputSchema": {
            "$defs": {
              "CardProduct": {
                "additionalProperties": false,
                "properties": {
                  "product_id": {
                    "description": "Kapruka product ID (from a search result).",
                    "maxLength": 80,
                    "minLength": 3,
                    "title": "Product Id",
                    "type": "string"
                  },
                  "ref": {
                    "description": "The reference number to print on this product's badge (customer replies with it). Assign sequentially per conversation and NEVER reuse a number.",
                    "maximum": 99,
                    "minimum": 1,
                    "title": "Ref",
                    "type": "integer"
                  }
                },
                "required": [
                  "product_id",
                  "ref"
                ],
                "title": "CardProduct",
                "type": "object"
              },
              "Courtesy": {
                "additionalProperties": false,
                "description": "A home-currency approximation to print under each USD price.\n\nThe CALLER supplies the rate it used for its own chat text, so the card and\nthe text agree to the unit; the card never converts on its own. Only\napplied when the card is priced in USD — the courtesy figure describes\nwhat a USD charge will look like on the customer's statement.",
                "properties": {
                  "currency": {
                    "description": "ISO-4217 code of the customer's home currency, e.g. JPY.",
                    "maxLength": 3,
                    "minLength": 3,
                    "pattern": "^[A-Za-z]{3}$",
                    "title": "Currency",
                    "type": "string"
                  },
                  "per_usd": {
                    "description": "Units of that currency per 1 USD, as used for the chat text.",
                    "exclusiveMaximum": 1000000,
                    "exclusiveMinimum": 0,
                    "title": "Per Usd",
                    "type": "number"
                  }
                },
                "required": [
                  "currency",
                  "per_usd"
                ],
                "title": "Courtesy",
                "type": "object"
              },
              "RenderOptionsCardInput": {
                "additionalProperties": false,
                "properties": {
                  "courtesy": {
                    "anyOf": [
                      {
                        "$ref": "#/$defs/Courtesy"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Optional: print '≈ \u003chome currency\u003e' under each USD price (see Courtesy). Ignored unless currency is USD."
                  },
                  "currency": {
                    "default": "LKR",
                    "description": "Price currency: LKR, USD, GBP, AUD, EUR.",
                    "title": "Currency",
                    "type": "string"
                  },
                  "footer_note": {
                    "anyOf": [
                      {
                        "maxLength": 60,
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Optional short note appended to the footer's reply hint, e.g. 'Checkout charges USD'.",
                    "title": "Footer Note"
                  },
                  "items": {
                    "description": "1-4 products to show side by side.",
                    "items": {
                      "$ref": "#/$defs/CardProduct"
                    },
                    "maxItems": 4,
                    "minItems": 1,
                    "title": "Items",
                    "type": "array"
                  }
                },
                "required": [
                  "items"
                ],
                "title": "RenderOptionsCardInput",
                "type": "object"
              }
            },
            "properties": {
              "params": {
                "$ref": "#/$defs/RenderOptionsCardInput"
              }
            },
            "required": [
              "params"
            ],
            "title": "kapruka_render_options_cardArguments",
            "type": "object"
          },
          "outputSchema": {
            "properties": {
              "result": {
                "title": "Result",
                "type": "string"
              }
            },
            "required": [
              "result"
            ],
            "title": "kapruka_render_options_cardOutput",
            "type": "object"
          },
          "annotations": {
            "destructiveHint": false,
            "idempotentHint": true,
            "openWorldHint": true,
            "readOnlyHint": true,
            "title": "Render Product Options Card (Image)"
          }
        },
        {
          "name": "kapruka_list_delivery_cities",
          "description": "List or search Sri Lankan cities Kapruka delivers to.\n\n    Use the `query` param to filter (e.g. \"colombo\" → all Colombo zones,\n    \"anur\" → Anuradhapura). Without a query you get the first 25 cities\n    alphabetically, which is rarely what an agent needs — pass a query.\n\n    Returns canonical city names (use these as the `city` argument to\n    kapruka_check_delivery) plus any common aliases / vernacular spellings.\n\n    Args:\n        params (ListDeliveryCitiesInput):\n            - query (Optional[str]): Partial match filter\n            - limit (int): Max results, 1–50 (default 25)\n            - response_format (str): 'markdown' (default) or 'json'\n\n    Returns:\n        str: Cities list in the requested format.\n\n        JSON schema:\n        {\n          \"cities\": [{\"name\": str, \"aliases\": [str]}],\n          \"total_matched\": int,\n          \"showing\": int\n        }\n    ",
          "inputSchema": {
            "$defs": {
              "ListDeliveryCitiesInput": {
                "additionalProperties": false,
                "properties": {
                  "limit": {
                    "default": 25,
                    "description": "Max cities to return (1–50).",
                    "maximum": 50,
                    "minimum": 1,
                    "title": "Limit",
                    "type": "integer"
                  },
                  "query": {
                    "anyOf": [
                      {
                        "maxLength": 50,
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Filter cities by partial match against name or aliases (case-insensitive). Omit to see the first `limit` cities alphabetically.",
                    "title": "Query"
                  },
                  "response_format": {
                    "default": "markdown",
                    "description": "'markdown' (default) or 'json'",
                    "title": "Response Format",
                    "type": "string"
                  }
                },
                "title": "ListDeliveryCitiesInput",
                "type": "object"
              }
            },
            "properties": {
              "params": {
                "$ref": "#/$defs/ListDeliveryCitiesInput"
              }
            },
            "required": [
              "params"
            ],
            "title": "kapruka_list_delivery_citiesArguments",
            "type": "object"
          },
          "outputSchema": {
            "properties": {
              "result": {
                "title": "Result",
                "type": "string"
              }
            },
            "required": [
              "result"
            ],
            "title": "kapruka_list_delivery_citiesOutput",
            "type": "object"
          },
          "annotations": {
            "destructiveHint": false,
            "idempotentHint": true,
            "openWorldHint": true,
            "readOnlyHint": true,
            "title": "List Kapruka Delivery Cities"
          }
        },
        {
          "name": "kapruka_check_delivery",
          "description": "Check whether Kapruka can deliver to a given city on a given date, and the delivery fee.\n\n    Returns whether the requested date is available (if not, the next available\n    date plus reason) and the delivery fee the checkout will charge.\n\n    THE FEE DEPENDS ON THE CART AND THE CURRENCY — the city's base rate is not\n    what checkout charges. Sri Lankan customers (LKR) pay the city rate capped\n    by the cart value (25% of the item value, at least LKR 300; some remote\n    cities 50%), so a small cart to a far city pays far less than the rate.\n    Overseas customers (USD) pay a fixed USD fee per city, whatever the cart.\n    Pass `currency` and the `cart` you are about to order and the answer gives\n    the EXACT fee kapruka_create_order will charge (\"Delivery fee for this\n    cart\"). Without a cart, an LKR answer gives only the MOST the fee can be\n    (\"up to\"). One shipment per order: the fee covers the whole cart.\n\n    Pass `product_id` whenever the customer has named a product: the answer then\n    also checks that ITEM's delivery scope (restaurant food, hotel cakes and\n    liquor only reach selected cities, typically the Colombo area). With a\n    product_id, `available` is true only if the date is open AND the item is\n    deliverable to that city. When `item_deliverable` is false, offer the\n    customer one of the returned `deliverable_cities` or an island-wide\n    alternative — do not attempt kapruka_create_order with the same city, it\n    will be rejected. An unknown product_id is silently ignored (no item fields\n    in the result), so check `item_deliverable` is present before relying on it.\n\n    product_id also selects the SAME-DAY rule the checkout applies to that item:\n    vendor-delivered items (restaurant food) can go today until late afternoon;\n    ordinary items move to the next date; ordinary same-day is only possible early\n    in the morning near Colombo. So a check without product_id can differ from one\n    with it — always pass it once an item is chosen. When `available` is false,\n    offer `next_available_date`. Do not interpret the `reason` text (it is the\n    website's wording and may say slots are full when the real cause is the cutoff).\n\n    Perishable codes (CAKE*, FLOWER*, COMBO*) additionally get a freshness\n    warning when the chosen delivery date is more than 1 day out.\n\n    Args:\n        params (CheckDeliveryInput):\n            - city (str): Canonical city name (e.g. 'Colombo 03', 'Galle')\n            - delivery_date (Optional[str]): YYYY-MM-DD; defaults to today (LK time)\n            - product_id (Optional[str]): Check the city against this item's delivery scope\n            - currency (Optional[str]): LKR (default) or USD/GBP/AUD/EUR (charged in USD)\n            - cart (Optional[list]): [{product_id, quantity, icing_text?}] for the exact fee\n            - other_items_total (Optional[float]): LKR value of items not in `cart` (custom cake quote)\n            - response_format (str): 'markdown' (default) or 'json'\n\n    Returns:\n        str: Delivery feasibility + fee in the requested format.\n\n        JSON schema:\n        {\n          \"city\": str,\n          \"now\": str,                       # ISO timestamp, Sri Lanka time\n          \"checked_date\": str,              # YYYY-MM-DD\n          \"available\": bool,                # date open AND (if product_id) item deliverable\n          \"delivery_fee\": number,           # what checkout charges (see fee_basis)\n          \"fee_currency\": \"LKR\" | \"USD\",\n          \"fee_basis\": \"cart\" | \"max\" | \"fixed\",  # exact for the cart | LKR without a cart: the most it can be | USD: same for any cart\n          \"fee_items_value\": number,        # LKR item value the fee was computed from (fee_basis=cart)\n          \"fee_cart_error\": str,            # the cart could not be priced (fee falls back to max)\n          \"rate\": number,                   # the city's BASE rate (LKR) — not the checkout fee\n          \"currency\": \"LKR\",\n          \"reason\": str | null,             # date-block message, else \"This item is not delivered to \u003cCity\u003e.\"\n          \"next_available_date\": str|null,  # only for date blocks\n          \"item_deliverable\": bool,         # only when product_id resolved to a real product\n          \"deliverable_cities\": [str],      # only when item_deliverable=false (capped at 60)\n          \"perishable_warning\": str | null  # populated when product_id is perishable\n        }\n    ",
          "inputSchema": {
            "$defs": {
              "CheckDeliveryInput": {
                "additionalProperties": false,
                "properties": {
                  "cart": {
                    "anyOf": [
                      {
                        "items": {
                          "$ref": "#/$defs/DeliveryCartLine"
                        },
                        "maxItems": 30,
                        "type": "array"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "The items the customer is buying (product_id, quantity, optional icing_text) — the same lines you will send to kapruka_create_order. With it the answer gives the EXACT delivery fee checkout will charge; without it, only the most it can be.",
                    "title": "Cart"
                  },
                  "city": {
                    "description": "Canonical city name (use kapruka_list_delivery_cities to find one). Examples: 'Colombo 03', 'Anuradhapura', 'Galle'.",
                    "maxLength": 100,
                    "minLength": 2,
                    "title": "City",
                    "type": "string"
                  },
                  "currency": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "The currency the customer will check out in (LKR or USD; GBP/AUD/EUR check out in USD). Decides which delivery fee applies: Sri Lankan customers pay a fee capped by the cart value, overseas customers a fixed USD fee. Default LKR.",
                    "title": "Currency"
                  },
                  "delivery_date": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Target delivery date in ISO format (YYYY-MM-DD), Sri Lanka time. Omit to check today.",
                    "title": "Delivery Date"
                  },
                  "other_items_total": {
                    "anyOf": [
                      {
                        "minimum": 0,
                        "type": "number"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "LKR value of items not listed in `cart` (e.g. a quoted custom cake's total). Added to the cart value for the fee.",
                    "title": "Other Items Total"
                  },
                  "product_id": {
                    "anyOf": [
                      {
                        "maxLength": 80,
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Product ID to check the city AGAINST THAT ITEM'S delivery scope. Food, hotel cakes and liquor only reach selected cities — always pass this when a customer names a product and a city, and only promise delivery if `available` is true. Also adds a freshness warning for perishable codes (cake/flower/combo) when the date is \u003e1 day out.",
                    "title": "Product Id"
                  },
                  "response_format": {
                    "default": "markdown",
                    "description": "'markdown' (default) or 'json'",
                    "title": "Response Format",
                    "type": "string"
                  }
                },
                "required": [
                  "city"
                ],
                "title": "CheckDeliveryInput",
                "type": "object"
              },
              "DeliveryCartLine": {
                "description": "A catalogue cart line, the same shape kapruka_create_order takes.\n\nCustom cake lines cannot be priced here; pass the quote's total as\n`other_items_total` instead.",
                "properties": {
                  "icing_text": {
                    "anyOf": [
                      {
                        "maxLength": 120,
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "default": null,
                    "description": "Set when the cake carries icing text — the checkout adds a small charge for it.",
                    "title": "Icing Text"
                  },
                  "product_id": {
                    "maxLength": 50,
                    "minLength": 3,
                    "pattern": "^[A-Za-z0-9_\\-]+$",
                    "title": "Product Id",
                    "type": "string"
                  },
                  "quantity": {
                    "default": 1,
                    "maximum": 99,
                    "minimum": 1,
                    "title": "Quantity",
                    "type": "integer"
                  }
                },
                "required": [
                  "product_id"
                ],
                "title": "DeliveryCartLine",
                "type": "object"
              }
            },
            "properties": {
              "params": {
                "$ref": "#/$defs/CheckDeliveryInput"
              }
            },
            "required": [
              "params"
            ],
            "title": "kapruka_check_deliveryArguments",
            "type": "object"
          },
          "outputSchema": {
            "properties": {
              "result": {
                "title": "Result",
                "type": "string"
              }
            },
            "required": [
              "result"
            ],
            "title": "kapruka_check_deliveryOutput",
            "type": "object"
          },
          "annotations": {
            "destructiveHint": false,
            "idempotentHint": false,
            "openWorldHint": true,
            "readOnlyHint": true,
            "title": "Check Kapruka Delivery Availability and Rate"
          }
        }
      ]
    },
    "name": "Kapruka MCP",
    "note": "Indexed from the official MCP registry: facts and our own checks, not reviewed, so no score, grade or rank.",
    "packages": null,
    "pageJsonUrl": "https://www.anchorterminal.com/tools/kapruka-mcp.json",
    "popularity": {
      "githubStars": 21
    },
    "registryName": "com.kapruka/kapruka-mcp",
    "remotes": [
      {
        "type": "streamable-http",
        "url": "https://mcp.kapruka.com/mcp"
      }
    ],
    "repository": "https://github.com/kapruka/mcp",
    "reviewed": false,
    "slug": "kapruka-mcp",
    "source": "the official MCP registry",
    "sourceUrl": "https://registry.modelcontextprotocol.io/v0.1/servers?search=com.kapruka/kapruka-mcp",
    "summary": "Free public MCP server for Kapruka.com — Sri Lanka's largest e-commerce platform.",
    "updatedAt": "2026-10-03T10:55:30Z",
    "url": "https://www.anchorterminal.com/tools/kapruka-mcp",
    "vendor": "kapruka.com",
    "vendorUrl": "https://mcp.kapruka.com/",
    "version": "0.6.0",
    "websiteUrl": "https://mcp.kapruka.com/",
    "where": "hosted",
    "why": [
      "vendor"
    ]
  }
}
