@open-loyalty/mcp-server 1.5.3 → 1.7.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 (132) hide show
  1. package/dist/config.d.ts +4 -0
  2. package/dist/config.js +11 -0
  3. package/dist/index.js +0 -8
  4. package/dist/server.js +13 -0
  5. package/dist/tools/achievement/handlers.js +47 -0
  6. package/dist/tools/achievement/index.d.ts +11 -4
  7. package/dist/tools/achievement/index.js +12 -1
  8. package/dist/tools/achievement/schemas.d.ts +4 -4
  9. package/dist/tools/achievement/schemas.js +13 -12
  10. package/dist/tools/admin/handlers.d.ts +48 -0
  11. package/dist/tools/admin/handlers.js +159 -0
  12. package/dist/tools/admin/index.d.ts +86 -0
  13. package/dist/tools/admin/index.js +64 -0
  14. package/dist/tools/admin/schemas.d.ts +40 -0
  15. package/dist/tools/admin/schemas.js +40 -0
  16. package/dist/tools/analytics/handlers.d.ts +42 -0
  17. package/dist/tools/analytics/handlers.js +282 -0
  18. package/dist/tools/analytics/index.d.ts +108 -0
  19. package/dist/tools/analytics/index.js +91 -0
  20. package/dist/tools/analytics/schemas.d.ts +42 -0
  21. package/dist/tools/analytics/schemas.js +47 -0
  22. package/dist/tools/apikey/handlers.d.ts +15 -0
  23. package/dist/tools/apikey/handlers.js +53 -0
  24. package/dist/tools/apikey/index.d.ts +41 -0
  25. package/dist/tools/apikey/index.js +38 -0
  26. package/dist/tools/apikey/schemas.d.ts +31 -0
  27. package/dist/tools/apikey/schemas.js +15 -0
  28. package/dist/tools/audit/handlers.d.ts +20 -0
  29. package/dist/tools/audit/handlers.js +82 -0
  30. package/dist/tools/audit/index.d.ts +36 -0
  31. package/dist/tools/audit/index.js +28 -0
  32. package/dist/tools/audit/schemas.d.ts +62 -0
  33. package/dist/tools/audit/schemas.js +18 -0
  34. package/dist/tools/badge/handlers.d.ts +45 -0
  35. package/dist/tools/badge/handlers.js +135 -0
  36. package/dist/tools/badge/index.d.ts +68 -0
  37. package/dist/tools/badge/index.js +47 -0
  38. package/dist/tools/badge/schemas.d.ts +37 -0
  39. package/dist/tools/badge/schemas.js +31 -0
  40. package/dist/tools/campaign/handlers.js +61 -0
  41. package/dist/tools/campaign/index.d.ts +12 -0
  42. package/dist/tools/campaign/index.js +20 -1
  43. package/dist/tools/campaign/member-handlers.js +37 -1
  44. package/dist/tools/campaign/schemas.js +16 -14
  45. package/dist/tools/custom-event/handlers.d.ts +98 -0
  46. package/dist/tools/custom-event/handlers.js +238 -0
  47. package/dist/tools/custom-event/index.d.ts +139 -0
  48. package/dist/tools/custom-event/index.js +78 -0
  49. package/dist/tools/custom-event/schemas.d.ts +87 -0
  50. package/dist/tools/custom-event/schemas.js +59 -0
  51. package/dist/tools/export/handlers.d.ts +29 -0
  52. package/dist/tools/export/handlers.js +128 -0
  53. package/dist/tools/export/index.d.ts +56 -0
  54. package/dist/tools/export/index.js +46 -0
  55. package/dist/tools/export/schemas.d.ts +42 -0
  56. package/dist/tools/export/schemas.js +41 -0
  57. package/dist/tools/import/handlers.d.ts +22 -0
  58. package/dist/tools/import/handlers.js +123 -0
  59. package/dist/tools/import/index.d.ts +45 -0
  60. package/dist/tools/import/index.js +41 -0
  61. package/dist/tools/import/schemas.d.ts +57 -0
  62. package/dist/tools/import/schemas.js +39 -0
  63. package/dist/tools/index.d.ts +1 -0
  64. package/dist/tools/index.js +11 -11
  65. package/dist/tools/member/handlers.js +30 -0
  66. package/dist/tools/member/index.d.ts +10 -0
  67. package/dist/tools/member/index.js +10 -0
  68. package/dist/tools/member/schemas.js +13 -13
  69. package/dist/tools/points/handlers.js +73 -0
  70. package/dist/tools/points/index.d.ts +6 -0
  71. package/dist/tools/points/index.js +6 -0
  72. package/dist/tools/points/schemas.js +1 -1
  73. package/dist/tools/referral/index.d.ts +3 -0
  74. package/dist/tools/referral/index.js +3 -0
  75. package/dist/tools/reward/handlers.js +21 -13
  76. package/dist/tools/reward/index.d.ts +9 -0
  77. package/dist/tools/reward/index.js +12 -1
  78. package/dist/tools/reward/schemas.js +2 -2
  79. package/dist/tools/role/handlers.d.ts +35 -0
  80. package/dist/tools/role/handlers.js +127 -0
  81. package/dist/tools/role/index.d.ts +99 -0
  82. package/dist/tools/role/index.js +65 -0
  83. package/dist/tools/role/schemas.d.ts +56 -0
  84. package/dist/tools/role/schemas.js +35 -0
  85. package/dist/tools/segment/handlers.js +68 -1
  86. package/dist/tools/segment/index.d.ts +9 -0
  87. package/dist/tools/segment/index.js +13 -0
  88. package/dist/tools/segment/schemas.js +8 -5
  89. package/dist/tools/store/handlers.d.ts +25 -0
  90. package/dist/tools/store/handlers.js +89 -0
  91. package/dist/tools/store/index.d.ts +55 -0
  92. package/dist/tools/store/index.js +46 -0
  93. package/dist/tools/store/schemas.d.ts +38 -0
  94. package/dist/tools/store/schemas.js +23 -0
  95. package/dist/tools/tierset/handlers.js +92 -1
  96. package/dist/tools/tierset/index.d.ts +6 -0
  97. package/dist/tools/tierset/index.js +8 -1
  98. package/dist/tools/transaction/handlers.js +40 -0
  99. package/dist/tools/transaction/index.d.ts +4 -0
  100. package/dist/tools/transaction/index.js +4 -0
  101. package/dist/tools/transaction/schemas.js +3 -3
  102. package/dist/tools/wallet-type/index.d.ts +4 -0
  103. package/dist/tools/wallet-type/index.js +5 -1
  104. package/dist/tools/webhook/handlers.d.ts +34 -0
  105. package/dist/tools/webhook/handlers.js +147 -0
  106. package/dist/tools/webhook/index.d.ts +97 -0
  107. package/dist/tools/webhook/index.js +65 -0
  108. package/dist/tools/webhook/schemas.d.ts +72 -0
  109. package/dist/tools/{webhook.js → webhook/schemas.js} +0 -140
  110. package/dist/types/schemas/tierset.js +3 -1
  111. package/package.json +1 -1
  112. package/dist/tools/admin.d.ts +0 -165
  113. package/dist/tools/admin.js +0 -205
  114. package/dist/tools/analytics.d.ts +0 -180
  115. package/dist/tools/analytics.js +0 -255
  116. package/dist/tools/apikey.d.ts +0 -79
  117. package/dist/tools/apikey.js +0 -85
  118. package/dist/tools/audit.d.ts +0 -111
  119. package/dist/tools/audit.js +0 -94
  120. package/dist/tools/badge.d.ts +0 -143
  121. package/dist/tools/badge.js +0 -174
  122. package/dist/tools/custom-event.d.ts +0 -315
  123. package/dist/tools/custom-event.js +0 -271
  124. package/dist/tools/export.d.ts +0 -118
  125. package/dist/tools/export.js +0 -152
  126. package/dist/tools/import.d.ts +0 -116
  127. package/dist/tools/import.js +0 -143
  128. package/dist/tools/role.d.ts +0 -180
  129. package/dist/tools/role.js +0 -173
  130. package/dist/tools/store.d.ts +0 -109
  131. package/dist/tools/store.js +0 -125
  132. package/dist/tools/webhook.d.ts +0 -192
