@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.
Files changed (100) hide show
  1. package/dist/config.d.ts +4 -3
  2. package/dist/config.js +9 -7
  3. package/dist/instructions.d.ts +1 -1
  4. package/dist/instructions.js +26 -8
  5. package/dist/tools/achievement/handlers.js +60 -1
  6. package/dist/tools/achievement/index.d.ts +8 -8
  7. package/dist/tools/achievement/index.js +10 -11
  8. package/dist/tools/achievement/schemas.d.ts +8 -8
  9. package/dist/tools/achievement/schemas.js +36 -32
  10. package/dist/tools/analytics.js +9 -9
  11. package/dist/tools/badge.js +4 -4
  12. package/dist/tools/campaign/handlers.js +35 -1
  13. package/dist/tools/campaign/index.d.ts +97 -34
  14. package/dist/tools/campaign/index.js +14 -12
  15. package/dist/tools/campaign/schemas.d.ts +96 -33
  16. package/dist/tools/campaign/schemas.js +89 -55
  17. package/dist/tools/custom-event.js +10 -9
  18. package/dist/tools/export.js +4 -4
  19. package/dist/tools/import.d.ts +1 -1
  20. package/dist/tools/import.js +11 -6
  21. package/dist/tools/index.js +4 -4
  22. package/dist/tools/member/handlers.js +48 -1
  23. package/dist/tools/member/index.d.ts +1 -1
  24. package/dist/tools/member/index.js +3 -1
  25. package/dist/tools/member/schemas.js +12 -12
  26. package/dist/tools/points/handlers.d.ts +75 -0
  27. package/dist/tools/{points.js → points/handlers.js} +21 -112
  28. package/dist/tools/points/index.d.ts +84 -0
  29. package/dist/tools/points/index.js +63 -0
  30. package/dist/tools/points/schemas.d.ts +45 -0
  31. package/dist/tools/points/schemas.js +47 -0
  32. package/dist/tools/referral/schemas.js +3 -3
  33. package/dist/tools/reward/handlers.d.ts +2 -0
  34. package/dist/tools/reward/handlers.js +79 -6
  35. package/dist/tools/reward/index.d.ts +2 -0
  36. package/dist/tools/reward/index.js +10 -10
  37. package/dist/tools/reward/schemas.d.ts +2 -0
  38. package/dist/tools/reward/schemas.js +42 -23
  39. package/dist/tools/segment/index.js +7 -5
  40. package/dist/tools/segment/schemas.js +11 -11
  41. package/dist/tools/tierset/handlers.d.ts +26 -0
  42. package/dist/tools/tierset/handlers.js +196 -0
  43. package/dist/tools/tierset/index.d.ts +124 -0
  44. package/dist/tools/tierset/index.js +88 -0
  45. package/dist/tools/tierset/schemas.d.ts +127 -0
  46. package/dist/tools/tierset/schemas.js +62 -0
  47. package/dist/tools/transaction/handlers.d.ts +89 -0
  48. package/dist/tools/transaction/handlers.js +159 -0
  49. package/dist/tools/transaction/index.d.ts +153 -0
  50. package/dist/tools/transaction/index.js +54 -0
  51. package/dist/tools/transaction/schemas.d.ts +126 -0
  52. package/dist/tools/transaction/schemas.js +60 -0
  53. package/dist/tools/wallet-type/handlers.d.ts +63 -0
  54. package/dist/tools/{wallet-type.js → wallet-type/handlers.js} +15 -78
  55. package/dist/tools/{wallet-type.d.ts → wallet-type/index.d.ts} +3 -64
  56. package/dist/tools/wallet-type/index.js +65 -0
  57. package/dist/tools/wallet-type/schemas.d.ts +1 -0
  58. package/dist/tools/wallet-type/schemas.js +1 -0
  59. package/dist/tools/webhook.js +6 -6
  60. package/dist/types/schemas/achievement.d.ts +48 -48
  61. package/dist/types/schemas/admin.d.ts +10 -10
  62. package/dist/types/schemas/campaign.d.ts +12 -12
  63. package/dist/types/schemas/common.js +1 -1
  64. package/dist/types/schemas/member.d.ts +18 -18
  65. package/dist/types/schemas/role.d.ts +4 -4
  66. package/dist/types/schemas/tierset.js +2 -1
  67. package/dist/types/schemas/transaction.d.ts +12 -12
  68. package/dist/types/schemas/wallet-type.js +12 -10
  69. package/dist/types/schemas/webhook.d.ts +6 -6
  70. package/dist/utils/errors.js +40 -0
  71. package/package.json +3 -2
  72. package/dist/prompts/fan-engagement-setup.d.ts +0 -107
  73. package/dist/prompts/fan-engagement-setup.js +0 -492
  74. package/dist/tools/achievement.d.ts +0 -1017
  75. package/dist/tools/achievement.js +0 -354
  76. package/dist/tools/campaign.d.ts +0 -1800
  77. package/dist/tools/campaign.js +0 -737
  78. package/dist/tools/member.d.ts +0 -366
  79. package/dist/tools/member.js +0 -352
  80. package/dist/tools/points.d.ts +0 -201
  81. package/dist/tools/reward.d.ts +0 -279
  82. package/dist/tools/reward.js +0 -361
  83. package/dist/tools/segment.d.ts +0 -816
  84. package/dist/tools/segment.js +0 -333
  85. package/dist/tools/tierset.d.ts +0 -273
  86. package/dist/tools/tierset.js +0 -289
  87. package/dist/tools/transaction.d.ts +0 -365
  88. package/dist/tools/transaction.js +0 -259
  89. package/dist/workflows/app-login-streak.d.ts +0 -39
  90. package/dist/workflows/app-login-streak.js +0 -298
  91. package/dist/workflows/early-arrival.d.ts +0 -33
  92. package/dist/workflows/early-arrival.js +0 -148
  93. package/dist/workflows/index.d.ts +0 -101
  94. package/dist/workflows/index.js +0 -208
  95. package/dist/workflows/match-attendance.d.ts +0 -45
  96. package/dist/workflows/match-attendance.js +0 -308
  97. package/dist/workflows/sportsbar-visit.d.ts +0 -41
  98. package/dist/workflows/sportsbar-visit.js +0 -284
  99. package/dist/workflows/vod-watching.d.ts +0 -43
  100. package/dist/workflows/vod-watching.js +0 -326
