@open-loyalty/mcp-server 1.5.3 → 1.7.0
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/config.d.ts +4 -0
- package/dist/config.js +11 -0
- package/dist/index.js +0 -8
- package/dist/server.js +13 -0
- package/dist/tools/achievement/handlers.js +47 -0
- package/dist/tools/achievement/index.d.ts +11 -4
- package/dist/tools/achievement/index.js +12 -1
- package/dist/tools/achievement/schemas.d.ts +4 -4
- package/dist/tools/achievement/schemas.js +13 -12
- package/dist/tools/admin/handlers.d.ts +48 -0
- package/dist/tools/admin/handlers.js +159 -0
- package/dist/tools/admin/index.d.ts +86 -0
- package/dist/tools/admin/index.js +64 -0
- package/dist/tools/admin/schemas.d.ts +40 -0
- package/dist/tools/admin/schemas.js +40 -0
- package/dist/tools/analytics/handlers.d.ts +42 -0
- package/dist/tools/analytics/handlers.js +282 -0
- package/dist/tools/analytics/index.d.ts +108 -0
- package/dist/tools/analytics/index.js +91 -0
- package/dist/tools/analytics/schemas.d.ts +42 -0
- package/dist/tools/analytics/schemas.js +47 -0
- package/dist/tools/apikey/handlers.d.ts +15 -0
- package/dist/tools/apikey/handlers.js +53 -0
- package/dist/tools/apikey/index.d.ts +41 -0
- package/dist/tools/apikey/index.js +38 -0
- package/dist/tools/apikey/schemas.d.ts +31 -0
- package/dist/tools/apikey/schemas.js +15 -0
- package/dist/tools/audit/handlers.d.ts +20 -0
- package/dist/tools/audit/handlers.js +82 -0
- package/dist/tools/audit/index.d.ts +36 -0
- package/dist/tools/audit/index.js +28 -0
- package/dist/tools/audit/schemas.d.ts +62 -0
- package/dist/tools/audit/schemas.js +18 -0
- package/dist/tools/badge/handlers.d.ts +45 -0
- package/dist/tools/badge/handlers.js +135 -0
- package/dist/tools/badge/index.d.ts +68 -0
- package/dist/tools/badge/index.js +47 -0
- package/dist/tools/badge/schemas.d.ts +37 -0
- package/dist/tools/badge/schemas.js +31 -0
- package/dist/tools/campaign/handlers.js +61 -0
- package/dist/tools/campaign/index.d.ts +12 -0
- package/dist/tools/campaign/index.js +20 -1
- package/dist/tools/campaign/member-handlers.js +37 -1
- package/dist/tools/campaign/schemas.js +16 -14
- package/dist/tools/custom-event/handlers.d.ts +98 -0
- package/dist/tools/custom-event/handlers.js +238 -0
- package/dist/tools/custom-event/index.d.ts +139 -0
- package/dist/tools/custom-event/index.js +78 -0
- package/dist/tools/custom-event/schemas.d.ts +87 -0
- package/dist/tools/custom-event/schemas.js +59 -0
- package/dist/tools/export/handlers.d.ts +29 -0
- package/dist/tools/export/handlers.js +128 -0
- package/dist/tools/export/index.d.ts +56 -0
- package/dist/tools/export/index.js +46 -0
- package/dist/tools/export/schemas.d.ts +42 -0
- package/dist/tools/export/schemas.js +41 -0
- package/dist/tools/import/handlers.d.ts +22 -0
- package/dist/tools/import/handlers.js +123 -0
- package/dist/tools/import/index.d.ts +45 -0
- package/dist/tools/import/index.js +41 -0
- package/dist/tools/import/schemas.d.ts +57 -0
- package/dist/tools/import/schemas.js +39 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +11 -11
- package/dist/tools/member/handlers.js +30 -0
- package/dist/tools/member/index.d.ts +10 -0
- package/dist/tools/member/index.js +10 -0
- package/dist/tools/member/schemas.js +13 -13
- package/dist/tools/points/handlers.js +73 -0
- package/dist/tools/points/index.d.ts +6 -0
- package/dist/tools/points/index.js +6 -0
- package/dist/tools/points/schemas.js +1 -1
- package/dist/tools/referral/index.d.ts +3 -0
- package/dist/tools/referral/index.js +3 -0
- package/dist/tools/reward/handlers.js +21 -13
- package/dist/tools/reward/index.d.ts +9 -0
- package/dist/tools/reward/index.js +12 -1
- package/dist/tools/reward/schemas.js +2 -2
- package/dist/tools/role/handlers.d.ts +35 -0
- package/dist/tools/role/handlers.js +127 -0
- package/dist/tools/role/index.d.ts +99 -0
- package/dist/tools/role/index.js +65 -0
- package/dist/tools/role/schemas.d.ts +56 -0
- package/dist/tools/role/schemas.js +35 -0
- package/dist/tools/segment/handlers.js +68 -1
- package/dist/tools/segment/index.d.ts +9 -0
- package/dist/tools/segment/index.js +13 -0
- package/dist/tools/segment/schemas.js +8 -5
- package/dist/tools/store/handlers.d.ts +25 -0
- package/dist/tools/store/handlers.js +89 -0
- package/dist/tools/store/index.d.ts +55 -0
- package/dist/tools/store/index.js +46 -0
- package/dist/tools/store/schemas.d.ts +38 -0
- package/dist/tools/store/schemas.js +23 -0
- package/dist/tools/tierset/handlers.js +92 -1
- package/dist/tools/tierset/index.d.ts +6 -0
- package/dist/tools/tierset/index.js +8 -1
- package/dist/tools/transaction/handlers.js +40 -0
- package/dist/tools/transaction/index.d.ts +4 -0
- package/dist/tools/transaction/index.js +4 -0
- package/dist/tools/transaction/schemas.js +3 -3
- package/dist/tools/wallet-type/index.d.ts +4 -0
- package/dist/tools/wallet-type/index.js +5 -1
- package/dist/tools/webhook/handlers.d.ts +34 -0
- package/dist/tools/webhook/handlers.js +147 -0
- package/dist/tools/webhook/index.d.ts +97 -0
- package/dist/tools/webhook/index.js +65 -0
- package/dist/tools/webhook/schemas.d.ts +72 -0
- package/dist/tools/{webhook.js → webhook/schemas.js} +0 -140
- package/dist/types/schemas/tierset.js +3 -1
- package/package.json +1 -1
- package/dist/tools/admin.d.ts +0 -165
- package/dist/tools/admin.js +0 -205
- package/dist/tools/analytics.d.ts +0 -180
- package/dist/tools/analytics.js +0 -255
- package/dist/tools/apikey.d.ts +0 -79
- package/dist/tools/apikey.js +0 -85
- package/dist/tools/audit.d.ts +0 -111
- package/dist/tools/audit.js +0 -94
- package/dist/tools/badge.d.ts +0 -143
- package/dist/tools/badge.js +0 -174
- package/dist/tools/custom-event.d.ts +0 -315
- package/dist/tools/custom-event.js +0 -271
- package/dist/tools/export.d.ts +0 -118
- package/dist/tools/export.js +0 -152
- package/dist/tools/import.d.ts +0 -116
- package/dist/tools/import.js +0 -143
- package/dist/tools/role.d.ts +0 -180
- package/dist/tools/role.js +0 -173
- package/dist/tools/store.d.ts +0 -109
- package/dist/tools/store.js +0 -125
- package/dist/tools/webhook.d.ts +0 -192
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { BadgeTypeListItem, MemberBadge } from "../../types/schemas/badge.js";
|
|
2
|
+
export declare function badgeList(input: {
|
|
3
|
+
storeCode?: string;
|
|
4
|
+
page?: number;
|
|
5
|
+
perPage?: number;
|
|
6
|
+
name?: string;
|
|
7
|
+
badgeTypeId?: string;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
badges: BadgeTypeListItem[];
|
|
10
|
+
total: {
|
|
11
|
+
all?: number;
|
|
12
|
+
filtered?: number;
|
|
13
|
+
};
|
|
14
|
+
}>;
|
|
15
|
+
export declare function badgeGet(input: {
|
|
16
|
+
storeCode?: string;
|
|
17
|
+
badgeTypeId: string;
|
|
18
|
+
}): Promise<Record<string, unknown>>;
|
|
19
|
+
interface BadgeUpdateInput {
|
|
20
|
+
storeCode?: string;
|
|
21
|
+
badgeTypeId: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
translations?: Record<string, {
|
|
24
|
+
name: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
}>;
|
|
27
|
+
imageUrl?: string;
|
|
28
|
+
active?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function badgeUpdate(input: BadgeUpdateInput): Promise<void>;
|
|
31
|
+
export declare function badgeGetMemberBadges(input: {
|
|
32
|
+
storeCode?: string;
|
|
33
|
+
memberId: string;
|
|
34
|
+
page?: number;
|
|
35
|
+
perPage?: number;
|
|
36
|
+
name?: string;
|
|
37
|
+
badgeTypeId?: string;
|
|
38
|
+
}): Promise<{
|
|
39
|
+
badges: MemberBadge[];
|
|
40
|
+
total: {
|
|
41
|
+
all?: number;
|
|
42
|
+
filtered?: number;
|
|
43
|
+
};
|
|
44
|
+
}>;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { apiGet, apiPut } from "../../client/http.js";
|
|
2
|
+
import { formatApiError, OpenLoyaltyError } from "../../utils/errors.js";
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
import { getStoreCode } from "../../config.js";
|
|
5
|
+
export async function badgeList(input) {
|
|
6
|
+
const storeCode = getStoreCode(input.storeCode);
|
|
7
|
+
const params = new URLSearchParams();
|
|
8
|
+
if (input.page)
|
|
9
|
+
params.append("_page", String(input.page));
|
|
10
|
+
if (input.perPage)
|
|
11
|
+
params.append("_itemsOnPage", String(input.perPage));
|
|
12
|
+
if (input.name)
|
|
13
|
+
params.append("name", input.name);
|
|
14
|
+
if (input.badgeTypeId)
|
|
15
|
+
params.append("badgeTypeId", input.badgeTypeId);
|
|
16
|
+
const queryString = params.toString();
|
|
17
|
+
const url = `/${storeCode}/badge-type${queryString ? `?${queryString}` : ""}`;
|
|
18
|
+
try {
|
|
19
|
+
const response = await apiGet(url);
|
|
20
|
+
const badges = (response.items || []).map((item) => ({
|
|
21
|
+
badgeTypeId: item.badgeTypeId,
|
|
22
|
+
name: item.name,
|
|
23
|
+
createdAt: item.createdAt,
|
|
24
|
+
updatedAt: item.updatedAt,
|
|
25
|
+
}));
|
|
26
|
+
const total = response.total || {};
|
|
27
|
+
return {
|
|
28
|
+
badges,
|
|
29
|
+
total: {
|
|
30
|
+
all: typeof total.all === "number" ? total.all : undefined,
|
|
31
|
+
filtered: typeof total.filtered === "number" ? total.filtered : undefined,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
|
37
|
+
throw new OpenLoyaltyError({
|
|
38
|
+
code: "STORE_NOT_FOUND",
|
|
39
|
+
message: "Cannot list badges - store not found",
|
|
40
|
+
hint: "Use ol_store_list() to verify the store code is correct.",
|
|
41
|
+
relatedTool: "ol_badge_list",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (axios.isAxiosError(error) && error.response?.status === 403) {
|
|
45
|
+
throw new OpenLoyaltyError({
|
|
46
|
+
code: "BADGE_PERMISSION_DENIED",
|
|
47
|
+
message: "You don't have permission to view badges",
|
|
48
|
+
hint: "Badge access requires the ACHIEVEMENT:VIEW permission. Use ol_admin_get_permissions() to check your access.",
|
|
49
|
+
relatedTool: "ol_badge_list",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
throw formatApiError(error, "ol_badge_list");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function badgeGet(input) {
|
|
56
|
+
const storeCode = getStoreCode(input.storeCode);
|
|
57
|
+
try {
|
|
58
|
+
const response = await apiGet(`/${storeCode}/badge-type/${input.badgeTypeId}`);
|
|
59
|
+
return response;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
|
63
|
+
throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Badge type '${input.badgeTypeId}' not found`,
|
|
64
|
+
hint: "Use ol_badge_list() to find existing badge types and their IDs.", relatedTool: "ol_badge_get" });
|
|
65
|
+
}
|
|
66
|
+
throw formatApiError(error, "ol_badge_get");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export async function badgeUpdate(input) {
|
|
70
|
+
const storeCode = getStoreCode(input.storeCode);
|
|
71
|
+
const badgePayload = {};
|
|
72
|
+
// API requires name as direct field, not nested in translations
|
|
73
|
+
// Extract from translations.en.name if name not provided directly
|
|
74
|
+
let badgeName = input.name;
|
|
75
|
+
if (!badgeName && input.translations?.en?.name) {
|
|
76
|
+
badgeName = input.translations.en.name;
|
|
77
|
+
}
|
|
78
|
+
if (badgeName !== undefined)
|
|
79
|
+
badgePayload.name = badgeName;
|
|
80
|
+
// Note: API does not accept nested translations - only direct fields
|
|
81
|
+
if (input.imageUrl !== undefined)
|
|
82
|
+
badgePayload.imageUrl = input.imageUrl;
|
|
83
|
+
if (input.active !== undefined)
|
|
84
|
+
badgePayload.active = input.active;
|
|
85
|
+
try {
|
|
86
|
+
// CRITICAL: Wrap body as { badgeType: {...} }
|
|
87
|
+
await apiPut(`/${storeCode}/badge-type/${input.badgeTypeId}`, { badgeType: badgePayload });
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
|
91
|
+
throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Badge type '${input.badgeTypeId}' not found`,
|
|
92
|
+
hint: "Use ol_badge_list() to find existing badge types and their IDs.", relatedTool: "ol_badge_update" });
|
|
93
|
+
}
|
|
94
|
+
throw formatApiError(error, "ol_badge_update");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export async function badgeGetMemberBadges(input) {
|
|
98
|
+
const storeCode = getStoreCode(input.storeCode);
|
|
99
|
+
const params = new URLSearchParams();
|
|
100
|
+
if (input.page)
|
|
101
|
+
params.append("_page", String(input.page));
|
|
102
|
+
if (input.perPage)
|
|
103
|
+
params.append("_itemsOnPage", String(input.perPage));
|
|
104
|
+
if (input.name)
|
|
105
|
+
params.append("name", input.name);
|
|
106
|
+
if (input.badgeTypeId)
|
|
107
|
+
params.append("badgeTypeId", input.badgeTypeId);
|
|
108
|
+
const queryString = params.toString();
|
|
109
|
+
const url = `/${storeCode}/member/${input.memberId}/badge${queryString ? `?${queryString}` : ""}`;
|
|
110
|
+
try {
|
|
111
|
+
const response = await apiGet(url);
|
|
112
|
+
const badges = (response.items || []).map((item) => ({
|
|
113
|
+
badgeTypeId: item.badgeTypeId,
|
|
114
|
+
name: item.name,
|
|
115
|
+
completedCount: item.completedCount,
|
|
116
|
+
createdAt: item.createdAt,
|
|
117
|
+
updatedAt: item.updatedAt,
|
|
118
|
+
}));
|
|
119
|
+
const total = response.total || {};
|
|
120
|
+
return {
|
|
121
|
+
badges,
|
|
122
|
+
total: {
|
|
123
|
+
all: typeof total.all === "number" ? total.all : undefined,
|
|
124
|
+
filtered: typeof total.filtered === "number" ? total.filtered : undefined,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
|
130
|
+
throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Member '${input.memberId}' not found`,
|
|
131
|
+
hint: "Use ol_member_list() to search for the member by email, name, or phone.", relatedTool: "ol_badge_get_member_badges" });
|
|
132
|
+
}
|
|
133
|
+
throw formatApiError(error, "ol_badge_get_member_badges");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export { BadgeListInputSchema, BadgeGetInputSchema, BadgeUpdateInputSchema, BadgeGetMemberBadgesInputSchema, } from "./schemas.js";
|
|
2
|
+
export { badgeList, badgeGet, badgeUpdate, badgeGetMemberBadges, } from "./handlers.js";
|
|
3
|
+
import { badgeList, badgeGet, badgeUpdate, badgeGetMemberBadges } from "./handlers.js";
|
|
4
|
+
export declare const badgeToolDefinitions: readonly [{
|
|
5
|
+
readonly name: "ol_badge_list";
|
|
6
|
+
readonly title: "List Badges";
|
|
7
|
+
readonly description: "List badge types. Badges are visual rewards linked to achievements. When a member completes an achievement with a badgeTypeId, they earn that badge. Use for displaying available badges.";
|
|
8
|
+
readonly readOnly: true;
|
|
9
|
+
readonly idempotent: true;
|
|
10
|
+
readonly inputSchema: {
|
|
11
|
+
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
12
|
+
page: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
13
|
+
perPage: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
14
|
+
name: import("zod").ZodOptional<import("zod").ZodString>;
|
|
15
|
+
badgeTypeId: import("zod").ZodOptional<import("zod").ZodString>;
|
|
16
|
+
};
|
|
17
|
+
readonly handler: typeof badgeList;
|
|
18
|
+
}, {
|
|
19
|
+
readonly name: "ol_badge_get";
|
|
20
|
+
readonly title: "Get Badge Details";
|
|
21
|
+
readonly description: "Get badge type details including name, image URL, and linked achievements count.";
|
|
22
|
+
readonly readOnly: true;
|
|
23
|
+
readonly idempotent: true;
|
|
24
|
+
readonly inputSchema: {
|
|
25
|
+
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
26
|
+
badgeTypeId: import("zod").ZodString;
|
|
27
|
+
};
|
|
28
|
+
readonly handler: typeof badgeGet;
|
|
29
|
+
}, {
|
|
30
|
+
readonly name: "ol_badge_update";
|
|
31
|
+
readonly title: "Update Badge";
|
|
32
|
+
readonly description: string;
|
|
33
|
+
readonly readOnly: false;
|
|
34
|
+
readonly idempotent: true;
|
|
35
|
+
readonly inputSchema: {
|
|
36
|
+
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
37
|
+
badgeTypeId: import("zod").ZodString;
|
|
38
|
+
name: import("zod").ZodString;
|
|
39
|
+
translations: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
|
|
40
|
+
name: import("zod").ZodString;
|
|
41
|
+
description: import("zod").ZodOptional<import("zod").ZodString>;
|
|
42
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
43
|
+
name: string;
|
|
44
|
+
description?: string | undefined;
|
|
45
|
+
}, {
|
|
46
|
+
name: string;
|
|
47
|
+
description?: string | undefined;
|
|
48
|
+
}>>>;
|
|
49
|
+
imageUrl: import("zod").ZodOptional<import("zod").ZodString>;
|
|
50
|
+
active: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
51
|
+
};
|
|
52
|
+
readonly handler: typeof badgeUpdate;
|
|
53
|
+
}, {
|
|
54
|
+
readonly name: "ol_badge_get_member_badges";
|
|
55
|
+
readonly title: "Get Member Badges";
|
|
56
|
+
readonly description: "Get badges earned by a member. Returns each badge with completedCount showing how many times it was earned. Use for displaying member's badge collection.";
|
|
57
|
+
readonly readOnly: true;
|
|
58
|
+
readonly idempotent: true;
|
|
59
|
+
readonly inputSchema: {
|
|
60
|
+
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
61
|
+
memberId: import("zod").ZodString;
|
|
62
|
+
page: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
63
|
+
perPage: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
64
|
+
name: import("zod").ZodOptional<import("zod").ZodString>;
|
|
65
|
+
badgeTypeId: import("zod").ZodOptional<import("zod").ZodString>;
|
|
66
|
+
};
|
|
67
|
+
readonly handler: typeof badgeGetMemberBadges;
|
|
68
|
+
}];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Re-export schemas
|
|
2
|
+
export { BadgeListInputSchema, BadgeGetInputSchema, BadgeUpdateInputSchema, BadgeGetMemberBadgesInputSchema, } from "./schemas.js";
|
|
3
|
+
// Re-export handlers
|
|
4
|
+
export { badgeList, badgeGet, badgeUpdate, badgeGetMemberBadges, } from "./handlers.js";
|
|
5
|
+
// Imports for tool definitions
|
|
6
|
+
import { BadgeListInputSchema, BadgeGetInputSchema, BadgeUpdateInputSchema, BadgeGetMemberBadgesInputSchema, } from "./schemas.js";
|
|
7
|
+
import { badgeList, badgeGet, badgeUpdate, badgeGetMemberBadges, } from "./handlers.js";
|
|
8
|
+
// Tool definitions
|
|
9
|
+
export const badgeToolDefinitions = [
|
|
10
|
+
{
|
|
11
|
+
name: "ol_badge_list",
|
|
12
|
+
title: "List Badges",
|
|
13
|
+
description: "List badge types. Badges are visual rewards linked to achievements. When a member completes an achievement with a badgeTypeId, they earn that badge. Use for displaying available badges.",
|
|
14
|
+
readOnly: true,
|
|
15
|
+
idempotent: true,
|
|
16
|
+
inputSchema: BadgeListInputSchema,
|
|
17
|
+
handler: badgeList,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "ol_badge_get",
|
|
21
|
+
title: "Get Badge Details",
|
|
22
|
+
description: "Get badge type details including name, image URL, and linked achievements count.",
|
|
23
|
+
readOnly: true,
|
|
24
|
+
idempotent: true,
|
|
25
|
+
inputSchema: BadgeGetInputSchema,
|
|
26
|
+
handler: badgeGet,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "ol_badge_update",
|
|
30
|
+
title: "Update Badge",
|
|
31
|
+
description: "Update badge type configuration. Badge types are created automatically when referenced by achievements. " +
|
|
32
|
+
"FIELD FORMAT: Pass 'name' as a direct top-level string, NOT inside translations. The API requires { name: 'Badge Name' }, NOT { translations: { en: { name: '...' } } }.",
|
|
33
|
+
readOnly: false,
|
|
34
|
+
idempotent: true,
|
|
35
|
+
inputSchema: BadgeUpdateInputSchema,
|
|
36
|
+
handler: badgeUpdate,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "ol_badge_get_member_badges",
|
|
40
|
+
title: "Get Member Badges",
|
|
41
|
+
description: "Get badges earned by a member. Returns each badge with completedCount showing how many times it was earned. Use for displaying member's badge collection.",
|
|
42
|
+
readOnly: true,
|
|
43
|
+
idempotent: true,
|
|
44
|
+
inputSchema: BadgeGetMemberBadgesInputSchema,
|
|
45
|
+
handler: badgeGetMemberBadges,
|
|
46
|
+
},
|
|
47
|
+
];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const BadgeListInputSchema: {
|
|
3
|
+
storeCode: z.ZodOptional<z.ZodString>;
|
|
4
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
5
|
+
perPage: z.ZodOptional<z.ZodNumber>;
|
|
6
|
+
name: z.ZodOptional<z.ZodString>;
|
|
7
|
+
badgeTypeId: z.ZodOptional<z.ZodString>;
|
|
8
|
+
};
|
|
9
|
+
export declare const BadgeGetInputSchema: {
|
|
10
|
+
storeCode: z.ZodOptional<z.ZodString>;
|
|
11
|
+
badgeTypeId: z.ZodString;
|
|
12
|
+
};
|
|
13
|
+
export declare const BadgeUpdateInputSchema: {
|
|
14
|
+
storeCode: z.ZodOptional<z.ZodString>;
|
|
15
|
+
badgeTypeId: z.ZodString;
|
|
16
|
+
name: z.ZodString;
|
|
17
|
+
translations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
18
|
+
name: z.ZodString;
|
|
19
|
+
description: z.ZodOptional<z.ZodString>;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
name: string;
|
|
22
|
+
description?: string | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
name: string;
|
|
25
|
+
description?: string | undefined;
|
|
26
|
+
}>>>;
|
|
27
|
+
imageUrl: z.ZodOptional<z.ZodString>;
|
|
28
|
+
active: z.ZodOptional<z.ZodBoolean>;
|
|
29
|
+
};
|
|
30
|
+
export declare const BadgeGetMemberBadgesInputSchema: {
|
|
31
|
+
storeCode: z.ZodOptional<z.ZodString>;
|
|
32
|
+
memberId: z.ZodString;
|
|
33
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
34
|
+
perPage: z.ZodOptional<z.ZodNumber>;
|
|
35
|
+
name: z.ZodOptional<z.ZodString>;
|
|
36
|
+
badgeTypeId: z.ZodOptional<z.ZodString>;
|
|
37
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const BadgeListInputSchema = {
|
|
3
|
+
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."),
|
|
4
|
+
page: z.number().optional().describe("Page number (default: 1)."),
|
|
5
|
+
perPage: z.number().optional().describe("Items per page (default: 10)."),
|
|
6
|
+
name: z.string().optional().describe("Filter by badge name."),
|
|
7
|
+
badgeTypeId: z.string().optional().describe("Filter by specific badge type ID."),
|
|
8
|
+
};
|
|
9
|
+
export const BadgeGetInputSchema = {
|
|
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
|
+
badgeTypeId: z.string().describe("The badge type ID (UUID) to retrieve."),
|
|
12
|
+
};
|
|
13
|
+
export const BadgeUpdateInputSchema = {
|
|
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
|
+
badgeTypeId: z.string().describe("The badge type ID (UUID) to update."),
|
|
16
|
+
name: z.string().describe("Badge name (REQUIRED). API requires name as a direct field."),
|
|
17
|
+
translations: z.record(z.string(), z.object({
|
|
18
|
+
name: z.string(),
|
|
19
|
+
description: z.string().optional(),
|
|
20
|
+
})).optional().describe("Badge translations (for convenience - will extract name from translations.en.name if name not provided directly)."),
|
|
21
|
+
imageUrl: z.string().optional().describe("URL to badge image."),
|
|
22
|
+
active: z.boolean().optional().describe("Whether badge type is active."),
|
|
23
|
+
};
|
|
24
|
+
export const BadgeGetMemberBadgesInputSchema = {
|
|
25
|
+
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."),
|
|
26
|
+
memberId: z.string().describe("The member ID (UUID)."),
|
|
27
|
+
page: z.number().optional().describe("Page number (default: 1)."),
|
|
28
|
+
perPage: z.number().optional().describe("Items per page (default: 10)."),
|
|
29
|
+
name: z.string().optional().describe("Filter by badge name."),
|
|
30
|
+
badgeTypeId: z.string().optional().describe("Filter by specific badge type ID."),
|
|
31
|
+
};
|
|
@@ -62,6 +62,15 @@ export async function campaignGet(input) {
|
|
|
62
62
|
return response;
|
|
63
63
|
}
|
|
64
64
|
catch (error) {
|
|
65
|
+
const axiosError = error;
|
|
66
|
+
if (axiosError.response?.status === 404) {
|
|
67
|
+
throw new OpenLoyaltyError({
|
|
68
|
+
code: "NOT_FOUND",
|
|
69
|
+
message: `Campaign '${input.campaignId}' not found`,
|
|
70
|
+
hint: "Use ol_campaign_list() to find existing campaigns and their IDs.",
|
|
71
|
+
relatedTool: "ol_campaign_get",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
65
74
|
throw formatApiError(error, "ol_campaign_get");
|
|
66
75
|
}
|
|
67
76
|
}
|
|
@@ -140,6 +149,23 @@ export async function campaignUpdate(input) {
|
|
|
140
149
|
await apiPut(`/${storeCode}/campaign/${campaignId}`, { campaign: campaignPayload });
|
|
141
150
|
}
|
|
142
151
|
catch (error) {
|
|
152
|
+
const axiosError = error;
|
|
153
|
+
if (axiosError.response?.status === 404) {
|
|
154
|
+
throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Campaign '${campaignId}' not found`,
|
|
155
|
+
hint: "Use ol_campaign_list() to find existing campaigns and their IDs.", relatedTool: "ol_campaign_update" });
|
|
156
|
+
}
|
|
157
|
+
const apiErrors = axiosError.response?.data?.errors || [];
|
|
158
|
+
const allMessages = [error instanceof Error ? error.message : "", axiosError.response?.data?.message || "",
|
|
159
|
+
...apiErrors.map(e => `${e.path || ""}: ${e.message}`)].join(" ").toLowerCase();
|
|
160
|
+
if (allMessages.includes("trigger") && allMessages.includes("choice")) {
|
|
161
|
+
throw new OpenLoyaltyError({ code: "INVALID_TRIGGER", message: "Invalid campaign trigger type",
|
|
162
|
+
hint: "Valid trigger values: 'transaction', 'custom_event', 'geolocation', 'time', 'achievement'.", relatedTool: "ol_campaign_update" });
|
|
163
|
+
}
|
|
164
|
+
if (allMessages.includes("operator") || (allMessages.includes("condition") && allMessages.includes("choice"))) {
|
|
165
|
+
throw new OpenLoyaltyError({ code: "INVALID_CONDITION", message: "Invalid rule condition in campaign",
|
|
166
|
+
hint: "Use full operator names: 'is_greater_or_equal' (NOT 'gte'). Use full attribute paths: 'transaction.grossValue' (NOT 'grossValue').",
|
|
167
|
+
relatedTool: "ol_campaign_update" });
|
|
168
|
+
}
|
|
143
169
|
throw formatApiError(error, "ol_campaign_update");
|
|
144
170
|
}
|
|
145
171
|
}
|
|
@@ -154,6 +180,11 @@ export async function campaignPatch(input) {
|
|
|
154
180
|
await apiPatch(`/${storeCode}/campaign/${input.campaignId}`, { campaign: patchPayload });
|
|
155
181
|
}
|
|
156
182
|
catch (error) {
|
|
183
|
+
const axiosError = error;
|
|
184
|
+
if (axiosError.response?.status === 404) {
|
|
185
|
+
throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Campaign '${input.campaignId}' not found`,
|
|
186
|
+
hint: "Use ol_campaign_list() to find existing campaigns and their IDs.", relatedTool: "ol_campaign_patch" });
|
|
187
|
+
}
|
|
157
188
|
throw formatApiError(error, "ol_campaign_patch");
|
|
158
189
|
}
|
|
159
190
|
}
|
|
@@ -163,6 +194,11 @@ export async function campaignDelete(input) {
|
|
|
163
194
|
await apiDelete(`/${storeCode}/campaign/${input.campaignId}`);
|
|
164
195
|
}
|
|
165
196
|
catch (error) {
|
|
197
|
+
const axiosError = error;
|
|
198
|
+
if (axiosError.response?.status === 404) {
|
|
199
|
+
throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Campaign '${input.campaignId}' not found`,
|
|
200
|
+
hint: "Use ol_campaign_list() to find existing campaigns and their IDs.", relatedTool: "ol_campaign_delete" });
|
|
201
|
+
}
|
|
166
202
|
throw formatApiError(error, "ol_campaign_delete");
|
|
167
203
|
}
|
|
168
204
|
}
|
|
@@ -252,6 +288,31 @@ export async function campaignSimulate(input) {
|
|
|
252
288
|
};
|
|
253
289
|
}
|
|
254
290
|
catch (error) {
|
|
291
|
+
const axiosError = error;
|
|
292
|
+
const apiErrors = axiosError.response?.data?.errors || [];
|
|
293
|
+
const allMessages = [
|
|
294
|
+
error instanceof Error ? error.message : "",
|
|
295
|
+
axiosError.response?.data?.message || "",
|
|
296
|
+
...apiErrors.map(e => `${e.path || ""}: ${e.message}`)
|
|
297
|
+
].join(" ").toLowerCase();
|
|
298
|
+
if (allMessages.includes("transaction") && (allMessages.includes("required") || allMessages.includes("missing") || allMessages.includes("should not be blank"))) {
|
|
299
|
+
throw new OpenLoyaltyError({
|
|
300
|
+
code: "MISSING_TRANSACTION_DATA",
|
|
301
|
+
message: "Transaction data is required for simulation with transaction trigger",
|
|
302
|
+
hint: "For trigger='transaction', provide transaction data: { grossValue: 100, documentNumber: 'SIM-001', " +
|
|
303
|
+
"purchasedAt: '2026-01-01T10:00:00Z', items: [{ sku: 'SKU-1', name: 'Product', grossValue: 100, qty: 1 }] }. " +
|
|
304
|
+
"Also provide customer: { customerId: 'member-uuid' } to identify the member.",
|
|
305
|
+
relatedTool: "ol_campaign_simulate",
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
if (allMessages.includes("customer") && (allMessages.includes("not found") || allMessages.includes("required"))) {
|
|
309
|
+
throw new OpenLoyaltyError({
|
|
310
|
+
code: "MEMBER_NOT_FOUND",
|
|
311
|
+
message: "The specified member was not found for simulation",
|
|
312
|
+
hint: "Provide a valid member ID in customer.customerId. Use ol_member_list() to find members.",
|
|
313
|
+
relatedTool: "ol_campaign_simulate",
|
|
314
|
+
});
|
|
315
|
+
}
|
|
255
316
|
throw formatApiError(error, "ol_campaign_simulate");
|
|
256
317
|
}
|
|
257
318
|
}
|
|
@@ -9,6 +9,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
9
9
|
readonly title: "List Campaigns";
|
|
10
10
|
readonly description: "List all campaigns. Filter by type (direct/referral) or trigger (transaction/custom_event/time/etc). Use campaign_get for full configuration including rules and effects.";
|
|
11
11
|
readonly readOnly: true;
|
|
12
|
+
readonly idempotent: true;
|
|
12
13
|
readonly inputSchema: {
|
|
13
14
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
14
15
|
page: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
@@ -23,6 +24,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
23
24
|
readonly title: "Create Campaign";
|
|
24
25
|
readonly description: string;
|
|
25
26
|
readonly readOnly: false;
|
|
27
|
+
readonly idempotent: false;
|
|
26
28
|
readonly inputSchema: {
|
|
27
29
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
28
30
|
type: import("zod").ZodEnum<["direct", "referral"]>;
|
|
@@ -313,6 +315,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
313
315
|
readonly title: "Get Campaign Details";
|
|
314
316
|
readonly description: "Get full campaign configuration including all rules, conditions, effects, and targeting.";
|
|
315
317
|
readonly readOnly: true;
|
|
318
|
+
readonly idempotent: true;
|
|
316
319
|
readonly inputSchema: {
|
|
317
320
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
318
321
|
campaignId: import("zod").ZodString;
|
|
@@ -323,6 +326,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
323
326
|
readonly title: "Update Campaign";
|
|
324
327
|
readonly description: "Full update of campaign configuration. Requires all campaign fields. Use campaign_get first to retrieve current configuration, modify it, then send the complete object.";
|
|
325
328
|
readonly readOnly: false;
|
|
329
|
+
readonly idempotent: true;
|
|
326
330
|
readonly inputSchema: {
|
|
327
331
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
328
332
|
campaignId: import("zod").ZodString;
|
|
@@ -603,6 +607,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
603
607
|
readonly title: "Patch Campaign";
|
|
604
608
|
readonly description: "Partial update of campaign - only active status and displayOrder can be patched. For full updates, use campaign_update.";
|
|
605
609
|
readonly readOnly: false;
|
|
610
|
+
readonly idempotent: true;
|
|
606
611
|
readonly inputSchema: {
|
|
607
612
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
608
613
|
campaignId: import("zod").ZodString;
|
|
@@ -616,6 +621,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
616
621
|
readonly description: "Permanently delete a campaign. This cannot be undone. Consider deactivating instead if you may need the campaign later.";
|
|
617
622
|
readonly readOnly: false;
|
|
618
623
|
readonly destructive: true;
|
|
624
|
+
readonly idempotent: true;
|
|
619
625
|
readonly inputSchema: {
|
|
620
626
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
621
627
|
campaignId: import("zod").ZodString;
|
|
@@ -624,6 +630,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
624
630
|
}, {
|
|
625
631
|
readonly name: "ol_campaign_simulate";
|
|
626
632
|
readonly title: "Simulate Campaign Effects";
|
|
633
|
+
readonly idempotent: true;
|
|
627
634
|
readonly description: string;
|
|
628
635
|
readonly readOnly: true;
|
|
629
636
|
readonly inputSchema: {
|
|
@@ -785,6 +792,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
785
792
|
readonly title: "Generate Campaign Codes";
|
|
786
793
|
readonly description: "Generate unique redemption codes for a campaign. Use for promotions requiring unique codes. Codes are auto-generated and can be retrieved with campaign_list_codes.";
|
|
787
794
|
readonly readOnly: false;
|
|
795
|
+
readonly idempotent: false;
|
|
788
796
|
readonly inputSchema: {
|
|
789
797
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
790
798
|
campaignId: import("zod").ZodString;
|
|
@@ -796,6 +804,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
796
804
|
readonly title: "List Campaign Codes";
|
|
797
805
|
readonly description: "List redemption codes for a campaign. Filter by status (active/used) or specific code. Returns code details including usage status.";
|
|
798
806
|
readonly readOnly: true;
|
|
807
|
+
readonly idempotent: true;
|
|
799
808
|
readonly inputSchema: {
|
|
800
809
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
801
810
|
campaignId: import("zod").ZodString;
|
|
@@ -810,6 +819,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
810
819
|
readonly title: "Get Available Campaigns for Member";
|
|
811
820
|
readonly description: "Get campaigns available to a specific member. Returns campaigns the member can participate in based on their tier, segments, and campaign targeting rules. Includes limitReached indicator.";
|
|
812
821
|
readonly readOnly: true;
|
|
822
|
+
readonly idempotent: true;
|
|
813
823
|
readonly inputSchema: {
|
|
814
824
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
815
825
|
memberId: import("zod").ZodString;
|
|
@@ -824,6 +834,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
824
834
|
readonly title: "Get Visible Campaigns for Member";
|
|
825
835
|
readonly description: "Get campaigns visible to a specific member. May include campaigns not yet available (e.g., upcoming campaigns or those requiring certain conditions). Use get_available for campaigns the member can currently participate in.";
|
|
826
836
|
readonly readOnly: true;
|
|
837
|
+
readonly idempotent: true;
|
|
827
838
|
readonly inputSchema: {
|
|
828
839
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
829
840
|
memberId: import("zod").ZodString;
|
|
@@ -837,6 +848,7 @@ export declare const campaignToolDefinitions: readonly [{
|
|
|
837
848
|
readonly title: "Get Campaign Leaderboard";
|
|
838
849
|
readonly description: "Get leaderboard rankings for a campaign. Returns ranked list of members with scores and positions. Use for leaderboard-type campaigns to show competition standings.";
|
|
839
850
|
readonly readOnly: true;
|
|
851
|
+
readonly idempotent: true;
|
|
840
852
|
readonly inputSchema: {
|
|
841
853
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
842
854
|
campaignId: import("zod").ZodString;
|
|
@@ -14,6 +14,7 @@ export const campaignToolDefinitions = [
|
|
|
14
14
|
title: "List Campaigns",
|
|
15
15
|
description: "List all campaigns. Filter by type (direct/referral) or trigger (transaction/custom_event/time/etc). Use campaign_get for full configuration including rules and effects.",
|
|
16
16
|
readOnly: true,
|
|
17
|
+
idempotent: true,
|
|
17
18
|
inputSchema: CampaignListInputSchema,
|
|
18
19
|
handler: campaignList,
|
|
19
20
|
},
|
|
@@ -27,8 +28,16 @@ export const campaignToolDefinitions = [
|
|
|
27
28
|
"3. Audience -- all members, or specific tiers/segments? " +
|
|
28
29
|
"4. End date -- campaigns without endsAt run forever, which is a budget commitment. " +
|
|
29
30
|
"REQUIRED: type, trigger, translations.en.name, activity.startsAt, rules with effects. " +
|
|
30
|
-
"
|
|
31
|
+
"For trigger='custom_event': ALSO set event='your_event_code' (must match existing schema from custom_event_schema_list). " +
|
|
32
|
+
"EFFECT KEY: Use 'effect' NOT 'type' in effects array: { effect: 'give_points', pointsRule: '100' }. pointsRule MUST be a STRING: '100' not 100. " +
|
|
33
|
+
"RULE CONDITIONS EXAMPLES: " +
|
|
34
|
+
"{ conditions: [{ operator: 'is_greater_or_equal', attribute: 'transaction.grossValue', data: 100 }] }. " +
|
|
35
|
+
"For labels: { operator: 'has_at_least_one_label', attribute: 'transaction.labels', data: [{key: 'category', value: 'electronics'}] }. " +
|
|
36
|
+
"For between: { operator: 'is_between', attribute: 'transaction.grossValue', data: {from: 50, to: 200} }. " +
|
|
37
|
+
"AUDIENCE: OMIT entirely for all members. Do NOT pass audience: { target: 'all' } -- the API rejects 'all'. Only use audience for tier/segment targeting. " +
|
|
38
|
+
"VISIBILITY vs AUDIENCE: visibility = who can see the campaign. audience = who can participate. Both accept target='tier' or 'segment'. OMIT audience for all members.",
|
|
31
39
|
readOnly: false,
|
|
40
|
+
idempotent: false,
|
|
32
41
|
inputSchema: CampaignCreateInputSchema,
|
|
33
42
|
handler: campaignCreate,
|
|
34
43
|
},
|
|
@@ -37,6 +46,7 @@ export const campaignToolDefinitions = [
|
|
|
37
46
|
title: "Get Campaign Details",
|
|
38
47
|
description: "Get full campaign configuration including all rules, conditions, effects, and targeting.",
|
|
39
48
|
readOnly: true,
|
|
49
|
+
idempotent: true,
|
|
40
50
|
inputSchema: CampaignGetInputSchema,
|
|
41
51
|
handler: campaignGet,
|
|
42
52
|
},
|
|
@@ -45,6 +55,7 @@ export const campaignToolDefinitions = [
|
|
|
45
55
|
title: "Update Campaign",
|
|
46
56
|
description: "Full update of campaign configuration. Requires all campaign fields. Use campaign_get first to retrieve current configuration, modify it, then send the complete object.",
|
|
47
57
|
readOnly: false,
|
|
58
|
+
idempotent: true,
|
|
48
59
|
inputSchema: CampaignUpdateInputSchema,
|
|
49
60
|
handler: campaignUpdate,
|
|
50
61
|
},
|
|
@@ -53,6 +64,7 @@ export const campaignToolDefinitions = [
|
|
|
53
64
|
title: "Patch Campaign",
|
|
54
65
|
description: "Partial update of campaign - only active status and displayOrder can be patched. For full updates, use campaign_update.",
|
|
55
66
|
readOnly: false,
|
|
67
|
+
idempotent: true,
|
|
56
68
|
inputSchema: CampaignPatchInputSchema,
|
|
57
69
|
handler: campaignPatch,
|
|
58
70
|
},
|
|
@@ -62,12 +74,14 @@ export const campaignToolDefinitions = [
|
|
|
62
74
|
description: "Permanently delete a campaign. This cannot be undone. Consider deactivating instead if you may need the campaign later.",
|
|
63
75
|
readOnly: false,
|
|
64
76
|
destructive: true,
|
|
77
|
+
idempotent: true,
|
|
65
78
|
inputSchema: CampaignDeleteInputSchema,
|
|
66
79
|
handler: campaignDelete,
|
|
67
80
|
},
|
|
68
81
|
{
|
|
69
82
|
name: "ol_campaign_simulate",
|
|
70
83
|
title: "Simulate Campaign Effects",
|
|
84
|
+
idempotent: true,
|
|
71
85
|
description: "Simulate campaign effects without executing them. Use to preview what points/rewards a transaction would earn. " +
|
|
72
86
|
"Example for transaction trigger: { trigger: 'transaction', " +
|
|
73
87
|
"transaction: { grossValue: 100, documentNumber: 'SIM-001', purchasedAt: '2026-01-01T10:00:00Z', " +
|
|
@@ -83,6 +97,7 @@ export const campaignToolDefinitions = [
|
|
|
83
97
|
title: "Generate Campaign Codes",
|
|
84
98
|
description: "Generate unique redemption codes for a campaign. Use for promotions requiring unique codes. Codes are auto-generated and can be retrieved with campaign_list_codes.",
|
|
85
99
|
readOnly: false,
|
|
100
|
+
idempotent: false,
|
|
86
101
|
inputSchema: CampaignGenerateCodesInputSchema,
|
|
87
102
|
handler: campaignGenerateCodes,
|
|
88
103
|
},
|
|
@@ -91,6 +106,7 @@ export const campaignToolDefinitions = [
|
|
|
91
106
|
title: "List Campaign Codes",
|
|
92
107
|
description: "List redemption codes for a campaign. Filter by status (active/used) or specific code. Returns code details including usage status.",
|
|
93
108
|
readOnly: true,
|
|
109
|
+
idempotent: true,
|
|
94
110
|
inputSchema: CampaignListCodesInputSchema,
|
|
95
111
|
handler: campaignListCodes,
|
|
96
112
|
},
|
|
@@ -99,6 +115,7 @@ export const campaignToolDefinitions = [
|
|
|
99
115
|
title: "Get Available Campaigns for Member",
|
|
100
116
|
description: "Get campaigns available to a specific member. Returns campaigns the member can participate in based on their tier, segments, and campaign targeting rules. Includes limitReached indicator.",
|
|
101
117
|
readOnly: true,
|
|
118
|
+
idempotent: true,
|
|
102
119
|
inputSchema: CampaignGetAvailableInputSchema,
|
|
103
120
|
handler: campaignGetAvailable,
|
|
104
121
|
},
|
|
@@ -107,6 +124,7 @@ export const campaignToolDefinitions = [
|
|
|
107
124
|
title: "Get Visible Campaigns for Member",
|
|
108
125
|
description: "Get campaigns visible to a specific member. May include campaigns not yet available (e.g., upcoming campaigns or those requiring certain conditions). Use get_available for campaigns the member can currently participate in.",
|
|
109
126
|
readOnly: true,
|
|
127
|
+
idempotent: true,
|
|
110
128
|
inputSchema: CampaignGetVisibleInputSchema,
|
|
111
129
|
handler: campaignGetVisible,
|
|
112
130
|
},
|
|
@@ -115,6 +133,7 @@ export const campaignToolDefinitions = [
|
|
|
115
133
|
title: "Get Campaign Leaderboard",
|
|
116
134
|
description: "Get leaderboard rankings for a campaign. Returns ranked list of members with scores and positions. Use for leaderboard-type campaigns to show competition standings.",
|
|
117
135
|
readOnly: true,
|
|
136
|
+
idempotent: true,
|
|
118
137
|
inputSchema: CampaignGetLeaderboardInputSchema,
|
|
119
138
|
handler: campaignGetLeaderboard,
|
|
120
139
|
},
|