@open-loyalty/mcp-server 1.3.7 → 1.4.1

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 (39) hide show
  1. package/dist/instructions.d.ts +1 -1
  2. package/dist/instructions.js +18 -5
  3. package/dist/tools/reward/handlers.d.ts +2 -0
  4. package/dist/tools/reward/handlers.js +52 -6
  5. package/dist/tools/reward/index.d.ts +2 -0
  6. package/dist/tools/reward/index.js +13 -7
  7. package/dist/tools/reward/schemas.d.ts +2 -0
  8. package/dist/tools/reward/schemas.js +15 -5
  9. package/dist/tools/tierset.d.ts +1 -1
  10. package/dist/tools/tierset.js +49 -25
  11. package/dist/tools/transaction.js +5 -2
  12. package/dist/tools/wallet-type.js +26 -17
  13. package/dist/types/schemas/admin.d.ts +6 -6
  14. package/dist/types/schemas/role.d.ts +4 -4
  15. package/package.json +1 -1
  16. package/dist/prompts/fan-engagement-setup.d.ts +0 -107
  17. package/dist/prompts/fan-engagement-setup.js +0 -492
  18. package/dist/tools/achievement.d.ts +0 -1017
  19. package/dist/tools/achievement.js +0 -354
  20. package/dist/tools/campaign.d.ts +0 -1800
  21. package/dist/tools/campaign.js +0 -737
  22. package/dist/tools/member.d.ts +0 -366
  23. package/dist/tools/member.js +0 -352
  24. package/dist/tools/reward.d.ts +0 -279
  25. package/dist/tools/reward.js +0 -361
  26. package/dist/tools/segment.d.ts +0 -816
  27. package/dist/tools/segment.js +0 -333
  28. package/dist/workflows/app-login-streak.d.ts +0 -39
  29. package/dist/workflows/app-login-streak.js +0 -298
  30. package/dist/workflows/early-arrival.d.ts +0 -33
  31. package/dist/workflows/early-arrival.js +0 -148
  32. package/dist/workflows/index.d.ts +0 -101
  33. package/dist/workflows/index.js +0 -208
  34. package/dist/workflows/match-attendance.d.ts +0 -45
  35. package/dist/workflows/match-attendance.js +0 -308
  36. package/dist/workflows/sportsbar-visit.d.ts +0 -41
  37. package/dist/workflows/sportsbar-visit.js +0 -284
  38. package/dist/workflows/vod-watching.d.ts +0 -43
  39. package/dist/workflows/vod-watching.js +0 -326
