@open-loyalty/mcp-server 1.7.0 → 1.12.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 (98) hide show
  1. package/dist/instructions.d.ts +1 -1
  2. package/dist/instructions.js +58 -22
  3. package/dist/server.js +3 -0
  4. package/dist/tools/apps/campaign-builder/handlers.d.ts +25 -0
  5. package/dist/tools/apps/campaign-builder/handlers.js +47 -0
  6. package/dist/tools/apps/campaign-builder/tool.d.ts +2 -0
  7. package/dist/tools/apps/campaign-builder/tool.js +36 -0
  8. package/dist/tools/apps/dashboard/handlers.d.ts +13 -0
  9. package/dist/tools/apps/dashboard/handlers.js +23 -0
  10. package/dist/tools/apps/dashboard/tool.d.ts +2 -0
  11. package/dist/tools/apps/dashboard/tool.js +48 -0
  12. package/dist/tools/apps/index.d.ts +6 -0
  13. package/dist/tools/apps/index.js +18 -0
  14. package/dist/tools/apps/member-profile/handlers.d.ts +49 -0
  15. package/dist/tools/apps/member-profile/handlers.js +48 -0
  16. package/dist/tools/apps/member-profile/tool.d.ts +2 -0
  17. package/dist/tools/apps/member-profile/tool.js +37 -0
  18. package/dist/tools/apps/rewards-catalog/handlers.d.ts +29 -0
  19. package/dist/tools/apps/rewards-catalog/handlers.js +25 -0
  20. package/dist/tools/apps/rewards-catalog/tool.d.ts +2 -0
  21. package/dist/tools/apps/rewards-catalog/tool.js +40 -0
  22. package/dist/tools/apps/tier-visualizer/handlers.d.ts +27 -0
  23. package/dist/tools/apps/tier-visualizer/handlers.js +60 -0
  24. package/dist/tools/apps/tier-visualizer/tool.d.ts +2 -0
  25. package/dist/tools/apps/tier-visualizer/tool.js +39 -0
  26. package/dist/tools/apps/transaction-timeline/handlers.d.ts +27 -0
  27. package/dist/tools/apps/transaction-timeline/handlers.js +64 -0
  28. package/dist/tools/apps/transaction-timeline/tool.d.ts +2 -0
  29. package/dist/tools/apps/transaction-timeline/tool.js +37 -0
  30. package/dist/tools/campaign/index.d.ts +16 -3
  31. package/dist/tools/campaign/index.js +15 -4
  32. package/dist/tools/campaign/member-handlers.d.ts +12 -0
  33. package/dist/tools/campaign/member-handlers.js +33 -0
  34. package/dist/tools/campaign/schemas.d.ts +6 -0
  35. package/dist/tools/campaign/schemas.js +6 -0
  36. package/dist/tools/channel/handlers.d.ts +32 -0
  37. package/dist/tools/channel/handlers.js +130 -0
  38. package/dist/tools/channel/index.d.ts +68 -0
  39. package/dist/tools/channel/index.js +59 -0
  40. package/dist/tools/channel/schemas.d.ts +29 -0
  41. package/dist/tools/channel/schemas.js +30 -0
  42. package/dist/tools/context/handlers.d.ts +49 -0
  43. package/dist/tools/context/handlers.js +131 -0
  44. package/dist/tools/context/index.d.ts +15 -0
  45. package/dist/tools/context/index.js +20 -0
  46. package/dist/tools/context/schemas.d.ts +7 -0
  47. package/dist/tools/context/schemas.js +4 -0
  48. package/dist/tools/group-of-values/handlers.d.ts +39 -0
  49. package/dist/tools/group-of-values/handlers.js +133 -0
  50. package/dist/tools/group-of-values/index.d.ts +82 -0
  51. package/dist/tools/group-of-values/index.js +72 -0
  52. package/dist/tools/group-of-values/schemas.d.ts +36 -0
  53. package/dist/tools/group-of-values/schemas.js +39 -0
  54. package/dist/tools/index.js +12 -0
  55. package/dist/tools/language/handlers.d.ts +24 -0
  56. package/dist/tools/language/handlers.js +127 -0
  57. package/dist/tools/language/index.d.ts +64 -0
  58. package/dist/tools/language/index.js +60 -0
  59. package/dist/tools/language/schemas.d.ts +25 -0
  60. package/dist/tools/language/schemas.js +25 -0
  61. package/dist/tools/member/handlers.d.ts +4 -0
  62. package/dist/tools/member/handlers.js +27 -0
  63. package/dist/tools/member/index.d.ts +14 -2
  64. package/dist/tools/member/index.js +15 -2
  65. package/dist/tools/points/fraud-handlers.d.ts +21 -0
  66. package/dist/tools/points/fraud-handlers.js +96 -0
  67. package/dist/tools/points/index.d.ts +50 -1
  68. package/dist/tools/points/index.js +45 -2
  69. package/dist/tools/points/schemas.d.ts +11 -0
  70. package/dist/tools/points/schemas.js +11 -0
  71. package/dist/tools/reward/category-handlers.d.ts +27 -0
  72. package/dist/tools/reward/category-handlers.js +70 -0
  73. package/dist/tools/reward/handlers.d.ts +0 -12
  74. package/dist/tools/reward/handlers.js +0 -28
  75. package/dist/tools/reward/index.d.ts +76 -3
  76. package/dist/tools/reward/index.js +63 -4
  77. package/dist/tools/reward/photo-handlers.d.ts +10 -0
  78. package/dist/tools/reward/photo-handlers.js +97 -0
  79. package/dist/tools/reward/redemption-handlers.d.ts +23 -0
  80. package/dist/tools/reward/redemption-handlers.js +50 -0
  81. package/dist/tools/reward/schemas.d.ts +31 -0
  82. package/dist/tools/reward/schemas.js +33 -0
  83. package/dist/tools/segment/handlers.js +14 -10
  84. package/dist/tools/segment/index.js +1 -1
  85. package/dist/tools/segment/schemas.js +3 -3
  86. package/dist/tools/store/handlers.d.ts +24 -0
  87. package/dist/tools/store/handlers.js +29 -1
  88. package/dist/tools/store/index.d.ts +41 -3
  89. package/dist/tools/store/index.js +27 -4
  90. package/dist/tools/store/schemas.d.ts +24 -0
  91. package/dist/tools/store/schemas.js +24 -0
  92. package/dist/ui/campaign-builder.html +648 -0
  93. package/dist/ui/dashboard.html +299 -0
  94. package/dist/ui/member-profile.html +592 -0
  95. package/dist/ui/rewards-catalog.html +274 -0
  96. package/dist/ui/tier-visualizer.html +393 -0
  97. package/dist/ui/transaction-timeline.html +360 -0
  98. package/package.json +7 -2
