@open-loyalty/mcp-server 1.3.6 → 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 (42) hide show
  1. package/dist/instructions.d.ts +1 -1
  2. package/dist/instructions.js +18 -5
  3. package/dist/tools/achievement/index.js +10 -10
  4. package/dist/tools/achievement/schemas.js +16 -8
  5. package/dist/tools/reward/handlers.d.ts +3 -3
  6. package/dist/tools/reward/handlers.js +54 -8
  7. package/dist/tools/reward/index.d.ts +4 -8
  8. package/dist/tools/reward/index.js +13 -5
  9. package/dist/tools/reward/schemas.d.ts +3 -7
  10. package/dist/tools/reward/schemas.js +16 -8
  11. package/dist/tools/tierset.d.ts +1 -1
  12. package/dist/tools/tierset.js +49 -25
  13. package/dist/tools/transaction.js +5 -2
  14. package/dist/tools/wallet-type.js +27 -16
  15. package/dist/types/schemas/admin.d.ts +6 -6
  16. package/dist/types/schemas/role.d.ts +4 -4
  17. package/dist/types/schemas/wallet-type.js +7 -5
  18. package/package.json +1 -1
  19. package/dist/prompts/fan-engagement-setup.d.ts +0 -107
  20. package/dist/prompts/fan-engagement-setup.js +0 -492
  21. package/dist/tools/achievement.d.ts +0 -1017
  22. package/dist/tools/achievement.js +0 -354
  23. package/dist/tools/campaign.d.ts +0 -1800
  24. package/dist/tools/campaign.js +0 -737
  25. package/dist/tools/member.d.ts +0 -366
  26. package/dist/tools/member.js +0 -352
  27. package/dist/tools/reward.d.ts +0 -279
  28. package/dist/tools/reward.js +0 -361
  29. package/dist/tools/segment.d.ts +0 -816
  30. package/dist/tools/segment.js +0 -333
  31. package/dist/workflows/app-login-streak.d.ts +0 -39
  32. package/dist/workflows/app-login-streak.js +0 -298
  33. package/dist/workflows/early-arrival.d.ts +0 -33
  34. package/dist/workflows/early-arrival.js +0 -148
  35. package/dist/workflows/index.d.ts +0 -101
  36. package/dist/workflows/index.js +0 -208
  37. package/dist/workflows/match-attendance.d.ts +0 -45
  38. package/dist/workflows/match-attendance.js +0 -308
  39. package/dist/workflows/sportsbar-visit.d.ts +0 -41
  40. package/dist/workflows/sportsbar-visit.js +0 -284
  41. package/dist/workflows/vod-watching.d.ts +0 -43
  42. package/dist/workflows/vod-watching.js +0 -326
@@ -42,14 +42,14 @@ export const TierSetUpdateTiersInputSchema = {
42
42
  tierSetId: z.string().describe("The tier set ID to update tiers for."),
43
43
  tiers: z.array(z.object({
44
44
  levelId: z.string().optional().describe("Existing level ID (for updates). Omit for new tiers."),
45
- name: z.string().describe("Name of the tier (e.g., Bronze, Silver, Gold)."),
46
- description: z.string().optional().describe("Description of the tier."),
45
+ name: z.string().describe("Tier name as a TOP-LEVEL string (e.g., 'Bronze'). NOT in translations wrapper."),
46
+ description: z.string().optional().describe("Tier description as a TOP-LEVEL string. NOT in translations wrapper."),
47
47
  active: z.boolean().optional().describe("Whether the tier is active. Defaults to true."),
48
48
  conditions: z.array(z.object({
49
49
  conditionId: z.string().describe("Condition ID from tierset_get response."),
50
50
  value: z.number().describe("Threshold value for this condition (e.g., 400 points for Bronze)."),
51
51
  })).describe("Array of condition thresholds. Each uses conditionId from tierset_get."),
52
- })).describe("Array of tier definitions."),
52
+ })).describe("Array of tier definitions. Each tier needs 'name' as a direct string field, NOT wrapped in translations."),
53
53
  };