@@ -0,0 +1,99 @@
1
+ export { RoleListInputSchema, RoleCreateInputSchema, RoleGetInputSchema, RoleUpdateInputSchema, RoleDeleteInputSchema, } from "./schemas.js";
2
+ export type { RolePermissionInput } from "./schemas.js";
3
+ export { roleList, roleCreate, roleGet, roleUpdate, roleDelete, aclGetResources, } from "./handlers.js";
4
+ import { roleList, roleCreate, roleGet, roleUpdate, roleDelete, aclGetResources } from "./handlers.js";
5
+ export declare const roleToolDefinitions: readonly [{
6
+ readonly name: "ol_role_list";
7
+ readonly title: "List Roles";
8
+ readonly description: "List all roles with optional filtering. Returns paginated list of roles with id, name, permissions, master status, and default status.";
9
+ readonly readOnly: true;
10
+ readonly idempotent: true;
11
+ readonly inputSchema: {
12
+ page: import("zod").ZodOptional<import("zod").ZodNumber>;
13
+ perPage: import("zod").ZodOptional<import("zod").ZodNumber>;
14
+ name: import("zod").ZodOptional<import("zod").ZodString>;
15
+ master: import("zod").ZodOptional<import("zod").ZodBoolean>;
16
+ default: import("zod").ZodOptional<import("zod").ZodBoolean>;
17
+ };
18
+ readonly handler: typeof roleList;
19
+ }, {
20
+ readonly name: "ol_role_create";
21
+ readonly title: "Create Role";
22
+ readonly description: "Create a new role with permissions. Requires name. Optionally specify permissions (array of {resource, access, filterQuery}) and stores. Returns void on success (201 Created).";
23
+ readonly readOnly: false;
24
+ readonly idempotent: false;
25
+ readonly inputSchema: {
26
+ name: import("zod").ZodString;
27
+ permissions: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{
28
+ resource: import("zod").ZodString;
29
+ access: import("zod").ZodString;
30
+ filterQuery: import("zod").ZodOptional<import("zod").ZodString>;
31
+ }, "strip", import("zod").ZodTypeAny, {
32
+ resource: string;
33
+ access: string;
34
+ filterQuery?: string | undefined;
35
+ }, {
36
+ resource: string;
37
+ access: string;
38
+ filterQuery?: string | undefined;
39
+ }>, "many">>;
40
+ stores: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
41
+ default: import("zod").ZodOptional<import("zod").ZodBoolean>;
42
+ };
43
+ readonly handler: typeof roleCreate;
44
+ }, {
45
+ readonly name: "ol_role_get";
46
+ readonly title: "Get Role Details";
47
+ readonly description: "Get full role details by ID. Returns role with name, permissions array, master status, default status, and stores.";
48
+ readonly readOnly: true;
49
+ readonly idempotent: true;
50
+ readonly inputSchema: {
51
+ roleId: import("zod").ZodUnion<[import("zod").ZodString, import("zod").ZodNumber]>;
52
+ };
53
+ readonly handler: typeof roleGet;
54
+ }, {
55
+ readonly name: "ol_role_update";
56
+ readonly title: "Update Role";
57
+ readonly description: "Update a role's name, permissions, or stores. Returns void on success (204 No Content).";
58
+ readonly readOnly: false;
59
+ readonly idempotent: true;
60
+ readonly inputSchema: {
61
+ roleId: import("zod").ZodUnion<[import("zod").ZodString, import("zod").ZodNumber]>;
62
+ name: import("zod").ZodOptional<import("zod").ZodString>;
63
+ permissions: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{
64
+ resource: import("zod").ZodString;
65
+ access: import("zod").ZodString;
66
+ filterQuery: import("zod").ZodOptional<import("zod").ZodString>;
67
+ }, "strip", import("zod").ZodTypeAny, {
68
+ resource: string;
69
+ access: string;
70
+ filterQuery?: string | undefined;
71
+ }, {
72
+ resource: string;
73
+ access: string;
74
+ filterQuery?: string | undefined;
75
+ }>, "many">>;
76
+ stores: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
77
+ default: import("zod").ZodOptional<import("zod").ZodBoolean>;
78
+ };
79
+ readonly handler: typeof roleUpdate;
80
+ }, {
81
+ readonly name: "ol_role_delete";
82
+ readonly title: "Delete Role (Permanent)";
83
+ readonly description: "Delete a role by ID. Returns void on success (204 No Content). Cannot delete roles that are assigned to admin users.";
84
+ readonly readOnly: false;
85
+ readonly destructive: true;
86
+ readonly idempotent: true;
87
+ readonly inputSchema: {
88
+ roleId: import("zod").ZodUnion<[import("zod").ZodString, import("zod").ZodNumber]>;
89
+ };
90
+ readonly handler: typeof roleDelete;
91
+ }, {
92
+ readonly name: "ol_acl_get_resources";
93
+ readonly title: "Get ACL Resources";
94
+ readonly description: "Get list of available ACL resources. Returns items with code and name for each resource type. Use this to discover available permissions before creating roles.";
95
+ readonly readOnly: true;
96
+ readonly idempotent: true;
97
+ readonly inputSchema: {};
98
+ readonly handler: typeof aclGetResources;
99
+ }];
@@ -0,0 +1,65 @@
1
+ // Re-export schemas and types
2
+ export { RoleListInputSchema, RoleCreateInputSchema, RoleGetInputSchema, RoleUpdateInputSchema, RoleDeleteInputSchema, } from "./schemas.js";
3
+ // Re-export handlers
4
+ export { roleList, roleCreate, roleGet, roleUpdate, roleDelete, aclGetResources, } from "./handlers.js";
5
+ // Imports for tool definitions
6
+ import { RoleListInputSchema, RoleCreateInputSchema, RoleGetInputSchema, RoleUpdateInputSchema, RoleDeleteInputSchema, } from "./schemas.js";
7
+ import { roleList, roleCreate, roleGet, roleUpdate, roleDelete, aclGetResources, } from "./handlers.js";
8
+ // Tool definitions
9
+ export const roleToolDefinitions = [
10
+ {
11
+ name: "ol_role_list",
12
+ title: "List Roles",
13
+ description: "List all roles with optional filtering. Returns paginated list of roles with id, name, permissions, master status, and default status.",
14
+ readOnly: true,
15
+ idempotent: true,
16
+ inputSchema: RoleListInputSchema,
17
+ handler: roleList,
18
+ },
19
+ {
20
+ name: "ol_role_create",
21
+ title: "Create Role",
22
+ description: "Create a new role with permissions. Requires name. Optionally specify permissions (array of {resource, access, filterQuery}) and stores. Returns void on success (201 Created).",
23
+ readOnly: false,
24
+ idempotent: false,
25
+ inputSchema: RoleCreateInputSchema,
26
+ handler: roleCreate,
27
+ },
28
+ {
29
+ name: "ol_role_get",
30
+ title: "Get Role Details",
31
+ description: "Get full role details by ID. Returns role with name, permissions array, master status, default status, and stores.",
32
+ readOnly: true,
33
+ idempotent: true,
34
+ inputSchema: RoleGetInputSchema,
35
+ handler: roleGet,
36
+ },
37
+ {
38
+ name: "ol_role_update",
39
+ title: "Update Role",
40
+ description: "Update a role's name, permissions, or stores. Returns void on success (204 No Content).",
41
+ readOnly: false,
42
+ idempotent: true,
43
+ inputSchema: RoleUpdateInputSchema,
44
+ handler: roleUpdate,
45
+ },
46
+ {
47
+ name: "ol_role_delete",
48
+ title: "Delete Role (Permanent)",
49
+ description: "Delete a role by ID. Returns void on success (204 No Content). Cannot delete roles that are assigned to admin users.",
50
+ readOnly: false,
51
+ destructive: true,
52
+ idempotent: true,
53
+ inputSchema: RoleDeleteInputSchema,
54
+ handler: roleDelete,
55
+ },
56
+ {
57
+ name: "ol_acl_get_resources",
58
+ title: "Get ACL Resources",
59
+ description: "Get list of available ACL resources. Returns items with code and name for each resource type. Use this to discover available permissions before creating roles.",
60
+ readOnly: true,
61
+ idempotent: true,
62
+ inputSchema: {},
63
+ handler: aclGetResources,
64
+ },
65
+ ];
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ export interface RolePermissionInput {
3
+ resource: string;
4
+ access: string;
5
+ filterQuery?: string;
6
+ }
7
+ export declare const RoleListInputSchema: {
8
+ page: z.ZodOptional<z.ZodNumber>;
9
+ perPage: z.ZodOptional<z.ZodNumber>;
10
+ name: z.ZodOptional<z.ZodString>;
11
+ master: z.ZodOptional<z.ZodBoolean>;
12
+ default: z.ZodOptional<z.ZodBoolean>;
13
+ };
14
+ export declare const RoleCreateInputSchema: {
15
+ name: z.ZodString;
16
+ permissions: z.ZodOptional<z.ZodArray<z.ZodObject<{
17
+ resource: z.ZodString;
18
+ access: z.ZodString;
19
+ filterQuery: z.ZodOptional<z.ZodString>;
20
+ }, "strip", z.ZodTypeAny, {
21
+ resource: string;
22
+ access: string;
23
+ filterQuery?: string | undefined;
24
+ }, {
25
+ resource: string;
26
+ access: string;
27
+ filterQuery?: string | undefined;
28
+ }>, "many">>;
29
+ stores: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
30
+ default: z.ZodOptional<z.ZodBoolean>;
31
+ };
32
+ export declare const RoleGetInputSchema: {
33
+ roleId: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
34
+ };
35
+ export declare const RoleUpdateInputSchema: {
36
+ roleId: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
37
+ name: z.ZodOptional<z.ZodString>;
38
+ permissions: z.ZodOptional<z.ZodArray<z.ZodObject<{
39
+ resource: z.ZodString;
40
+ access: z.ZodString;
41
+ filterQuery: z.ZodOptional<z.ZodString>;
42
+ }, "strip", z.ZodTypeAny, {
43
+ resource: string;
44
+ access: string;
45
+ filterQuery?: string | undefined;
46
+ }, {
47
+ resource: string;
48
+ access: string;
49
+ filterQuery?: string | undefined;
50
+ }>, "many">>;
51
+ stores: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
52
+ default: z.ZodOptional<z.ZodBoolean>;
53
+ };
54
+ export declare const RoleDeleteInputSchema: {
55
+ roleId: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
56
+ };
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ export const RoleListInputSchema = {
3
+ page: z.number().optional().describe("Page number (default: 1)."),
4
+ perPage: z.number().optional().describe("Items per page (default: 25)."),
5
+ name: z.string().optional().describe("Filter by role name."),
6
+ master: z.boolean().optional().describe("Filter by master role status."),
7
+ default: z.boolean().optional().describe("Filter by default role status."),
8
+ };
9
+ export const RoleCreateInputSchema = {
10
+ name: z.string().describe("Role name (required)."),
11
+ permissions: z.array(z.object({
12
+ resource: z.string().describe("Resource name (e.g., ANALYTICS, EVENTS). Use ol_acl_get_resources to discover all available resource codes."),
13
+ access: z.string().describe("Access level: VIEW (read-only) or MODIFY (read-write)."),
14
+ filterQuery: z.string().optional().describe("Optional filter query."),
15
+ })).optional().describe("Array of permission definitions."),
16
+ stores: z.array(z.string()).optional().describe("Store codes to assign access (e.g., ['DEFAULT'])."),
17
+ default: z.boolean().optional().describe("Whether this is a default role for new admins."),
18
+ };
19
+ export const RoleGetInputSchema = {
20
+ roleId: z.union([z.string(), z.number()]).describe("The role ID to retrieve."),
21
+ };
22
+ export const RoleUpdateInputSchema = {
23
+ roleId: z.union([z.string(), z.number()]).describe("The role ID to update."),
24
+ name: z.string().optional().describe("Role name."),
25
+ permissions: z.array(z.object({
26
+ resource: z.string().describe("Resource name. Use ol_acl_get_resources to discover all available resource codes."),
27
+ access: z.string().describe("Access level: VIEW (read-only) or MODIFY (read-write)."),
28
+ filterQuery: z.string().optional().describe("Optional filter query."),
29
+ })).optional().describe("Array of permission definitions."),
30
+ stores: z.array(z.string()).optional().describe("Store codes to assign access."),
31
+ default: z.boolean().optional().describe("Whether this is a default role."),
32
+ };
33
+ export const RoleDeleteInputSchema = {
34
+ roleId: z.union([z.string(), z.number()]).describe("The role ID to delete."),
35
+ };
@@ -1,5 +1,5 @@
1
1
  import { apiGet, apiPost, apiPut, apiDelete } from "../../client/http.js";