@@ -1,6 +1,12 @@
1
- export { RewardListInputSchema, RewardCreateInputSchema, RewardGetInputSchema, RewardUpdateInputSchema, RewardIdInputSchema, RewardBuyInputSchema, RewardRedeemInputSchema, RewardCategoryListInputSchema, } from "./schemas.js";
2
- export { rewardList, rewardCreate, rewardGet, rewardUpdate, rewardActivate, rewardDeactivate, rewardBuy, rewardRedeem, rewardCategoryList, } from "./handlers.js";
3
- import { rewardList, rewardCreate, rewardGet, rewardUpdate, rewardActivate, rewardDeactivate, rewardBuy, rewardRedeem, rewardCategoryList } from "./handlers.js";
1
+ export { RewardListInputSchema, RewardCreateInputSchema, RewardGetInputSchema, RewardUpdateInputSchema, RewardIdInputSchema, RewardBuyInputSchema, RewardRedeemInputSchema, RewardCategoryListInputSchema, RewardCategoryCreateInputSchema, RewardCategoryUpdateInputSchema, RewardGetIssuedRewardsInputSchema, RewardPhotoUploadInputSchema, RewardPhotoDeleteInputSchema, } from "./schemas.js";
2
+ export { rewardList, rewardCreate, rewardGet, rewardUpdate, rewardActivate, rewardDeactivate, rewardBuy, rewardRedeem, } from "./handlers.js";
3
+ export { rewardCategoryList, rewardCategoryCreate, rewardCategoryUpdate, } from "./category-handlers.js";
4
+ export { rewardGetIssuedRewards, } from "./redemption-handlers.js";
5
+ export { rewardPhotoUpload, rewardPhotoDelete, } from "./photo-handlers.js";
6
+ import { rewardList, rewardCreate, rewardGet, rewardUpdate, rewardActivate, rewardDeactivate, rewardBuy, rewardRedeem } from "./handlers.js";
7
+ import { rewardCategoryList, rewardCategoryCreate, rewardCategoryUpdate } from "./category-handlers.js";
8
+ import { rewardGetIssuedRewards } from "./redemption-handlers.js";
9
+ import { rewardPhotoUpload, rewardPhotoDelete } from "./photo-handlers.js";
4
10
  export declare const rewardToolDefinitions: readonly [{
5
11
  readonly name: "ol_reward_list";
6
12
  readonly title: "Browse Rewards";
@@ -176,4 +182,71 @@ export declare const rewardToolDefinitions: readonly [{
176
182
  active: import("zod").ZodOptional<import("zod").ZodBoolean>;
177
183
  };
178
184
  readonly handler: typeof rewardCategoryList;
185
+ }, {
186
+ readonly name: "ol_reward_category_create";
187
+ readonly title: "Create Reward Category";
188
+ readonly description: string;
189
+ readonly readOnly: false;
190
+ readonly idempotent: false;
191
+ readonly inputSchema: {
192
+ storeCode: import("zod").ZodOptional<import("zod").ZodString>;
193
+ name: import("zod").ZodString;
194
+ active: import("zod").ZodOptional<import("zod").ZodBoolean>;
195
+ sortOrder: import("zod").ZodOptional<import("zod").ZodNumber>;
196
+ };
197
+ readonly handler: typeof rewardCategoryCreate;
198
+ }, {
199
+ readonly name: "ol_reward_category_update";
200
+ readonly title: "Update Reward Category";
201
+ readonly description: string;
202
+ readonly readOnly: false;
203
+ readonly idempotent: true;
204
+ readonly inputSchema: {
205
+ storeCode: import("zod").ZodOptional<import("zod").ZodString>;
206
+ categoryId: import("zod").ZodString;
207
+ name: import("zod").ZodString;
208
+ active: import("zod").ZodOptional<import("zod").ZodBoolean>;
209
+ sortOrder: import("zod").ZodOptional<import("zod").ZodNumber>;
210
+ };
211
+ readonly handler: typeof rewardCategoryUpdate;
212
+ }, {
213
+ readonly name: "ol_member_get_issued_rewards";
214
+ readonly title: "Get Member's Issued Rewards";
215
+ readonly description: string;
216
+ readonly readOnly: true;
217
+ readonly idempotent: true;
218
+ readonly inputSchema: {
219
+ storeCode: import("zod").ZodOptional<import("zod").ZodString>;
220
+ memberId: import("zod").ZodString;
221
+ page: import("zod").ZodOptional<import("zod").ZodNumber>;
222
+ perPage: import("zod").ZodOptional<import("zod").ZodNumber>;
223
+ status: import("zod").ZodOptional<import("zod").ZodString>;
224
+ rewardType: import("zod").ZodOptional<import("zod").ZodString>;
225
+ };
226
+ readonly handler: typeof rewardGetIssuedRewards;
227
+ }, {
228
+ readonly name: "ol_reward_photo_upload";
229
+ readonly title: "Upload Reward Photo";
230
+ readonly description: string;
231
+ readonly readOnly: false;
232
+ readonly idempotent: false;
233
+ readonly inputSchema: {
234
+ storeCode: import("zod").ZodOptional<import("zod").ZodString>;
235
+ rewardId: import("zod").ZodString;
236
+ photoUrl: import("zod").ZodString;
237
+ };
238
+ readonly handler: typeof rewardPhotoUpload;
239
+ }, {
240
+ readonly name: "ol_reward_photo_delete";
241
+ readonly title: "Delete Reward Photo (Permanent)";
242
+ readonly description: string;
243
+ readonly readOnly: false;
244
+ readonly destructive: true;
245
+ readonly idempotent: true;
246
+ readonly inputSchema: {
247
+ storeCode: import("zod").ZodOptional<import("zod").ZodString>;
248
+ rewardId: import("zod").ZodString;
249
+ photoId: import("zod").ZodString;
250
+ };
251
+ readonly handler: typeof rewardPhotoDelete;
179
252
  }];
@@ -1,10 +1,16 @@
1
1
  // Re-export schemas
2
- export { RewardListInputSchema, RewardCreateInputSchema, RewardGetInputSchema, RewardUpdateInputSchema, RewardIdInputSchema, RewardBuyInputSchema, RewardRedeemInputSchema, RewardCategoryListInputSchema, } from "./schemas.js";
2
+ export { RewardListInputSchema, RewardCreateInputSchema, RewardGetInputSchema, RewardUpdateInputSchema, RewardIdInputSchema, RewardBuyInputSchema, RewardRedeemInputSchema, RewardCategoryListInputSchema, RewardCategoryCreateInputSchema, RewardCategoryUpdateInputSchema, RewardGetIssuedRewardsInputSchema, RewardPhotoUploadInputSchema, RewardPhotoDeleteInputSchema, } from "./schemas.js";
3
3
  // Re-export handlers
4
- export { rewardList, rewardCreate, rewardGet, rewardUpdate, rewardActivate, rewardDeactivate, rewardBuy, rewardRedeem, rewardCategoryList, } from "./handlers.js";
4
+ export { rewardList, rewardCreate, rewardGet, rewardUpdate, rewardActivate, rewardDeactivate, rewardBuy, rewardRedeem, } from "./handlers.js";
5
+ export { rewardCategoryList, rewardCategoryCreate, rewardCategoryUpdate, } from "./category-handlers.js";
6
+ export { rewardGetIssuedRewards, } from "./redemption-handlers.js";
7
+ export { rewardPhotoUpload, rewardPhotoDelete, } from "./photo-handlers.js";
5
8
  // Imports for tool definitions
6
- import { RewardListInputSchema, RewardCreateInputSchema, RewardGetInputSchema, RewardUpdateInputSchema, RewardIdInputSchema, RewardBuyInputSchema, RewardRedeemInputSchema, RewardCategoryListInputSchema, } from "./schemas.js";
7
- import { rewardList, rewardCreate, rewardGet, rewardUpdate, rewardActivate, rewardDeactivate, rewardBuy, rewardRedeem, rewardCategoryList, } from "./handlers.js";
9
+ import { RewardListInputSchema, RewardCreateInputSchema, RewardGetInputSchema, RewardUpdateInputSchema, RewardIdInputSchema, RewardBuyInputSchema, RewardRedeemInputSchema, RewardCategoryListInputSchema, RewardCategoryCreateInputSchema, RewardCategoryUpdateInputSchema, RewardGetIssuedRewardsInputSchema, RewardPhotoUploadInputSchema, RewardPhotoDeleteInputSchema, } from "./schemas.js";
10
+ import { rewardList, rewardCreate, rewardGet, rewardUpdate, rewardActivate, rewardDeactivate, rewardBuy, rewardRedeem, } from "./handlers.js";
11
+ import { rewardCategoryList, rewardCategoryCreate, rewardCategoryUpdate, } from "./category-handlers.js";
12
+ import { rewardGetIssuedRewards } from "./redemption-handlers.js";
13
+ import { rewardPhotoUpload, rewardPhotoDelete, } from "./photo-handlers.js";
8
14
  // Tool definitions
9
15
  export const rewardToolDefinitions = [
10
16
  {
@@ -103,4 +109,57 @@ export const rewardToolDefinitions = [
103
109
  inputSchema: RewardCategoryListInputSchema,
104
110
  handler: rewardCategoryList,
105
111
  },
112
+ {
113
+ name: "ol_reward_category_create",
114
+ title: "Create Reward Category",
115
+ description: "Create a new reward category for organizing rewards. " +
116
+ "REQUIRED: name (English). Use ol_reward_update to assign rewards to this category after creation.",
117
+ readOnly: false,
118
+ idempotent: false,
119
+ inputSchema: RewardCategoryCreateInputSchema,
120
+ handler: rewardCategoryCreate,
121
+ },
122
+ {
123
+ name: "ol_reward_category_update",
124
+ title: "Update Reward Category",
125
+ description: "Update a reward category's name, active status, or sort order. " +
126
+ "Use ol_reward_category_list to find the categoryId.",
127
+ readOnly: false,
128
+ idempotent: true,
129
+ inputSchema: RewardCategoryUpdateInputSchema,
130
+ handler: rewardCategoryUpdate,
131
+ },
132
+ {
133
+ name: "ol_member_get_issued_rewards",
134
+ title: "Get Member's Issued Rewards",
135
+ description: "List rewards redeemed/issued to a specific member. " +
136
+ "Returns issuedRewardId, name, status, costInPoints, rewardType, token (coupon code), redemptionDate. " +
137
+ "Use to answer 'what has member X redeemed?', check coupon codes, or audit reward history. " +
138
+ "Filter by status: 'pending', 'fulfilled', 'cancelled'. Filter by rewardType: 'material', 'static_coupon', etc.",
139
+ readOnly: true,
140
+ idempotent: true,
141
+ inputSchema: RewardGetIssuedRewardsInputSchema,
142
+ handler: rewardGetIssuedRewards,
143
+ },
144
+ {
145
+ name: "ol_reward_photo_upload",
146
+ title: "Upload Reward Photo",
147
+ description: "Attach a photo to a reward. Provide a publicly accessible photoUrl — the handler fetches and uploads it automatically. " +
148
+ "Allowed types: PNG, JPEG, GIF. Max: 2MB. Use ol_reward_get to see existing photos on the reward.",
149
+ readOnly: false,
150
+ idempotent: false,
151
+ inputSchema: RewardPhotoUploadInputSchema,
152
+ handler: rewardPhotoUpload,
153
+ },
154
+ {
155
+ name: "ol_reward_photo_delete",
156
+ title: "Delete Reward Photo (Permanent)",
157
+ description: "Permanently delete a photo from a reward. " +
158
+ "Use ol_reward_get to find existing photoIds on the reward.",
159
+ readOnly: false,
160
+ destructive: true,
161
+ idempotent: true,
162
+ inputSchema: RewardPhotoDeleteInputSchema,
163
+ handler: rewardPhotoDelete,
164
+ },
106
165
  ];
@@ -0,0 +1,10 @@
1
+ export declare function rewardPhotoUpload(input: {
2
+ storeCode?: string;
3
+ rewardId: string;
4
+ photoUrl: string;
5
+ }): Promise<void>;
6
+ export declare function rewardPhotoDelete(input: {
7
+ storeCode?: string;
8
+ rewardId: string;
9
+ photoId: string;
10
+ }): Promise<void>;
@@ -0,0 +1,97 @@
1
+ import { apiDelete } from "../../client/http.js";
2
+ import { formatApiError, OpenLoyaltyError } from "../../utils/errors.js";
3
+ import { getConfig, getStoreCode } from "../../config.js";
4
+ import axios from "axios";
5
+ import FormDataNode from "form-data";
6
+ export async function rewardPhotoUpload(input) {
7
+ const config = getConfig();
8
+ const storeCode = getStoreCode(input.storeCode);
9
+ // Fetch the image from the provided URL
10
+ let imageBuffer;
11
+ let contentType;
12
+ let fileName;
13
+ try {
14
+ const imageResponse = await axios.get(input.photoUrl, {
15
+ responseType: "arraybuffer",
16
+ timeout: 30000,
17
+ });
18
+ imageBuffer = Buffer.from(imageResponse.data);
19
+ contentType = imageResponse.headers["content-type"] || "image/jpeg";
20
+ // Extract file name from URL or use default
21
+ const urlPath = new URL(input.photoUrl).pathname;
22
+ fileName = urlPath.split("/").pop() || "photo.jpg";
23
+ }
24
+ catch {
25
+ throw new OpenLoyaltyError({
26
+ code: "PHOTO_FETCH_FAILED",
27
+ message: `Failed to fetch image from URL: ${input.photoUrl}`,
28
+ hint: "Ensure the URL is publicly accessible and points to a valid image (PNG, JPEG, or GIF). Max size: 2MB.",
29
+ relatedTool: "ol_reward_photo_upload",
30
+ });
31
+ }
32
+ // Validate content type
33
+ const allowed = ["image/png", "image/gif", "image/jpeg", "image/jpg"];
34
+ if (!allowed.some(t => contentType.startsWith(t.split("/")[0] + "/" + t.split("/")[1]))) {
35
+ const normalizedType = contentType.toLowerCase();
36
+ if (!allowed.includes(normalizedType)) {
37
+ throw new OpenLoyaltyError({
38
+ code: "INVALID_PHOTO_TYPE",
39
+ message: `Image type '${contentType}' is not allowed`,
40
+ hint: "Only PNG, JPEG, and GIF images are accepted. Ensure the URL points to a valid image file.",
41
+ relatedTool: "ol_reward_photo_upload",
42
+ });
43
+ }
44
+ }
45
+ // Upload as multipart form data — field name: photo[file]
46
+ const formData = new FormDataNode();
47
+ formData.append("photo[file]", imageBuffer, {
48
+ filename: fileName,
49
+ contentType: contentType,
50
+ });
51
+ try {
52
+ await axios.post(`${config.apiUrl}/${storeCode}/reward/${input.rewardId}/photo`, formData, {
53
+ headers: {
54
+ "X-AUTH-TOKEN": config.apiToken,
55
+ ...formData.getHeaders(),
56
+ },
57
+ });
58
+ }
59
+ catch (error) {
60
+ if (axios.isAxiosError(error)) {
61
+ if (error.response?.status === 404) {
62
+ throw new OpenLoyaltyError({
63
+ code: "NOT_FOUND",
64
+ message: `Reward '${input.rewardId}' not found`,
65
+ hint: "Use ol_reward_list() to find existing rewards and their IDs.",
66
+ relatedTool: "ol_reward_photo_upload",
67
+ });
68
+ }
69
+ if (error.response?.status === 413) {
70
+ throw new OpenLoyaltyError({
71
+ code: "PHOTO_TOO_LARGE",
72
+ message: "The photo exceeds the 2MB size limit",
73
+ hint: "Compress or resize the image before uploading. Maximum allowed size is 2MB.",
74
+ relatedTool: "ol_reward_photo_upload",
75
+ });
76
+ }
77
+ }
78
+ throw formatApiError(error, "ol_reward_photo_upload");
79
+ }
80
+ }
81
+ export async function rewardPhotoDelete(input) {
82
+ const storeCode = getStoreCode(input.storeCode);
83
+ try {
84
+ await apiDelete(`/${storeCode}/reward/${input.rewardId}/photo/${input.photoId}`);
85
+ }
86
+ catch (error) {
87
+ if (axios.isAxiosError(error) && error.response?.status === 404) {
88
+ throw new OpenLoyaltyError({
89
+ code: "NOT_FOUND",
90
+ message: `Reward photo not found (rewardId: ${input.rewardId}, photoId: ${input.photoId})`,
91
+ hint: "Use ol_reward_get() to see the reward's current photos and their photoIds.",
92
+ relatedTool: "ol_reward_photo_delete",
93
+ });
94
+ }
95
+ throw formatApiError(error, "ol_reward_photo_delete");
96
+ }
97
+ }
@@ -0,0 +1,23 @@
1
+ export declare function rewardGetIssuedRewards(input: {
2
+ storeCode?: string;
3
+ memberId: string;
4
+ page?: number;
5
+ perPage?: number;
6
+ status?: string;
7
+ rewardType?: string;
8
+ }): Promise<{
9
+ issuedRewards: Array<{
10
+ issuedRewardId: string;
11
+ name?: string;
12
+ status?: string;
13
+ costInPoints?: number;
14
+ rewardType?: string;
15
+ token?: string;
16
+ redemptionDate?: string;
17
+ cancelledAt?: string;
18
+ }>;
19
+ total: {
20
+ all?: number;
21
+ filtered?: number;
22
+ };
23
+ }>;
@@ -0,0 +1,50 @@
1
+ import { apiGet } from "../../client/http.js";
2
+ import { formatApiError, OpenLoyaltyError } from "../../utils/errors.js";
3
+ import { getStoreCode } from "../../config.js";
4
+ export async function rewardGetIssuedRewards(input) {
5
+ const storeCode = getStoreCode(input.storeCode);
6
+ const params = new URLSearchParams();
7
+ params.append("customerId", input.memberId);
8
+ if (input.page)
9
+ params.append("_page", String(input.page));
10
+ if (input.perPage)
11
+ params.append("_itemsOnPage", String(input.perPage));
12
+ if (input.status)
13
+ params.append("status", input.status);
14
+ if (input.rewardType)
15
+ params.append("rewardType", input.rewardType);
16
+ const url = `/${storeCode}/redemption?${params.toString()}`;
17
+ try {
18
+ const response = await apiGet(url);
19
+ const issuedRewards = (response.items || []).map((item) => ({
20
+ issuedRewardId: item.issuedRewardId,
21
+ name: item.name,
22
+ status: item.status,
23
+ costInPoints: item.costInPoints,
24
+ rewardType: item.rewardType,
25
+ token: item.token,
26
+ redemptionDate: item.redemptionDate,
27
+ cancelledAt: item.cancelledAt,
28
+ }));
29
+ const total = response.total || {};
30
+ return {
31
+ issuedRewards,
32
+ total: {
33
+ all: typeof total.all === "number" ? total.all : undefined,
34
+ filtered: typeof total.filtered === "number" ? total.filtered : undefined,
35
+ },
36
+ };
37
+ }
38
+ catch (error) {
39
+ const axiosError = error;
40
+ if (axiosError.response?.status === 404) {
41
+ throw new OpenLoyaltyError({
42
+ code: "NOT_FOUND",
43
+ message: `Member '${input.memberId}' not found`,
44
+ hint: "Use ol_member_list() to find the member by email, name, or phone.",
45
+ relatedTool: "ol_member_get_issued_rewards",
46
+ });
47
+ }
48
+ throw formatApiError(error, "ol_member_get_issued_rewards");
49
+ }
50
+ }
@@ -107,3 +107,34 @@ export declare const RewardCategoryListInputSchema: {
107
107
  perPage: z.ZodOptional<z.ZodNumber>;
108
108
  active: z.ZodOptional<z.ZodBoolean>;
109
109
  };
110
+ export declare const RewardCategoryCreateInputSchema: {
111
+ storeCode: z.ZodOptional<z.ZodString>;
112
+ name: z.ZodString;
113
+ active: z.ZodOptional<z.ZodBoolean>;
114
+ sortOrder: z.ZodOptional<z.ZodNumber>;
115
+ };
116
+ export declare const RewardCategoryUpdateInputSchema: {
117
+ storeCode: z.ZodOptional<z.ZodString>;
118
+ categoryId: z.ZodString;
119
+ name: z.ZodString;
120
+ active: z.ZodOptional<z.ZodBoolean>;
121
+ sortOrder: z.ZodOptional<z.ZodNumber>;
122
+ };
123
+ export declare const RewardGetIssuedRewardsInputSchema: {
124
+ storeCode: z.ZodOptional<z.ZodString>;
125
+ memberId: z.ZodString;
126
+ page: z.ZodOptional<z.ZodNumber>;
127
+ perPage: z.ZodOptional<z.ZodNumber>;
128
+ status: z.ZodOptional<z.ZodString>;
129
+ rewardType: z.ZodOptional<z.ZodString>;
130
+ };
131
+ export declare const RewardPhotoUploadInputSchema: {
132
+ storeCode: z.ZodOptional<z.ZodString>;
133
+ rewardId: z.ZodString;
134
+ photoUrl: z.ZodString;
135
+ };
136
+ export declare const RewardPhotoDeleteInputSchema: {
137
+ storeCode: z.ZodOptional<z.ZodString>;
138
+ rewardId: z.ZodString;
139
+ photoId: z.ZodString;
140
+ };
@@ -105,3 +105,36 @@ export const RewardCategoryListInputSchema = {
105
105
  perPage: z.number().optional().describe("Items per page (default: 10)."),
106
106
  active: z.boolean().optional().describe("Filter by active status."),
107
107
  };
108
+ export const RewardCategoryCreateInputSchema = {
109
+ storeCode: z.string().optional().describe("INTERNAL: Auto-configured from server settings. NEVER ask the user for this value. Only set if the user explicitly requests a different store."),
110
+ name: z.string().describe("Category name in English (required)."),
111
+ active: z.boolean().optional().describe("Whether category is active (default: true)."),
112
+ sortOrder: z.number().optional().describe("Display sort order (0 = first, higher = later). Default: 0."),
113
+ };
114
+ export const RewardCategoryUpdateInputSchema = {
115
+ storeCode: z.string().optional().describe("INTERNAL: Auto-configured from server settings. NEVER ask the user for this value. Only set if the user explicitly requests a different store."),
116
+ categoryId: z.string().describe("The reward category ID (UUID) to update."),
117
+ name: z.string().describe("Category name in English (required)."),
118
+ active: z.boolean().optional().describe("Whether category is active."),
119
+ sortOrder: z.number().optional().describe("Display sort order (0 = first)."),
120
+ };
121
+ export const RewardGetIssuedRewardsInputSchema = {
122
+ storeCode: z.string().optional().describe("INTERNAL: Auto-configured from server settings. NEVER ask the user for this value. Only set if the user explicitly requests a different store."),
123
+ memberId: z.string().describe("Member ID (UUID) whose issued/redeemed rewards to list."),
124
+ page: z.number().optional().describe("Page number (default: 1)."),
125
+ perPage: z.number().optional().describe("Items per page (default: 10)."),
126
+ status: z.string().optional().describe("Filter by status (e.g., 'pending', 'fulfilled', 'cancelled')."),
127
+ rewardType: z.string().optional().describe("Filter by reward type (e.g., 'material', 'static_coupon')."),
128
+ };
129
+ export const RewardPhotoUploadInputSchema = {
130
+ storeCode: z.string().optional().describe("INTERNAL: Auto-configured from server settings. NEVER ask the user for this value. Only set if the user explicitly requests a different store."),
131
+ rewardId: z.string().describe("Reward ID (UUID) to attach the photo to."),
132
+ photoUrl: z.string().describe("Publicly accessible URL of the image to upload. " +
133
+ "Allowed types: PNG, JPEG, GIF. Max size: 2MB. " +
134
+ "The handler will fetch and upload the image automatically."),
135
+ };
136
+ export const RewardPhotoDeleteInputSchema = {
137
+ storeCode: z.string().optional().describe("INTERNAL: Auto-configured from server settings. NEVER ask the user for this value. Only set if the user explicitly requests a different store."),
138
+ rewardId: z.string().describe("Reward ID (UUID) the photo belongs to."),
139
+ photoId: z.string().describe("Photo ID (UUID) to delete. Use ol_reward_get to find existing photoIds."),
140
+ };
@@ -1,21 +1,25 @@
1
1
  import { apiGet, apiPost, apiPut, apiDelete } from "../../client/http.js";
2
2
  import { formatApiError, OpenLoyaltyError } from "../../utils/errors.js";
3
3
  import { getStoreCode } from "../../config.js";
4
- const DEFAULT_TRANSACTION_COUNT_MAX = 999999;
5
- function normalizeSegmentParts(parts) {
4
+ function validateSegmentParts(parts, toolName) {
6
5
  return parts.map((part) => ({
7
6
  ...part,
8
7
  criteria: part.criteria.map((criterion) => {
9
8
  if (!criterion || typeof criterion !== "object") {
10
9
  return criterion;
11
10
  }
12
- const normalized = { ...criterion };
13
- if (normalized.type === "transaction_count" &&
14
- normalized.min !== undefined &&
15
- normalized.max === undefined) {
16
- normalized.max = DEFAULT_TRANSACTION_COUNT_MAX;
11
+ if (criterion.type === "transaction_count" &&
12
+ criterion.min !== undefined &&
13
+ criterion.max === undefined) {
14
+ throw new OpenLoyaltyError({
15
+ code: "MISSING_MAX_VALUE",
16
+ message: "transaction_count criterion requires both min and max values",
17
+ hint: "Provide an explicit max to define the upper bound. Use max: 999999 for 'min or more' behavior. " +
18
+ "Example: { type: 'transaction_count', min: 5, max: 999999 } for '5 or more purchases'.",
19
+ relatedTool: toolName,
20
+ });
17
21
  }
18
- return normalized;
22
+ return criterion;
19
23
  }),
20
24
  }));
21
25
  }
@@ -59,7 +63,7 @@ export async function segmentCreate(input) {
59
63
  const storeCode = getStoreCode(input.storeCode);
60
64
  const segmentPayload = {
61
65
  name: input.name,
62
- parts: normalizeSegmentParts(input.parts),
66
+ parts: validateSegmentParts(input.parts, "ol_segment_create"),
63
67
  };
64
68
  if (input.description)
65
69
  segmentPayload.description = input.description;
@@ -116,7 +120,7 @@ export async function segmentUpdate(input) {
116
120
  const storeCode = getStoreCode(input.storeCode);
117
121
  const segmentPayload = {
118
122
  name: input.name,
119
- parts: normalizeSegmentParts(input.parts),
123
+ parts: validateSegmentParts(input.parts, "ol_segment_update"),
120
124
  };
121
125
  if (input.description !== undefined)
122
126
  segmentPayload.description = input.description;
@@ -26,7 +26,7 @@ export const segmentToolDefinitions = [
26
26
  "3. Intended use -- campaign targeting (activate immediately) or analytics only (can stay inactive)? " +
27
27
  "REQUIRED: name, parts array with criteria. Parts use OR logic, criteria within parts use AND logic. " +
28
28
  "CRITERION FIELDS BY TYPE: " +
29
- "transaction_count: { type: 'transaction_count', min: N, max: N } (both required, MCP auto-fills max=999999 if omitted). " +
29
+ "transaction_count: { type: 'transaction_count', min: N, max: N } (BOTH required; use max: 999999 for 'min or more' behavior). " +
30
30
  "purchase_in_period: { type: 'purchase_in_period', days: N, min: N, max: N }. " +
31
31
  "average_transaction_value: { type: 'average_transaction_value', min: N, max: N }. " +
32
32
  "NOTE: 'tier' and 'points_balance' types are REJECTED by the API despite being documented.",
@@ -9,17 +9,17 @@ export const SegmentListInputSchema = {
9
9
  };
10
10
  // Criterion input schema - flexible to support all criterion types
11
11
  // NOTE: Only certain criterion types are supported by the API.
12
- // WORKING: transaction_count (requires both min AND max; MCP will auto-fill max if omitted)
12
+ // WORKING: transaction_count (requires BOTH min AND max omitting max throws MISSING_MAX_VALUE error)
13
13
  // NOT WORKING: tier, points_balance (rejected by API despite being documented)
14
14
  const SegmentCriterionInputSchema = z.object({
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."),
15
+ type: z.string().describe("Criterion type. 'transaction_count' requires BOTH min AND max (omitting max is an error). 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
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
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."),
22
+ max: z.number().optional().describe("Maximum value. REQUIRED for transaction_count type (omitting max throws an error). Use max: 999999 for 'min or more' behavior. 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."),
@@ -23,3 +23,27 @@ export declare function storeUpdate(input: {
23
23
  active?: boolean;
24
24
  currency?: string;
25
25
  }): Promise<void>;
26
+ export declare function storeGetSettings(input: {
27
+ storeCode?: string;
28
+ }): Promise<Record<string, unknown>>;
29
+ export declare function storeUpdateSettings(input: {
30
+ storeCode?: string;
31
+ programName?: string;
32
+ allowCustomersProfileEdits?: boolean;
33
+ tierAssignType?: "points" | "transactions";
34
+ tierWalletCode?: string;
35
+ excludeDeliveryCostsFromTierAssignment?: boolean;
36
+ excludedLevelCategories?: string[];
37
+ levelDowngradeMode?: "none" | "automatic" | "after_x_days";
38
+ levelDowngradeDays?: number;
39
+ levelDowngradeBase?: "none" | "active_points" | "earned_points" | "earned_points_since_last_level_change";
40
+ levelResetPointsOnDowngrade?: boolean;
41
+ accountActivationRequired?: boolean;
42
+ identificationMethod?: "email" | "phone" | "loyaltyCardNumber";
43
+ timezone?: string;
44
+ timezoneStrategy?: "none" | "default" | "localtime";
45
+ rewardWalletCode?: string;
46
+ expirePointsNotificationDays?: number;
47
+ expireCouponsNotificationDays?: number;
48
+ expireLevelsNotificationDays?: number;
49
+ }): Promise<Record<string, unknown>>;
@@ -1,6 +1,7 @@
1
- import { apiGet, apiPost, apiPut } from "../../client/http.js";
1
+ import { apiGet, apiPost, apiPut, apiPatch } from "../../client/http.js";
2
2
  import { formatApiError, OpenLoyaltyError } from "../../utils/errors.js";
3
3
  import axios from "axios";
4
+ import { getStoreCode } from "../../config.js";
4
5
  export async function storeList(input) {
5
6
  const params = new URLSearchParams();
6
7
  if (input.page)
@@ -87,3 +88,30 @@ export async function storeUpdate(input) {
87
88
  throw formatApiError(error, "ol_store_update");
88
89
  }
89
90
  }
91
+ export async function storeGetSettings(input) {
92
+ const storeCode = getStoreCode(input.storeCode);
93
+ try {
94
+ const response = await apiGet(`/${storeCode}/settings`);
95
+ return response.settings ?? response;
96
+ }
97
+ catch (error) {
98
+ throw formatApiError(error, "ol_store_get_settings");
99
+ }
100
+ }
101
+ export async function storeUpdateSettings(input) {
102
+ const storeCode = getStoreCode(input.storeCode);
103
+ const { storeCode: _sc, ...fields } = input;
104
+ const settings = {};
105
+ for (const [key, value] of Object.entries(fields)) {
106
+ if (value !== undefined) {
107
+ settings[key] = value;
108
+ }
109
+ }
110
+ try {
111
+ const response = await apiPatch(`/${storeCode}/settings`, { settings });
112
+ return response?.settings ?? response;
113
+ }
114
+ catch (error) {
115
+ throw formatApiError(error, "ol_store_update_settings");
116
+ }
117
+ }
@@ -1,7 +1,7 @@
1
- export { StoreListInputSchema, StoreCreateInputSchema, StoreGetInputSchema, StoreUpdateInputSchema, } from "./schemas.js";
1
+ export { StoreListInputSchema, StoreCreateInputSchema, StoreGetInputSchema, StoreUpdateInputSchema, StoreGetSettingsInputSchema, StoreUpdateSettingsInputSchema, } from "./schemas.js";
2
2
  export type { Store, StoreListResponse } from "./schemas.js";
3
- export { storeList, storeCreate, storeGet, storeUpdate, } from "./handlers.js";
4
- import { storeList, storeCreate, storeGet, storeUpdate } from "./handlers.js";
3
+ export { storeList, storeCreate, storeGet, storeUpdate, storeGetSettings, storeUpdateSettings, } from "./handlers.js";
4
+ import { storeList, storeCreate, storeGet, storeUpdate, storeGetSettings, storeUpdateSettings } from "./handlers.js";
5
5
  export declare const storeToolDefinitions: readonly [{
6
6
  readonly name: "ol_store_list";
7
7
  readonly title: "List Stores";
@@ -52,4 +52,42 @@ export declare const storeToolDefinitions: readonly [{
52
52
  currency: import("zod").ZodOptional<import("zod").ZodString>;
53
53
  };
54
54
  readonly handler: typeof storeUpdate;
55
+ }, {
56
+ readonly name: "ol_store_get_settings";
57
+ readonly title: "Get Store Settings";
58
+ readonly description: string;
59
+ readonly readOnly: true;
60
+ readonly idempotent: true;
61
+ readonly inputSchema: {
62
+ storeCode: import("zod").ZodOptional<import("zod").ZodString>;
63
+ };
64
+ readonly handler: typeof storeGetSettings;
65
+ }, {
66
+ readonly name: "ol_store_update_settings";
67
+ readonly title: "Update Store Settings";
68
+ readonly description: string;
69
+ readonly readOnly: false;
70
+ readonly idempotent: true;
71
+ readonly inputSchema: {
72
+ storeCode: import("zod").ZodOptional<import("zod").ZodString>;
73
+ programName: import("zod").ZodOptional<import("zod").ZodString>;
74
+ allowCustomersProfileEdits: import("zod").ZodOptional<import("zod").ZodBoolean>;
75
+ tierAssignType: import("zod").ZodOptional<import("zod").ZodEnum<["points", "transactions"]>>;
76
+ tierWalletCode: import("zod").ZodOptional<import("zod").ZodString>;
77
+ excludeDeliveryCostsFromTierAssignment: import("zod").ZodOptional<import("zod").ZodBoolean>;
78
+ excludedLevelCategories: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
79
+ levelDowngradeMode: import("zod").ZodOptional<import("zod").ZodEnum<["none", "automatic", "after_x_days"]>>;
80
+ levelDowngradeDays: import("zod").ZodOptional<import("zod").ZodNumber>;
81
+ levelDowngradeBase: import("zod").ZodOptional<import("zod").ZodEnum<["none", "active_points", "earned_points", "earned_points_since_last_level_change"]>>;
82
+ levelResetPointsOnDowngrade: import("zod").ZodOptional<import("zod").ZodBoolean>;
83
+ accountActivationRequired: import("zod").ZodOptional<import("zod").ZodBoolean>;
84
+ identificationMethod: import("zod").ZodOptional<import("zod").ZodEnum<["email", "phone", "loyaltyCardNumber"]>>;
85
+ timezone: import("zod").ZodOptional<import("zod").ZodString>;
86
+ timezoneStrategy: import("zod").ZodOptional<import("zod").ZodEnum<["none", "default", "localtime"]>>;
87
+ rewardWalletCode: import("zod").ZodOptional<import("zod").ZodString>;
88
+ expirePointsNotificationDays: import("zod").ZodOptional<import("zod").ZodNumber>;
89
+ expireCouponsNotificationDays: import("zod").ZodOptional<import("zod").ZodNumber>;
90
+ expireLevelsNotificationDays: import("zod").ZodOptional<import("zod").ZodNumber>;
91
+ };
92
+ readonly handler: typeof storeUpdateSettings;
55
93
  }];