@open-loyalty/mcp-server 1.3.7 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +18 -5
- package/dist/tools/reward/handlers.d.ts +2 -0
- package/dist/tools/reward/handlers.js +52 -6
- package/dist/tools/reward/index.d.ts +2 -0
- package/dist/tools/reward/index.js +13 -7
- package/dist/tools/reward/schemas.d.ts +2 -0
- package/dist/tools/reward/schemas.js +15 -5
- package/dist/tools/tierset.d.ts +1 -1
- package/dist/tools/tierset.js +49 -25
- package/dist/tools/transaction.js +5 -2
- package/dist/tools/wallet-type.js +26 -17
- package/dist/types/schemas/admin.d.ts +6 -6
- package/dist/types/schemas/role.d.ts +4 -4
- package/package.json +1 -1
- package/dist/prompts/fan-engagement-setup.d.ts +0 -107
- package/dist/prompts/fan-engagement-setup.js +0 -492
- package/dist/tools/achievement.d.ts +0 -1017
- package/dist/tools/achievement.js +0 -354
- package/dist/tools/campaign.d.ts +0 -1800
- package/dist/tools/campaign.js +0 -737
- package/dist/tools/member.d.ts +0 -366
- package/dist/tools/member.js +0 -352
- package/dist/tools/reward.d.ts +0 -279
- package/dist/tools/reward.js +0 -361
- package/dist/tools/segment.d.ts +0 -816
- package/dist/tools/segment.js +0 -333
- package/dist/workflows/app-login-streak.d.ts +0 -39
- package/dist/workflows/app-login-streak.js +0 -298
- package/dist/workflows/early-arrival.d.ts +0 -33
- package/dist/workflows/early-arrival.js +0 -148
- package/dist/workflows/index.d.ts +0 -101
- package/dist/workflows/index.js +0 -208
- package/dist/workflows/match-attendance.d.ts +0 -45
- package/dist/workflows/match-attendance.js +0 -308
- package/dist/workflows/sportsbar-visit.d.ts +0 -41
- package/dist/workflows/sportsbar-visit.js +0 -284
- package/dist/workflows/vod-watching.d.ts +0 -43
- package/dist/workflows/vod-watching.js +0 -326
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { apiGet, apiPost, apiPut, apiPatch } from "../client/http.js";
|
|
3
|
-
import { formatApiError } from "../utils/errors.js";
|
|
4
|
-
import { getStoreCode } from "../config.js";
|
|
5
|
-
// Input Schemas
|
|
6
|
-
export const AchievementListInputSchema = {
|
|
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."),
|
|
8
|
-
page: z.number().optional().describe("Page number (default: 1)."),
|
|
9
|
-
perPage: z.number().optional().describe("Items per page (default: 10)."),
|
|
10
|
-
active: z.boolean().optional().describe("Filter by active status."),
|
|
11
|
-
name: z.string().optional().describe("Filter by achievement name."),
|
|
12
|
-
};
|
|
13
|
-
// Period type enum for achievements
|
|
14
|
-
// NOTE: "forever" is NOT valid - use "day" with consecutive=1 for all-time tracking
|
|
15
|
-
const AchievementPeriodTypeEnum = z.enum([
|
|
16
|
-
"day",
|
|
17
|
-
"week",
|
|
18
|
-
"month",
|
|
19
|
-
"year",
|
|
20
|
-
"last_day",
|
|
21
|
-
"calendarDays",
|
|
22
|
-
"calendarWeeks",
|
|
23
|
-
"calendarMonths",
|
|
24
|
-
"calendarYears",
|
|
25
|
-
]);
|
|
26
|
-
// Aggregation type enum for achievements
|
|
27
|
-
const AchievementAggregationTypeEnum = z.enum([
|
|
28
|
-
"quantity",
|
|
29
|
-
"sum",
|
|
30
|
-
"average",
|
|
31
|
-
"min",
|
|
32
|
-
"max",
|
|
33
|
-
]);
|
|
34
|
-
// Rule input schema
|
|
35
|
-
const AchievementRuleInputSchema = z.object({
|
|
36
|
-
achievementRuleId: z.string().optional().describe("Rule ID (for updates)."),
|
|
37
|
-
translations: z.record(z.string(), z.object({
|
|
38
|
-
name: z.string(),
|
|
39
|
-
description: z.string().optional(),
|
|
40
|
-
})).optional().describe("Rule name and description translations."),
|
|
41
|
-
trigger: z.enum([
|
|
42
|
-
"transaction",
|
|
43
|
-
"custom_event",
|
|
44
|
-
"points_transfer",
|
|
45
|
-
"reward_redemption",
|
|
46
|
-
"referral",
|
|
47
|
-
"achievement",
|
|
48
|
-
"tier_change",
|
|
49
|
-
"registration",
|
|
50
|
-
"profile_update",
|
|
51
|
-
]).optional().describe("Event type that triggers rule progress. For login tracking, use custom_event with a custom 'app_login' event schema."),
|
|
52
|
-
type: z.enum(["direct", "referral"]).optional().describe("Rule type: direct (default) or referral."),
|
|
53
|
-
event: z.string().optional().describe("Custom event code (required for trigger='custom_event'). Must match an existing custom event schema."),
|
|
54
|
-
completeRule: z.object({
|
|
55
|
-
periodGoal: z.union([z.number(), z.string()]).describe("Goal value to reach (e.g., 5 for 5 purchases, 100 for 100 points)."),
|
|
56
|
-
period: z.object({
|
|
57
|
-
type: AchievementPeriodTypeEnum.describe("Period type: 'day' (all-time when consecutive=1), 'last_day' (rolling N days), " +
|
|
58
|
-
"'calendarDays' (reset daily), 'calendarWeeks' (reset weekly), 'calendarMonths' (reset monthly)."),
|
|
59
|
-
consecutive: z.number().min(1).describe("Required consecutive periods. Use 1 for all-time tracking, 7 for weekly streaks, etc."),
|
|
60
|
-
}).describe("Period configuration. REQUIRED: both type and consecutive must be provided."),
|
|
61
|
-
uniqueAttribute: z.string().optional().describe("Attribute for unique counting (e.g., 'sku' to count unique products)."),
|
|
62
|
-
}).describe("Completion goal configuration."),
|
|
63
|
-
aggregation: z.object({
|
|
64
|
-
type: AchievementAggregationTypeEnum.describe("How to aggregate: 'quantity' (count events), 'sum' (add values), etc."),
|
|
65
|
-
rule: z.string().optional().describe("Expression for value extraction (e.g., 'transaction.grossValue' for sum of transaction values)."),
|
|
66
|
-
}).optional().describe("Value aggregation. Use type='quantity' for event counting, type='sum' with rule for value-based goals."),
|
|
67
|
-
conditions: z.array(z.record(z.unknown())).optional().describe("Conditions that must be met."),
|
|
68
|
-
limit: z.object({
|
|
69
|
-
value: z.number().optional().describe("Max events per interval (e.g., 1 for once-per-day limit)."),
|
|
70
|
-
interval: z.object({
|
|
71
|
-
type: z.string().describe("Interval type: 'calendarDays', 'calendarWeeks', etc."),
|
|
72
|
-
value: z.number().optional().describe("Interval value."),
|
|
73
|
-
}).optional().describe("Time interval for the limit."),
|
|
74
|
-
}).optional().describe("Per-rule execution limit (use for streak patterns: limit 1 per day)."),
|
|
75
|
-
uniqueReferee: z.boolean().optional().describe("Whether referee must be unique (for referral achievements)."),
|
|
76
|
-
});
|
|
77
|
-
export const AchievementCreateInputSchema = {
|
|
78
|
-
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
|
-
translations: z.record(z.string(), z.object({
|
|
80
|
-
name: z.string(),
|
|
81
|
-
description: z.string().optional(),
|
|
82
|
-
})).describe("Achievement name and description. At least 'en' key required."),
|
|
83
|
-
active: z.boolean().optional().describe("Whether achievement is active (default: false)."),
|
|
84
|
-
activity: z.object({
|
|
85
|
-
startsAt: z.string().optional().describe("ISO datetime when achievement becomes active."),
|
|
86
|
-
endsAt: z.string().optional().describe("ISO datetime when achievement ends."),
|
|
87
|
-
operator: z.string().optional().describe("Activity condition operator."),
|
|
88
|
-
}).optional().describe("Time period configuration."),
|
|
89
|
-
limit: z.object({
|
|
90
|
-
value: z.number().optional().describe("Maximum completions."),
|
|
91
|
-
interval: z.object({
|
|
92
|
-
type: z.string(),
|
|
93
|
-
value: z.number().optional(),
|
|
94
|
-
}).optional(),
|
|
95
|
-
}).optional().describe("Overall limit configuration."),
|
|
96
|
-
rules: z.array(AchievementRuleInputSchema).describe("Achievement rules defining completion criteria."),
|
|
97
|
-
badgeTypeId: z.string().optional().describe("Badge type ID to award upon completion."),
|
|
98
|
-
};
|
|
99
|
-
export const AchievementGetInputSchema = {
|
|
100
|
-
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."),
|
|
101
|
-
achievementId: z.string().describe("The achievement ID (UUID) to retrieve."),
|
|
102
|
-
};
|
|
103
|
-
export const AchievementUpdateInputSchema = {
|
|
104
|
-
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
|
-
achievementId: z.string().describe("The achievement ID (UUID) to update."),
|
|
106
|
-
translations: z.record(z.string(), z.object({
|
|
107
|
-
name: z.string(),
|
|
108
|
-
description: z.string().optional(),
|
|
109
|
-
})).describe("Achievement name and description."),
|
|
110
|
-
active: z.boolean().optional().describe("Whether achievement is active."),
|
|
111
|
-
activity: z.object({
|
|
112
|
-
startsAt: z.string().optional(),
|
|
113
|
-
endsAt: z.string().optional(),
|
|
114
|
-
operator: z.string().optional(),
|
|
115
|
-
}).optional().describe("Time period configuration."),
|
|
116
|
-
limit: z.object({
|
|
117
|
-
value: z.number().optional(),
|
|
118
|
-
interval: z.object({
|
|
119
|
-
type: z.string(),
|
|
120
|
-
value: z.number().optional(),
|
|
121
|
-
}).optional(),
|
|
122
|
-
}).optional().describe("Limit configuration."),
|
|
123
|
-
rules: z.array(AchievementRuleInputSchema).describe("Achievement rules."),
|
|
124
|
-
badgeTypeId: z.string().optional().describe("Badge type ID to award."),
|
|
125
|
-
};
|
|
126
|
-
export const AchievementPatchInputSchema = {
|
|
127
|
-
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."),
|
|
128
|
-
achievementId: z.string().describe("The achievement ID (UUID) to patch."),
|
|
129
|
-
active: z.boolean().optional().describe("Whether achievement is active."),
|
|
130
|
-
translations: z.record(z.string(), z.object({
|
|
131
|
-
name: z.string().optional(),
|
|
132
|
-
description: z.string().optional(),
|
|
133
|
-
})).optional().describe("Partial translation updates."),
|
|
134
|
-
};
|
|
135
|
-
export const AchievementGetMemberProgressInputSchema = {
|
|
136
|
-
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."),
|
|
137
|
-
memberId: z.string().describe("The member ID (UUID)."),
|
|
138
|
-
achievementId: z.string().describe("The achievement ID (UUID)."),
|
|
139
|
-
};
|
|
140
|
-
export const AchievementListMemberAchievementsInputSchema = {
|
|
141
|
-
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."),
|
|
142
|
-
memberId: z.string().describe("The member ID (UUID)."),
|
|
143
|
-
page: z.number().optional().describe("Page number (default: 1)."),
|
|
144
|
-
perPage: z.number().optional().describe("Items per page (default: 25)."),
|
|
145
|
-
name: z.string().optional().describe("Filter by achievement name."),
|
|
146
|
-
achievementId: z.string().optional().describe("Filter by specific achievement ID."),
|
|
147
|
-
};
|
|
148
|
-
// Handler functions
|
|
149
|
-
export async function achievementList(input) {
|
|
150
|
-
const storeCode = getStoreCode(input.storeCode);
|
|
151
|
-
const params = new URLSearchParams();
|
|
152
|
-
if (input.page)
|
|
153
|
-
params.append("_page", String(input.page));
|
|
154
|
-
if (input.perPage)
|
|
155
|
-
params.append("_itemsOnPage", String(input.perPage));
|
|
156
|
-
if (input.active !== undefined)
|
|
157
|
-
params.append("active", String(input.active));
|
|
158
|
-
if (input.name)
|
|
159
|
-
params.append("name", input.name);
|
|
160
|
-
const queryString = params.toString();
|
|
161
|
-
const url = `/${storeCode}/achievement${queryString ? `?${queryString}` : ""}`;
|
|
162
|
-
try {
|
|
163
|
-
const response = await apiGet(url);
|
|
164
|
-
const achievements = (response.items || []).map((item) => ({
|
|
165
|
-
achievementId: item.achievementId,
|
|
166
|
-
name: item.name,
|
|
167
|
-
active: item.active,
|
|
168
|
-
createdAt: item.createdAt,
|
|
169
|
-
badgeTypeId: item.badgeTypeId,
|
|
170
|
-
}));
|
|
171
|
-
const total = response.total || {};
|
|
172
|
-
return {
|
|
173
|
-
achievements,
|
|
174
|
-
total: {
|
|
175
|
-
all: typeof total.all === "number" ? total.all : undefined,
|
|
176
|
-
filtered: typeof total.filtered === "number" ? total.filtered : undefined,
|
|
177
|
-
},
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
catch (error) {
|
|
181
|
-
throw formatApiError(error, "openloyalty_achievement_list");
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
export async function achievementCreate(input) {
|
|
185
|
-
const storeCode = getStoreCode(input.storeCode);
|
|
186
|
-
const achievementPayload = {
|
|
187
|
-
translations: input.translations,
|
|
188
|
-
rules: input.rules,
|
|
189
|
-
};
|
|
190
|
-
if (input.active !== undefined)
|
|
191
|
-
achievementPayload.active = input.active;
|
|
192
|
-
if (input.activity)
|
|
193
|
-
achievementPayload.activity = input.activity;
|
|
194
|
-
if (input.limit)
|
|
195
|
-
achievementPayload.limit = input.limit;
|
|
196
|
-
if (input.badgeTypeId)
|
|
197
|
-
achievementPayload.badgeTypeId = input.badgeTypeId;
|
|
198
|
-
try {
|
|
199
|
-
// CRITICAL: Wrap body as { achievement: {...} }
|
|
200
|
-
const response = await apiPost(`/${storeCode}/achievement`, { achievement: achievementPayload });
|
|
201
|
-
return { achievementId: response.achievementId };
|
|
202
|
-
}
|
|
203
|
-
catch (error) {
|
|
204
|
-
throw formatApiError(error, "openloyalty_achievement_create");
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
export async function achievementGet(input) {
|
|
208
|
-
const storeCode = getStoreCode(input.storeCode);
|
|
209
|
-
try {
|
|
210
|
-
const response = await apiGet(`/${storeCode}/achievement/${input.achievementId}`);
|
|
211
|
-
return response;
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
throw formatApiError(error, "openloyalty_achievement_get");
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
export async function achievementUpdate(input) {
|
|
218
|
-
const storeCode = getStoreCode(input.storeCode);
|
|
219
|
-
const achievementPayload = {
|
|
220
|
-
translations: input.translations,
|
|
221
|
-
rules: input.rules,
|
|
222
|
-
};
|
|
223
|
-
if (input.active !== undefined)
|
|
224
|
-
achievementPayload.active = input.active;
|
|
225
|
-
if (input.activity)
|
|
226
|
-
achievementPayload.activity = input.activity;
|
|
227
|
-
if (input.limit)
|
|
228
|
-
achievementPayload.limit = input.limit;
|
|
229
|
-
if (input.badgeTypeId !== undefined)
|
|
230
|
-
achievementPayload.badgeTypeId = input.badgeTypeId;
|
|
231
|
-
try {
|
|
232
|
-
// CRITICAL: Wrap body as { achievement: {...} }
|
|
233
|
-
await apiPut(`/${storeCode}/achievement/${input.achievementId}`, { achievement: achievementPayload });
|
|
234
|
-
}
|
|
235
|
-
catch (error) {
|
|
236
|
-
throw formatApiError(error, "openloyalty_achievement_update");
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
export async function achievementPatch(input) {
|
|
240
|
-
const storeCode = getStoreCode(input.storeCode);
|
|
241
|
-
const achievementPayload = {};
|
|
242
|
-
if (input.active !== undefined)
|
|
243
|
-
achievementPayload.active = input.active;
|
|
244
|
-
if (input.translations)
|
|
245
|
-
achievementPayload.translations = input.translations;
|
|
246
|
-
try {
|
|
247
|
-
// CRITICAL: Wrap body as { achievement: {...} }
|
|
248
|
-
await apiPatch(`/${storeCode}/achievement/${input.achievementId}`, { achievement: achievementPayload });
|
|
249
|
-
}
|
|
250
|
-
catch (error) {
|
|
251
|
-
throw formatApiError(error, "openloyalty_achievement_patch");
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
export async function achievementGetMemberProgress(input) {
|
|
255
|
-
const storeCode = getStoreCode(input.storeCode);
|
|
256
|
-
try {
|
|
257
|
-
const response = await apiGet(`/${storeCode}/member/${input.memberId}/achievement/${input.achievementId}`);
|
|
258
|
-
return response;
|
|
259
|
-
}
|
|
260
|
-
catch (error) {
|
|
261
|
-
throw formatApiError(error, "openloyalty_achievement_get_member_progress");
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
export async function achievementListMemberAchievements(input) {
|
|
265
|
-
const storeCode = getStoreCode(input.storeCode);
|
|
266
|
-
const params = new URLSearchParams();
|
|
267
|
-
if (input.page)
|
|
268
|
-
params.append("_page", String(input.page));
|
|
269
|
-
if (input.perPage)
|
|
270
|
-
params.append("_itemsOnPage", String(input.perPage));
|
|
271
|
-
if (input.name)
|
|
272
|
-
params.append("name", input.name);
|
|
273
|
-
if (input.achievementId)
|
|
274
|
-
params.append("achievementId", input.achievementId);
|
|
275
|
-
const queryString = params.toString();
|
|
276
|
-
const url = `/${storeCode}/member/${input.memberId}/achievement${queryString ? `?${queryString}` : ""}`;
|
|
277
|
-
try {
|
|
278
|
-
const response = await apiGet(url);
|
|
279
|
-
const achievements = response.items || [];
|
|
280
|
-
const total = response.total || {};
|
|
281
|
-
return {
|
|
282
|
-
achievements,
|
|
283
|
-
total: {
|
|
284
|
-
all: typeof total.all === "number" ? total.all : undefined,
|
|
285
|
-
filtered: typeof total.filtered === "number" ? total.filtered : undefined,
|
|
286
|
-
},
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
catch (error) {
|
|
290
|
-
throw formatApiError(error, "openloyalty_achievement_list_member_achievements");
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
// Tool definitions
|
|
294
|
-
export const achievementToolDefinitions = [
|
|
295
|
-
{
|
|
296
|
-
name: "openloyalty_achievement_list",
|
|
297
|
-
title: "List Achievements",
|
|
298
|
-
description: "List achievements. Achievements gamify member behavior by setting goals (e.g., 'Make 5 purchases this month'). Returns achievementId, name, active status, and associated badge. Use achievement_get for full rules and configuration.",
|
|
299
|
-
readOnly: true,
|
|
300
|
-
inputSchema: AchievementListInputSchema,
|
|
301
|
-
handler: achievementList,
|
|
302
|
-
},
|
|
303
|
-
{
|
|
304
|
-
name: "openloyalty_achievement_create",
|
|
305
|
-
title: "Create Achievement",
|
|
306
|
-
description: "Create achievement with rules that track member progress. Triggers: transaction, custom_event, points_transfer, referral, etc. " +
|
|
307
|
-
"REQUIRED in each rule: type='direct', completeRule with periodGoal and period (type + consecutive). " +
|
|
308
|
-
"Example - '5 purchases all-time': rules: [{ type: 'direct', trigger: 'transaction', aggregation: { type: 'quantity' }, completeRule: { periodGoal: 5, period: { type: 'day', consecutive: 1 } } }]. " +
|
|
309
|
-
"For streaks, use consecutive > 1 with limit: { value: 1, interval: { type: 'calendarDays', value: 1 } }.",
|
|
310
|
-
readOnly: false,
|
|
311
|
-
inputSchema: AchievementCreateInputSchema,
|
|
312
|
-
handler: achievementCreate,
|
|
313
|
-
},
|
|
314
|
-
{
|
|
315
|
-
name: "openloyalty_achievement_get",
|
|
316
|
-
title: "Get Achievement Details",
|
|
317
|
-
description: "Get achievement details including all rules, conditions, activity period, limits, and completions count.",
|
|
318
|
-
readOnly: true,
|
|
319
|
-
inputSchema: AchievementGetInputSchema,
|
|
320
|
-
handler: achievementGet,
|
|
321
|
-
},
|
|
322
|
-
{
|
|
323
|
-
name: "openloyalty_achievement_update",
|
|
324
|
-
title: "Update Achievement",
|
|
325
|
-
description: "Update achievement configuration. Requires full achievement object (translations, rules). Use achievement_get first to retrieve current configuration.",
|
|
326
|
-
readOnly: false,
|
|
327
|
-
inputSchema: AchievementUpdateInputSchema,
|
|
328
|
-
handler: achievementUpdate,
|
|
329
|
-
},
|
|
330
|
-
{
|
|
331
|
-
name: "openloyalty_achievement_patch",
|
|
332
|
-
title: "Patch Achievement",
|
|
333
|
-
description: "Partial update of achievement. Use for simple changes like activating/deactivating or updating translations without providing full rules.",
|
|
334
|
-
readOnly: false,
|
|
335
|
-
inputSchema: AchievementPatchInputSchema,
|
|
336
|
-
handler: achievementPatch,
|
|
337
|
-
},
|
|
338
|
-
{
|
|
339
|
-
name: "openloyalty_achievement_get_member_progress",
|
|
340
|
-
title: "Get Member Achievement Progress",
|
|
341
|
-
description: "Get member's progress on a specific achievement. Returns completedCount, and for each rule: periodGoal, currentPeriodValue, and consecutive period tracking.",
|
|
342
|
-
readOnly: true,
|
|
343
|
-
inputSchema: AchievementGetMemberProgressInputSchema,
|
|
344
|
-
handler: achievementGetMemberProgress,
|
|
345
|
-
},
|
|
346
|
-
{
|
|
347
|
-
name: "openloyalty_achievement_list_member_achievements",
|
|
348
|
-
title: "List Member Achievements",
|
|
349
|
-
description: "List all achievements with member's progress. Returns each achievement's status, completion count, and per-rule progress. Use for displaying gamification dashboard.",
|
|
350
|
-
readOnly: true,
|
|
351
|
-
inputSchema: AchievementListMemberAchievementsInputSchema,
|
|
352
|
-
handler: achievementListMemberAchievements,
|
|
353
|
-
},
|
|
354
|
-
];
|