@@ -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("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
+ 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("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."),
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("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."),
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("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."),
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
@@ -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: "Create a new bulk import from CSV content. Returns importId for async tracking. Import types: member (member data), groupValue (group values), segmentMembers (segment membership), unitTransferAdding (add points), unitTransferSpending (spend points), transaction (transactions), campaignCode (campaign codes), rewardCoupon (reward coupons). CSV format required - provide plain text CSV content.";
82
+ readonly description: string;
83
83
  readonly readOnly: false;
84
84
  readonly inputSchema: {
85
85
  storeCode: z.ZodOptional<z.ZodString>;
@@ -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("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."),
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("CSV file content (plain text, not base64 encoded). Each line should be a comma-separated row."),
21
- fileName: z.string().optional().describe("Optional file name for the import (default: import.csv)."),
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("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."),
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("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."),
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 content. Returns importId for async tracking. Import types: member (member data), groupValue (group values), segmentMembers (segment membership), unitTransferAdding (add points), unitTransferSpending (spend points), transaction (transactions), campaignCode (campaign codes), rewardCoupon (reward coupons). CSV format required - provide plain text CSV content.",
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,
@@ -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: "Register a new loyalty program member. Returns memberId for subsequent operations like points_add or member_get. Email must be unique within the store.";
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. Email must be unique within the store.",
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("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."),
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("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."),
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("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
+ 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("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."),
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("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."),
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("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."),
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 { z } from "zod";
2
- import { apiGet, apiPost } from "../client/http.js";
3
- import "../types/schemas/points.js";
4
- import { formatApiError, OpenLoyaltyError } from "../utils/errors.js";
5
- import { getStoreCode } from "../config.js";
6
- import { buildPaginationParams } from "../utils/pagination.js";
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 Error("Cannot transfer points to yourself. senderId and receiverId must be different.");
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
+ }];