2
- import { formatApiError } from "../../utils/errors.js";
2
+ import { formatApiError, OpenLoyaltyError } from "../../utils/errors.js";
3
3
  import { getStoreCode } from "../../config.js";
4
4
  const DEFAULT_TRANSACTION_COUNT_MAX = 999999;
5
5
  function normalizeSegmentParts(parts) {
@@ -71,6 +71,25 @@ export async function segmentCreate(input) {
71
71
  return { segmentId: response.segmentId };
72
72
  }
73
73
  catch (error) {
74
+ const axiosError = error;
75
+ const apiErrors = axiosError.response?.data?.errors || [];
76
+ const allMessages = [
77
+ axiosError.response?.data?.message || "",
78
+ ...apiErrors.map(e => `${e.path || ""}: ${e.message}`)
79
+ ].join(" ").toLowerCase();
80
+ if (allMessages.includes("type") && (allMessages.includes("choice is invalid") || allMessages.includes("not valid") || allMessages.includes("invalid"))) {
81
+ throw new OpenLoyaltyError({
82
+ code: "INVALID_CRITERION_TYPE",
83
+ message: "Invalid criterion type in segment definition",
84
+ hint: "Valid criterion types: 'transaction_count' (purchase frequency), 'average_transaction_value', " +
85
+ "'transaction_amount' (total spend), 'transaction_percent_in_period', 'purchase_in_period', " +
86
+ "'bought_skus' (specific products), 'bought_labels' (product categories/tags), " +
87
+ "'bought_makers' (brands), 'customer_list' (manual list), 'anniversary', 'last_purchase_n_days_before', " +
88
+ "'customer_has_labels', 'customer_with_labels_values'. " +
89
+ "NOTE: 'tier' and 'points_balance' are REJECTED by the API despite being documented.",
90
+ relatedTool: "ol_segment_create",
91
+ });
92
+ }
74
93
  throw formatApiError(error, "ol_segment_create");
75
94
  }
76
95
  }
