@open-loyalty/mcp-server 1.3.7 → 1.5.3

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 (100) hide show
  1. package/dist/config.d.ts +4 -3
  2. package/dist/config.js +9 -7
  3. package/dist/instructions.d.ts +1 -1
  4. package/dist/instructions.js +26 -8
  5. package/dist/tools/achievement/handlers.js +60 -1
  6. package/dist/tools/achievement/index.d.ts +8 -8
  7. package/dist/tools/achievement/index.js +10 -11
  8. package/dist/tools/achievement/schemas.d.ts +8 -8
  9. package/dist/tools/achievement/schemas.js +36 -32
  10. package/dist/tools/analytics.js +9 -9
  11. package/dist/tools/badge.js +4 -4
  12. package/dist/tools/campaign/handlers.js +35 -1
  13. package/dist/tools/campaign/index.d.ts +97 -34
  14. package/dist/tools/campaign/index.js +14 -12
  15. package/dist/tools/campaign/schemas.d.ts +96 -33
  16. package/dist/tools/campaign/schemas.js +89 -55
  17. package/dist/tools/custom-event.js +10 -9
  18. package/dist/tools/export.js +4 -4
  19. package/dist/tools/import.d.ts +1 -1
  20. package/dist/tools/import.js +11 -6
  21. package/dist/tools/index.js +4 -4
  22. package/dist/tools/member/handlers.js +48 -1
  23. package/dist/tools/member/index.d.ts +1 -1
  24. package/dist/tools/member/index.js +3 -1
  25. package/dist/tools/member/schemas.js +12 -12
  26. package/dist/tools/points/handlers.d.ts +75 -0
  27. package/dist/tools/{points.js → points/handlers.js} +21 -112
  28. package/dist/tools/points/index.d.ts +84 -0
  29. package/dist/tools/points/index.js +63 -0
  30. package/dist/tools/points/schemas.d.ts +45 -0
  31. package/dist/tools/points/schemas.js +47 -0
  32. package/dist/tools/referral/schemas.js +3 -3
  33. package/dist/tools/reward/handlers.d.ts +2 -0
  34. package/dist/tools/reward/handlers.js +79 -6
  35. package/dist/tools/reward/index.d.ts +2 -0
  36. package/dist/tools/reward/index.js +10 -10
  37. package/dist/tools/reward/schemas.d.ts +2 -0
  38. package/dist/tools/reward/schemas.js +42 -23
  39. package/dist/tools/segment/index.js +7 -5
  40. package/dist/tools/segment/schemas.js +11 -11
  41. package/dist/tools/tierset/handlers.d.ts +26 -0
  42. package/dist/tools/tierset/handlers.js +196 -0
  43. package/dist/tools/tierset/index.d.ts +124 -0
  44. package/dist/tools/tierset/index.js +88 -0
  45. package/dist/tools/tierset/schemas.d.ts +127 -0
  46. package/dist/tools/tierset/schemas.js +62 -0
  47. package/dist/tools/transaction/handlers.d.ts +89 -0
  48. package/dist/tools/transaction/handlers.js +159 -0
  49. package/dist/tools/transaction/index.d.ts +153 -0
  50. package/dist/tools/transaction/index.js +54 -0
  51. package/dist/tools/transaction/schemas.d.ts +126 -0
  52. package/dist/tools/transaction/schemas.js +60 -0
  53. package/dist/tools/wallet-type/handlers.d.ts +63 -0
  54. package/dist/tools/{wallet-type.js → wallet-type/handlers.js} +15 -78
  55. package/dist/tools/{wallet-type.d.ts → wallet-type/index.d.ts} +3 -64
  56. package/dist/tools/wallet-type/index.js +65 -0
  57. package/dist/tools/wallet-type/schemas.d.ts +1 -0
  58. package/dist/tools/wallet-type/schemas.js +1 -0
  59. package/dist/tools/webhook.js +6 -6
  60. package/dist/types/schemas/achievement.d.ts +48 -48
  61. package/dist/types/schemas/admin.d.ts +10 -10
  62. package/dist/types/schemas/campaign.d.ts +12 -12
  63. package/dist/types/schemas/common.js +1 -1
  64. package/dist/types/schemas/member.d.ts +18 -18
  65. package/dist/types/schemas/role.d.ts +4 -4
  66. package/dist/types/schemas/tierset.js +2 -1
  67. package/dist/types/schemas/transaction.d.ts +12 -12
  68. package/dist/types/schemas/wallet-type.js +12 -10
  69. package/dist/types/schemas/webhook.d.ts +6 -6
  70. package/dist/utils/errors.js +40 -0
  71. package/package.json +3 -2
  72. package/dist/prompts/fan-engagement-setup.d.ts +0 -107
  73. package/dist/prompts/fan-engagement-setup.js +0 -492
  74. package/dist/tools/achievement.d.ts +0 -1017
  75. package/dist/tools/achievement.js +0 -354
  76. package/dist/tools/campaign.d.ts +0 -1800
  77. package/dist/tools/campaign.js +0 -737
  78. package/dist/tools/member.d.ts +0 -366
  79. package/dist/tools/member.js +0 -352
  80. package/dist/tools/points.d.ts +0 -201
  81. package/dist/tools/reward.d.ts +0 -279
  82. package/dist/tools/reward.js +0 -361
  83. package/dist/tools/segment.d.ts +0 -816
  84. package/dist/tools/segment.js +0 -333
  85. package/dist/tools/tierset.d.ts +0 -273
  86. package/dist/tools/tierset.js +0 -289
  87. package/dist/tools/transaction.d.ts +0 -365
  88. package/dist/tools/transaction.js +0 -259
  89. package/dist/workflows/app-login-streak.d.ts +0 -39
  90. package/dist/workflows/app-login-streak.js +0 -298
  91. package/dist/workflows/early-arrival.d.ts +0 -33
  92. package/dist/workflows/early-arrival.js +0 -148
  93. package/dist/workflows/index.d.ts +0 -101
  94. package/dist/workflows/index.js +0 -208
  95. package/dist/workflows/match-attendance.d.ts +0 -45
  96. package/dist/workflows/match-attendance.js +0 -308
  97. package/dist/workflows/sportsbar-visit.d.ts +0 -41
  98. package/dist/workflows/sportsbar-visit.js +0 -284
  99. package/dist/workflows/vod-watching.d.ts +0 -43
  100. package/dist/workflows/vod-watching.js +0 -326
