@open-loyalty/mcp-server 1.3.6 → 1.3.7
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.
- package/dist/tools/achievement/index.js +10 -10
- package/dist/tools/achievement/schemas.js +16 -8
- package/dist/tools/reward/handlers.d.ts +1 -3
- package/dist/tools/reward/handlers.js +7 -7
- package/dist/tools/reward/index.d.ts +2 -8
- package/dist/tools/reward/index.js +6 -4
- package/dist/tools/reward/schemas.d.ts +1 -7
- package/dist/tools/reward/schemas.js +4 -6
- package/dist/tools/wallet-type.js +3 -1
- package/dist/types/schemas/wallet-type.js +7 -5
- package/package.json +1 -1
|
@@ -17,16 +17,16 @@ export const achievementToolDefinitions = [
|
|
|
17
17
|
name: "ol_achievement_create",
|
|
18
18
|
title: "Create Achievement",
|
|
19
19
|
description: "Create achievement with rules that track member progress. " +
|
|
20
|
-
"
|
|
21
|
-
"1
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
20
|
+
"⭐ TWO MAIN PATTERNS: " +
|
|
21
|
+
"1️⃣ ONE-TIME BADGE (e.g., 'First Purchase', 'Welcome'): " +
|
|
22
|
+
"• Set achievement-level limit: { value: 1 } (earn once ever). " +
|
|
23
|
+
"• Rule completeRule: { periodGoal: 1, period: { type: 'day', consecutive: 1 } }. " +
|
|
24
|
+
"• NO rule-level limit needed. " +
|
|
25
|
+
"2️⃣ STREAK BADGE (e.g., '7-day streak'): " +
|
|
26
|
+
"• Rule completeRule: { periodGoal: 1, period: { type: 'calendarDays', consecutive: 7 } }. " +
|
|
27
|
+
"• Rule limit: { value: 1, interval: { type: 'calendarDays', value: 1 } } (count 1 per day). " +
|
|
28
|
+
"REQUIRED in each rule: type='direct', trigger (transaction/custom_event/etc), aggregation: { type: 'quantity' }, completeRule. " +
|
|
29
|
+
"⚠️ COMMON MISTAKE: Using streak pattern for one-time badges. For one-time, use consecutive=1 with NO rule limit.",
|
|
30
30
|
readOnly: false,
|
|
31
31
|
inputSchema: AchievementCreateInputSchema,
|
|
32
32
|
handler: achievementCreate,
|
|
@@ -40,11 +40,15 @@ 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: 'day'
|
|
44
|
-
"'
|
|
45
|
-
value: z.number().optional().describe("Period value (e.g., 7 for last 7 days when type='last_day')."),
|
|
46
|
-
consecutive: z.number().min(1).describe("
|
|
47
|
-
|
|
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)."),
|
|
45
|
+
value: z.number().optional().describe("Period value (e.g., 7 for 'last 7 days' when type='last_day'). Usually omit for simple achievements."),
|
|
46
|
+
consecutive: z.number().min(1).describe("⭐ CONSECUTIVE PERIODS: For ONE-TIME achievements (first purchase): use consecutive=1. " +
|
|
47
|
+
"For STREAKS (7 consecutive days): use consecutive=7 with type='calendarDays'. " +
|
|
48
|
+
"consecutive=1 means 'no streak required' - just reach the goal once."),
|
|
49
|
+
}).describe("Period configuration. REQUIRED: both type and consecutive must be provided. " +
|
|
50
|
+
"ONE-TIME PATTERN: { type: 'day', consecutive: 1 } - reach goal anytime, no streak. " +
|
|
51
|
+
"STREAK PATTERN: { type: 'calendarDays', consecutive: 7 } - reach goal for 7 consecutive days."),
|
|
48
52
|
uniqueAttribute: z.string().optional().describe("Attribute for unique counting (e.g., 'sku' to count unique products)."),
|
|
49
53
|
}).describe("Completion goal configuration."),
|
|
50
54
|
aggregation: z.object({
|
|
@@ -57,7 +61,8 @@ const AchievementRuleInputSchema = z.object({
|
|
|
57
61
|
type: z.string().describe("Interval type: 'calendarDays', 'calendarWeeks', etc."),
|
|
58
62
|
value: z.number().optional().describe("Interval value."),
|
|
59
63
|
}).optional().describe("Time interval for the limit."),
|
|
60
|
-
}).optional().describe("
|
|
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."),
|
|
61
66
|
uniqueReferee: z.boolean().optional().describe("Whether referee must be unique (for referral achievements)."),
|
|
62
67
|
});
|
|
63
68
|
export const AchievementListInputSchema = {
|
|
@@ -80,12 +85,15 @@ export const AchievementCreateInputSchema = {
|
|
|
80
85
|
operator: z.string().optional().describe("Activity condition operator."),
|
|
81
86
|
}).optional().describe("Time period configuration."),
|
|
82
87
|
limit: z.object({
|
|
83
|
-
value: z.number().optional().describe("Maximum completions."),
|
|
88
|
+
value: z.number().optional().describe("Maximum completions (e.g., 1 for one-time badges)."),
|
|
84
89
|
interval: z.object({
|
|
85
90
|
type: z.string(),
|
|
86
91
|
value: z.number().optional(),
|
|
87
92
|
}).optional(),
|
|
88
|
-
}).optional().describe("
|
|
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!"),
|
|
89
97
|
rules: z.array(AchievementRuleInputSchema).describe("Achievement rules defining completion criteria."),
|
|
90
98
|
badgeTypeId: z.string().optional().describe("Badge type ID to award upon completion. " +
|
|
91
99
|
"NOTE: This field may cause 'extra fields' validation errors in some API versions. " +
|
|
@@ -35,9 +35,6 @@ export declare function rewardCreate(input: {
|
|
|
35
35
|
from: string;
|
|
36
36
|
to: string;
|
|
37
37
|
};
|
|
38
|
-
usageLimit?: {
|
|
39
|
-
perUser: number;
|
|
40
|
-
};
|
|
41
38
|
costInPoints?: number;
|
|
42
39
|
usageInstruction?: string;
|
|
43
40
|
active?: boolean;
|
|
@@ -67,6 +64,7 @@ export declare function rewardUpdate(input: {
|
|
|
67
64
|
categories?: string[];
|
|
68
65
|
levels?: string[];
|
|
69
66
|
segments?: string[];
|
|
67
|
+
usageLimitPerUser?: number;
|
|
70
68
|
}): Promise<void>;
|
|
71
69
|
export declare function rewardActivate(input: {
|
|
72
70
|
storeCode?: string;
|
|
@@ -47,18 +47,15 @@ export async function rewardList(input) {
|
|
|
47
47
|
export async function rewardCreate(input) {
|
|
48
48
|
const storeCode = getStoreCode(input.storeCode);
|
|
49
49
|
// API requires: translations (name only), reward (type), activity, visibility
|
|
50
|
-
// NOTE: usageLimit
|
|
50
|
+
// NOTE: usageLimit is NOT supported at creation time - API rejects it as "extra fields"
|
|
51
|
+
// Use reward_update after creation to set usageLimit
|
|
51
52
|
// NOTE: description is NOT supported by the API at creation time
|
|
52
|
-
// Sanitize usageLimit - ONLY pass perUser, strip any other fields (like 'general')
|
|
53
|
-
const sanitizedUsageLimit = input.usageLimit?.perUser !== undefined
|
|
54
|
-
? { perUser: input.usageLimit.perUser }
|
|
55
|
-
: undefined;
|
|
56
53
|
const payload = omitUndefined({
|
|
57
54
|
translations: input.translations,
|
|
58
55
|
reward: input.reward,
|
|
59
56
|
activity: input.activity,
|
|
60
57
|
visibility: input.visibility,
|
|
61
|
-
usageLimit:
|
|
58
|
+
// usageLimit: NOT SUPPORTED at creation - use update after creation
|
|
62
59
|
costInPoints: input.costInPoints,
|
|
63
60
|
usageInstruction: input.usageInstruction,
|
|
64
61
|
active: input.active,
|
|
@@ -98,7 +95,6 @@ export async function rewardUpdate(input) {
|
|
|
98
95
|
const existing = await apiGet(`/${storeCode}/reward/${input.rewardId}`);
|
|
99
96
|
// Extract only the fields that PUT accepts (API is strict about extra fields)
|
|
100
97
|
// Note: GET returns name at root level, but PUT expects it in translations.en.name
|
|
101
|
-
// Note: usageLimit is NOT supported by the API - it rejects it as "extra fields"
|
|
102
98
|
const existingName = existing.name;
|
|
103
99
|
const existingActivity = existing.activity;
|
|
104
100
|
const existingVisibility = existing.visibility;
|
|
@@ -168,6 +164,10 @@ export async function rewardUpdate(input) {
|
|
|
168
164
|
payload.levels = input.levels;
|
|
169
165
|
if (input.segments !== undefined)
|
|
170
166
|
payload.segments = input.segments;
|
|
167
|
+
// usageLimit - only pass perUser (API rejects 'general' as extra field)
|
|
168
|
+
if (input.usageLimitPerUser !== undefined) {
|
|
169
|
+
payload.usageLimit = { perUser: input.usageLimitPerUser };
|
|
170
|
+
}
|
|
171
171
|
try {
|
|
172
172
|
await apiPut(`/${storeCode}/reward/${input.rewardId}`, { reward: payload });
|
|
173
173
|
}
|
|
@@ -68,13 +68,6 @@ export declare const rewardToolDefinitions: readonly [{
|
|
|
68
68
|
from: string;
|
|
69
69
|
to: string;
|
|
70
70
|
}>;
|
|
71
|
-
usageLimit: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
72
|
-
perUser: import("zod").ZodNumber;
|
|
73
|
-
}, "strict", import("zod").ZodTypeAny, {
|
|
74
|
-
perUser: number;
|
|
75
|
-
}, {
|
|
76
|
-
perUser: number;
|
|
77
|
-
}>>;
|
|
78
71
|
costInPoints: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
79
72
|
usageInstruction: import("zod").ZodOptional<import("zod").ZodString>;
|
|
80
73
|
active: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
@@ -100,7 +93,7 @@ export declare const rewardToolDefinitions: readonly [{
|
|
|
100
93
|
}, {
|
|
101
94
|
readonly name: "ol_reward_update";
|
|
102
95
|
readonly title: "Update Reward";
|
|
103
|
-
readonly description:
|
|
96
|
+
readonly description: string;
|
|
104
97
|
readonly readOnly: false;
|
|
105
98
|
readonly inputSchema: {
|
|
106
99
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
@@ -112,6 +105,7 @@ export declare const rewardToolDefinitions: readonly [{
|
|
|
112
105
|
categories: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
|
|
113
106
|
levels: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
|
|
114
107
|
segments: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
|
|
108
|
+
usageLimitPerUser: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
115
109
|
};
|
|
116
110
|
readonly handler: typeof rewardUpdate;
|
|
117
111
|
}, {
|
|
@@ -25,9 +25,10 @@ export const rewardToolDefinitions = [
|
|
|
25
25
|
"2. reward: 'material' or 'static_coupon' or 'dynamic_coupon', " +
|
|
26
26
|
"3. activity: { from: '2026-01-01 00:00', to: '2027-12-31 23:59' }, " +
|
|
27
27
|
"4. visibility: { from: '2026-01-01 00:00', to: '2027-12-31 23:59' }. " +
|
|
28
|
-
"Types: material (physical goods), static_coupon (fixed discount), dynamic_coupon (variable value). " +
|
|
29
|
-
"For coupons: add couponValue, couponValueType ('money' or 'percentage'), daysValid. " +
|
|
30
|
-
"For tier targeting: set target='level' and levels=['tier-uuid-1', 'tier-uuid-2']."
|
|
28
|
+
"Types: material (physical goods), static_coupon (fixed discount - requires couponValue), dynamic_coupon (variable value). " +
|
|
29
|
+
"For coupons: add couponValue (required!), couponValueType ('money' or 'percentage'), daysValid. " +
|
|
30
|
+
"For tier targeting: set target='level' and levels=['tier-uuid-1', 'tier-uuid-2']. " +
|
|
31
|
+
"⚠️ usageLimit is NOT supported at creation - use reward_update after creation to set limits.",
|
|
31
32
|
readOnly: false,
|
|
32
33
|
inputSchema: RewardCreateInputSchema,
|
|
33
34
|
handler: rewardCreate,
|
|
@@ -43,7 +44,8 @@ export const rewardToolDefinitions = [
|
|
|
43
44
|
{
|
|
44
45
|
name: "ol_reward_update",
|
|
45
46
|
title: "Update Reward",
|
|
46
|
-
description: "Update reward configuration. Cannot change reward type after creation."
|
|
47
|
+
description: "Update reward configuration. Cannot change reward type after creation. " +
|
|
48
|
+
"Use usageLimitPerUser to set maximum redemptions per member (this is NOT supported at creation time).",
|
|
47
49
|
readOnly: false,
|
|
48
50
|
inputSchema: RewardUpdateInputSchema,
|
|
49
51
|
handler: rewardUpdate,
|
|
@@ -55,13 +55,6 @@ export declare const RewardCreateInputSchema: {
|
|
|
55
55
|
from: string;
|
|
56
56
|
to: string;
|
|
57
57
|
}>;
|
|
58
|
-
usageLimit: z.ZodOptional<z.ZodObject<{
|
|
59
|
-
perUser: z.ZodNumber;
|
|
60
|
-
}, "strict", z.ZodTypeAny, {
|
|
61
|
-
perUser: number;
|
|
62
|
-
}, {
|
|
63
|
-
perUser: number;
|
|
64
|
-
}>>;
|
|
65
58
|
costInPoints: z.ZodOptional<z.ZodNumber>;
|
|
66
59
|
usageInstruction: z.ZodOptional<z.ZodString>;
|
|
67
60
|
active: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -87,6 +80,7 @@ export declare const RewardUpdateInputSchema: {
|
|
|
87
80
|
categories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
88
81
|
levels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
89
82
|
segments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
83
|
+
usageLimitPerUser: z.ZodOptional<z.ZodNumber>;
|
|
90
84
|
};
|
|
91
85
|
export declare const RewardIdInputSchema: {
|
|
92
86
|
storeCode: z.ZodOptional<z.ZodString>;
|
|
@@ -26,11 +26,8 @@ const RewardVisibilityInputSchema = z.object({
|
|
|
26
26
|
from: z.string().describe("Visibility start datetime (format: 'YYYY-MM-DD HH:mm'). REQUIRED."),
|
|
27
27
|
to: z.string().describe("Visibility end datetime (format: 'YYYY-MM-DD HH:mm'). REQUIRED."),
|
|
28
28
|
});
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
const RewardUsageLimitInputSchema = z.object({
|
|
32
|
-
perUser: z.number().describe("Maximum redemptions per member. This is the ONLY supported field."),
|
|
33
|
-
}).strict().describe("Usage limits. ⚠️ ONLY 'perUser' is supported. Do NOT pass 'general' or any other field - they will be rejected.");
|
|
29
|
+
// NOTE: usageLimit is NOT supported at creation time - API rejects it as "extra fields"
|
|
30
|
+
// Use reward_update with usageLimitPerUser after creation instead
|
|
34
31
|
export const RewardCreateInputSchema = {
|
|
35
32
|
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."),
|
|
36
33
|
translations: RewardTranslationsInputSchema.describe("Reward name translations. At least 'en' key with { name } is REQUIRED."),
|
|
@@ -38,7 +35,7 @@ export const RewardCreateInputSchema = {
|
|
|
38
35
|
"conversion_coupon (converts points to coupon), material (physical goods), fortune_wheel (gamified)."),
|
|
39
36
|
activity: RewardActivityInputSchema.describe("Activity period when reward can be purchased. Use datetime format 'YYYY-MM-DD HH:mm'. REQUIRED."),
|
|
40
37
|
visibility: RewardVisibilityInputSchema.describe("Visibility period when reward is shown. Use datetime format 'YYYY-MM-DD HH:mm'. REQUIRED."),
|
|
41
|
-
usageLimit:
|
|
38
|
+
// usageLimit: NOT SUPPORTED at creation - removed. Use reward_update after creation.
|
|
42
39
|
costInPoints: z.number().optional().describe("Points required to redeem this reward."),
|
|
43
40
|
usageInstruction: z.string().optional().describe("Instructions for using the reward."),
|
|
44
41
|
active: z.boolean().optional().describe("Whether reward is active (default: false)."),
|
|
@@ -64,6 +61,7 @@ export const RewardUpdateInputSchema = {
|
|
|
64
61
|
categories: z.array(z.string()).optional().describe("Array of category UUIDs."),
|
|
65
62
|
levels: z.array(z.string()).optional().describe("Array of tier level UUIDs."),
|
|
66
63
|
segments: z.array(z.string()).optional().describe("Array of segment UUIDs."),
|
|
64
|
+
usageLimitPerUser: z.number().optional().describe("Maximum redemptions per member. Set this after creation to limit usage."),
|
|
67
65
|
};
|
|
68
66
|
export const RewardIdInputSchema = {
|
|
69
67
|
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."),
|
|
@@ -261,7 +261,8 @@ export const walletTypeToolDefinitions = [
|
|
|
261
261
|
"• code: Unique identifier (auto-generated if omitted, cannot change later). " +
|
|
262
262
|
"• allowNegativeBalance: true/false (default: false). " +
|
|
263
263
|
"• unitExpiryDate: Annual expiry in 'MM-DD' format (e.g., '12-31'). " +
|
|
264
|
-
"•
|
|
264
|
+
"• allTimeNotLocked: TRUE = 'No pending' (points immediately available). " +
|
|
265
|
+
"• unitDaysLocked: Only if allTimeNotLocked=false, set days for pending period. " +
|
|
265
266
|
"⚠️ NOT SUPPORTED AT CREATION: 'active' and 'limits' - use ol_wallet_type_update after creation. " +
|
|
266
267
|
"⏰ NOTE: New wallets are 'blocked' for ~2 minutes after creation - wait before updating. " +
|
|
267
268
|
"💡 EXAMPLE: { translations: { en: { name: 'Bonus Points' } }, unitSingularName: 'point', " +
|
|
@@ -283,6 +284,7 @@ export const walletTypeToolDefinitions = [
|
|
|
283
284
|
"• allowNegativeBalance: Allow/disallow negative balances. " +
|
|
284
285
|
"• limits: Update earning limits (same format as create). " +
|
|
285
286
|
"• Expiry settings: unitExpiryDate, unitDaysExpiryAfter, etc. " +
|
|
287
|
+
"• ⭐ PENDING: allTimeNotLocked=true for 'No pending', or =false with unitDaysLocked for pending period. " +
|
|
286
288
|
"💡 TIP: Use ol_wallet_type_get(walletTypeId) first to see current configuration.",
|
|
287
289
|
readOnly: false,
|
|
288
290
|
inputSchema: WalletTypeUpdateInputSchema,
|
|
@@ -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
|
-
"
|
|
80
|
-
allTimeNotLocked: z.boolean().optional().describe("
|
|
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
|
|
97
|
-
allTimeNotLocked: z.boolean().optional().describe("
|
|
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.
|
|
3
|
+
"version": "1.3.7",
|
|
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>",
|