@@ -81,6 +100,15 @@ export async function segmentGet(input) {
81
100
  return response;
82
101
  }
83
102
  catch (error) {
103
+ const axiosError = error;
104
+ if (axiosError.response?.status === 404) {
105
+ throw new OpenLoyaltyError({
106
+ code: "NOT_FOUND",
107
+ message: `Segment '${input.segmentId}' not found`,
108
+ hint: "Use ol_segment_list() to find existing segments and their IDs.",
109
+ relatedTool: "ol_segment_get",
110
+ });
111
+ }
84
112
  throw formatApiError(error, "ol_segment_get");
85
113
  }
86
114
  }
@@ -99,6 +127,20 @@ export async function segmentUpdate(input) {
99
127
  await apiPut(`/${storeCode}/segment/${input.segmentId}`, { segment: segmentPayload });
100
128
  }
101
129
  catch (error) {
130
+ const axiosError = error;
131
+ if (axiosError.response?.status === 404) {
132
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Segment '${input.segmentId}' not found`,
133
+ hint: "Use ol_segment_list() to find existing segments and their IDs.", relatedTool: "ol_segment_update" });
134
+ }
135
+ const apiErrors = axiosError.response?.data?.errors || [];
136
+ const allMessages = [axiosError.response?.data?.message || "", ...apiErrors.map(e => `${e.path || ""}: ${e.message}`)].join(" ").toLowerCase();
137
+ if (allMessages.includes("type") && (allMessages.includes("choice is invalid") || allMessages.includes("not valid") || allMessages.includes("invalid"))) {
138
+ throw new OpenLoyaltyError({ code: "INVALID_CRITERION_TYPE", message: "Invalid criterion type in segment definition",
139
+ hint: "Valid criterion types: 'transaction_count', 'average_transaction_value', 'transaction_amount', " +
140
+ "'purchase_in_period', 'bought_skus', 'bought_labels', 'bought_makers', 'customer_list', 'anniversary', " +
141
+ "'last_purchase_n_days_before', 'customer_has_labels', 'customer_with_labels_values'.",
142
+ relatedTool: "ol_segment_update" });
143
+ }
102
144
  throw formatApiError(error, "ol_segment_update");
103
145
  }
104
146
  }
@@ -108,6 +150,11 @@ export async function segmentDelete(input) {
108
150
  await apiDelete(`/${storeCode}/segment/${input.segmentId}`);
109
151
  }
110
152
  catch (error) {
153
+ const axiosError = error;
154
+ if (axiosError.response?.status === 404) {
155
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Segment '${input.segmentId}' not found`,
156
+ hint: "Use ol_segment_list() to find existing segments and their IDs.", relatedTool: "ol_segment_delete" });
157
+ }
111
158
  throw formatApiError(error, "ol_segment_delete");