@@ -40,8 +40,9 @@ const AchievementRuleInputSchema = z.object({
40
40
  completeRule: z.object({
41
41
  periodGoal: z.union([z.number(), z.string()]).describe("Goal value to reach (e.g., 5 for 5 purchases, 100 for 100 points)."),
42
42
  period: z.object({
43
- type: AchievementPeriodTypeEnum.describe("Period type. For ONE-TIME achievements: use 'day'. For STREAKS: use 'calendarDays'. " +
44
- "Options: 'day' (all-time), 'last_day' (rolling), 'calendarDays/Weeks/Months/Years' (calendar-based)."),
43
+ type: AchievementPeriodTypeEnum.describe("Period type. BUSINESS DECISION: 'day' with consecutive=1 = all-time tracking (for one-time badges). " +
44
+ "'calendarDays' with consecutive=N = streak tracking (for N consecutive days). " +
45
+ "BUSINESS IMPACT: Using streak settings for a one-time badge means members must act on consecutive days to earn it -- nobody will."),
45
46
  value: z.number().optional().describe("Period value (e.g., 7 for 'last 7 days' when type='last_day'). Usually omit for simple achievements."),
46
47
  consecutive: z.number().min(1).describe("⭐ CONSECUTIVE PERIODS: For ONE-TIME achievements (first purchase): use consecutive=1. " +
47
48
  "For STREAKS (7 consecutive days): use consecutive=7 with type='calendarDays'. " +
@@ -54,26 +55,28 @@ const AchievementRuleInputSchema = z.object({
54
55
  aggregation: z.object({
55
56
  type: AchievementAggregationTypeEnum.describe("How to aggregate: 'quantity' (count events). NOTE: Only 'quantity' is supported by the API."),
56
57
  }).describe("Value aggregation. Use type='quantity' for event counting. NOTE: 'sum', 'average', 'min', 'max' are NOT supported by the API."),
57
- conditions: z.array(z.record(z.unknown())).optional().describe("Conditions that must be met."),
58
+ conditions: z.array(z.record(z.unknown())).optional().describe("Conditions that must be met for rule progress. Same structure as campaign conditions: " +
59
+ "{ operator: 'is_greater_or_equal', attribute: 'transaction.grossValue', data: 100 }. " +
60
+ "For labels: { operator: 'has_at_least_one_label', attribute: 'transaction.labels', data: [{key: 'category', value: 'electronics'}] }."),
58
61
  limit: z.object({
59
62
  value: z.number().optional().describe("Max events per interval (e.g., 1 for once-per-day limit)."),
60
63
  interval: z.object({
61
- type: z.string().describe("Interval type: 'calendarDays', 'calendarWeeks', etc."),
62
- value: z.number().optional().describe("Interval value."),
64
+ type: z.string().describe("Interval type. Valid values: 'calendarDays' (NOT 'days'), 'calendarWeeks' (NOT 'weeks'), 'calendarMonths', 'calendarYears'."),
65
+ value: z.number().optional().describe("Interval length (e.g., 1 for every 1 calendar day)."),
63
66
  }).optional().describe("Time interval for the limit."),
64
- }).optional().describe("⚠️ RULE LIMIT - ONLY for STREAKS. For ONE-TIME achievements, DO NOT set rule limit. " +
65
- "For STREAKS: use { value: 1, interval: { type: 'calendarDays', value: 1 } } to count max 1 event per day."),
67
+ }).optional().describe("Only for streak achievements -- DO NOT set for one-time badges. " +
68
+ "BUSINESS IMPACT: Controls how events are counted per period. For streaks: { value: 1, interval: { type: 'calendarDays', value: 1 } } ensures max 1 event per day counts toward the streak."),
66
69
  uniqueReferee: z.boolean().optional().describe("Whether referee must be unique (for referral achievements)."),
67
70
  });
68
71
  export const AchievementListInputSchema = {
69
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
72
+ 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."),
70
73
  page: z.number().optional().describe("Page number (default: 1)."),
71
74
  perPage: z.number().optional().describe("Items per page (default: 10)."),
72
75
  active: z.boolean().optional().describe("Filter by active status."),
73
76
  name: z.string().optional().describe("Filter by achievement name."),
74
77
  };
75
78
  export const AchievementCreateInputSchema = {
76
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
79
+ 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."),
77
80
  translations: z.record(z.string(), z.object({
78
81
  name: z.string(),
79
82
  description: z.string().optional(),
@@ -87,47 +90,48 @@ export const AchievementCreateInputSchema = {
87
90
  limit: z.object({
88
91
  value: z.number().optional().describe("Maximum completions (e.g., 1 for one-time badges)."),
89
92
  interval: z.object({
90
- type: z.string(),
91
- value: z.number().optional(),
92
- }).optional(),
93
- }).optional().describe("⭐ ACHIEVEMENT LIMIT: How many times a member can earn this achievement. " +
94
- "For ONE-TIME badges (like 'First Purchase'): set limit.value=1 with NO interval. " +
95
- "For repeatable achievements: omit limit or set higher value. " +
96
- "IMPORTANT: Without limit.value=1, members can earn the badge unlimited times!"),
93
+ type: z.string().describe("Interval type. Valid values: 'calendarDays', 'calendarWeeks', 'calendarMonths', 'calendarYears'."),
94
+ value: z.number().optional().describe("Interval length (e.g., 1 for every 1 calendar period)."),
95
+ }).optional().describe("Interval for repeatable achievements (omit for one-time badges)."),
96
+ }).optional().describe("How many times a member can earn this achievement. " +
97
+ "BUSINESS DECISION: For one-time badges ('First Purchase'): set value=1 with NO interval. For repeatable challenges: omit or set higher. " +
98
+ "BUSINESS IMPACT: Without limit.value=1, members earn the badge unlimited times -- this defeats milestone purpose and can trigger unlimited achievement-based rewards."),
97
99
  rules: z.array(AchievementRuleInputSchema).describe("Achievement rules defining completion criteria."),
98
100
  badgeTypeId: z.string().optional().describe("Badge type ID to award upon completion. " +
99
101
  "NOTE: This field may cause 'extra fields' validation errors in some API versions. " +
100
102
  "If creation fails, try without badgeTypeId - the system will assign a default badge."),
101
103
  };
102
104
  export const AchievementGetInputSchema = {
103
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
105
+ 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."),
104
106
  achievementId: z.string().describe("The achievement ID (UUID) to retrieve."),
105
107
  };
106
108
  export const AchievementUpdateInputSchema = {
107
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
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."),
108
110
  achievementId: z.string().describe("The achievement ID (UUID) to update."),
109
111
  translations: z.record(z.string(), z.object({
110
112
  name: z.string(),
111
113
  description: z.string().optional(),
112
- })).describe("Achievement name and description."),
114
+ })).describe("Achievement name and description. At least 'en' key required."),
113
115
  active: z.boolean().optional().describe("Whether achievement is active."),
114
116
  activity: z.object({
115
- startsAt: z.string().optional(),
116
- endsAt: z.string().optional(),
117
- operator: z.string().optional(),
117
+ startsAt: z.string().optional().describe("ISO datetime when achievement becomes active (e.g., '2026-01-01T00:00:00+00:00')."),
118
+ endsAt: z.string().optional().describe("ISO datetime when achievement ends."),
119
+ operator: z.string().optional().describe("Activity condition operator: 'between' (both dates) or 'from' (start only) or 'to' (end only)."),
118
120
  }).optional().describe("Time period configuration."),
119
121
  limit: z.object({
120
- value: z.number().optional(),
122
+ value: z.number().optional().describe("Maximum completions (e.g., 1 for one-time badges)."),
121
123
  interval: z.object({
122
- type: z.string(),
123
- value: z.number().optional(),
124
- }).optional(),
125
- }).optional().describe("Limit configuration."),
126
- rules: z.array(AchievementRuleInputSchema).describe("Achievement rules."),
127
- badgeTypeId: z.string().optional().describe("Badge type ID to award."),
124
+ type: z.string().describe("Interval type. Valid values: 'calendarDays', 'calendarWeeks', 'calendarMonths', 'calendarYears'."),
125
+ value: z.number().optional().describe("Interval length (e.g., 1 for every 1 calendar period)."),
126
+ }).optional().describe("Interval for repeatable achievements (omit for one-time badges)."),
127
+ }).optional().describe("Achievement limit. For ONE-TIME badges: { value: 1 } with NO interval. " +
128
+ "For repeatable: omit or set higher value."),
129
+ rules: z.array(AchievementRuleInputSchema).describe("Achievement rules. Use achievement_get first to retrieve current rules and include achievementRuleId for existing rules."),
130
+ badgeTypeId: z.string().optional().describe("Badge type ID to award. Use ol_badge_list() to find valid IDs. " +
131
+ "May cause 'extra fields' errors in some API versions - try without if creation fails."),
128
132
  };
