@open-loyalty/mcp-server 1.3.7 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +4 -3
- package/dist/config.js +9 -7
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +26 -8
- package/dist/tools/achievement/handlers.js +60 -1
- package/dist/tools/achievement/index.d.ts +8 -8
- package/dist/tools/achievement/index.js +10 -11
- package/dist/tools/achievement/schemas.d.ts +8 -8
- package/dist/tools/achievement/schemas.js +36 -32
- package/dist/tools/analytics.js +9 -9
- package/dist/tools/badge.js +4 -4
- package/dist/tools/campaign/handlers.js +35 -1
- package/dist/tools/campaign/index.d.ts +97 -34
- package/dist/tools/campaign/index.js +14 -12
- package/dist/tools/campaign/schemas.d.ts +96 -33
- package/dist/tools/campaign/schemas.js +89 -55
- package/dist/tools/custom-event.js +10 -9
- package/dist/tools/export.js +4 -4
- package/dist/tools/import.d.ts +1 -1
- package/dist/tools/import.js +11 -6
- package/dist/tools/index.js +4 -4
- package/dist/tools/member/handlers.js +48 -1
- package/dist/tools/member/index.d.ts +1 -1
- package/dist/tools/member/index.js +3 -1
- package/dist/tools/member/schemas.js +12 -12
- package/dist/tools/points/handlers.d.ts +75 -0
- package/dist/tools/{points.js → points/handlers.js} +21 -112
- package/dist/tools/points/index.d.ts +84 -0
- package/dist/tools/points/index.js +63 -0
- package/dist/tools/points/schemas.d.ts +45 -0
- package/dist/tools/points/schemas.js +47 -0
- package/dist/tools/referral/schemas.js +3 -3
- package/dist/tools/reward/handlers.d.ts +2 -0
- package/dist/tools/reward/handlers.js +79 -6
- package/dist/tools/reward/index.d.ts +2 -0
- package/dist/tools/reward/index.js +10 -10
- package/dist/tools/reward/schemas.d.ts +2 -0
- package/dist/tools/reward/schemas.js +42 -23
- package/dist/tools/segment/index.js +7 -5
- package/dist/tools/segment/schemas.js +11 -11
- package/dist/tools/tierset/handlers.d.ts +26 -0
- package/dist/tools/tierset/handlers.js +196 -0
- package/dist/tools/tierset/index.d.ts +124 -0
- package/dist/tools/tierset/index.js +88 -0
- package/dist/tools/tierset/schemas.d.ts +127 -0
- package/dist/tools/tierset/schemas.js +62 -0
- package/dist/tools/transaction/handlers.d.ts +89 -0
- package/dist/tools/transaction/handlers.js +159 -0
- package/dist/tools/transaction/index.d.ts +153 -0
- package/dist/tools/transaction/index.js +54 -0
- package/dist/tools/transaction/schemas.d.ts +126 -0
- package/dist/tools/transaction/schemas.js +60 -0
- package/dist/tools/wallet-type/handlers.d.ts +63 -0
- package/dist/tools/{wallet-type.js → wallet-type/handlers.js} +15 -78
- package/dist/tools/{wallet-type.d.ts → wallet-type/index.d.ts} +3 -64
- package/dist/tools/wallet-type/index.js +65 -0
- package/dist/tools/wallet-type/schemas.d.ts +1 -0
- package/dist/tools/wallet-type/schemas.js +1 -0
- package/dist/tools/webhook.js +6 -6
- package/dist/types/schemas/achievement.d.ts +48 -48
- package/dist/types/schemas/admin.d.ts +10 -10
- package/dist/types/schemas/campaign.d.ts +12 -12
- package/dist/types/schemas/common.js +1 -1
- package/dist/types/schemas/member.d.ts +18 -18
- package/dist/types/schemas/role.d.ts +4 -4
- package/dist/types/schemas/tierset.js +2 -1
- package/dist/types/schemas/transaction.d.ts +12 -12
- package/dist/types/schemas/wallet-type.js +12 -10
- package/dist/types/schemas/webhook.d.ts +6 -6
- package/dist/utils/errors.js +40 -0
- package/package.json +3 -2
- 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/points.d.ts +0 -201
- 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/tools/tierset.d.ts +0 -273
- package/dist/tools/tierset.js +0 -289
- package/dist/tools/transaction.d.ts +0 -365
- package/dist/tools/transaction.js +0 -259
- 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
package/dist/tools/export.js
CHANGED
|
@@ -5,7 +5,7 @@ import { getConfig, getStoreCode } from "../config.js";
|
|
|
5
5
|
import axios from "axios";
|
|
6
6
|
// Input Schemas
|
|
7
7
|
export const ExportCreateInputSchema = {
|
|
8
|
-
storeCode: z.string().optional().describe("
|
|
8
|
+
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."),
|
|
9
9
|
type: z.enum([
|
|
10
10
|
"campaignCode",
|
|
11
11
|
"member",
|
|
@@ -18,7 +18,7 @@ export const ExportCreateInputSchema = {
|
|
|
18
18
|
perPage: z.number().optional().describe("Items per page for export."),
|
|
19
19
|
};
|
|
20
20
|
export const ExportListInputSchema = {
|
|
21
|
-
storeCode: z.string().optional().describe("
|
|
21
|
+
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."),
|
|
22
22
|
page: z.number().optional().describe("Page number (default: 1)."),
|
|
23
23
|
perPage: z.number().optional().describe("Items per page (default: 25)."),
|
|
24
24
|
exportId: z.string().optional().describe("Filter by export ID."),
|
|
@@ -32,11 +32,11 @@ export const ExportListInputSchema = {
|
|
|
32
32
|
status: z.enum(["pending", "done", "failed", "error"]).optional().describe("Filter by export status."),
|
|
33
33
|
};
|
|
34
34
|
export const ExportGetInputSchema = {
|
|
35
|
-
storeCode: z.string().optional().describe("
|
|
35
|
+
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."),
|
|
36
36
|
exportId: z.string().describe("The export ID (UUID) to retrieve."),
|
|
37
37
|
};
|
|
38
38
|
export const ExportDownloadInputSchema = {
|
|
39
|
-
storeCode: z.string().optional().describe("
|
|
39
|
+
storeCode: z.string().optional().describe("INTERNAL: Auto-configured from server settings. NEVER ask the user for this value. Only set if the user explicitly requests a different store."),
|
|
40
40
|
exportId: z.string().describe("The export ID (UUID) to download. Export must have status 'done'."),
|
|
41
41
|
};
|
|
42
42
|
// Handler functions
|
package/dist/tools/import.d.ts
CHANGED
|
@@ -79,7 +79,7 @@ export declare function importGet(input: {
|
|
|
79
79
|
export declare const importToolDefinitions: readonly [{
|
|
80
80
|
readonly name: "ol_import_create";
|
|
81
81
|
readonly title: "Create Bulk Import";
|
|
82
|
-
readonly description:
|
|
82
|
+
readonly description: string;
|
|
83
83
|
readonly readOnly: false;
|
|
84
84
|
readonly inputSchema: {
|
|
85
85
|
storeCode: z.ZodOptional<z.ZodString>;
|
package/dist/tools/import.js
CHANGED
|
@@ -6,7 +6,7 @@ import axios from "axios";
|
|
|
6
6
|
import FormDataNode from "form-data";
|
|
7
7
|
// Input Schemas
|
|
8
8
|
export const ImportCreateInputSchema = {
|
|
9
|
-
storeCode: z.string().optional().describe("
|
|
9
|
+
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."),
|
|
10
10
|
type: z.enum([
|
|
11
11
|
"member",
|
|
12
12
|
"groupValue",
|
|
@@ -17,11 +17,12 @@ export const ImportCreateInputSchema = {
|
|
|
17
17
|
"campaignCode",
|
|
18
18
|
"rewardCoupon",
|
|
19
19
|
]).describe("Import type. Options: member, groupValue, segmentMembers, unitTransferAdding, unitTransferSpending, transaction, campaignCode, rewardCoupon."),
|
|
20
|
-
fileContent: z.string().describe("
|
|
21
|
-
|
|
20
|
+
fileContent: z.string().describe("File content as plain text (not base64 encoded). Accepts CSV (comma-separated rows) or XML format. " +
|
|
21
|
+
"For member imports, XML is the standard format. Auto-detected based on content or fileName extension."),
|
|
22
|
+
fileName: z.string().optional().describe("Optional file name for the import (default: import.xml). Use .xml extension for XML content, .csv for CSV content."),
|
|
22
23
|
};
|
|
23
24
|
export const ImportListInputSchema = {
|
|
24
|
-
storeCode: z.string().optional().describe("
|
|
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."),
|
|
25
26
|
page: z.number().optional().describe("Page number (default: 1)."),
|
|
26
27
|
perPage: z.number().optional().describe("Items per page (default: 25)."),
|
|
27
28
|
importId: z.string().optional().describe("Filter by import ID."),
|
|
@@ -37,7 +38,7 @@ export const ImportListInputSchema = {
|
|
|
37
38
|
]).optional().describe("Filter by import type."),
|
|
38
39
|
};
|
|
39
40
|
export const ImportGetInputSchema = {
|
|
40
|
-
storeCode: z.string().optional().describe("
|
|
41
|
+
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."),
|
|
41
42
|
importId: z.string().describe("The import ID (UUID) to retrieve."),
|
|
42
43
|
page: z.number().optional().describe("Page number for import items (default: 1)."),
|
|
43
44
|
perPage: z.number().optional().describe("Items per page for import items (default: 25)."),
|
|
@@ -114,7 +115,11 @@ export const importToolDefinitions = [
|
|
|
114
115
|
{
|
|
115
116
|
name: "ol_import_create",
|
|
116
117
|
title: "Create Bulk Import",
|
|
117
|
-
description: "Create a new bulk import from CSV
|
|
118
|
+
description: "Create a new bulk import from file content (CSV or XML). Returns importId for async tracking. " +
|
|
119
|
+
"Import types: member (member data - typically XML), groupValue (group values), segmentMembers (segment membership), " +
|
|
120
|
+
"unitTransferAdding (add points), unitTransferSpending (spend points), transaction (transactions), " +
|
|
121
|
+
"campaignCode (campaign codes), rewardCoupon (reward coupons). " +
|
|
122
|
+
"Provide plain text file content (CSV or XML). Format is auto-detected from content or fileName extension.",
|
|
118
123
|
readOnly: false,
|
|
119
124
|
inputSchema: ImportCreateInputSchema,
|
|
120
125
|
handler: importCreate,
|
package/dist/tools/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { walletTypeToolDefinitions } from "./wallet-type.js";
|
|
2
|
-
import { tiersetToolDefinitions } from "./tierset.js";
|
|
1
|
+
import { walletTypeToolDefinitions } from "./wallet-type/index.js";
|
|
2
|
+
import { tiersetToolDefinitions } from "./tierset/index.js";
|
|
3
3
|
import { memberToolDefinitions } from "./member/index.js";
|
|
4
|
-
import { pointsToolDefinitions } from "./points.js";
|
|
4
|
+
import { pointsToolDefinitions } from "./points/index.js";
|
|
5
5
|
import { rewardToolDefinitions } from "./reward/index.js";
|
|
6
|
-
import { transactionToolDefinitions } from "./transaction.js";
|
|
6
|
+
import { transactionToolDefinitions } from "./transaction/index.js";
|
|
7
7
|
// Phase 2 tools
|
|
8
8
|
import { campaignToolDefinitions } from "./campaign/index.js";
|
|
9
9
|
import { segmentToolDefinitions } from "./segment/index.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { apiGet, apiPost, apiPut, apiDelete } from "../../client/http.js";
|
|
2
2
|
import { MemberSchema } from "../../types/schemas/member.js";
|
|
3
|
-
import { formatApiError } from "../../utils/errors.js";
|
|
3
|
+
import { formatApiError, OpenLoyaltyError } from "../../utils/errors.js";
|
|
4
4
|
import { getStoreCode } from "../../config.js";
|
|
5
5
|
import { buildPaginationParams } from "../../utils/pagination.js";
|
|
6
6
|
import { omitUndefined } from "../../utils/payload.js";
|
|
@@ -28,6 +28,21 @@ export async function memberCreate(input) {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
catch (error) {
|
|
31
|
+
const axiosError = error;
|
|
32
|
+
const apiErrors = axiosError.response?.data?.errors || [];
|
|
33
|
+
const allMessages = [
|
|
34
|
+
error instanceof Error ? error.message : "",
|
|
35
|
+
axiosError.response?.data?.message || "",
|
|
36
|
+
...apiErrors.map(e => e.message)
|
|
37
|
+
].join(" ").toLowerCase();
|
|
38
|
+
if (allMessages.includes("email") && (allMessages.includes("already") || allMessages.includes("unique") || allMessages.includes("exists"))) {
|
|
39
|
+
throw new OpenLoyaltyError({
|
|
40
|
+
code: "DUPLICATE_EMAIL",
|
|
41
|
+
message: `Member with email '${input.email}' already exists`,
|
|
42
|
+
hint: `Use ol_member_list(email: "${input.email}") to find the existing member. Each email must be unique per store.`,
|
|
43
|
+
relatedTool: "ol_member_create",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
31
46
|
throw formatApiError(error, "ol_member_create");
|
|
32
47
|
}
|
|
33
48
|
}
|
|
@@ -59,6 +74,15 @@ export async function memberGet(input) {
|
|
|
59
74
|
return MemberSchema.parse(mapped);
|
|
60
75
|
}
|
|
61
76
|
catch (error) {
|
|
77
|
+
const axiosError = error;
|
|
78
|
+
if (axiosError.response?.status === 404) {
|
|
79
|
+
throw new OpenLoyaltyError({
|
|
80
|
+
code: "NOT_FOUND",
|
|
81
|
+
message: `Member '${input.memberId}' not found`,
|
|
82
|
+
hint: `Use ol_member_list() to search for the member by email, name, or phone. The member ID may have changed or the member may have been deleted.`,
|
|
83
|
+
relatedTool: "ol_member_get",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
62
86
|
throw formatApiError(error, "ol_member_get");
|
|
63
87
|
}
|
|
64
88
|
}
|
|
@@ -191,6 +215,29 @@ export async function memberAssignTier(input) {
|
|
|
191
215
|
await apiPost(`/${storeCode}/member/${input.memberId}/tier`, { levelId: input.levelId });
|
|
192
216
|
}
|
|
193
217
|
catch (error) {
|
|
218
|
+
const axiosError = error;
|
|
219
|
+
const apiErrors = axiosError.response?.data?.errors || [];
|
|
220
|
+
const allMessages = [
|
|
221
|
+
error instanceof Error ? error.message : "",
|
|
222
|
+
axiosError.response?.data?.message || "",
|
|
223
|
+
...apiErrors.map(e => e.message)
|
|
224
|
+
].join(" ").toLowerCase();
|
|
225
|
+
if (allMessages.includes("level") && (allMessages.includes("not found") || allMessages.includes("invalid") || allMessages.includes("does not exist"))) {
|
|
226
|
+
throw new OpenLoyaltyError({
|
|
227
|
+
code: "INVALID_LEVEL",
|
|
228
|
+
message: `Level ID '${input.levelId}' is not valid`,
|
|
229
|
+
hint: `Use ol_tierset_get_tiers(tierSetId: "...") to find valid levelId values. The levelId must be a UUID from an existing tier in an active tier set.`,
|
|
230
|
+
relatedTool: "ol_member_assign_tier",
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (axiosError.response?.status === 404) {
|
|
234
|
+
throw new OpenLoyaltyError({
|
|
235
|
+
code: "NOT_FOUND",
|
|
236
|
+
message: `Member '${input.memberId}' not found`,
|
|
237
|
+
hint: `Use ol_member_list() to search for the member by email, name, or phone. Verify the member ID is correct.`,
|
|
238
|
+
relatedTool: "ol_member_assign_tier",
|
|
239
|
+
});
|
|
240
|
+
}
|
|
194
241
|
throw formatApiError(error, "ol_member_assign_tier");
|
|
195
242
|
}
|
|
196
243
|
}
|
|
@@ -4,7 +4,7 @@ import { memberCreate, memberGet, memberList, memberUpdate, memberActivate, memb
|
|
|
4
4
|
export declare const memberToolDefinitions: readonly [{
|
|
5
5
|
readonly name: "ol_member_create";
|
|
6
6
|
readonly title: "Register New Member";
|
|
7
|
-
readonly description:
|
|
7
|
+
readonly description: string;
|
|
8
8
|
readonly readOnly: false;
|
|
9
9
|
readonly inputSchema: {
|
|
10
10
|
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
@@ -10,7 +10,9 @@ export const memberToolDefinitions = [
|
|
|
10
10
|
{
|
|
11
11
|
name: "ol_member_create",
|
|
12
12
|
title: "Register New Member",
|
|
13
|
-
description: "Register a new loyalty program member. Returns memberId for subsequent operations like points_add or member_get.
|
|
13
|
+
description: "Register a new loyalty program member. Returns memberId for subsequent operations like points_add or member_get. " +
|
|
14
|
+
"REQUIRED: email (must be unique). OPTIONAL: firstName, lastName, phone, birthDate (YYYY-MM-DD), gender, loyaltyCardNumber. " +
|
|
15
|
+
"NOTE: agreement1 (terms) and agreement2 (marketing consent) are consent decisions -- never default these to true without explicit user approval.",
|
|
14
16
|
readOnly: false,
|
|
15
17
|
inputSchema: MemberCreateInputSchema,
|
|
16
18
|
handler: memberCreate,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export const MemberCreateInputSchema = {
|
|
3
|
-
storeCode: z.string().optional().describe("
|
|
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
4
|
email: z.string().describe("Member email address (required, must be unique)."),
|
|
5
5
|
firstName: z.string().optional().describe("Member first name."),
|
|
6
6
|
lastName: z.string().optional().describe("Member last name."),
|
|
@@ -8,9 +8,9 @@ export const MemberCreateInputSchema = {
|
|
|
8
8
|
birthDate: z.string().optional().describe("Member birth date (YYYY-MM-DD format)."),
|
|
9
9
|
gender: z.enum(["male", "female", "not_disclosed"]).optional().describe("Member gender."),
|
|
10
10
|
loyaltyCardNumber: z.string().optional().describe("Loyalty card number. Auto-generated if not provided."),
|
|
11
|
-
agreement1: z.boolean().optional().describe("Terms acceptance."),
|
|
12
|
-
agreement2: z.boolean().optional().describe("Marketing consent."),
|
|
13
|
-
agreement3: z.boolean().optional().describe("Additional agreement."),
|
|
11
|
+
agreement1: z.boolean().optional().describe("Terms acceptance. BUSINESS DECISION: Legal requirement. Cannot default to true without explicit consent from the member."),
|
|
12
|
+
agreement2: z.boolean().optional().describe("Marketing consent. BUSINESS IMPACT: GDPR violation risk. Defaulting marketing consent to true without explicit approval can result in fines up to 4% of annual revenue or EUR 20M. NEVER silently default to true."),
|
|
13
|
+
agreement3: z.boolean().optional().describe("Additional agreement. Depends on store configuration."),
|
|
14
14
|
address: z.object({
|
|
15
15
|
street: z.string().optional(),
|
|
16
16
|
city: z.string().optional(),
|
|
@@ -20,11 +20,11 @@ export const MemberCreateInputSchema = {
|
|
|
20
20
|
}).optional().describe("Member address."),
|
|
21
21
|
};
|
|
22
22
|
export const MemberGetInputSchema = {
|
|
23
|
-
storeCode: z.string().optional().describe("
|
|
23
|
+
storeCode: z.string().optional().describe("INTERNAL: Auto-configured from server settings. NEVER ask the user for this value. Only set if the user explicitly requests a different store."),
|
|
24
24
|
memberId: z.string().describe("The member ID (UUID) to retrieve."),
|
|
25
25
|
};
|
|
26
26
|
export const MemberListInputSchema = {
|
|
27
|
-
storeCode: z.string().optional().describe("
|
|
27
|
+
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."),
|
|
28
28
|
cursor: z.string().optional().describe("Pagination cursor from previous response. If provided, page/perPage are ignored."),
|
|
29
29
|
page: z.number().optional().describe("Page number (default: 1)."),
|
|
30
30
|
perPage: z.number().optional().describe("Items per page (default: 10)."),
|
|
@@ -36,7 +36,7 @@ export const MemberListInputSchema = {
|
|
|
36
36
|
active: z.boolean().optional().describe("Filter by active status."),
|
|
37
37
|
};
|
|
38
38
|
export const MemberUpdateInputSchema = {
|
|
39
|
-
storeCode: z.string().optional().describe("
|
|
39
|
+
storeCode: z.string().optional().describe("INTERNAL: Auto-configured from server settings. NEVER ask the user for this value. Only set if the user explicitly requests a different store."),
|
|
40
40
|
memberId: z.string().describe("The member ID (UUID) to update."),
|
|
41
41
|
firstName: z.string().optional().describe("Member first name."),
|
|
42
42
|
lastName: z.string().optional().describe("Member last name."),
|
|
@@ -50,16 +50,16 @@ export const MemberUpdateInputSchema = {
|
|
|
50
50
|
country: z.string().optional(),
|
|
51
51
|
province: z.string().optional(),
|
|
52
52
|
}).optional().describe("Member address."),
|
|
53
|
-
agreement1: z.boolean().optional().describe("Terms acceptance."),
|
|
54
|
-
agreement2: z.boolean().optional().describe("Marketing consent."),
|
|
55
|
-
agreement3: z.boolean().optional().describe("Additional agreement."),
|
|
53
|
+
agreement1: z.boolean().optional().describe("Terms acceptance. BUSINESS DECISION: Legal requirement. Cannot default to true without explicit consent."),
|
|
54
|
+
agreement2: z.boolean().optional().describe("Marketing consent. BUSINESS IMPACT: GDPR violation risk. Changing marketing consent requires explicit member approval."),
|
|
55
|
+
agreement3: z.boolean().optional().describe("Additional agreement. Depends on store configuration."),
|
|
56
56
|
};
|
|
57
57
|
export const MemberIdInputSchema = {
|
|
58
|
-
storeCode: z.string().optional().describe("
|
|
58
|
+
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."),
|
|
59
59
|
memberId: z.string().describe("The member ID (UUID)."),
|
|
60
60
|
};
|
|
61
61
|
export const MemberAssignTierInputSchema = {
|
|
62
|
-
storeCode: z.string().optional().describe("
|
|
62
|
+
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."),
|
|
63
63
|
memberId: z.string().describe("The member ID (UUID)."),
|
|
64
64
|
levelId: z.string().describe("The tier level ID (UUID) to assign."),
|
|
65
65
|
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import "../../types/schemas/points.js";
|
|
2
|
+
export declare function pointsAdd(input: {
|
|
3
|
+
storeCode?: string;
|
|
4
|
+
memberId: string;
|
|
5
|
+
points: number;
|
|
6
|
+
walletCode?: string;
|
|
7
|
+
comment?: string;
|
|
8
|
+
expiresInDays?: number;
|
|
9
|
+
lockedUntilDays?: number;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
transferId: string;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function pointsSpend(input: {
|
|
14
|
+
storeCode?: string;
|
|
15
|
+
memberId: string;
|
|
16
|
+
points: number;
|
|
17
|
+
walletCode?: string;
|
|
18
|
+
comment?: string;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
transferId: string;
|
|
21
|
+
}>;
|
|
22
|
+
export declare function pointsTransfer(input: {
|
|
23
|
+
storeCode?: string;
|
|
24
|
+
senderId: string;
|
|
25
|
+
receiverId: string;
|
|
26
|
+
points: number;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
transferId: string;
|
|
29
|
+
}>;
|
|
30
|
+
export declare function pointsGetBalance(input: {
|
|
31
|
+
storeCode?: string;
|
|
32
|
+
memberId: string;
|
|
33
|
+
walletCode?: string;
|
|
34
|
+
}): Promise<{
|
|
35
|
+
activeUnits: number;
|
|
36
|
+
earnedUnits: number;
|
|
37
|
+
spentUnits: number;
|
|
38
|
+
lockedUnits: number;
|
|
39
|
+
expiredUnits: number;
|
|
40
|
+
}>;
|
|
41
|
+
export declare function pointsGetHistory(input: {
|
|
42
|
+
storeCode?: string;
|
|
43
|
+
memberId: string;
|
|
44
|
+
cursor?: string;
|
|
45
|
+
page?: number;
|
|
46
|
+
perPage?: number;
|
|
47
|
+
type?: string;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
transfers: Array<{
|
|
50
|
+
transferId: string;
|
|
51
|
+
type: string;
|
|
52
|
+
points: number;
|
|
53
|
+
comment?: string;
|
|
54
|
+
createdAt: string;
|
|
55
|
+
}>;
|
|
56
|
+
total: {
|
|
57
|
+
all?: number;
|
|
58
|
+
filtered?: number;
|
|
59
|
+
};
|
|
60
|
+
cursor?: string;
|
|
61
|
+
}>;
|
|
62
|
+
export declare function pointsGetHistogram(input: {
|
|
63
|
+
storeCode?: string;
|
|
64
|
+
memberId: string;
|
|
65
|
+
pointType: string;
|
|
66
|
+
walletCode?: string;
|
|
67
|
+
interval?: string;
|
|
68
|
+
dateFrom?: string;
|
|
69
|
+
dateTo?: string;
|
|
70
|
+
}): Promise<Array<{
|
|
71
|
+
date: string;
|
|
72
|
+
earned: number;
|
|
73
|
+
spent: number;
|
|
74
|
+
balance: number;
|
|
75
|
+
}>>;
|
|
@@ -1,56 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import "
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { omitUndefined } from "../utils/payload.js";
|
|
8
|
-
// Input Schemas
|
|
9
|
-
export const PointsAddInputSchema = {
|
|
10
|
-
storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
|
|
11
|
-
memberId: z.string().describe("The member ID (UUID) to add points to."),
|
|
12
|
-
points: z.number().describe("Number of points to add (positive number)."),
|
|
13
|
-
walletCode: z.string().optional().describe("Wallet type code (e.g., 'default'). Uses default wallet if not specified."),
|
|
14
|
-
comment: z.string().optional().describe("Reason for adding points (max 500 chars)."),
|
|
15
|
-
expiresInDays: z.number().optional().describe("Days until points expire (1-9999)."),
|
|
16
|
-
lockedUntilDays: z.number().optional().describe("Days until points become available (1-9999)."),
|
|
17
|
-
};
|
|
18
|
-
export const PointsSpendInputSchema = {
|
|
19
|
-
storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
|
|
20
|
-
memberId: z.string().describe("The member ID (UUID) to spend points from."),
|
|
21
|
-
points: z.number().describe("Number of points to spend (positive number)."),
|
|
22
|
-
walletCode: z.string().optional().describe("Wallet type code (e.g., 'default'). Uses default wallet if not specified."),
|
|
23
|
-
comment: z.string().optional().describe("Reason for spending points (max 500 chars)."),
|
|
24
|
-
};
|
|
25
|
-
export const PointsTransferInputSchema = {
|
|
26
|
-
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."),
|
|
27
|
-
senderId: z.string().describe("The sender member ID (UUID)."),
|
|
28
|
-
receiverId: z.string().describe("The receiver member ID (UUID)."),
|
|
29
|
-
points: z.number().describe("Number of points to transfer."),
|
|
30
|
-
};
|
|
31
|
-
export const PointsBalanceInputSchema = {
|
|
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."),
|
|
33
|
-
memberId: z.string().describe("The member ID (UUID) to get balance for."),
|
|
34
|
-
walletCode: z.string().optional().describe("Wallet type code. Returns all wallets if not specified."),
|
|
35
|
-
};
|
|
36
|
-
export const PointsHistoryInputSchema = {
|
|
37
|
-
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."),
|
|
38
|
-
memberId: z.string().describe("The member ID (UUID) to get history for."),
|
|
39
|
-
cursor: z.string().optional().describe("Pagination cursor from previous response. If provided, page/perPage are ignored."),
|
|
40
|
-
page: z.number().optional().describe("Page number (default: 1)."),
|
|
41
|
-
perPage: z.number().optional().describe("Items per page (default: 10)."),
|
|
42
|
-
type: z.enum(["adding", "spending", "p2p_spending", "p2p_adding", "blocked", "expired"]).optional().describe("Filter by transfer type."),
|
|
43
|
-
};
|
|
44
|
-
export const PointsHistogramInputSchema = {
|
|
45
|
-
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."),
|
|
46
|
-
memberId: z.string().describe("The member ID (UUID) to get histogram for."),
|
|
47
|
-
pointType: z.enum(["spent", "earned", "expired", "pending"]).describe("Filter by point type (required). One of: spent, earned, expired, pending."),
|
|
48
|
-
walletCode: z.string().optional().describe("Wallet type code. Uses default if not specified."),
|
|
49
|
-
interval: z.enum(["day", "week", "month"]).optional().describe("Time interval for aggregation (default: month)."),
|
|
50
|
-
dateFrom: z.string().optional().describe("Start date for histogram (ISO format: YYYY-MM-DD)."),
|
|
51
|
-
dateTo: z.string().optional().describe("End date for histogram (ISO format: YYYY-MM-DD)."),
|
|
52
|
-
};
|
|
53
|
-
// Handler functions
|
|
1
|
+
import { apiGet, apiPost } from "../../client/http.js";
|
|
2
|
+
import "../../types/schemas/points.js";
|
|
3
|
+
import { formatApiError, OpenLoyaltyError } from "../../utils/errors.js";
|
|
4
|
+
import { getStoreCode } from "../../config.js";
|
|
5
|
+
import { buildPaginationParams } from "../../utils/pagination.js";
|
|
6
|
+
import { omitUndefined } from "../../utils/payload.js";
|
|
54
7
|
export async function pointsAdd(input) {
|
|
55
8
|
const storeCode = getStoreCode(input.storeCode);
|
|
56
9
|
const payload = omitUndefined({
|
|
@@ -65,6 +18,15 @@ export async function pointsAdd(input) {
|
|
|
65
18
|
return { transferId: response.transferId };
|
|
66
19
|
}
|
|
67
20
|
catch (error) {
|
|
21
|
+
const axiosError = error;
|
|
22
|
+
if (axiosError.response?.status === 404) {
|
|
23
|
+
throw new OpenLoyaltyError({
|
|
24
|
+
code: "MEMBER_NOT_FOUND",
|
|
25
|
+
message: `Member '${input.memberId}' not found`,
|
|
26
|
+
hint: `Verify the member ID exists using ol_member_get(memberId: "${input.memberId}"). If not found, use ol_member_list(email: "...") to search by email.`,
|
|
27
|
+
relatedTool: "ol_points_add",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
68
30
|
throw formatApiError(error, "ol_points_add");
|
|
69
31
|
}
|
|
70
32
|
}
|
|
@@ -104,7 +66,12 @@ export async function pointsSpend(input) {
|
|
|
104
66
|
export async function pointsTransfer(input) {
|
|
105
67
|
const storeCode = getStoreCode(input.storeCode);
|
|
106
68
|
if (input.senderId === input.receiverId) {
|
|
107
|
-
throw new
|
|
69
|
+
throw new OpenLoyaltyError({
|
|
70
|
+
code: "SELF_TRANSFER",
|
|
71
|
+
message: "Cannot transfer points to yourself. senderId and receiverId must be different.",
|
|
72
|
+
hint: `The senderId and receiverId are both "${input.senderId}". Provide two different member IDs. Use ol_member_list() to find the correct receiver.`,
|
|
73
|
+
relatedTool: "ol_points_transfer",
|
|
74
|
+
});
|
|
108
75
|
}
|
|
109
76
|
const payload = {
|
|
110
77
|
transfer: {
|
|
@@ -264,61 +231,3 @@ export async function pointsGetHistogram(input) {
|
|
|
264
231
|
throw formatApiError(error, "ol_points_get_histogram");
|
|
265
232
|
}
|
|
266
233
|
}
|
|
267
|
-
// Tool definitions
|
|
268
|
-
export const pointsToolDefinitions = [
|
|
269
|
-
{
|
|
270
|
-
name: "ol_points_add",
|
|
271
|
-
title: "Add Points to Member",
|
|
272
|
-
description: "Add points to member wallet. Points can have optional expiration and lock period. " +
|
|
273
|
-
"Use for welcome bonuses, manual adjustments, or custom rewards. Returns transferId.",
|
|
274
|
-
readOnly: false,
|
|
275
|
-
inputSchema: PointsAddInputSchema,
|
|
276
|
-
handler: pointsAdd,
|
|
277
|
-
},
|
|
278
|
-
{
|
|
279
|
-
name: "ol_points_spend",
|
|
280
|
-
title: "Spend Member Points",
|
|
281
|
-
description: "Deduct points from member wallet. Fails if insufficient balance. " +
|
|
282
|
-
"Use points_get_balance first to verify available points. Returns transferId.",
|
|
283
|
-
readOnly: false,
|
|
284
|
-
inputSchema: PointsSpendInputSchema,
|
|
285
|
-
handler: pointsSpend,
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
name: "ol_points_transfer",
|
|
289
|
-
title: "Transfer Points Between Members",
|
|
290
|
-
description: "Transfer points from one member to another (P2P transfer). " +
|
|
291
|
-
"Sender must have sufficient balance. Returns transferId.",
|
|
292
|
-
readOnly: false,
|
|
293
|
-
inputSchema: PointsTransferInputSchema,
|
|
294
|
-
handler: pointsTransfer,
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
name: "ol_points_get_balance",
|
|
298
|
-
title: "Check Points Balance",
|
|
299
|
-
description: "Get member points balance breakdown. activeUnits is available for spending. " +
|
|
300
|
-
"earnedUnits shows lifetime earnings, lockedUnits shows pending points.",
|
|
301
|
-
readOnly: true,
|
|
302
|
-
inputSchema: PointsBalanceInputSchema,
|
|
303
|
-
handler: pointsGetBalance,
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
name: "ol_points_get_history",
|
|
307
|
-
title: "View Points History",
|
|
308
|
-
description: "Get points transaction history for a member. Filter by type: adding, spending, " +
|
|
309
|
-
"p2p_spending, p2p_adding, blocked, expired. Returns paginated list of transfers. " +
|
|
310
|
-
"Supports cursor pagination: provide 'cursor' from previous response to get next page.",
|
|
311
|
-
readOnly: true,
|
|
312
|
-
inputSchema: PointsHistoryInputSchema,
|
|
313
|
-
handler: pointsGetHistory,
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
name: "ol_points_get_histogram",
|
|
317
|
-
title: "View Points Trends",
|
|
318
|
-
description: "Get points histogram data for visualization. Shows earning and spending patterns over time. " +
|
|
319
|
-
"Use interval (day/week/month) to aggregate data and dateFrom/dateTo to filter range.",
|
|
320
|
-
readOnly: true,
|
|
321
|
-
inputSchema: PointsHistogramInputSchema,
|
|
322
|
-
handler: pointsGetHistogram,
|
|
323
|
-
},
|
|
324
|
-
];
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export { PointsAddInputSchema, PointsSpendInputSchema, PointsTransferInputSchema, PointsBalanceInputSchema, PointsHistoryInputSchema, PointsHistogramInputSchema, } from "./schemas.js";
|
|
2
|
+
export { pointsAdd, pointsSpend, pointsTransfer, pointsGetBalance, pointsGetHistory, pointsGetHistogram, } from "./handlers.js";
|
|
3
|
+
import { pointsAdd, pointsSpend, pointsTransfer, pointsGetBalance, pointsGetHistory, pointsGetHistogram } from "./handlers.js";
|
|
4
|
+
export declare const pointsToolDefinitions: readonly [{
|
|
5
|
+
readonly name: "ol_points_add";
|
|
6
|
+
readonly title: "Add Points to Member";
|
|
7
|
+
readonly description: string;
|
|
8
|
+
readonly readOnly: false;
|
|
9
|
+
readonly inputSchema: {
|
|
10
|
+
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
11
|
+
memberId: import("zod").ZodString;
|
|
12
|
+
points: import("zod").ZodNumber;
|
|
13
|
+
walletCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
14
|
+
comment: import("zod").ZodOptional<import("zod").ZodString>;
|
|
15
|
+
expiresInDays: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
16
|
+
lockedUntilDays: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
17
|
+
};
|
|
18
|
+
readonly handler: typeof pointsAdd;
|
|
19
|
+
}, {
|
|
20
|
+
readonly name: "ol_points_spend";
|
|
21
|
+
readonly title: "Spend Member Points";
|
|
22
|
+
readonly description: string;
|
|
23
|
+
readonly readOnly: false;
|
|
24
|
+
readonly inputSchema: {
|
|
25
|
+
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
26
|
+
memberId: import("zod").ZodString;
|
|
27
|
+
points: import("zod").ZodNumber;
|
|
28
|
+
walletCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
29
|
+
comment: import("zod").ZodOptional<import("zod").ZodString>;
|
|
30
|
+
};
|
|
31
|
+
readonly handler: typeof pointsSpend;
|
|
32
|
+
}, {
|
|
33
|
+
readonly name: "ol_points_transfer";
|
|
34
|
+
readonly title: "Transfer Points Between Members";
|
|
35
|
+
readonly description: string;
|
|
36
|
+
readonly readOnly: false;
|
|
37
|
+
readonly inputSchema: {
|
|
38
|
+
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
39
|
+
senderId: import("zod").ZodString;
|
|
40
|
+
receiverId: import("zod").ZodString;
|
|
41
|
+
points: import("zod").ZodNumber;
|
|
42
|
+
};
|
|
43
|
+
readonly handler: typeof pointsTransfer;
|
|
44
|
+
}, {
|
|
45
|
+
readonly name: "ol_points_get_balance";
|
|
46
|
+
readonly title: "Check Points Balance";
|
|
47
|
+
readonly description: string;
|
|
48
|
+
readonly readOnly: true;
|
|
49
|
+
readonly inputSchema: {
|
|
50
|
+
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
51
|
+
memberId: import("zod").ZodString;
|
|
52
|
+
walletCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
53
|
+
};
|
|
54
|
+
readonly handler: typeof pointsGetBalance;
|
|
55
|
+
}, {
|
|
56
|
+
readonly name: "ol_points_get_history";
|
|
57
|
+
readonly title: "View Points History";
|
|
58
|
+
readonly description: string;
|
|
59
|
+
readonly readOnly: true;
|
|
60
|
+
readonly inputSchema: {
|
|
61
|
+
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
62
|
+
memberId: import("zod").ZodString;
|
|
63
|
+
cursor: import("zod").ZodOptional<import("zod").ZodString>;
|
|
64
|
+
page: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
65
|
+
perPage: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
66
|
+
type: import("zod").ZodOptional<import("zod").ZodEnum<["adding", "spending", "p2p_spending", "p2p_adding", "blocked", "expired"]>>;
|
|
67
|
+
};
|
|
68
|
+
readonly handler: typeof pointsGetHistory;
|
|
69
|
+
}, {
|
|
70
|
+
readonly name: "ol_points_get_histogram";
|
|
71
|
+
readonly title: "View Points Trends";
|
|
72
|
+
readonly description: string;
|
|
73
|
+
readonly readOnly: true;
|
|
74
|
+
readonly inputSchema: {
|
|
75
|
+
storeCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
76
|
+
memberId: import("zod").ZodString;
|
|
77
|
+
pointType: import("zod").ZodEnum<["spent", "earned", "expired", "pending"]>;
|
|
78
|
+
walletCode: import("zod").ZodOptional<import("zod").ZodString>;
|
|
79
|
+
interval: import("zod").ZodOptional<import("zod").ZodEnum<["day", "week", "month"]>>;
|
|
80
|
+
dateFrom: import("zod").ZodOptional<import("zod").ZodString>;
|
|
81
|
+
dateTo: import("zod").ZodOptional<import("zod").ZodString>;
|
|
82
|
+
};
|
|
83
|
+
readonly handler: typeof pointsGetHistogram;
|
|
84
|
+
}];
|