112
159
  }
113
160
  }
@@ -140,6 +187,11 @@ export async function segmentGetMembers(input) {
140
187
  };
141
188
  }
142
189
  catch (error) {
190
+ const axiosError = error;
191
+ if (axiosError.response?.status === 404) {
192
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Segment '${input.segmentId}' not found`,
193
+ hint: "Use ol_segment_list() to find existing segments and their IDs.", relatedTool: "ol_segment_get_members" });
194
+ }
143
195
  throw formatApiError(error, "ol_segment_get_members");
144
196
  }
145
197
  }
@@ -149,6 +201,11 @@ export async function segmentActivate(input) {
149
201
  await apiPost(`/${storeCode}/segment/${input.segmentId}/activate`, {});
150
202
  }
151
203
  catch (error) {
204
+ const axiosError = error;
205
+ if (axiosError.response?.status === 404) {
206
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Segment '${input.segmentId}' not found`,
207
+ hint: "Use ol_segment_list() to find existing segments and their IDs.", relatedTool: "ol_segment_activate" });
208
+ }
152
209
  throw formatApiError(error, "ol_segment_activate");
153
210
  }
154
211
  }
@@ -158,6 +215,11 @@ export async function segmentDeactivate(input) {
158
215
  await apiPost(`/${storeCode}/segment/${input.segmentId}/deactivate`, {});
159
216
  }