129
133
  export const AchievementPatchInputSchema = {
130
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
134
+ 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
135
  achievementId: z.string().describe("The achievement ID (UUID) to patch."),
132
136
  active: z.boolean().optional().describe("Whether achievement is active."),
133
137
  translations: z.record(z.string(), z.object({
@@ -136,12 +140,12 @@ export const AchievementPatchInputSchema = {
136
140
  })).optional().describe("Partial translation updates."),
137
141
  };
138
142
  export const AchievementGetMemberProgressInputSchema = {
139
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
143
+ 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."),
140
144
  memberId: z.string().describe("The member ID (UUID)."),
141
145
  achievementId: z.string().describe("The achievement ID (UUID)."),
142
146
  };
143
147
  export const AchievementListMemberAchievementsInputSchema = {
144
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
148
+ 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."),
145
149
  memberId: z.string().describe("The member ID (UUID)."),
146
150
  page: z.number().optional().describe("Page number (default: 1)."),
147
151
  perPage: z.number().optional().describe("Items per page (default: 25)."),
@@ -4,29 +4,29 @@ import { formatApiError } from "../utils/errors.js";
4
4
  import { getStoreCode } from "../config.js";
5
5
  // Input Schemas
6
6
  export const AnalyticsTiersInputSchema = {
7
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
7
+ 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."),
8
8
  };
9
9
  export const AnalyticsMembersInputSchema = {
10
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
10
+ 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."),
11
11
  withTransaction: z.boolean().optional().describe("Filter: true for members with transactions, false for members without, omit for all."),
12
12
  };
13
13
  export const AnalyticsPointsInputSchema = {
14
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
14
+ 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."),
15
15
  };
16
16
  export const AnalyticsTransactionsInputSchema = {
17
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
17
+ 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."),
18
18
  };
19
19
  export const AnalyticsReferralsInputSchema = {
20
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
20
+ 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."),
21
21
  };
22
22
  export const AnalyticsCampaignsInputSchema = {
23
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
23
+ 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."),
24
24
  page: z.number().optional().describe("Page number (default: 1)."),
25
25
  perPage: z.number().optional().describe("Items per page (default: 25)."),
26
26
  executedAt: z.string().optional().describe("Filter by execution date (ISO format)."),
27
27
  };
28
28
  export const AnalyticsDashboardInputSchema = {
29
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
29
+ 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."),
30
30
  dataType: z.enum([
31
31
  "registeredMembers", "activeMembers", "revenue", "avgSpending",
32
32
  "transactions", "avgTransactionValue", "avgNumberOfTransactions"
@@ -36,7 +36,7 @@ export const AnalyticsDashboardInputSchema = {
36
36
  intervalEndDate: z.string().optional().describe("End date (YYYY-MM-DD format)."),
37
37
  };
38
38
  export const AnalyticsUnitsInputSchema = {
39
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
39
+ 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."),
40
40
  walletTypeCode: z.string().describe("Wallet type code (e.g., 'default' or 'points')."),
41
41
  dataType: z.enum([
42
42
  "unitsIssued", "unitsSpent", "unitsExpired", "unitsPending", "unitsActive"
@@ -46,7 +46,7 @@ export const AnalyticsUnitsInputSchema = {
46
46
  intervalEndDate: z.string().optional().describe("End date (YYYY-MM-DD format)."),
47
47
  };
48
48
  export const AnalyticsCampaignDetailInputSchema = {
49
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
49
+ 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."),
50
50
  campaignId: z.string().describe("The campaign ID to get analytics for."),
51
51
  };
52
52
  // Handler functions
@@ -4,18 +4,18 @@ import { formatApiError } from "../utils/errors.js";
4
4
  import { getStoreCode } from "../config.js";
5
5
  // Input Schemas
6
6
  export const BadgeListInputSchema = {
7
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
7
+ 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."),
8
8
  page: z.number().optional().describe("Page number (default: 1)."),
9
9
  perPage: z.number().optional().describe("Items per page (default: 10)."),
10
10
  name: z.string().optional().describe("Filter by badge name."),
11
11
  badgeTypeId: z.string().optional().describe("Filter by specific badge type ID."),
12
12
  };
13
13
  export const BadgeGetInputSchema = {
14
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
14
+ 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."),
15
15
  badgeTypeId: z.string().describe("The badge type ID (UUID) to retrieve."),
16
16
  };
17
17
  export const BadgeUpdateInputSchema = {
18
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
18
+ 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."),
19
19
  badgeTypeId: z.string().describe("The badge type ID (UUID) to update."),
20
20
  name: z.string().describe("Badge name (REQUIRED). API requires name as a direct field."),
21
21
  translations: z.record(z.string(), z.object({
@@ -26,7 +26,7 @@ export const BadgeUpdateInputSchema = {
26
26
  active: z.boolean().optional().describe("Whether badge type is active."),
27
27
  };
28
28
  export const BadgeGetMemberBadgesInputSchema = {
29
- storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
29
+ 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."),
30
30
  memberId: z.string().describe("The member ID (UUID)."),
31
31
  page: z.number().optional().describe("Page number (default: 1)."),
32
32
  perPage: z.number().optional().describe("Items per page (default: 10)."),
@@ -1,5 +1,5 @@
1
1
  import { apiGet, apiPost, apiPut, apiPatch, 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
  // Core CRUD handlers
5
5
  export async function campaignList(input) {
@@ -95,6 +95,40 @@ export async function campaignCreate(input) {
95
95
  return { campaignId: response.campaignId };
96
96
  }
97
97
  catch (error) {
98
+ const axiosError = error;
99
+ const apiErrors = axiosError.response?.data?.errors || [];
100
+ const allMessages = [
101
+ error instanceof Error ? error.message : "",
102
+ axiosError.response?.data?.message || "",
103
+ ...apiErrors.map(e => `${e.path || ""}: ${e.message}`)
104
+ ].join(" ").toLowerCase();
105
+ if (allMessages.includes("trigger") && allMessages.includes("choice")) {
106
+ throw new OpenLoyaltyError({
107
+ code: "INVALID_TRIGGER",
108
+ message: "Invalid campaign trigger type",
109
+ hint: "Valid trigger values: 'transaction', 'custom_event', 'geolocation', 'time', 'achievement'. For custom_event, also set event='your_event_type'. For achievement, set achievementId='uuid'.",
110
+ relatedTool: "ol_campaign_create",
111
+ });
112
+ }
113
+ if (allMessages.includes("operator") || (allMessages.includes("condition") && allMessages.includes("choice"))) {
114
+ throw new OpenLoyaltyError({
115
+ code: "INVALID_CONDITION",
116
+ message: "Invalid rule condition in campaign",
117
+ hint: "Use full operator names: 'is_greater_or_equal' (NOT 'gte'), 'is_less_or_equal' (NOT 'lte'). " +
118
+ "Use full attribute paths: 'transaction.grossValue' (NOT 'grossValue'). " +
119
+ "Example: { operator: 'is_greater_or_equal', attribute: 'transaction.grossValue', data: 100 }.",
120
+ relatedTool: "ol_campaign_create",
121
+ });
122
+ }
123
+ if (input.trigger === "custom_event" && !input.event && allMessages.includes("event")) {
124
+ throw new OpenLoyaltyError({
125
+ code: "MISSING_EVENT",
126
+ message: "Missing event field for custom_event trigger",
127
+ hint: "For trigger='custom_event', you MUST also set event='your_event_code'. " +
128
+ "Use ol_custom_event_schema_list() to find valid event codes.",
129
+ relatedTool: "ol_campaign_create",
130
+ });
131
+ }
98
132
  throw formatApiError(error, "ol_campaign_create");
99
133
  }
100
134
  }
@@ -150,11 +150,11 @@ export declare const campaignToolDefinitions: readonly [{
150
150
  tiers: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
151
151
  segments: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
152
152
  }, "strip", import("zod").ZodTypeAny, {
153
- target: "all" | "segment" | "tier";
153
+ target: "all" | "tier" | "segment";
154
154
  tiers?: string[] | undefined;
155
155
  segments?: string[] | undefined;
156
156
  }, {
157
- target: "all" | "segment" | "tier";
157
+ target: "all" | "tier" | "segment";
158
158
  tiers?: string[] | undefined;
159
159
  segments?: string[] | undefined;
160
160
  }>>;
@@ -163,11 +163,11 @@ export declare const campaignToolDefinitions: readonly [{
163
163
  tiers: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
164
164
  segments: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
165
165
  }, "strip", import("zod").ZodTypeAny, {
166
- target: "segment" | "tier";
166
+ target: "tier" | "segment";
167
167
  tiers?: string[] | undefined;
168
168
  segments?: string[] | undefined;
169
169
  }, {
170
- target: "segment" | "tier";
170
+ target: "tier" | "segment";
171
171
  tiers?: string[] | undefined;
172
172
  segments?: string[] | undefined;
173
173
  }>>;
@@ -377,17 +377,17 @@ export declare const campaignToolDefinitions: readonly [{
377
377
  description: import("zod").ZodOptional<import("zod").ZodString>;
378
378
  target: import("zod").ZodOptional<import("zod").ZodEnum<["self", "referrer"]>>;
379
379
  effects: import("zod").ZodArray<import("zod").ZodObject<{
380
- effect: import("zod").ZodString;
380
+ effect: import("zod").ZodEnum<["give_points", "give_reward", "deduct_unit", "assign_member_custom_attribute", "remove_member_custom_attribute"]>;
381
381
  pointsRule: import("zod").ZodOptional<import("zod").ZodString>;
382
382
  walletCode: import("zod").ZodOptional<import("zod").ZodString>;
383
383
  rewardId: import("zod").ZodOptional<import("zod").ZodString>;
384
384
  }, "strip", import("zod").ZodTypeAny, {
385
- effect: string;
385
+ effect: "give_points" | "give_reward" | "deduct_unit" | "assign_member_custom_attribute" | "remove_member_custom_attribute";
386
386
  walletCode?: string | undefined;
387
387
  rewardId?: string | undefined;
388
388
  pointsRule?: string | undefined;
389
389
  }, {
390
- effect: string;
390
+ effect: "give_points" | "give_reward" | "deduct_unit" | "assign_member_custom_attribute" | "remove_member_custom_attribute";
391
391
  walletCode?: string | undefined;
392
392
  rewardId?: string | undefined;
393
393
  pointsRule?: string | undefined;
@@ -395,20 +395,20 @@ export declare const campaignToolDefinitions: readonly [{
395
395
  conditions: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{
396
396
  operator: import("zod").ZodString;
397
397
  attribute: import("zod").ZodString;
398
- data: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
398
+ data: import("zod").ZodUnknown;
399
399
  }, "strip", import("zod").ZodTypeAny, {
400
400
  attribute: string;
401
401
  operator: string;
402
- data?: Record<string, unknown> | undefined;
402
+ data?: unknown;
403
403
  }, {
404
404
  attribute: string;
405
405
  operator: string;
406
- data?: Record<string, unknown> | undefined;
406
+ data?: unknown;
407
407
  }>, "many">>;
408
408
  }, "strip", import("zod").ZodTypeAny, {
409
409
  name: string;
410
410
  effects: {
411
- effect: string;
411
+ effect: "give_points" | "give_reward" | "deduct_unit" | "assign_member_custom_attribute" | "remove_member_custom_attribute";
412
412
  walletCode?: string | undefined;
413
413
  rewardId?: string | undefined;
414
414
  pointsRule?: string | undefined;
@@ -417,13 +417,13 @@ export declare const campaignToolDefinitions: readonly [{
417
417
  conditions?: {
418
418
  attribute: string;
419
419
  operator: string;
420
- data?: Record<string, unknown> | undefined;
420
+ data?: unknown;
421
421
  }[] | undefined;
422
422
  target?: "self" | "referrer" | undefined;
423
423
  }, {
424
424
  name: string;
425
425
  effects: {
426
- effect: string;
426
+ effect: "give_points" | "give_reward" | "deduct_unit" | "assign_member_custom_attribute" | "remove_member_custom_attribute";
427
427
  walletCode?: string | undefined;
428
428
  rewardId?: string | undefined;
429
429
  pointsRule?: string | undefined;
@@ -432,7 +432,7 @@ export declare const campaignToolDefinitions: readonly [{
432
432
  conditions?: {
433
433
  attribute: string;
434
434
  operator: string;
435
- data?: Record<string, unknown> | undefined;
435
+ data?: unknown;
436
436
  }[] | undefined;
437
437
  target?: "self" | "referrer" | undefined;
438
438
  }>, "many">;
@@ -441,11 +441,11 @@ export declare const campaignToolDefinitions: readonly [{
441
441
  tiers: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
442
442
  segments: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
443
443
  }, "strip", import("zod").ZodTypeAny, {
444
- target: "all" | "segment" | "tier";
444
+ target: "all" | "tier" | "segment";
445
445
  tiers?: string[] | undefined;
446
446
  segments?: string[] | undefined;
447
447
  }, {
448
- target: "all" | "segment" | "tier";
448
+ target: "all" | "tier" | "segment";
449
449
  tiers?: string[] | undefined;
450
450
  segments?: string[] | undefined;
451
451
  }>>;
@@ -454,70 +454,133 @@ export declare const campaignToolDefinitions: readonly [{
454
454
  tiers: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
455
455
  segments: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
456
456
  }, "strip", import("zod").ZodTypeAny, {
457
- target: "segment" | "tier";
457
+ target: "tier" | "segment";
458
458
  tiers?: string[] | undefined;
459
459
  segments?: string[] | undefined;
460
460
  }, {
461
- target: "segment" | "tier";
461
+ target: "tier" | "segment";
462
462
  tiers?: string[] | undefined;
463
463
  segments?: string[] | undefined;
464
464
  }>>;
465
465
  limits: import("zod").ZodOptional<import("zod").ZodObject<{
466
466
  points: import("zod").ZodOptional<import("zod").ZodObject<{
467
467
  value: import("zod").ZodNumber;
468
- interval: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
468
+ interval: import("zod").ZodOptional<import("zod").ZodObject<{
469
+ type: import("zod").ZodEnum<["calendarDays", "calendarWeeks", "calendarMonths", "calendarYears"]>;
470
+ value: import("zod").ZodOptional<import("zod").ZodNumber>;
471
+ }, "strip", import("zod").ZodTypeAny, {
472
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
473
+ value?: number | undefined;
474
+ }, {
475
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
476
+ value?: number | undefined;
477
+ }>>;
469
478
  }, "strip", import("zod").ZodTypeAny, {
470
479
  value: number;
471
- interval?: Record<string, unknown> | undefined;
480
+ interval?: {
481
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
482
+ value?: number | undefined;
483
+ } | undefined;
472
484
  }, {
473
485
  value: number;
474
- interval?: Record<string, unknown> | undefined;
486
+ interval?: {
487
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
488
+ value?: number | undefined;
489
+ } | undefined;
475
490
  }>>;
476
491
  pointsPerMember: import("zod").ZodOptional<import("zod").ZodObject<{
477
492
  value: import("zod").ZodNumber;
478
- interval: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
493
+ interval: import("zod").ZodOptional<import("zod").ZodObject<{
494
+ type: import("zod").ZodEnum<["calendarDays", "calendarWeeks", "calendarMonths", "calendarYears"]>;
495
+ value: import("zod").ZodOptional<import("zod").ZodNumber>;
496
+ }, "strip", import("zod").ZodTypeAny, {
497
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
498
+ value?: number | undefined;
499
+ }, {
500
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
501
+ value?: number | undefined;
502
+ }>>;
479
503
  }, "strip", import("zod").ZodTypeAny, {
480
504
  value: number;
481
- interval?: Record<string, unknown> | undefined;
505
+ interval?: {
506
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
507
+ value?: number | undefined;
508
+ } | undefined;
482
509
  }, {
483
510
  value: number;
484
- interval?: Record<string, unknown> | undefined;
511
+ interval?: {
512
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
513
+ value?: number | undefined;
514
+ } | undefined;
485
515
  }>>;
486
516
  executionsPerMember: import("zod").ZodOptional<import("zod").ZodObject<{
487
517
  value: import("zod").ZodNumber;
488
- interval: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>>;
518
+ interval: import("zod").ZodOptional<import("zod").ZodObject<{
519
+ type: import("zod").ZodEnum<["calendarDays", "calendarWeeks", "calendarMonths", "calendarYears"]>;
520
+ value: import("zod").ZodOptional<import("zod").ZodNumber>;
521
+ }, "strip", import("zod").ZodTypeAny, {
522
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
523
+ value?: number | undefined;
524
+ }, {
525
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
526
+ value?: number | undefined;
527
+ }>>;
489
528
  }, "strip", import("zod").ZodTypeAny, {
490
529
  value: number;
491
- interval?: Record<string, unknown> | undefined;
530
+ interval?: {
531
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
532
+ value?: number | undefined;
533
+ } | undefined;
492
534
  }, {
493
535
  value: number;
494
- interval?: Record<string, unknown> | undefined;
536
+ interval?: {
537
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
538
+ value?: number | undefined;
539
+ } | undefined;
495
540
  }>>;
496
541
  }, "strip", import("zod").ZodTypeAny, {
497
542
  points?: {
498
543
  value: number;
499
- interval?: Record<string, unknown> | undefined;
544
+ interval?: {
545
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
546
+ value?: number | undefined;
547
+ } | undefined;
500
548
  } | undefined;
501
549
  pointsPerMember?: {
502
550
  value: number;
503
- interval?: Record<string, unknown> | undefined;
551
+ interval?: {
552
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
553
+ value?: number | undefined;
554
+ } | undefined;
504
555
  } | undefined;
505
556
  executionsPerMember?: {
506
557
  value: number;
507
- interval?: Record<string, unknown> | undefined;
558
+ interval?: {
559
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
560
+ value?: number | undefined;
561
+ } | undefined;
508
562
  } | undefined;
509
563
  }, {
510
564
  points?: {
511
565
  value: number;
512
- interval?: Record<string, unknown> | undefined;
566
+ interval?: {
567
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
568
+ value?: number | undefined;
569
+ } | undefined;
513
570
  } | undefined;
514
571
  pointsPerMember?: {
515
572
  value: number;
516
- interval?: Record<string, unknown> | undefined;
573
+ interval?: {
574
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
575
+ value?: number | undefined;
576
+ } | undefined;
517
577
  } | undefined;
518
578
  executionsPerMember?: {
519
579
  value: number;
520
- interval?: Record<string, unknown> | undefined;
580
+ interval?: {
581
+ type: "calendarDays" | "calendarWeeks" | "calendarMonths" | "calendarYears";
582
+ value?: number | undefined;
583
+ } | undefined;
521
584
  } | undefined;
522
585
  }>>;
523
586
  active: import("zod").ZodOptional<import("zod").ZodBoolean>;
@@ -561,7 +624,7 @@ export declare const campaignToolDefinitions: readonly [{
561
624
  }, {
562
625
  readonly name: "ol_campaign_simulate";
563
626
  readonly title: "Simulate Campaign Effects";
564
- readonly 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.";
627
+ readonly description: string;
565
628
  readonly readOnly: true;
566
629
  readonly inputSchema: {
567
630
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
@@ -20,17 +20,14 @@ export const campaignToolDefinitions = [
20
20
  {
21
21
  name: "ol_campaign_create",
22
22
  title: "Create Campaign",
23
- description: "Create campaign to automate engagement (earning rules). " +
24
- "⚠️ REQUIRED FIELDS: " +
25
- "1. type: 'direct', " +
26
- "2. trigger: 'transaction', " +
27
- "3. translations: { en: { name: 'Campaign Name' } }, " +
28
- "4. activity: { startsAt: '2026-01-01 00:00+00:00' }, " +
29
- "5. rules: [{ name: 'Rule Name', effects: [{ effect: 'give_points', pointsRule: 'transaction.grossValue * 10' }] }]. " +
30
- "pointsRule is a STRING expression. Examples: '100' (fixed), 'transaction.grossValue * 10' (dynamic). " +
31
- "CONDITIONS: Use operator='is_greater_or_equal' (NOT 'gte'), attribute='transaction.grossValue', data=100. " +
32
- "For labels: operator='has_at_least_one_label', attribute='transaction.labels', data=[{key:'name',value:'val'}]. " +
33
- "To target ALL members, OMIT the audience parameter entirely.",
23
+ description: "Create a campaign (earning rule) that automatically awards points or rewards when triggered. " +
24
+ "KEY DECISIONS (confirm with user if not specified): " +
25
+ "1. Points formula -- fixed ('100') or dynamic ('transaction.grossValue * 10')? This determines cost per transaction. " +
26
+ "2. Budget caps -- total points limit and per-member limit? Without limits, the campaign has uncapped financial exposure. " +
27
+ "3. Audience -- all members, or specific tiers/segments? " +
28
+ "4. End date -- campaigns without endsAt run forever, which is a budget commitment. " +
29
+ "REQUIRED: type, trigger, translations.en.name, activity.startsAt, rules with effects. " +
30
+ "In effects array, use key 'effect' (NOT 'type'). pointsRule is a STRING: '100' not 100.",
34
31
  readOnly: false,
35
32
  inputSchema: CampaignCreateInputSchema,
36
33
  handler: campaignCreate,
@@ -71,7 +68,12 @@ export const campaignToolDefinitions = [
71
68
  {
72
69
  name: "ol_campaign_simulate",
73
70
  title: "Simulate Campaign Effects",
74
- 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.",
71
+ description: "Simulate campaign effects without executing them. Use to preview what points/rewards a transaction would earn. " +
72
+ "Example for transaction trigger: { trigger: 'transaction', " +
73
+ "transaction: { grossValue: 100, documentNumber: 'SIM-001', purchasedAt: '2026-01-01T10:00:00Z', " +
74
+ "items: [{ sku: 'SKU-1', name: 'Product', grossValue: 100, qty: 1 }] }, " +
75
+ "customer: { customerId: 'member-uuid' } }. " +
76
+ "Returns simulated effects grouped by campaign.",
75
77
  readOnly: true,
76
78
  inputSchema: CampaignSimulateInputSchema,
77
79
  handler: campaignSimulate,