54
54
  export const TierSetGetTiersInputSchema = {
55
55
  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."),
@@ -90,14 +90,30 @@ export async function tiersetCreate(input) {
90
90
  // REQUIRED fields:
91
91
  // - translations.en.name (string)
92
92
  // - conditions (array with attribute, optionally walletType)
93
+ //
94
+ // IMPORTANT: Only include description if actually provided - API rejects empty string as "extra field"
95
+ const enTranslation = {
96
+ name: input.name,
97
+ };
98
+ if (input.description) {
99
+ enTranslation.description = input.description;
100
+ }
101
+ // Explicitly map conditions to only include known fields (attribute + walletType)
102
+ // This strips any extra fields that might slip through Zod validation
103
+ const mappedConditions = input.conditions.map((c) => {
104
+ const condition = {
105
+ attribute: c.attribute,
106
+ };
107
+ if (c.walletType) {
108
+ condition.walletType = c.walletType;
109
+ }
110
+ return condition;
111
+ });
93
112
  const tierSetPayload = {
94
113
  translations: {
95
- en: {
96
- name: input.name,
97
- description: input.description || "",
98
- },
114
+ en: enTranslation,
99
115
  },
100
- conditions: input.conditions,
116
+ conditions: mappedConditions,
101
117
  };
102
118
  const payload = { tierSet: tierSetPayload };
103
119
  try {
@@ -220,8 +236,8 @@ export const tiersetToolDefinitions = [
220
236
  {
221
237
  name: "ol_tierset_list",
222
238
  title: "List Loyalty Programs",
223
- description: "List all tier sets. RECOMMENDED: Check if tier set already exists before creating a new one. " +
224
- "If exists, use tierset_get to fetch conditionId needed for defining tiers via tierset_update_tiers. " +
239
+ description: "List all tier sets. A tier set is a CONTAINER that holds MULTIPLE tiers (e.g., Bronze/Silver/Gold). " +
240
+ "⚠️ ALWAYS check existing tier sets FIRST before creating - reuse if one exists. " +
225
241
  "Returns tierSetId, name, active status, and tier count for each tier set.",
226
242
  readOnly: true,
227
243
  inputSchema: TierSetListInputSchema,
@@ -230,13 +246,13 @@ export const tiersetToolDefinitions = [
230
246
  {
231
247
  name: "ol_tierset_create",
232
248
  title: "Create Loyalty Program",
233
- description: "Create a new tier set (loyalty program structure). " +
234
- "⚠️ LIMIT: Maximum 3 ACTIVE tier sets per store. ALWAYS call tierset_list FIRST to check existing tier sets - if one exists that matches your needs, REUSE IT instead of creating a new one. " +
235
- "WORKFLOW: 1) tierset_list (check existing) 2) tierset_create (only if needed) 3) tierset_get (to get conditionId) → 4) tierset_update_tiers (to define thresholds). " +
249
+ description: "Create ONE tier set that will contain ALL your tiers (e.g., Bronze/Silver/Gold/Platinum). " +
250
+ "⚠️ CRITICAL: Create exactly ONE tier set, then add ALL tiers to it via tierset_update_tiers. " +
251
+ "DO NOT create multiple tier sets for different tiers - all tiers belong in ONE tier set! " +
252
+ "⚠️ LIMIT: Maximum 3 ACTIVE tier sets per store. ALWAYS call tierset_list FIRST to check existing tier sets. " +
253
+ "WORKFLOW: 1) tierset_list → 2) tierset_create (ONE tier set) → 3) tierset_get (get conditionId) → 4) tierset_update_tiers (add ALL tiers in one call). " +
236
254
  "⚠️ API LIMITATION: Only 'name', 'description', and 'conditions' are accepted at creation time. " +
237
- "Fields like 'active', 'downgrade', 'tiers' are NOT supported at creation - use tierset_update after creation to set these. " +
238
- "Valid condition attributes: 'totalEarnedUnits' (lifetime points), 'activeUnits' (current balance), 'totalSpending', 'monthsSinceJoiningProgram'. " +
239
- "COMMON MISTAKE: Use 'totalEarnedUnits' NOT 'earnedUnits' for lifetime points. " +
255
+ "Valid condition attributes: 'totalEarnedUnits' (lifetime points), 'activeUnits' (current balance), 'totalSpending'. " +
240
256
  "For unit-based attributes, set walletType to 'default'.",
241
257
  readOnly: false,
242
258
  inputSchema: TierSetCreateInputSchema,
@@ -246,9 +262,8 @@ export const tiersetToolDefinitions = [
246
262
  name: "ol_tierset_get",
247
263
  title: "Get Loyalty Program Details",
248
264
  description: "Get tier set details including conditionId values needed for tierset_update_tiers. " +
249
- "CRITICAL: After tierset_create, you MUST call this to get the conditionId from the conditions array. " +
250
- "The conditionId is then used in tierset_update_tiers to set tier thresholds. " +
251
- "Response includes conditions[].id (this is the conditionId) and conditions[].attribute.",
265
+ "CRITICAL: After tierset_create, call this to get the conditionId from conditions[].id. " +
266
+ "Use this SAME conditionId for ALL tiers when calling tierset_update_tiers.",
252
267
  readOnly: true,
253
268
  inputSchema: TierSetGetInputSchema,
254
269
  handler: tiersetGet,
@@ -266,14 +281,23 @@ export const tiersetToolDefinitions = [
266
281
  {
267
282
  name: "ol_tierset_update_tiers",
268
283
  title: "Configure Tier Thresholds",
269
- description: `Define tier thresholds for a tier set. PREREQUISITE: Call tierset_get first to obtain conditionId values. Each tier's conditions array uses conditionId from the parent tier set plus a threshold value.
284
+ description: `Add ALL tiers to a tier set in ONE call. A tier set is a CONTAINER - add all tiers (Bronze, Silver, Gold, etc.) to it together.
285
+
286
+ ⚠️ CRITICAL: Pass ALL tiers in a SINGLE call to this endpoint. DO NOT call this multiple times with one tier each.
287
+
288
+ ⚠️ INPUT FORMAT: Pass 'name' and 'description' as TOP-LEVEL fields on each tier object.
289
+ ❌ WRONG: { translations: { en: { name: "Bronze" } } }
290
+ ✅ CORRECT: { name: "Bronze", description: "Entry tier" }
270
291
 
271
- Example for a 3-tier program with points-based progression:
272
- - Bronze tier: conditions: [{ conditionId: "xxx", value: 400 }]
273
- - Silver tier: conditions: [{ conditionId: "xxx", value: 800 }]
274
- - Gold tier: conditions: [{ conditionId: "xxx", value: 1200 }]
292
+ Example - Creating 4 tiers in ONE call:
293
+ tiers: [
294
+ { name: "Bronze", description: "Entry tier", conditions: [{ conditionId: "xxx", value: 0 }] },
295
+ { name: "Silver", description: "500+ points", conditions: [{ conditionId: "xxx", value: 500 }] },
296
+ { name: "Gold", description: "1000+ points", conditions: [{ conditionId: "xxx", value: 1000 }] },
297
+ { name: "Platinum", description: "Top tier", conditions: [{ conditionId: "xxx", value: 2000 }] }
298
+ ]
275
299
 
276
- The conditionId must match one from tierset_get response.`,
300
+ The conditionId must match the one from tierset_get response - use the SAME conditionId for all tiers.`,
277
301
  readOnly: false,
278
302
  inputSchema: TierSetUpdateTiersInputSchema,
279
303
  handler: tiersetUpdateTiers,
@@ -10,14 +10,14 @@ const LabelInputSchema = z.object({
10
10
  });
11
11
  const TransactionHeaderInputSchema = z.object({
12
12
  documentNumber: z.string().describe("Unique document number (required, unique per store)."),
13
- purchasedAt: z.string().describe("Purchase date/time in ISO format (required)."),
13
+ purchasedAt: z.string().describe("Purchase date/time in ISO format (required). Use 'purchasedAt' NOT 'purchaseDate'. Example: '2026-01-15T10:00:00Z'."),
14
14
  documentType: z.enum(["sell", "return"]).optional().describe("Document type: sell (default) or return."),
15
15
  linkedDocumentNumber: z.string().optional().describe("Original document number (required for returns)."),
16
16
  purchasePlace: z.string().optional().describe("Location/store where purchase was made."),
17
17
  labels: z.array(LabelInputSchema).optional().describe("Custom key-value labels."),
18
18
  });
19
19
  const TransactionItemInputSchema = z.object({
20
- sku: z.string().describe("Product SKU (required, max 255 chars)."),
20
+ sku: z.string().describe("Product SKU as a STRING (required). Use 'sku: \"PROD-123\"' NOT 'sku: { code: \"...\" }'."),
21
21
  name: z.string().describe("Product name (required, max 255 chars)."),
22
22
  grossValue: z.number().describe("Item gross value (required)."),
23
23
  category: z.string().describe("Product category (required, max 255 chars)."),
@@ -219,6 +219,9 @@ export const transactionToolDefinitions = [
219
219
  title: "Record Purchase",
220
220
  description: "Record a purchase transaction. If customerData provided, auto-matches to member and triggers point campaigns. " +
221
221
  "For returns, set documentType='return' and provide linkedDocumentNumber referencing original sale. " +
222
+ "⚠️ FIELD NAMES - use exactly these: " +
223
+ "header.purchasedAt (NOT purchaseDate), items[].sku as STRING (NOT object). " +
224
+ "Example: { header: { documentNumber: 'INV-001', purchasedAt: '2026-01-15T10:00:00Z' }, items: [{ sku: 'PROD-123', name: 'Product', grossValue: 50, category: 'Sales' }] }. " +
222
225
  "Returns transactionId and pointsEarned if campaigns triggered.",
223
226
  readOnly: false,
224
227
  inputSchema: TransactionCreateInputSchema,
@@ -138,11 +138,18 @@ export async function walletTypeCreate(input) {
138
138
  // Check for duplicate code error
139
139
  const errorMsg = getErrorMessage(error);
140
140
  if (errorMsg.toLowerCase().includes("code") && errorMsg.toLowerCase().includes("already")) {
141
+ const isDefaultCode = input.code === "default" || !input.code;
141
142
  throw new OpenLoyaltyError({
142
143
  code: "DUPLICATE_CODE",
143
- message: `Wallet type with code '${input.code}' already exists`,
144
- hint: `Use a different code value, or use ol_wallet_type_list() to find existing wallet types. ` +
145
- `Each wallet type must have a unique code.`,
144
+ message: `Wallet type with code '${input.code || "default"}' already exists`,
145
+ hint: isDefaultCode
146
+ ? `Every store comes with a 'Default wallet' (code: 'default') pre-installed. ` +
147
+ `You do NOT need to create it. Use ol_wallet_type_list() to find it, then ` +
148
+ `ol_wallet_type_update() to customize its name, units, or expiry settings. ` +
149
+ `Only create a new wallet if you need a SECOND point currency with a different code.`
150
+ : `A wallet type with code '${input.code}' already exists. ` +
151
+ `Use ol_wallet_type_list() to see all existing wallet types. ` +
152
+ `Either use a different code, or find the existing wallet and update it with ol_wallet_type_update().`,
146
153
  relatedTool: "ol_wallet_type_create",
147
154
  });
148
155
  }
@@ -231,9 +238,9 @@ export const walletTypeToolDefinitions = [
231
238
  name: "ol_wallet_type_list",
232
239
  title: "List Point Currencies",
233
240
  description: "List all available wallet types (point currencies). " +
234
- "Use this to find wallet type codes and IDs for other operations. " +
235
- "Returns walletTypeId (UUID), code (unique identifier like 'default'), and name for each wallet type. " +
236
- "💡 TIP: Most stores have a 'default' wallet type for main loyalty points.",
241
+ "ALWAYS call this BEFORE creating a new wallet type - every store already has a 'Default wallet' " +
242
+ "(code: 'default', units: points) pre-installed. " +
243
+ "Returns walletTypeId (UUID), code (unique identifier like 'default'), and name for each wallet type.",
237
244
  readOnly: true,
238
245
  inputSchema: WalletTypeListInputSchema,
239
246
  handler: walletTypeList,
@@ -252,20 +259,23 @@ export const walletTypeToolDefinitions = [
252
259
  name: "ol_wallet_type_create",
253
260
  title: "Create Point Currency",
254
261
  description: "Create a new wallet type (point currency) for the loyalty program. " +
255
- "⚠️ REQUIRED FIELDS (will fail without these): " +
262
+ "IMPORTANT: Every store already has a 'Default wallet' (code: 'default', units: points) out of the box. " +
263
+ "ALWAYS call ol_wallet_type_list() FIRST to check what wallets exist before creating a new one. " +
264
+ "If the default wallet fits your needs, use it as-is or update it with ol_wallet_type_update(). " +
265
+ "Only create a new wallet if you need a SECOND currency (e.g., 'stars', 'coins', 'miles'). " +
266
+ "REQUIRED FIELDS (will fail without these): " +
256
267
  "1. translations: { en: { name: 'Currency Name' } } - Name is REQUIRED. " +
257
268
  "2. unitSingularName: 'point' (or 'coin', 'star', etc.) - The singular form. " +
258
269
  "3. unitPluralName: 'points' (or 'coins', 'stars', etc.) - The plural form. " +
259
270
  "4. unitDaysExpiryAfter: 'all_time_active' or number string like '365'. " +
260
- "📝 OPTIONAL: " +
261
- "code: Unique identifier (auto-generated if omitted, cannot change later). " +
262
- "allowNegativeBalance: true/false (default: false). " +
263
- "unitExpiryDate: Annual expiry in 'MM-DD' format (e.g., '12-31'). " +
264
- "• unitDaysLocked: Days before points become spendable (0 for immediate). " +
265
- "⚠️ NOT SUPPORTED AT CREATION: 'active' and 'limits' - use ol_wallet_type_update after creation. " +
266
- " NOTE: New wallets are 'blocked' for ~2 minutes after creation - wait before updating. " +
267
- "💡 EXAMPLE: { translations: { en: { name: 'Bonus Points' } }, unitSingularName: 'point', " +
268
- "unitPluralName: 'points', unitDaysExpiryAfter: 'all_time_active', code: 'bonus' }",
271
+ "OPTIONAL: " +
272
+ "code: Unique identifier (auto-generated if omitted, cannot change later). " +
273
+ "allowNegativeBalance: true/false (default: false). " +
274
+ "unitExpiryDate: Annual expiry in 'MM-DD' format (e.g., '12-31'). " +
275
+ "allTimeNotLocked: TRUE = 'No pending' (points immediately available). " +
276
+ "unitDaysLocked: Only if allTimeNotLocked=false, set days for pending period. " +
277
+ "NOT SUPPORTED AT CREATION: 'active' and 'limits' - use ol_wallet_type_update after creation. " +
278
+ "NOTE: New wallets are 'blocked' for ~2 minutes after creation - wait before updating.",
269
279
  readOnly: false,
270
280
  inputSchema: WalletTypeCreateInputSchema,
271
281
  handler: walletTypeCreate,
@@ -283,6 +293,7 @@ export const walletTypeToolDefinitions = [
283
293
  "• allowNegativeBalance: Allow/disallow negative balances. " +
284
294
  "• limits: Update earning limits (same format as create). " +
285
295
  "• Expiry settings: unitExpiryDate, unitDaysExpiryAfter, etc. " +
296
+ "• ⭐ PENDING: allTimeNotLocked=true for 'No pending', or =false with unitDaysLocked for pending period. " +
286
297
  "💡 TIP: Use ol_wallet_type_get(walletTypeId) first to see current configuration.",
287
298
  readOnly: false,
288
299
  inputSchema: WalletTypeUpdateInputSchema,
@@ -11,16 +11,16 @@ export declare const AdminRoleSchema: z.ZodObject<{
11
11
  default: z.ZodOptional<z.ZodBoolean>;
12
12
  }, "strip", z.ZodTypeAny, {
13
13
  name?: string | undefined;
14
+ default?: boolean | undefined;
14
15
  id?: string | number | undefined;
15
16
  role?: string | undefined;
16
17
  master?: boolean | undefined;
17
- default?: boolean | undefined;
18
18
  }, {
19
19
  name?: string | undefined;
20
+ default?: boolean | undefined;
20
21
  id?: string | number | undefined;
21
22
  role?: string | undefined;
22
23
  master?: boolean | undefined;
23
- default?: boolean | undefined;
24
24
  }>;
25
25
  export type AdminRole = z.infer<typeof AdminRoleSchema>;
26
26
  /**
@@ -58,16 +58,16 @@ export declare const AdminUserSchema: z.ZodObject<{
58
58
  default: z.ZodOptional<z.ZodBoolean>;
59
59
  }, "strip", z.ZodTypeAny, {
60
60
  name?: string | undefined;
61
+ default?: boolean | undefined;
61
62
  id?: string | number | undefined;
62
63
  role?: string | undefined;
63
64
  master?: boolean | undefined;
64
- default?: boolean | undefined;
65
65
  }, {
66
66
  name?: string | undefined;
67
+ default?: boolean | undefined;
67
68
  id?: string | number | undefined;
68
69
  role?: string | undefined;
69
70
  master?: boolean | undefined;
70
- default?: boolean | undefined;
71
71
  }>, "many">>;
72
72
  settings: z.ZodOptional<z.ZodObject<{
73
73
  notificationsEnabled: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
@@ -92,10 +92,10 @@ export declare const AdminUserSchema: z.ZodObject<{
92
92
  canChangePassword?: boolean | undefined;
93
93
  roles?: {
94
94
  name?: string | undefined;
95
+ default?: boolean | undefined;
95
96
  id?: string | number | undefined;
96
97
  role?: string | undefined;
97
98
  master?: boolean | undefined;
98
- default?: boolean | undefined;
99
99
  }[] | undefined;
100
100
  settings?: {
101
101
  notificationsEnabled?: string | boolean | undefined;
@@ -115,10 +115,10 @@ export declare const AdminUserSchema: z.ZodObject<{
115
115
  canChangePassword?: boolean | undefined;
116
116
  roles?: {
117
117
  name?: string | undefined;
118
+ default?: boolean | undefined;
118
119
  id?: string | number | undefined;
119
120
  role?: string | undefined;
120
121
  master?: boolean | undefined;
121
- default?: boolean | undefined;
122
122
  }[] | undefined;
123
123
  settings?: {
124
124
  notificationsEnabled?: string | boolean | undefined;
@@ -56,9 +56,9 @@ export declare const RoleSchema: z.ZodObject<{
56
56
  }, "strip", z.ZodTypeAny, {
57
57
  name: string;
58
58
  id: string | number;
59
+ default?: boolean | undefined;
59
60
  role?: string | undefined;
60
61
  master?: boolean | undefined;
61
- default?: boolean | undefined;
62
62
  permissions?: {
63
63
  resource: string;
64
64
  access: string;
@@ -70,9 +70,9 @@ export declare const RoleSchema: z.ZodObject<{
70
70
  }, {
71
71
  name: string;
72
72
  id: string | number;
73
+ default?: boolean | undefined;
73
74
  role?: string | undefined;
74
75
  master?: boolean | undefined;
75
- default?: boolean | undefined;
76
76
  permissions?: {
77
77
  resource: string;
78
78
  access: string;
@@ -116,9 +116,9 @@ export declare const RoleListItemSchema: z.ZodObject<{
116
116
  }, "strip", z.ZodTypeAny, {
117
117
  name: string;
118
118
  id: string | number;
119
+ default?: boolean | undefined;
119
120
  role?: string | undefined;
120
121
  master?: boolean | undefined;
121
- default?: boolean | undefined;
122
122
  permissions?: {
123
123
  resource: string;
124
124
  access: string;
@@ -130,9 +130,9 @@ export declare const RoleListItemSchema: z.ZodObject<{
130
130
  }, {
131
131
  name: string;
132
132
  id: string | number;
133
+ default?: boolean | undefined;
133
134
  role?: string | undefined;
134
135
  master?: boolean | undefined;
135
- default?: boolean | undefined;
136
136
  permissions?: {
137
137
  resource: string;
138
138
  access: string;
@@ -75,9 +75,11 @@ export const WalletTypeCreateInputSchema = {
75
75
  "IMPORTANT: Use 'MM-DD' format, NOT 'YYYY-MM-DD'."),
76
76
  unitDaysActiveCount: z.number().int().nonnegative().optional().describe("Number of days points remain active after earning. Use with unitDaysExpiryAfter."),
77
77
  unitYearsActiveCount: z.number().int().nonnegative().optional().describe("Number of years points remain active after earning."),
78
- unitDaysLocked: z.number().int().nonnegative().optional().describe("Number of days points are locked after earning before becoming spendable. " +
79
- "Use 0 or omit for immediately available points."),
80
- allTimeNotLocked: z.boolean().optional().describe("If true, points are never locked and immediately available. Overrides unitDaysLocked."),
78
+ unitDaysLocked: z.number().int().nonnegative().optional().describe("Number of days points are PENDING (locked) after earning before becoming spendable. " +
79
+ "Only used when allTimeNotLocked is false. Example: 7 for a 7-day pending period."),
80
+ allTimeNotLocked: z.boolean().optional().describe(" PENDING METHOD: Set to TRUE for 'No pending' (points immediately available). " +
81
+ "Set to FALSE (or omit) for 'Pending for X days' mode, then set unitDaysLocked to the number of days. " +
82
+ "This maps to the 'Unit pending method' dropdown in the UI."),
81
83
  };
82
84
  export const WalletTypeUpdateInputSchema = {
83
85
  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."),
@@ -93,6 +95,6 @@ export const WalletTypeUpdateInputSchema = {
93
95
  unitDaysExpiryAfter: z.string().optional().describe("Update days until expiry or 'all_time_active'."),
94
96
  unitDaysActiveCount: z.number().int().nonnegative().optional().describe("Update days active count."),
95
97
  unitYearsActiveCount: z.number().int().nonnegative().optional().describe("Update years active count."),
96
- unitDaysLocked: z.number().int().nonnegative().optional().describe("Update locked days."),
97
- allTimeNotLocked: z.boolean().optional().describe("Update not-locked setting."),
98
+ unitDaysLocked: z.number().int().nonnegative().optional().describe("Update pending period (days points are locked). Only applies when allTimeNotLocked is false."),
99
+ allTimeNotLocked: z.boolean().optional().describe(" PENDING METHOD: Set TRUE for 'No pending' (immediate), FALSE for 'Pending for X days'."),
98
100
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-loyalty/mcp-server",
3
- "version": "1.3.6",
3
+ "version": "1.4.1",
4
4
  "type": "module",
5
5
  "description": "MCP server for Open Loyalty API - enables AI agents to manage loyalty programs, members, points, rewards, and transactions",
6
6
  "author": "Marcin Dyguda <md@openloyalty.io>",
@@ -1,107 +0,0 @@
1
- /**
2
- * Fan Engagement Setup Prompts
3
- *
4
- * MCP prompt templates for guided campaign setup.
5
- * These define conversational flows and default values for AI agents
6
- * to help program admins create fan engagement campaigns.
7
- */
8
- export interface WorkflowQuestion {
9
- id: string;
10
- question: string;
11
- type: "number" | "boolean" | "string" | "select";
12
- default?: unknown;
13
- options?: string[];
14
- validation?: (value: unknown) => boolean;
15
- hint?: string;
16
- }
17
- export interface WorkflowStep {
18
- id: string;
19
- action: string;
20
- tool: string;
21
- description: string;
22
- }
23
- export interface WorkflowDefinition {
24
- id: string;
25
- name: string;
26
- description: string;
27
- triggerPhrases: string[];
28
- questions: WorkflowQuestion[];
29
- steps: WorkflowStep[];
30
- }
31
- export declare const DEFAULTS: {
32
- readonly matchAttendance: {
33
- readonly coinsPerMatch: 50;
34
- readonly milestones: number[];
35
- readonly milestoneBonuses: Record<string | number, number>;
36
- readonly limitPerDay: 1;
37
- readonly badges: Record<string | number, string>;
38
- };
39
- readonly earlyArrival: {
40
- readonly minutesBefore: 60;
41
- readonly coinsPerArrival: 25;
42
- readonly limitPerMatch: 1;
43
- };
44
- readonly sportsbarVisit: {
45
- readonly coinsPerVisit: 50;
46
- readonly visitMilestone: 5;
47
- readonly milestoneBonus: 500;
48
- readonly limitPerDay: 1;
49
- };
50
- readonly vodWatching: {
51
- readonly trackBy: "views" | "minutes";
52
- readonly coinsPerUnit: 10;
53
- readonly unitSize: 10;
54
- readonly achievementMinutes: 300;
55
- readonly achievementBonus: 200;
56
- };
57
- readonly appLoginStreak: {
58
- readonly coinsPerLogin: 5;
59
- readonly streakDays: 7;
60
- readonly streakBonus: 50;
61
- readonly limitPerDay: 1;
62
- };
63
- readonly seasonDates: {
64
- readonly start: string;
65
- readonly end: string;
66
- };
67
- };
68
- export declare const MATCH_ATTENDANCE_WORKFLOW: WorkflowDefinition;
69
- export declare const EARLY_ARRIVAL_WORKFLOW: WorkflowDefinition;
70
- export declare const SPORTSBAR_VISIT_WORKFLOW: WorkflowDefinition;
71
- export declare const VOD_WATCHING_WORKFLOW: WorkflowDefinition;
72
- export declare const APP_LOGIN_STREAK_WORKFLOW: WorkflowDefinition;
73
- export declare const ALL_WORKFLOWS: WorkflowDefinition[];
74
- /**
75
- * Find matching workflow based on user input
76
- */
77
- export declare function findMatchingWorkflow(userInput: string): WorkflowDefinition | undefined;
78
- /**
79
- * Get workflow by ID
80
- */
81
- export declare function getWorkflowById(id: string): WorkflowDefinition | undefined;
82
- /**
83
- * Parse comma-separated values to array
84
- */
85
- export declare function parseCommaSeparated(value: string): string[];
86
- /**
87
- * Parse milestones (handles both numbers and percentages like "80%")
88
- */
89
- export declare function parseMilestones(value: string): (number | string)[];
90
- /**
91
- * Format ISO date for Open Loyalty (YYYY-MM-DD HH:mm+TZ)
92
- */
93
- export declare function formatOLDate(date: string | Date): string;
94
- export declare const AGENT_PROMPTS: {
95
- /**
96
- * System prompt for fan engagement setup
97
- */
98
- systemPrompt: string;
99
- /**
100
- * Workflow completion template
101
- */
102
- completionTemplate: (results: {
103
- campaignIds: string[];
104
- achievementIds: string[];
105
- summary: string;
106
- }) => string;
107
- };