@@ -1,737 +0,0 @@
1
- import { z } from "zod";
2
- import { apiGet, apiPost, apiPut, apiPatch, apiDelete } from "../client/http.js";
3
- import { CampaignTypeEnum, CampaignTriggerEnum, } from "../types/schemas/campaign.js";
4
- import { formatApiError } from "../utils/errors.js";
5
- import { getStoreCode } from "../config.js";
6
- // Input Schemas
7
- export const CampaignListInputSchema = {
8
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
9
- page: z.number().optional().describe("Page number (default: 1)."),
10
- perPage: z.number().optional().describe("Items per page (default: 10)."),
11
- active: z.boolean().optional().describe("Filter by active status."),
12
- type: CampaignTypeEnum.optional().describe("Filter by campaign type: direct or referral."),
13
- trigger: CampaignTriggerEnum.optional().describe("Filter by trigger type: transaction, custom_event, time, etc."),
14
- };
15
- // Translations schema for campaign input
16
- const TranslationsInputSchema = z.object({
17
- en: z.object({
18
- name: z.string().describe("Campaign name in English."),
19
- description: z.string().optional().describe("Campaign description in English."),
20
- }),
21
- }).passthrough().describe("Translations object with at least 'en' key. Other languages (pl, de, etc.) are optional.");
22
- // Effect input schema
23
- const CampaignEffectInputSchema = z.object({
24
- effect: z.enum(["give_points", "give_reward", "deduct_unit", "assign_member_custom_attribute", "remove_member_custom_attribute"]).describe("Effect type: give_points, give_reward, deduct_unit, etc."),
25
- pointsRule: z.object({
26
- expression: z.string().optional().describe("Expression to calculate points (e.g., 'transaction.grossValue * 10')."),
27
- multiplier: z.number().optional().describe("Points multiplier."),
28
- fixedValue: z.number().optional().describe("Fixed points value."),
29
- }).optional().describe("Points calculation rule (required for give_points effect)."),
30
- walletCode: z.string().optional().describe("Wallet code for points (default wallet if not specified)."),
31
- rewardId: z.string().optional().describe("Reward ID (required for give_reward effect)."),
32
- customAttributeKey: z.string().optional().describe("Custom attribute key (for assign/remove member custom attribute effects)."),
33
- customAttributeValueRule: z.string().optional().describe("Custom attribute value rule."),
34
- });
35
- // Condition input schema
36
- const CampaignConditionInputSchema = z.object({
37
- operator: z.string().describe("Condition operator (is_equal, gte, lte, etc.)."),
38
- attribute: z.string().describe("Attribute to check."),
39
- data: z.record(z.unknown()).optional().describe("Condition-specific data."),
40
- });
41
- // Rule input schema
42
- const CampaignRuleInputSchema = z.object({
43
- name: z.string().describe("Rule name."),
44
- description: z.string().optional().describe("Rule description."),
45
- target: z.enum(["self", "referrer"]).optional().describe("Target for rule effects: self (default) or referrer."),
46
- effects: z.array(CampaignEffectInputSchema).describe("Array of effects to apply when rule matches."),
47
- conditions: z.array(CampaignConditionInputSchema).optional().describe("Array of conditions that must match for rule to apply."),
48
- });
49
- // Activity input schema
50
- const CampaignActivityInputSchema = z.object({
51
- startsAt: z.string().describe("Campaign start date/time (ISO format: YYYY-MM-DD HH:mm+TZ)."),
52
- endsAt: z.string().optional().describe("Campaign end date/time (ISO format). If not specified, campaign runs indefinitely."),
53
- });
54
- // Visibility input schema
55
- const CampaignVisibilityInputSchema = z.object({
56
- target: z.enum(["all", "tier", "segment"]).describe("Who can see the campaign: all, tier (specific tiers), or segment (specific segments)."),
57
- tiers: z.array(z.string()).optional().describe("Array of tier level IDs (required if target is 'tier')."),
58
- segments: z.array(z.string()).optional().describe("Array of segment IDs (required if target is 'segment')."),
59
- }).optional();
60
- // Audience input schema
61
- const CampaignAudienceInputSchema = z.object({
62
- target: z.enum(["all", "tier", "segment"]).describe("Who can participate: all, tier (specific tiers), or segment (specific segments)."),
63
- tiers: z.array(z.string()).optional().describe("Array of tier level IDs."),
64
- segments: z.array(z.string()).optional().describe("Array of segment IDs."),
65
- }).optional();
66
- // Limit value input schema
67
- const CampaignLimitValueInputSchema = z.object({
68
- value: z.number().describe("Limit value."),
69
- interval: z.object({
70
- type: z.enum(["days", "weeks", "months", "years", "forever"]).describe("Interval type."),
71
- value: z.number().optional().describe("Interval value (not needed for 'forever')."),
72
- }).optional().describe("Time interval for the limit."),
73
- });
74
- // Limits input schema
75
- const CampaignLimitsInputSchema = z.object({
76
- points: CampaignLimitValueInputSchema.optional().describe("Total points limit for the campaign."),
77
- pointsPerMember: CampaignLimitValueInputSchema.optional().describe("Points limit per member."),
78
- executionsPerMember: CampaignLimitValueInputSchema.optional().describe("Execution limit per member."),
79
- }).optional();
80
- // Label input schema
81
- const CampaignLabelInputSchema = z.object({
82
- key: z.string().describe("Label key."),
83
- value: z.string().describe("Label value."),
84
- });
85
- export const CampaignGetInputSchema = {
86
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
87
- campaignId: z.string().describe("The campaign ID (UUID) to retrieve."),
88
- };
89
- export const CampaignUpdateInputSchema = {
90
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
91
- campaignId: z.string().describe("The campaign ID (UUID) to update."),
92
- type: CampaignTypeEnum.describe("Campaign type: direct or referral."),
93
- trigger: CampaignTriggerEnum.describe("What triggers the campaign."),
94
- translations: z.object({
95
- en: z.object({
96
- name: z.string(),
97
- description: z.string().optional(),
98
- }),
99
- }).passthrough().describe("Translations object with at least 'en' key."),
100
- activity: z.object({
101
- startsAt: z.string(),
102
- endsAt: z.string().optional(),
103
- }).describe("Campaign active period."),
104
- rules: z.array(z.object({
105
- name: z.string(),
106
- description: z.string().optional(),
107
- target: z.enum(["self", "referrer"]).optional(),
108
- effects: z.array(z.object({
109
- effect: z.string(),
110
- pointsRule: z.record(z.unknown()).optional(),
111
- walletCode: z.string().optional(),
112
- rewardId: z.string().optional(),
113
- })),
114
- conditions: z.array(z.object({
115
- operator: z.string(),
116
- attribute: z.string(),
117
- data: z.record(z.unknown()).optional(),
118
- })).optional(),
119
- })).describe("Campaign rules."),
120
- visibility: z.object({
121
- target: z.enum(["all", "tier", "segment"]),
122
- tiers: z.array(z.string()).optional(),
123
- segments: z.array(z.string()).optional(),
124
- }).optional(),
125
- audience: z.object({
126
- target: z.enum(["all", "tier", "segment"]),
127
- tiers: z.array(z.string()).optional(),
128
- segments: z.array(z.string()).optional(),
129
- }).optional(),
130
- limits: z.object({
131
- points: z.object({ value: z.number(), interval: z.record(z.unknown()).optional() }).optional(),
132
- pointsPerMember: z.object({ value: z.number(), interval: z.record(z.unknown()).optional() }).optional(),
133
- executionsPerMember: z.object({ value: z.number(), interval: z.record(z.unknown()).optional() }).optional(),
134
- }).optional(),
135
- active: z.boolean().optional(),
136
- displayOrder: z.number().optional(),
137
- labels: z.array(z.object({ key: z.string(), value: z.string() })).optional(),
138
- event: z.string().optional(),
139
- };
140
- export const CampaignPatchInputSchema = {
141
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
142
- campaignId: z.string().describe("The campaign ID (UUID) to patch."),
143
- active: z.boolean().optional().describe("Set campaign active status."),
144
- displayOrder: z.number().optional().describe("Set campaign display order."),
145
- };
146
- export const CampaignDeleteInputSchema = {
147
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
148
- campaignId: z.string().describe("The campaign ID (UUID) to delete."),
149
- };
150
- // Transaction item for simulation
151
- const SimulateTransactionItemSchema = z.object({
152
- sku: z.string().describe("Product SKU."),
153
- name: z.string().describe("Product name."),
154
- qty: z.union([z.string(), z.number()]).optional().describe("Item quantity."),
155
- grossValue: z.union([z.string(), z.number()]).describe("Item gross value."),
156
- category: z.string().optional().describe("Product category."),
157
- maker: z.string().optional().describe("Product maker/brand."),
158
- labels: z.array(z.object({
159
- key: z.string(),
160
- value: z.string(),
161
- })).optional().describe("Custom labels."),
162
- });
163
- // Transaction for simulation
164
- const SimulateTransactionSchema = z.object({
165
- grossValue: z.number().optional().describe("Total transaction gross value (alternative to items)."),
166
- documentNumber: z.string().optional().describe("Document number."),
167
- purchasedAt: z.string().optional().describe("Purchase date/time (ISO format)."),
168
- documentType: z.enum(["sell", "return"]).optional().describe("Document type."),
169
- purchasePlace: z.string().optional().describe("Purchase location."),
170
- items: z.array(SimulateTransactionItemSchema).optional().describe("Transaction items."),
171
- labels: z.array(z.object({
172
- key: z.string(),
173
- value: z.string(),
174
- })).optional().describe("Custom labels."),
175
- }).optional();
176
- // Custom event for simulation
177
- const SimulateCustomEventSchema = z.object({
178
- eventCode: z.string().describe("Custom event code (must match campaign trigger event)."),
179
- attributes: z.record(z.unknown()).optional().describe("Custom event attributes."),
180
- }).optional();
181
- // Customer for simulation
182
- const SimulateCustomerSchema = z.object({
183
- customerId: z.string().optional().describe("Member ID (UUID) for explicit member matching."),
184
- id: z.string().optional().describe("Member ID (UUID) - alias for customerId."),
185
- email: z.string().optional().describe("Email for member matching."),
186
- loyaltyCardNumber: z.string().optional().describe("Loyalty card for member matching."),
187
- phone: z.string().optional().describe("Phone for member matching."),
188
- firstName: z.string().optional().describe("Customer first name."),
189
- lastName: z.string().optional().describe("Customer last name."),
190
- });
191
- // Referrer for simulation (referral campaigns)
192
- const SimulateReferrerSchema = z.object({
193
- id: z.string().optional().describe("Referrer member ID."),
194
- email: z.string().optional().describe("Referrer email."),
195
- loyaltyCardNumber: z.string().optional().describe("Referrer loyalty card number."),
196
- }).optional();
197
- export const CampaignSimulateInputSchema = {
198
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
199
- trigger: CampaignTriggerEnum.describe("Campaign trigger type to simulate: transaction, custom_event, etc."),
200
- transaction: SimulateTransactionSchema.describe("Transaction data (required for transaction trigger)."),
201
- customEvent: SimulateCustomEventSchema.describe("Custom event data (required for custom_event trigger)."),
202
- customer: SimulateCustomerSchema.describe("Customer/member data for simulation."),
203
- referrer: SimulateReferrerSchema.describe("Referrer data for referral campaigns."),
204
- };
205
- export const CampaignGenerateCodesInputSchema = {
206
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
207
- campaignId: z.string().describe("The campaign ID (UUID) to generate codes for."),
208
- quantity: z.number().min(1).describe("Number of codes to generate."),
209
- };
210
- export const CampaignListCodesInputSchema = {
211
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
212
- campaignId: z.string().describe("The campaign ID (UUID) to list codes for."),
213
- page: z.number().optional().describe("Page number (default: 1)."),
214
- perPage: z.number().optional().describe("Items per page (default: 25)."),
215
- status: z.string().optional().describe("Filter by code status (e.g., 'active', 'used')."),
216
- code: z.string().optional().describe("Filter by specific code."),
217
- };
218
- export const CampaignGetAvailableInputSchema = {
219
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
220
- memberId: z.string().describe("The member ID (UUID) to get available campaigns for."),
221
- page: z.number().optional().describe("Page number (default: 1)."),
222
- perPage: z.number().optional().describe("Items per page (default: 25)."),
223
- type: CampaignTypeEnum.optional().describe("Filter by campaign type."),
224
- trigger: CampaignTriggerEnum.optional().describe("Filter by campaign trigger."),
225
- };
226
- export const CampaignGetVisibleInputSchema = {
227
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
228
- memberId: z.string().describe("The member ID (UUID) to get visible campaigns for."),
229
- page: z.number().optional().describe("Page number (default: 1)."),
230
- perPage: z.number().optional().describe("Items per page (default: 25)."),
231
- active: z.boolean().optional().describe("Filter by active status."),
232
- };
233
- export const CampaignGetLeaderboardInputSchema = {
234
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
235
- campaignId: z.string().describe("The campaign ID (UUID) to get leaderboard for."),
236
- type: z.string().optional().describe("Cycle type for ranking (e.g., 'weekly', 'monthly')."),
237
- };
238
- export const CampaignCreateInputSchema = {
239
- storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
240
- type: CampaignTypeEnum.describe("Campaign type: direct (standard) or referral (for referral programs)."),
241
- trigger: CampaignTriggerEnum.describe("What triggers the campaign: transaction, custom_event, time, achievement, etc."),
242
- translations: TranslationsInputSchema,
243
- activity: CampaignActivityInputSchema.describe("Campaign active period with startsAt and optional endsAt."),
244
- rules: z.array(CampaignRuleInputSchema).describe("Array of rules defining when and what effects to apply."),
245
- visibility: CampaignVisibilityInputSchema.describe("Who can see the campaign."),
246
- audience: CampaignAudienceInputSchema.describe("Who can participate in the campaign."),
247
- limits: CampaignLimitsInputSchema.describe("Campaign limits for points and executions."),
248
- active: z.boolean().optional().describe("Whether campaign is active (default: false)."),
249
- displayOrder: z.number().optional().describe("Display order for sorting campaigns."),
250
- labels: z.array(CampaignLabelInputSchema).optional().describe("Custom labels/tags for the campaign."),
251
- event: z.string().optional().describe("Custom event name (required for custom_event trigger)."),
252
- };
253
- // Handler functions
254
- export async function campaignList(input) {
255
- const storeCode = getStoreCode(input.storeCode);
256
- const params = new URLSearchParams();
257
- if (input.page)
258
- params.append("_page", String(input.page));
259
- if (input.perPage)
260
- params.append("_itemsOnPage", String(input.perPage));
261
- if (input.active !== undefined)
262
- params.append("active", String(input.active));
263
- if (input.type)
264
- params.append("type", input.type);
265
- if (input.trigger)
266
- params.append("trigger", input.trigger);
267
- const queryString = params.toString();
268
- const url = `/${storeCode}/campaign${queryString ? `?${queryString}` : ""}`;
269
- try {
270
- const response = await apiGet(url);
271
- const campaigns = (response.items || []).map((item) => {
272
- const campaign = item;
273
- const translations = campaign.translations;
274
- // Handle both object and array translations format
275
- let name = "";
276
- if (Array.isArray(translations)) {
277
- const enTranslation = translations.find((t) => t.locale === "en" || t.locale?.startsWith("en"));
278
- name = enTranslation?.name || translations[0]?.name || campaign.name || "";
279
- }
280
- else if (translations && typeof translations === "object") {
281
- name = translations.en?.name || campaign.name || "";
282
- }
283
- else {
284
- name = campaign.name || "";
285
- }
286
- return {
287
- campaignId: campaign.campaignId,
288
- name,
289
- type: campaign.type,
290
- trigger: campaign.trigger,
291
- active: (campaign.active ?? false),
292
- displayOrder: campaign.displayOrder,
293
- };
294
- });
295
- const total = response.total || {};
296
- return {
297
- campaigns,
298
- total: {
299
- all: typeof total.all === "number" ? total.all : undefined,
300
- filtered: typeof total.filtered === "number" ? total.filtered : undefined,
301
- },
302
- };
303
- }
304
- catch (error) {
305
- throw formatApiError(error, "openloyalty_campaign_list");
306
- }
307
- }
308
- export async function campaignGet(input) {
309
- const storeCode = getStoreCode(input.storeCode);
310
- try {
311
- const response = await apiGet(`/${storeCode}/campaign/${input.campaignId}`);
312
- return response;
313
- }
314
- catch (error) {
315
- throw formatApiError(error, "openloyalty_campaign_get");
316
- }
317
- }
318
- export async function campaignUpdate(input) {
319
- const storeCode = getStoreCode(input.storeCode);
320
- const campaignId = input.campaignId;
321
- // Build campaign payload excluding storeCode and campaignId
322
- const { storeCode: _sc, campaignId: _cid, ...campaignPayload } = input;
323
- try {
324
- // CRITICAL: Wrap body as { campaign: {...} }
325
- await apiPut(`/${storeCode}/campaign/${campaignId}`, { campaign: campaignPayload });
326
- }
327
- catch (error) {
328
- throw formatApiError(error, "openloyalty_campaign_update");
329
- }
330
- }
331
- export async function campaignPatch(input) {
332
- const storeCode = getStoreCode(input.storeCode);
333
- const patchPayload = {};
334
- if (input.active !== undefined)
335
- patchPayload.active = input.active;
336
- if (input.displayOrder !== undefined)
337
- patchPayload.displayOrder = input.displayOrder;
338
- try {
339
- await apiPatch(`/${storeCode}/campaign/${input.campaignId}`, { campaign: patchPayload });
340
- }
341
- catch (error) {
342
- throw formatApiError(error, "openloyalty_campaign_patch");
343
- }
344
- }
345
- export async function campaignDelete(input) {
346
- const storeCode = getStoreCode(input.storeCode);
347
- try {
348
- await apiDelete(`/${storeCode}/campaign/${input.campaignId}`);
349
- }
350
- catch (error) {
351
- throw formatApiError(error, "openloyalty_campaign_delete");
352
- }
353
- }
354
- export async function campaignSimulate(input) {
355
- const storeCode = getStoreCode(input.storeCode);
356
- // Build the simulate payload
357
- const simulatePayload = {
358
- trigger: input.trigger,
359
- customer: {},
360
- };
361
- // Build customer object
362
- const customer = {};
363
- if (input.customer.customerId)
364
- customer.id = input.customer.customerId;
365
- if (input.customer.id)
366
- customer.id = input.customer.id;
367
- if (input.customer.email)
368
- customer.email = input.customer.email;
369
- if (input.customer.loyaltyCardNumber)
370
- customer.loyaltyCardNumber = input.customer.loyaltyCardNumber;
371
- if (input.customer.phone)
372
- customer.phone = input.customer.phone;
373
- if (input.customer.firstName)
374
- customer.firstName = input.customer.firstName;
375
- if (input.customer.lastName)
376
- customer.lastName = input.customer.lastName;
377
- simulatePayload.customer = customer;
378
- // Add transaction if provided
379
- if (input.transaction) {
380
- const transaction = {};
381
- if (input.transaction.grossValue !== undefined)
382
- transaction.grossValue = input.transaction.grossValue;
383
- if (input.transaction.documentNumber)
384
- transaction.documentNumber = input.transaction.documentNumber;
385
- if (input.transaction.purchasedAt)
386
- transaction.purchasedAt = input.transaction.purchasedAt;
387
- if (input.transaction.documentType)
388
- transaction.documentType = input.transaction.documentType;
389
- if (input.transaction.purchasePlace)
390
- transaction.purchasePlace = input.transaction.purchasePlace;
391
- if (input.transaction.items)
392
- transaction.items = input.transaction.items;
393
- if (input.transaction.labels)
394
- transaction.labels = input.transaction.labels;
395
- simulatePayload.transaction = transaction;
396
- }
397
- // Add custom event if provided (for custom_event trigger)
398
- if (input.customEvent) {
399
- simulatePayload.event = input.customEvent.eventCode;
400
- if (input.customEvent.attributes) {
401
- simulatePayload.eventAttributes = input.customEvent.attributes;
402
- }
403
- }
404
- // Add referrer if provided (for referral campaigns)
405
- if (input.referrer) {
406
- const referrer = {};
407
- if (input.referrer.id)
408
- referrer.id = input.referrer.id;
409
- if (input.referrer.email)
410
- referrer.email = input.referrer.email;
411
- if (input.referrer.loyaltyCardNumber)
412
- referrer.loyaltyCardNumber = input.referrer.loyaltyCardNumber;
413
- simulatePayload.referrer = referrer;
414
- }
415
- try {
416
- // CRITICAL: Wrap body as { simulate: {...} }
417
- const response = await apiPost(`/${storeCode}/campaign/simulate`, { simulate: simulatePayload });
418
- // Transform effects into simulated effects grouped by campaign
419
- const effectsByCampaign = new Map();
420
- for (const effect of response.effects || []) {
421
- const campaignId = effect.campaign?.campaignId || "unknown";
422
- const campaignName = effect.campaign?.name || undefined;
423
- if (!effectsByCampaign.has(campaignId)) {
424
- effectsByCampaign.set(campaignId, {
425
- campaignId,
426
- campaignName,
427
- effects: [],
428
- });
429
- }
430
- const campaignEffect = effectsByCampaign.get(campaignId);
431
- campaignEffect.effects.push({
432
- type: effect.type,
433
- target: effect.target,
434
- points: effect.points,
435
- wallet: effect.wallet,
436
- rewardId: effect.rewardId,
437
- });
438
- }
439
- return {
440
- simulatedEffects: Array.from(effectsByCampaign.values()),
441
- evaluationWarnings: response.evaluationWarnings,
442
- simulatedMember: response.simulatedMember,
443
- };
444
- }
445
- catch (error) {
446
- throw formatApiError(error, "openloyalty_campaign_simulate");
447
- }
448
- }
449
- export async function campaignGenerateCodes(input) {
450
- const storeCode = getStoreCode(input.storeCode);
451
- try {
452
- // Body wrapped as { generate: { numberOfCodes } }
453
- await apiPost(`/${storeCode}/campaign/${input.campaignId}/codes/generate`, { generate: { numberOfCodes: input.quantity } });
454
- // API returns 204 No Content, so we return the requested quantity
455
- return { codesGenerated: input.quantity };
456
- }
457
- catch (error) {
458
- throw formatApiError(error, "openloyalty_campaign_generate_codes");
459
- }
460
- }
461
- export async function campaignListCodes(input) {
462
- const storeCode = getStoreCode(input.storeCode);
463
- const params = new URLSearchParams();
464
- if (input.page)
465
- params.append("_page", String(input.page));
466
- if (input.perPage)
467
- params.append("_itemsOnPage", String(input.perPage));
468
- if (input.status)
469
- params.append("status", input.status);
470
- if (input.code)
471
- params.append("code", input.code);
472
- const queryString = params.toString();
473
- const url = `/${storeCode}/campaign/${input.campaignId}/codes${queryString ? `?${queryString}` : ""}`;
474
- try {
475
- const response = await apiGet(url);
476
- const codes = (response.items || []).map((item) => ({
477
- codeId: item.codeId,
478
- code: item.code,
479
- status: item.status,
480
- usedAt: item.usedAt,
481
- createdAt: item.createdAt,
482
- createdBy: item.createdBy,
483
- }));
484
- const total = response.total || {};
485
- return {
486
- codes,
487
- total: {
488
- all: typeof total.all === "number" ? total.all : undefined,
489
- filtered: typeof total.filtered === "number" ? total.filtered : undefined,
490
- },
491
- };
492
- }
493
- catch (error) {
494
- throw formatApiError(error, "openloyalty_campaign_list_codes");
495
- }
496
- }
497
- export async function campaignGetAvailable(input) {
498
- const storeCode = getStoreCode(input.storeCode);
499
- const params = new URLSearchParams();
500
- if (input.page)
501
- params.append("_page", String(input.page));
502
- if (input.perPage)
503
- params.append("_itemsOnPage", String(input.perPage));
504
- if (input.type)
505
- params.append("type", input.type);
506
- if (input.trigger)
507
- params.append("trigger", input.trigger);
508
- const queryString = params.toString();
509
- const url = `/${storeCode}/member/${input.memberId}/campaign${queryString ? `?${queryString}` : ""}`;
510
- try {
511
- const response = await apiGet(url);
512
- const campaigns = (response.items || []).map((item) => ({
513
- campaignId: item.campaignId,
514
- name: item.name,
515
- active: item.active,
516
- type: item.type,
517
- trigger: item.trigger,
518
- limitReached: item.limitReached,
519
- description: item.description,
520
- }));
521
- const total = response.total || {};
522
- return {
523
- campaigns,
524
- total: {
525
- all: typeof total.all === "number" ? total.all : undefined,
526
- filtered: typeof total.filtered === "number" ? total.filtered : undefined,
527
- },
528
- };
529
- }
530
- catch (error) {
531
- throw formatApiError(error, "openloyalty_campaign_get_available");
532
- }
533
- }
534
- export async function campaignGetVisible(input) {
535
- const storeCode = getStoreCode(input.storeCode);
536
- const params = new URLSearchParams();
537
- if (input.page)
538
- params.append("_page", String(input.page));
539
- if (input.perPage)
540
- params.append("_itemsOnPage", String(input.perPage));
541
- if (input.active !== undefined)
542
- params.append("active", String(input.active));
543
- const queryString = params.toString();
544
- const url = `/${storeCode}/member/${input.memberId}/campaign/visible${queryString ? `?${queryString}` : ""}`;
545
- try {
546
- const response = await apiGet(url);
547
- const campaigns = (response.items || []).map((item) => ({
548
- campaignId: item.campaignId,
549
- name: item.name,
550
- active: item.active,
551
- type: item.type,
552
- trigger: item.trigger,
553
- description: item.description,
554
- }));
555
- const total = response.total || {};
556
- return {
557
- campaigns,
558
- total: {
559
- all: typeof total.all === "number" ? total.all : undefined,
560
- filtered: typeof total.filtered === "number" ? total.filtered : undefined,
561
- },
562
- };
563
- }
564
- catch (error) {
565
- throw formatApiError(error, "openloyalty_campaign_get_visible");
566
- }
567
- }
568
- export async function campaignGetLeaderboard(input) {
569
- const storeCode = getStoreCode(input.storeCode);
570
- const params = new URLSearchParams();
571
- if (input.type)
572
- params.append("type", input.type);
573
- const queryString = params.toString();
574
- const url = `/${storeCode}/campaign/${input.campaignId}/leaderboard${queryString ? `?${queryString}` : ""}`;
575
- try {
576
- const response = await apiGet(url);
577
- const entries = (response.items || []).map((item) => ({
578
- member: {
579
- customerId: item.member.customerId,
580
- firstName: item.member.firstName,
581
- lastName: item.member.lastName,
582
- email: item.member.email,
583
- loyaltyCardNumber: item.member.loyaltyCardNumber,
584
- },
585
- ranking: {
586
- score: item.ranking.score,
587
- position: item.ranking.position,
588
- recalculatedAt: item.ranking.recalculatedAt,
589
- changedPositionAt: item.ranking.changedPositionAt,
590
- },
591
- }));
592
- const total = response.total || {};
593
- return {
594
- entries,
595
- total: {
596
- all: typeof total.all === "number" ? total.all : undefined,
597
- filtered: typeof total.filtered === "number" ? total.filtered : undefined,
598
- },
599
- };
600
- }
601
- catch (error) {
602
- throw formatApiError(error, "openloyalty_campaign_get_leaderboard");
603
- }
604
- }
605
- export async function campaignCreate(input) {
606
- const storeCode = getStoreCode(input.storeCode);
607
- // Build the campaign payload
608
- const campaignPayload = {
609
- type: input.type,
610
- trigger: input.trigger,
611
- translations: input.translations,
612
- activity: input.activity,
613
- rules: input.rules,
614
- };
615
- if (input.visibility)
616
- campaignPayload.visibility = input.visibility;
617
- if (input.audience)
618
- campaignPayload.audience = input.audience;
619
- if (input.limits)
620
- campaignPayload.limits = input.limits;
621
- if (input.active !== undefined)
622
- campaignPayload.active = input.active;
623
- if (input.displayOrder !== undefined)
624
- campaignPayload.displayOrder = input.displayOrder;
625
- if (input.labels)
626
- campaignPayload.labels = input.labels;
627
- if (input.event)
628
- campaignPayload.event = input.event;
629
- try {
630
- // CRITICAL: Wrap body as { campaign: {...} }
631
- const response = await apiPost(`/${storeCode}/campaign`, { campaign: campaignPayload });
632
- return { campaignId: response.campaignId };
633
- }
634
- catch (error) {
635
- throw formatApiError(error, "openloyalty_campaign_create");
636
- }
637
- }
638
- // Tool definitions
639
- export const campaignToolDefinitions = [
640
- {
641
- name: "openloyalty_campaign_list",
642
- title: "List Campaigns",
643
- description: "List all campaigns. Filter by type (direct/referral) or trigger (transaction/custom_event/time/etc). Use campaign_get for full configuration including rules and effects.",
644
- readOnly: true,
645
- inputSchema: CampaignListInputSchema,
646
- handler: campaignList,
647
- },
648
- {
649
- name: "openloyalty_campaign_create",
650
- title: "Create Campaign",
651
- description: "Create campaign to automate engagement. Triggers: transaction (purchase-based), custom_event (for custom actions), time (scheduled). Effects: give_points, give_reward, deduct_unit. Rules define when/what happens. Example - Double points: trigger=transaction, effect=give_points with pointsRule containing multiplier:2.",
652
- readOnly: false,
653
- inputSchema: CampaignCreateInputSchema,
654
- handler: campaignCreate,
655
- },
656
- {
657
- name: "openloyalty_campaign_get",
658
- title: "Get Campaign Details",
659
- description: "Get full campaign configuration including all rules, conditions, effects, and targeting.",
660
- readOnly: true,
661
- inputSchema: CampaignGetInputSchema,
662
- handler: campaignGet,
663
- },
664
- {
665
- name: "openloyalty_campaign_update",
666
- title: "Update Campaign",
667
- description: "Full update of campaign configuration. Requires all campaign fields. Use campaign_get first to retrieve current configuration, modify it, then send the complete object.",
668
- readOnly: false,
669
- inputSchema: CampaignUpdateInputSchema,
670
- handler: campaignUpdate,
671
- },
672
- {
673
- name: "openloyalty_campaign_patch",
674
- title: "Patch Campaign",
675
- description: "Partial update of campaign - only active status and displayOrder can be patched. For full updates, use campaign_update.",
676
- readOnly: false,
677
- inputSchema: CampaignPatchInputSchema,
678
- handler: campaignPatch,
679
- },
680
- {
681
- name: "openloyalty_campaign_delete",
682
- title: "Delete Campaign (Permanent)",
683
- description: "Permanently delete a campaign. This cannot be undone. Consider deactivating instead if you may need the campaign later.",
684
- readOnly: false,
685
- destructive: true,
686
- inputSchema: CampaignDeleteInputSchema,
687
- handler: campaignDelete,
688
- },
689
- {
690
- name: "openloyalty_campaign_simulate",
691
- title: "Simulate Campaign Effects",
692
- description: "Simulate campaign effects without executing them. Use to preview what points/rewards a transaction would earn. Provide trigger type, transaction/event data, and customer. Returns simulated effects grouped by campaign.",
693
- readOnly: true,
694
- inputSchema: CampaignSimulateInputSchema,
695
- handler: campaignSimulate,
696
- },
697
- {
698
- name: "openloyalty_campaign_generate_codes",
699
- title: "Generate Campaign Codes",
700
- description: "Generate unique redemption codes for a campaign. Use for promotions requiring unique codes. Codes are auto-generated and can be retrieved with campaign_list_codes.",
701
- readOnly: false,
702
- inputSchema: CampaignGenerateCodesInputSchema,
703
- handler: campaignGenerateCodes,
704
- },
705
- {
706
- name: "openloyalty_campaign_list_codes",
707
- title: "List Campaign Codes",
708
- description: "List redemption codes for a campaign. Filter by status (active/used) or specific code. Returns code details including usage status.",
709
- readOnly: true,
710
- inputSchema: CampaignListCodesInputSchema,
711
- handler: campaignListCodes,
712
- },
713
- {
714
- name: "openloyalty_campaign_get_available",
715
- title: "Get Available Campaigns for Member",
716
- description: "Get campaigns available to a specific member. Returns campaigns the member can participate in based on their tier, segments, and campaign targeting rules. Includes limitReached indicator.",
717
- readOnly: true,
718
- inputSchema: CampaignGetAvailableInputSchema,
719
- handler: campaignGetAvailable,
720
- },
721
- {
722
- name: "openloyalty_campaign_get_visible",
723
- title: "Get Visible Campaigns for Member",
724
- description: "Get campaigns visible to a specific member. May include campaigns not yet available (e.g., upcoming campaigns or those requiring certain conditions). Use get_available for campaigns the member can currently participate in.",
725
- readOnly: true,
726
- inputSchema: CampaignGetVisibleInputSchema,
727
- handler: campaignGetVisible,
728
- },
729
- {
730
- name: "openloyalty_campaign_get_leaderboard",
731
- title: "Get Campaign Leaderboard",
732
- description: "Get leaderboard rankings for a campaign. Returns ranked list of members with scores and positions. Use for leaderboard-type campaigns to show competition standings.",
733
- readOnly: true,
734
- inputSchema: CampaignGetLeaderboardInputSchema,
735
- handler: campaignGetLeaderboard,
736
- },
737
- ];