160
217
  catch (error) {
218
+ const axiosError = error;
219
+ if (axiosError.response?.status === 404) {
220
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Segment '${input.segmentId}' not found`,
221
+ hint: "Use ol_segment_list() to find existing segments and their IDs.", relatedTool: "ol_segment_deactivate" });
222
+ }
161
223
  throw formatApiError(error, "ol_segment_deactivate");
162
224
  }
163
225
  }
@@ -169,6 +231,11 @@ export async function segmentGetResources(input) {
169
231
  return { resources };
170
232
  }
171
233
  catch (error) {
234
+ const axiosError = error;
235
+ if (axiosError.response?.status === 404) {
236
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Segment '${input.segmentId}' not found`,
237
+ hint: "Use ol_segment_list() to find existing segments and their IDs.", relatedTool: "ol_segment_get_resources" });
238
+ }
172
239
  throw formatApiError(error, "ol_segment_get_resources");
173
240
  }
174
241
  }
@@ -6,6 +6,7 @@ export declare const segmentToolDefinitions: readonly [{
6
6
  readonly title: "List Segments";
7
7
  readonly description: "List customer segments. Segments group members by criteria (purchase behavior, tier, location, etc). Use for campaign targeting and analytics.";
8
8
  readonly readOnly: true;
9
+ readonly idempotent: true;
9
10
  readonly inputSchema: {
10
11
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
11
12
  page: import("zod").ZodOptional<import("zod").ZodNumber>;
@@ -19,6 +20,7 @@ export declare const segmentToolDefinitions: readonly [{
19
20
  readonly title: "Create Segment";
20
21
  readonly description: string;
21
22
  readonly readOnly: false;
23
+ readonly idempotent: false;
22
24
  readonly inputSchema: {
23
25
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
24
26
  name: import("zod").ZodString;
@@ -176,6 +178,7 @@ export declare const segmentToolDefinitions: readonly [{
176
178
  readonly title: "Get Segment Details";
177
179
  readonly description: "Get full segment details including all parts and criteria configurations.";
178
180
  readonly readOnly: true;
181
+ readonly idempotent: true;
179
182
  readonly inputSchema: {
180
183
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
181
184
  segmentId: import("zod").ZodString;
@@ -186,6 +189,7 @@ export declare const segmentToolDefinitions: readonly [{
186
189
  readonly title: "Update Segment";
187
190
  readonly description: "Update segment configuration. Requires full segment definition (name, parts with criteria). Use segment_get first to retrieve current configuration.";
188
191
  readonly readOnly: false;
192
+ readonly idempotent: true;
189
193
  readonly inputSchema: {
190
194
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
191
195
  segmentId: import("zod").ZodString;
@@ -345,6 +349,7 @@ export declare const segmentToolDefinitions: readonly [{
345
349
  readonly description: "Permanently delete a segment. Cannot be undone. Check segment_get_resources first to see what uses this segment.";
346
350
  readonly readOnly: false;
347
351
  readonly destructive: true;
352
+ readonly idempotent: true;
348
353
  readonly inputSchema: {
349
354
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
350
355
  segmentId: import("zod").ZodString;
@@ -355,6 +360,7 @@ export declare const segmentToolDefinitions: readonly [{
355
360
  readonly title: "Get Segment Members";
356
361
  readonly description: "Get members belonging to a segment. Returns paginated list of member details. Use for verifying segment criteria or exporting member lists.";
357
362
  readonly readOnly: true;
363
+ readonly idempotent: true;
358
364
  readonly inputSchema: {
359
365
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
360
366
  segmentId: import("zod").ZodString;
@@ -367,6 +373,7 @@ export declare const segmentToolDefinitions: readonly [{
367
373
  readonly title: "Activate Segment";
368
374
  readonly description: "Activate a segment. Active segments are used for campaign targeting and can be queried for members.";
369
375
  readonly readOnly: false;
376
+ readonly idempotent: true;
370
377
  readonly inputSchema: {
371
378
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
372
379
  segmentId: import("zod").ZodString;
@@ -377,6 +384,7 @@ export declare const segmentToolDefinitions: readonly [{
377
384
  readonly title: "Deactivate Segment";
378
385
  readonly description: "Deactivate a segment. Deactivated segments are not used for campaign targeting but retain their configuration.";
379
386
  readonly readOnly: false;
387
+ readonly idempotent: true;
380
388
  readonly inputSchema: {
381
389
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
382
390
  segmentId: import("zod").ZodString;
@@ -387,6 +395,7 @@ export declare const segmentToolDefinitions: readonly [{
387
395
  readonly title: "Get Segment Resources";
388
396
  readonly description: string;
389
397
  readonly readOnly: true;
398
+ readonly idempotent: true;
390
399
  readonly inputSchema: {
391
400
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
392
401
  segmentId: import("zod").ZodString;
@@ -12,6 +12,7 @@ export const segmentToolDefinitions = [
12
12
  title: "List Segments",
13
13
  description: "List customer segments. Segments group members by criteria (purchase behavior, tier, location, etc). Use for campaign targeting and analytics.",
14
14
  readOnly: true,
15
+ idempotent: true,
15
16
  inputSchema: SegmentListInputSchema,
16
17
  handler: segmentList,
17
18
  },
@@ -24,8 +25,13 @@ export const segmentToolDefinitions = [
24
25
  "2. Threshold values -- min/max boundaries? (e.g., min=5 transactions captures frequent buyers; min=2 captures nearly everyone) " +
25
26
  "3. Intended use -- campaign targeting (activate immediately) or analytics only (can stay inactive)? " +
26
27
  "REQUIRED: name, parts array with criteria. Parts use OR logic, criteria within parts use AND logic. " +
28
+ "CRITERION FIELDS BY TYPE: " +
29
+ "transaction_count: { type: 'transaction_count', min: N, max: N } (both required, MCP auto-fills max=999999 if omitted). " +
30
+ "purchase_in_period: { type: 'purchase_in_period', days: N, min: N, max: N }. " +
31
+ "average_transaction_value: { type: 'average_transaction_value', min: N, max: N }. " +
27
32
  "NOTE: 'tier' and 'points_balance' types are REJECTED by the API despite being documented.",
28
33
  readOnly: false,
34
+ idempotent: false,
29
35
  inputSchema: SegmentCreateInputSchema,
30
36
  handler: segmentCreate,
31
37
  },
@@ -34,6 +40,7 @@ export const segmentToolDefinitions = [
34
40
  title: "Get Segment Details",
35
41
  description: "Get full segment details including all parts and criteria configurations.",
36
42
  readOnly: true,
43
+ idempotent: true,
37
44
  inputSchema: SegmentGetInputSchema,
38
45
  handler: segmentGet,
39
46
  },
@@ -42,6 +49,7 @@ export const segmentToolDefinitions = [
42
49
  title: "Update Segment",
43
50
  description: "Update segment configuration. Requires full segment definition (name, parts with criteria). Use segment_get first to retrieve current configuration.",
44
51
  readOnly: false,
52
+ idempotent: true,
45
53
  inputSchema: SegmentUpdateInputSchema,
46
54
  handler: segmentUpdate,
47
55
  },
@@ -51,6 +59,7 @@ export const segmentToolDefinitions = [
51
59
  description: "Permanently delete a segment. Cannot be undone. Check segment_get_resources first to see what uses this segment.",
52
60
  readOnly: false,
53
61
  destructive: true,
62
+ idempotent: true,
54
63
  inputSchema: SegmentDeleteInputSchema,
55
64
  handler: segmentDelete,
56
65
  },
@@ -59,6 +68,7 @@ export const segmentToolDefinitions = [
59
68
  title: "Get Segment Members",
60
69
  description: "Get members belonging to a segment. Returns paginated list of member details. Use for verifying segment criteria or exporting member lists.",
61
70
  readOnly: true,
71
+ idempotent: true,
62
72
  inputSchema: SegmentGetMembersInputSchema,
63
73
  handler: segmentGetMembers,
64
74
  },
@@ -67,6 +77,7 @@ export const segmentToolDefinitions = [
67
77
  title: "Activate Segment",
68
78
  description: "Activate a segment. Active segments are used for campaign targeting and can be queried for members.",
69
79
  readOnly: false,
80
+ idempotent: true,
70
81
  inputSchema: SegmentActivateInputSchema,
71
82
  handler: segmentActivate,
72
83
  },
@@ -75,6 +86,7 @@ export const segmentToolDefinitions = [
75
86
  title: "Deactivate Segment",
76
87
  description: "Deactivate a segment. Deactivated segments are not used for campaign targeting but retain their configuration.",
77
88
  readOnly: false,
89
+ idempotent: true,
78
90
  inputSchema: SegmentDeactivateInputSchema,
79
91
  handler: segmentDeactivate,
80
92
  },
@@ -84,6 +96,7 @@ export const segmentToolDefinitions = [
84
96
  description: "Get resources (campaigns, rewards, etc.) that use this segment for targeting. Check before deleting a segment. " +
85
97
  "NOTE: This endpoint may not be available in all API versions - if it returns NOT_FOUND, manually check campaigns and rewards that reference this segment.",
86
98
  readOnly: true,
99
+ idempotent: true,
87
100
  inputSchema: SegmentGetResourcesInputSchema,
88
101
  handler: segmentGetResources,
89
102
  },
@@ -15,16 +15,19 @@ const SegmentCriterionInputSchema = z.object({
15
15
  type: z.string().describe("Criterion type. WORKING: 'transaction_count' (requires both min AND max; MCP auto-fills max if omitted). NOT WORKING: 'tier', 'points_balance' are rejected by the API."),
16
16
  criterionId: z.string().optional().describe("Criterion ID (optional, generated if not provided)."),
17
17
  // Common criterion data fields
18
- days: z.number().optional().describe("Days for time-based criteria."),
18
+ days: z.number().optional().describe("Days for time-based criteria (e.g., 'in the last N days'). Used with transaction_count, purchase_period criteria."),
19
19
  fromDate: z.string().optional().describe("Start date (ISO format)."),
20
20
  toDate: z.string().optional().describe("End date (ISO format)."),
21
- min: z.number().optional().describe("Minimum value. BUSINESS IMPACT: Too low a threshold (e.g., min=2 transactions) captures most members -- segment becomes too broad, wasting campaign budget."),
22
- max: z.number().optional().describe("Maximum value. BUSINESS IMPACT: Too narrow a range (e.g., max=3 with min=3) captures very few members -- limited campaign impact."),
21
+ min: z.number().optional().describe("Minimum value (e.g., min=5 for 'at least 5 transactions'). Required for transaction_count type. BUSINESS IMPACT: Too low a threshold (e.g., min=2 transactions) captures most members -- segment becomes too broad, wasting campaign budget."),
22
+ max: z.number().optional().describe("Maximum value. Required for transaction_count type (MCP auto-fills max=999999 if omitted). BUSINESS IMPACT: Too narrow a range (e.g., max=3 with min=3) captures very few members -- limited campaign impact."),
23
23
  posIds: z.array(z.string()).optional().describe("POS IDs."),
24
24
  skus: z.array(z.string()).optional().describe("SKU codes."),
25
25
  makers: z.array(z.string()).optional().describe("Maker/brand names."),
26
- labels: z.array(z.object({ key: z.string(), value: z.string() })).optional().describe("Label key-value pairs."),
27
- tierIds: z.array(z.string()).optional().describe("Tier level IDs."),
26
+ labels: z.array(z.object({
27
+ key: z.string().describe("Label key (e.g., 'category', 'brand', 'channel')."),
28
+ value: z.string().describe("Label value to match (e.g., 'electronics', 'nike', 'online')."),
29
+ })).optional().describe("Label key-value pairs for label-based criteria."),
30
+ tierIds: z.array(z.string()).optional().describe("Tier level IDs. WARNING: 'tier' criterion type is NOT WORKING -- API rejects it. Use campaign audience targeting for tier-based segmentation instead."),
28
31
  campaignId: z.string().optional().describe("Campaign ID for campaign_completion criterion."),
29
32
  countries: z.array(z.string()).optional().describe("Country codes."),
30
33
  anniversaryType: z.string().optional().describe("Anniversary type."),
@@ -0,0 +1,25 @@
1
+ import type { Store, StoreListResponse } from "./schemas.js";
2
+ export declare function storeList(input: {
3
+ page?: number;
4
+ perPage?: number;
5
+ active?: boolean;
6
+ name?: string;
7
+ code?: string;
8
+ }): Promise<StoreListResponse>;
9
+ export declare function storeCreate(input: {
10
+ code: string;
11
+ name: string;
12
+ currency?: string;
13
+ active?: boolean;
14
+ }): Promise<{
15
+ storeId: string;
16
+ }>;
17
+ export declare function storeGet(input: {
18
+ storeId: string;
19
+ }): Promise<Store>;
20
+ export declare function storeUpdate(input: {
21
+ storeId: string;
22
+ name?: string;
23
+ active?: boolean;
24
+ currency?: string;
25
+ }): Promise<void>;