@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.
Files changed (132) hide show
  1. package/dist/config.d.ts +4 -0
  2. package/dist/config.js +11 -0
  3. package/dist/index.js +0 -8
  4. package/dist/server.js +13 -0
  5. package/dist/tools/achievement/handlers.js +47 -0
  6. package/dist/tools/achievement/index.d.ts +11 -4
  7. package/dist/tools/achievement/index.js +12 -1
  8. package/dist/tools/achievement/schemas.d.ts +4 -4
  9. package/dist/tools/achievement/schemas.js +13 -12
  10. package/dist/tools/admin/handlers.d.ts +48 -0
  11. package/dist/tools/admin/handlers.js +159 -0
  12. package/dist/tools/admin/index.d.ts +86 -0
  13. package/dist/tools/admin/index.js +64 -0
  14. package/dist/tools/admin/schemas.d.ts +40 -0
  15. package/dist/tools/admin/schemas.js +40 -0
  16. package/dist/tools/analytics/handlers.d.ts +42 -0
  17. package/dist/tools/analytics/handlers.js +282 -0
  18. package/dist/tools/analytics/index.d.ts +108 -0
  19. package/dist/tools/analytics/index.js +91 -0
  20. package/dist/tools/analytics/schemas.d.ts +42 -0
  21. package/dist/tools/analytics/schemas.js +47 -0
  22. package/dist/tools/apikey/handlers.d.ts +15 -0
  23. package/dist/tools/apikey/handlers.js +53 -0
  24. package/dist/tools/apikey/index.d.ts +41 -0
  25. package/dist/tools/apikey/index.js +38 -0
  26. package/dist/tools/apikey/schemas.d.ts +31 -0
  27. package/dist/tools/apikey/schemas.js +15 -0
  28. package/dist/tools/audit/handlers.d.ts +20 -0
  29. package/dist/tools/audit/handlers.js +82 -0
  30. package/dist/tools/audit/index.d.ts +36 -0
  31. package/dist/tools/audit/index.js +28 -0
  32. package/dist/tools/audit/schemas.d.ts +62 -0
  33. package/dist/tools/audit/schemas.js +18 -0
  34. package/dist/tools/badge/handlers.d.ts +45 -0
  35. package/dist/tools/badge/handlers.js +135 -0
  36. package/dist/tools/badge/index.d.ts +68 -0
  37. package/dist/tools/badge/index.js +47 -0
  38. package/dist/tools/badge/schemas.d.ts +37 -0
  39. package/dist/tools/badge/schemas.js +31 -0
  40. package/dist/tools/campaign/handlers.js +61 -0
  41. package/dist/tools/campaign/index.d.ts +12 -0
  42. package/dist/tools/campaign/index.js +20 -1
  43. package/dist/tools/campaign/member-handlers.js +37 -1
  44. package/dist/tools/campaign/schemas.js +16 -14
  45. package/dist/tools/custom-event/handlers.d.ts +98 -0
  46. package/dist/tools/custom-event/handlers.js +238 -0
  47. package/dist/tools/custom-event/index.d.ts +139 -0
  48. package/dist/tools/custom-event/index.js +78 -0
  49. package/dist/tools/custom-event/schemas.d.ts +87 -0
  50. package/dist/tools/custom-event/schemas.js +59 -0
  51. package/dist/tools/export/handlers.d.ts +29 -0
  52. package/dist/tools/export/handlers.js +128 -0
  53. package/dist/tools/export/index.d.ts +56 -0
  54. package/dist/tools/export/index.js +46 -0
  55. package/dist/tools/export/schemas.d.ts +42 -0
  56. package/dist/tools/export/schemas.js +41 -0
  57. package/dist/tools/import/handlers.d.ts +22 -0
  58. package/dist/tools/import/handlers.js +123 -0
  59. package/dist/tools/import/index.d.ts +45 -0
  60. package/dist/tools/import/index.js +41 -0
  61. package/dist/tools/import/schemas.d.ts +57 -0
  62. package/dist/tools/import/schemas.js +39 -0
  63. package/dist/tools/index.d.ts +1 -0
  64. package/dist/tools/index.js +11 -11
  65. package/dist/tools/member/handlers.js +30 -0
  66. package/dist/tools/member/index.d.ts +10 -0
  67. package/dist/tools/member/index.js +10 -0
  68. package/dist/tools/member/schemas.js +13 -13
  69. package/dist/tools/points/handlers.js +73 -0
  70. package/dist/tools/points/index.d.ts +6 -0
  71. package/dist/tools/points/index.js +6 -0
  72. package/dist/tools/points/schemas.js +1 -1
  73. package/dist/tools/referral/index.d.ts +3 -0
  74. package/dist/tools/referral/index.js +3 -0
  75. package/dist/tools/reward/handlers.js +21 -13
  76. package/dist/tools/reward/index.d.ts +9 -0
  77. package/dist/tools/reward/index.js +12 -1
  78. package/dist/tools/reward/schemas.js +2 -2
  79. package/dist/tools/role/handlers.d.ts +35 -0
  80. package/dist/tools/role/handlers.js +127 -0
  81. package/dist/tools/role/index.d.ts +99 -0
  82. package/dist/tools/role/index.js +65 -0
  83. package/dist/tools/role/schemas.d.ts +56 -0
  84. package/dist/tools/role/schemas.js +35 -0
  85. package/dist/tools/segment/handlers.js +68 -1
  86. package/dist/tools/segment/index.d.ts +9 -0
  87. package/dist/tools/segment/index.js +13 -0
  88. package/dist/tools/segment/schemas.js +8 -5
  89. package/dist/tools/store/handlers.d.ts +25 -0
  90. package/dist/tools/store/handlers.js +89 -0
  91. package/dist/tools/store/index.d.ts +55 -0
  92. package/dist/tools/store/index.js +46 -0
  93. package/dist/tools/store/schemas.d.ts +38 -0
  94. package/dist/tools/store/schemas.js +23 -0
  95. package/dist/tools/tierset/handlers.js +92 -1
  96. package/dist/tools/tierset/index.d.ts +6 -0
  97. package/dist/tools/tierset/index.js +8 -1
  98. package/dist/tools/transaction/handlers.js +40 -0
  99. package/dist/tools/transaction/index.d.ts +4 -0
  100. package/dist/tools/transaction/index.js +4 -0
  101. package/dist/tools/transaction/schemas.js +3 -3
  102. package/dist/tools/wallet-type/index.d.ts +4 -0
  103. package/dist/tools/wallet-type/index.js +5 -1
  104. package/dist/tools/webhook/handlers.d.ts +34 -0
  105. package/dist/tools/webhook/handlers.js +147 -0
  106. package/dist/tools/webhook/index.d.ts +97 -0
  107. package/dist/tools/webhook/index.js +65 -0
  108. package/dist/tools/webhook/schemas.d.ts +72 -0
  109. package/dist/tools/{webhook.js → webhook/schemas.js} +0 -140
  110. package/dist/types/schemas/tierset.js +3 -1
  111. package/package.json +1 -1
  112. package/dist/tools/admin.d.ts +0 -165
  113. package/dist/tools/admin.js +0 -205
  114. package/dist/tools/analytics.d.ts +0 -180
  115. package/dist/tools/analytics.js +0 -255
  116. package/dist/tools/apikey.d.ts +0 -79
  117. package/dist/tools/apikey.js +0 -85
  118. package/dist/tools/audit.d.ts +0 -111
  119. package/dist/tools/audit.js +0 -94
  120. package/dist/tools/badge.d.ts +0 -143
  121. package/dist/tools/badge.js +0 -174
  122. package/dist/tools/custom-event.d.ts +0 -315
  123. package/dist/tools/custom-event.js +0 -271
  124. package/dist/tools/export.d.ts +0 -118
  125. package/dist/tools/export.js +0 -152
  126. package/dist/tools/import.d.ts +0 -116
  127. package/dist/tools/import.js +0 -143
  128. package/dist/tools/role.d.ts +0 -180
  129. package/dist/tools/role.js +0 -173
  130. package/dist/tools/store.d.ts +0 -109
  131. package/dist/tools/store.js +0 -125
  132. package/dist/tools/webhook.d.ts +0 -192
@@ -0,0 +1,123 @@
1
+ import { apiGet } from "../../client/http.js";
2
+ import { formatApiError, OpenLoyaltyError } from "../../utils/errors.js";
3
+ import { getConfig, getStoreCode } from "../../config.js";
4
+ import axios from "axios";
5
+ import FormDataNode from "form-data";
6
+ export async function importCreate(input) {
7
+ const config = getConfig();
8
+ const storeCode = getStoreCode(input.storeCode);
9
+ // Determine file extension and content type
10
+ // API accepts: application/xml, text/xml for member imports; text/csv for others
11
+ const fileName = input.fileName || "import.xml";
12
+ const isXml = fileName.endsWith('.xml') || input.fileContent.trim().startsWith('<?xml') || input.fileContent.trim().startsWith('<');
13
+ const contentType = isXml ? "application/xml" : "text/csv";
14
+ // Create form data for multipart upload using Node.js form-data package
15
+ // API expects field name "import[file]" per documentation
16
+ const formData = new FormDataNode();
17
+ formData.append("import[file]", Buffer.from(input.fileContent), {
18
+ filename: fileName,
19
+ contentType: contentType,
20
+ });
21
+ try {
22
+ const response = await axios.post(`${config.apiUrl}/${storeCode}/import/${input.type}`, formData, {
23
+ headers: {
24
+ "X-AUTH-TOKEN": config.apiToken,
25
+ ...formData.getHeaders(),
26
+ },
27
+ });
28
+ return response.data;
29
+ }
30
+ catch (error) {
31
+ if (axios.isAxiosError(error) && error.response?.status === 400) {
32
+ const allMessages = [error.response?.data?.message || "", ...(error.response?.data?.errors || []).map((e) => e.message)].join(" ").toLowerCase();
33
+ if (allMessages.includes("file") || allMessages.includes("format") || allMessages.includes("invalid") || allMessages.includes("parse")) {
34
+ throw new OpenLoyaltyError({ code: "INVALID_FILE_FORMAT", message: "The import file content is invalid or in the wrong format",
35
+ hint: `For '${input.type}' imports: member imports typically use XML format, others use CSV. Ensure the file content is properly formatted and the fileName extension (.xml or .csv) matches the content type.`,
36
+ relatedTool: "ol_import_create" });
37
+ }
38
+ }
39
+ if (axios.isAxiosError(error) && error.response?.status === 404) {
40
+ throw new OpenLoyaltyError({
41
+ code: "STORE_NOT_FOUND",
42
+ message: "Cannot create import - store not found",
43
+ hint: "Use ol_store_list() to verify the store code is correct.",
44
+ relatedTool: "ol_import_create",
45
+ });
46
+ }
47
+ if (axios.isAxiosError(error) && error.response?.status === 403) {
48
+ throw new OpenLoyaltyError({
49
+ code: "IMPORT_PERMISSION_DENIED",
50
+ message: "You don't have permission to create imports",
51
+ hint: "Bulk imports require the IMPORT:MODIFY permission. Use ol_admin_get_permissions() to check your access.",
52
+ relatedTool: "ol_import_create",
53
+ });
54
+ }
55
+ if (axios.isAxiosError(error) && error.response?.status === 413) {
56
+ throw new OpenLoyaltyError({
57
+ code: "FILE_TOO_LARGE",
58
+ message: "The import file is too large",
59
+ hint: "Split the file into smaller chunks and import each chunk separately. Most import endpoints accept files up to 10MB.",
60
+ relatedTool: "ol_import_create",
61
+ });
62
+ }
63
+ throw formatApiError(error, "ol_import_create");
64
+ }
65
+ }
66
+ export async function importList(input) {
67
+ const storeCode = getStoreCode(input.storeCode);
68
+ const params = new URLSearchParams();
69
+ if (input.page)
70
+ params.append("_page", String(input.page));
71
+ if (input.perPage)
72
+ params.append("_itemsOnPage", String(input.perPage));
73
+ if (input.importId)
74
+ params.append("importId", input.importId);
75
+ if (input.type)
76
+ params.append("type", input.type);
77
+ const queryString = params.toString();
78
+ const url = `/${storeCode}/import${queryString ? `?${queryString}` : ""}`;
79
+ try {
80
+ const response = await apiGet(url);
81
+ return response;
82
+ }
83
+ catch (error) {
84
+ if (axios.isAxiosError(error) && error.response?.status === 404) {
85
+ throw new OpenLoyaltyError({
86
+ code: "STORE_NOT_FOUND",
87
+ message: "Cannot list imports - store not found",
88
+ hint: "Use ol_store_list() to verify the store code is correct.",
89
+ relatedTool: "ol_import_list",
90
+ });
91
+ }
92
+ if (axios.isAxiosError(error) && error.response?.status === 403) {
93
+ throw new OpenLoyaltyError({
94
+ code: "IMPORT_PERMISSION_DENIED",
95
+ message: "You don't have permission to view imports",
96
+ hint: "Import access requires the IMPORT:VIEW permission. Use ol_admin_get_permissions() to check your access.",
97
+ relatedTool: "ol_import_list",
98
+ });
99
+ }
100
+ throw formatApiError(error, "ol_import_list");
101
+ }
102
+ }
103
+ export async function importGet(input) {
104
+ const storeCode = getStoreCode(input.storeCode);
105
+ const params = new URLSearchParams();
106
+ if (input.page)
107
+ params.append("_page", String(input.page));
108
+ if (input.perPage)
109
+ params.append("_itemsOnPage", String(input.perPage));
110
+ const queryString = params.toString();
111
+ const url = `/${storeCode}/import/${input.importId}${queryString ? `?${queryString}` : ""}`;
112
+ try {
113
+ const response = await apiGet(url);
114
+ return response;
115
+ }
116
+ catch (error) {
117
+ if (axios.isAxiosError(error) && error.response?.status === 404) {
118
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Import '${input.importId}' not found`,
119
+ hint: "Use ol_import_list() to find existing imports and their IDs.", relatedTool: "ol_import_get" });
120
+ }
121
+ throw formatApiError(error, "ol_import_get");
122
+ }
123
+ }
@@ -0,0 +1,45 @@
1
+ export { ImportCreateInputSchema, ImportListInputSchema, ImportGetInputSchema, } from "./schemas.js";
2
+ export type { Import, ImportItem, ImportListResponse, ImportDetailsResponse, } from "./schemas.js";
3
+ export { importCreate, importList, importGet, } from "./handlers.js";
4
+ import { importCreate, importList, importGet } from "./handlers.js";
5
+ export declare const importToolDefinitions: readonly [{
6
+ readonly name: "ol_import_create";
7
+ readonly title: "Create Bulk Import";
8
+ readonly description: string;
9
+ readonly readOnly: false;
10
+ readonly idempotent: false;
11
+ readonly inputSchema: {
12
+ storeCode: import("zod").ZodOptional<import("zod").ZodString>;
13
+ type: import("zod").ZodEnum<["member", "groupValue", "segmentMembers", "unitTransferAdding", "unitTransferSpending", "transaction", "campaignCode", "rewardCoupon"]>;
14
+ fileContent: import("zod").ZodString;
15
+ fileName: import("zod").ZodOptional<import("zod").ZodString>;
16
+ };
17
+ readonly handler: typeof importCreate;
18
+ }, {
19
+ readonly name: "ol_import_list";
20
+ readonly title: "List Imports";
21
+ readonly description: "List imports with optional filtering. Returns paginated list of imports with importId, type, uploadedDate, fileName, and status information.";
22
+ readonly readOnly: true;
23
+ readonly idempotent: true;
24
+ readonly inputSchema: {
25
+ storeCode: import("zod").ZodOptional<import("zod").ZodString>;
26
+ page: import("zod").ZodOptional<import("zod").ZodNumber>;
27
+ perPage: import("zod").ZodOptional<import("zod").ZodNumber>;
28
+ importId: import("zod").ZodOptional<import("zod").ZodString>;
29
+ type: import("zod").ZodOptional<import("zod").ZodEnum<["member", "groupValue", "segmentMembers", "unitTransferAdding", "unitTransferSpending", "transaction", "campaignCode", "rewardCoupon"]>>;
30
+ };
31
+ readonly handler: typeof importList;
32
+ }, {
33
+ readonly name: "ol_import_get";
34
+ readonly title: "Get Import Details";
35
+ readonly description: "Get import details and individual item statuses. Returns paginated list of import items with their success/failure status and error messages.";
36
+ readonly readOnly: true;
37
+ readonly idempotent: true;
38
+ readonly inputSchema: {
39
+ storeCode: import("zod").ZodOptional<import("zod").ZodString>;
40
+ importId: import("zod").ZodString;
41
+ page: import("zod").ZodOptional<import("zod").ZodNumber>;
42
+ perPage: import("zod").ZodOptional<import("zod").ZodNumber>;
43
+ };
44
+ readonly handler: typeof importGet;
45
+ }];
@@ -0,0 +1,41 @@
1
+ // Re-export schemas and types
2
+ export { ImportCreateInputSchema, ImportListInputSchema, ImportGetInputSchema, } from "./schemas.js";
3
+ // Re-export handlers
4
+ export { importCreate, importList, importGet, } from "./handlers.js";
5
+ // Imports for tool definitions
6
+ import { ImportCreateInputSchema, ImportListInputSchema, ImportGetInputSchema, } from "./schemas.js";
7
+ import { importCreate, importList, importGet, } from "./handlers.js";
8
+ // Tool definitions
9
+ export const importToolDefinitions = [
10
+ {
11
+ name: "ol_import_create",
12
+ title: "Create Bulk Import",
13
+ description: "Create a new bulk import from file content (CSV or XML). Returns importId for async tracking. " +
14
+ "Import types: member (member data - typically XML), groupValue (group values), segmentMembers (segment membership), " +
15
+ "unitTransferAdding (add points), unitTransferSpending (spend points), transaction (transactions), " +
16
+ "campaignCode (campaign codes), rewardCoupon (reward coupons). " +
17
+ "Provide plain text file content (CSV or XML). Format is auto-detected from content or fileName extension.",
18
+ readOnly: false,
19
+ idempotent: false,
20
+ inputSchema: ImportCreateInputSchema,
21
+ handler: importCreate,
22
+ },
23
+ {
24
+ name: "ol_import_list",
25
+ title: "List Imports",
26
+ description: "List imports with optional filtering. Returns paginated list of imports with importId, type, uploadedDate, fileName, and status information.",
27
+ readOnly: true,
28
+ idempotent: true,
29
+ inputSchema: ImportListInputSchema,
30
+ handler: importList,
31
+ },
32
+ {
33
+ name: "ol_import_get",
34
+ title: "Get Import Details",
35
+ description: "Get import details and individual item statuses. Returns paginated list of import items with their success/failure status and error messages.",
36
+ readOnly: true,
37
+ idempotent: true,
38
+ inputSchema: ImportGetInputSchema,
39
+ handler: importGet,
40
+ },
41
+ ];
@@ -0,0 +1,57 @@
1
+ import { z } from "zod";
2
+ export interface Import {
3
+ importId: string;
4
+ type?: string;
5
+ uploadedDate?: string;
6
+ fileName?: string;
7
+ itemsToImport?: number;
8
+ user?: string;
9
+ errorMessage?: string;
10
+ additionalData?: Record<string, string>;
11
+ }
12
+ export interface ImportItem {
13
+ importId?: string;
14
+ status?: string;
15
+ createdAt?: string;
16
+ updatedAt?: string;
17
+ message?: string;
18
+ entity?: {
19
+ id?: string;
20
+ data?: Record<string, unknown>;
21
+ };
22
+ }
23
+ export interface ImportListResponse {
24
+ items: Import[];
25
+ total?: {
26
+ all?: number | string;
27
+ filtered?: number | string;
28
+ estimated?: boolean | number;
29
+ };
30
+ }
31
+ export interface ImportDetailsResponse {
32
+ items: ImportItem[];
33
+ total?: {
34
+ all?: number | string;
35
+ filtered?: number | string;
36
+ estimated?: boolean | number;
37
+ };
38
+ }
39
+ export declare const ImportCreateInputSchema: {
40
+ storeCode: z.ZodOptional<z.ZodString>;
41
+ type: z.ZodEnum<["member", "groupValue", "segmentMembers", "unitTransferAdding", "unitTransferSpending", "transaction", "campaignCode", "rewardCoupon"]>;
42
+ fileContent: z.ZodString;
43
+ fileName: z.ZodOptional<z.ZodString>;
44
+ };
45
+ export declare const ImportListInputSchema: {
46
+ storeCode: z.ZodOptional<z.ZodString>;
47
+ page: z.ZodOptional<z.ZodNumber>;
48
+ perPage: z.ZodOptional<z.ZodNumber>;
49
+ importId: z.ZodOptional<z.ZodString>;
50
+ type: z.ZodOptional<z.ZodEnum<["member", "groupValue", "segmentMembers", "unitTransferAdding", "unitTransferSpending", "transaction", "campaignCode", "rewardCoupon"]>>;
51
+ };
52
+ export declare const ImportGetInputSchema: {
53
+ storeCode: z.ZodOptional<z.ZodString>;
54
+ importId: z.ZodString;
55
+ page: z.ZodOptional<z.ZodNumber>;
56
+ perPage: z.ZodOptional<z.ZodNumber>;
57
+ };
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ export const ImportCreateInputSchema = {
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
+ type: z.enum([
5
+ "member",
6
+ "groupValue",
7
+ "segmentMembers",
8
+ "unitTransferAdding",
9
+ "unitTransferSpending",
10
+ "transaction",
11
+ "campaignCode",
12
+ "rewardCoupon",
13
+ ]).describe("Import type. Options: member, groupValue, segmentMembers, unitTransferAdding, unitTransferSpending, transaction, campaignCode, rewardCoupon."),
14
+ fileContent: z.string().describe("File content as plain text (not base64 encoded). Accepts CSV (comma-separated rows) or XML format. " +
15
+ "For member imports, XML is the standard format. Auto-detected based on content or fileName extension."),
16
+ fileName: z.string().optional().describe("Optional file name for the import (default: import.xml). Use .xml extension for XML content, .csv for CSV content."),
17
+ };
18
+ export const ImportListInputSchema = {
19
+ 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."),
20
+ page: z.number().optional().describe("Page number (default: 1)."),
21
+ perPage: z.number().optional().describe("Items per page (default: 25)."),
22
+ importId: z.string().optional().describe("Filter by import ID."),
23
+ type: z.enum([
24
+ "member",
25
+ "groupValue",
26
+ "segmentMembers",
27
+ "unitTransferAdding",
28
+ "unitTransferSpending",
29
+ "transaction",
30
+ "campaignCode",
31
+ "rewardCoupon",
32
+ ]).optional().describe("Filter by import type."),
33
+ };
34
+ export const ImportGetInputSchema = {
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
+ importId: z.string().describe("The import ID (UUID) to retrieve."),
37
+ page: z.number().optional().describe("Page number for import items (default: 1)."),
38
+ perPage: z.number().optional().describe("Items per page for import items (default: 25)."),
39
+ };
@@ -6,6 +6,7 @@ export interface ToolDefinition {
6
6
  description: string;
7
7
  readOnly: boolean;
8
8
  destructive?: boolean;
9
+ idempotent?: boolean;
9
10
  inputSchema: ZodShape;
10
11
  handler: (args: Record<string, unknown>) => Promise<unknown>;
11
12
  }
@@ -8,20 +8,20 @@ import { transactionToolDefinitions } from "./transaction/index.js";
8
8
  import { campaignToolDefinitions } from "./campaign/index.js";
9
9
  import { segmentToolDefinitions } from "./segment/index.js";
10
10
  import { achievementToolDefinitions } from "./achievement/index.js";
11
- import { badgeToolDefinitions } from "./badge.js";
11
+ import { badgeToolDefinitions } from "./badge/index.js";
12
12
  // Phase 3 tools
13
- import { analyticsToolDefinitions } from "./analytics.js";
14
- import { adminToolDefinitions } from "./admin.js";
15
- import { roleToolDefinitions } from "./role.js";
16
- import { apiKeyToolDefinitions } from "./apikey.js";
17
- import { auditToolDefinitions } from "./audit.js";
18
- import { storeToolDefinitions } from "./store.js";
13
+ import { analyticsToolDefinitions } from "./analytics/index.js";
14
+ import { adminToolDefinitions } from "./admin/index.js";
15
+ import { roleToolDefinitions } from "./role/index.js";
16
+ import { apiKeyToolDefinitions } from "./apikey/index.js";
17
+ import { auditToolDefinitions } from "./audit/index.js";
18
+ import { storeToolDefinitions } from "./store/index.js";
19
19
  // Phase 4 tools
20
- import { webhookToolDefinitions } from "./webhook.js";
21
- import { importToolDefinitions } from "./import.js";
22
- import { exportToolDefinitions } from "./export.js";
20
+ import { webhookToolDefinitions } from "./webhook/index.js";
21
+ import { importToolDefinitions } from "./import/index.js";
22
+ import { exportToolDefinitions } from "./export/index.js";
23
23
  // Custom event tools (needed for custom_event trigger in campaigns/achievements)
24
- import { customEventToolDefinitions } from "./custom-event.js";
24
+ import { customEventToolDefinitions } from "./custom-event/index.js";
25
25
  // Referral tools (needed for referral tracking between members)
26
26
  import { referralToolDefinitions } from "./referral/index.js";
27
27
  // Collect all tool definitions
@@ -153,6 +153,11 @@ export async function memberUpdate(input) {
153
153
  await apiPut(`/${storeCode}/member/${input.memberId}`, { customer: payload });
154
154
  }
155
155
  catch (error) {
156
+ const axiosError = error;
157
+ if (axiosError.response?.status === 404) {
158
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Member '${input.memberId}' not found`,
159
+ hint: "Use ol_member_list() to search for the member by email, name, or phone.", relatedTool: "ol_member_update" });
160
+ }
156
161
  throw formatApiError(error, "ol_member_update");
157
162
  }
158
163
  }
@@ -162,6 +167,11 @@ export async function memberActivate(input) {
162
167
  await apiPost(`/${storeCode}/member/${input.memberId}/activate`);
163
168
  }
164
169
  catch (error) {
170
+ const axiosError = error;
171
+ if (axiosError.response?.status === 404) {
172
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Member '${input.memberId}' not found`,
173
+ hint: "Use ol_member_list() to search for the member by email, name, or phone.", relatedTool: "ol_member_activate" });
174
+ }
165
175
  throw formatApiError(error, "ol_member_activate");
166
176
  }
167
177
  }
@@ -171,6 +181,11 @@ export async function memberDeactivate(input) {
171
181
  await apiPost(`/${storeCode}/member/${input.memberId}/deactivate`);
172
182
  }
173
183
  catch (error) {
184
+ const axiosError = error;
185
+ if (axiosError.response?.status === 404) {
186
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Member '${input.memberId}' not found`,
187
+ hint: "Use ol_member_list() to search for the member by email, name, or phone.", relatedTool: "ol_member_deactivate" });
188
+ }
174
189
  throw formatApiError(error, "ol_member_deactivate");
175
190
  }
176
191
  }
@@ -180,6 +195,11 @@ export async function memberDelete(input) {
180
195
  await apiDelete(`/${storeCode}/member/${input.memberId}`);
181
196
  }
182
197
  catch (error) {
198
+ const axiosError = error;
199
+ if (axiosError.response?.status === 404) {
200
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Member '${input.memberId}' not found`,
201
+ hint: "Use ol_member_list() to search for the member by email, name, or phone.", relatedTool: "ol_member_delete" });
202
+ }
183
203
  throw formatApiError(error, "ol_member_delete");
184
204
  }
185
205
  }
@@ -206,6 +226,11 @@ export async function memberGetTierProgress(input) {
206
226
  };
207
227
  }
208
228
  catch (error) {
229
+ const axiosError = error;
230
+ if (axiosError.response?.status === 404) {
231
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Member '${input.memberId}' not found`,
232
+ hint: "Use ol_member_list() to search for the member by email, name, or phone.", relatedTool: "ol_member_get_tier_progress" });
233
+ }
209
234
  throw formatApiError(error, "ol_member_get_tier_progress");
210
235
  }
211
236
  }
@@ -248,6 +273,11 @@ export async function memberRemoveManualTier(input) {
248
273
  await apiPost(`/${storeCode}/member/${input.memberId}/remove-manually-level`);
249
274
  }
250
275
  catch (error) {
276
+ const axiosError = error;
277
+ if (axiosError.response?.status === 404) {
278
+ throw new OpenLoyaltyError({ code: "NOT_FOUND", message: `Member '${input.memberId}' not found`,
279
+ hint: "Use ol_member_list() to search for the member by email, name, or phone.", relatedTool: "ol_member_remove_manual_tier" });
280
+ }
251
281
  throw formatApiError(error, "ol_member_remove_manual_tier");
252
282
  }
253
283
  }
@@ -6,6 +6,7 @@ export declare const memberToolDefinitions: readonly [{
6
6
  readonly title: "Register New Member";
7
7
  readonly description: string;
8
8
  readonly readOnly: false;
9
+ readonly idempotent: false;
9
10
  readonly inputSchema: {
10
11
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
11
12
  email: import("zod").ZodString;
@@ -44,6 +45,7 @@ export declare const memberToolDefinitions: readonly [{
44
45
  readonly title: "Get Member Profile";
45
46
  readonly description: "Get member details including profile, points balance, and tier status. Use memberId from member_create or member_list.";
46
47
  readonly readOnly: true;
48
+ readonly idempotent: true;
47
49
  readonly inputSchema: {
48
50
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
49
51
  memberId: import("zod").ZodString;
@@ -54,6 +56,7 @@ export declare const memberToolDefinitions: readonly [{
54
56
  readonly title: "Search Members";
55
57
  readonly description: string;
56
58
  readonly readOnly: true;
59
+ readonly idempotent: true;
57
60
  readonly inputSchema: {
58
61
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
59
62
  cursor: import("zod").ZodOptional<import("zod").ZodString>;
@@ -72,6 +75,7 @@ export declare const memberToolDefinitions: readonly [{
72
75
  readonly title: "Update Member Profile";
73
76
  readonly description: "Update member profile fields. Cannot change email. Use member_get first to see current values.";
74
77
  readonly readOnly: false;
78
+ readonly idempotent: true;
75
79
  readonly inputSchema: {
76
80
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
77
81
  memberId: import("zod").ZodString;
@@ -109,6 +113,7 @@ export declare const memberToolDefinitions: readonly [{
109
113
  readonly title: "Activate Member Account";
110
114
  readonly description: "Activate a member account. Inactive members cannot earn or spend points.";
111
115
  readonly readOnly: false;
116
+ readonly idempotent: true;
112
117
  readonly inputSchema: {
113
118
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
114
119
  memberId: import("zod").ZodString;
@@ -119,6 +124,7 @@ export declare const memberToolDefinitions: readonly [{
119
124
  readonly title: "Deactivate Member Account";
120
125
  readonly description: "Deactivate a member account. Deactivated members cannot earn or spend points but retain their balance.";
121
126
  readonly readOnly: false;
127
+ readonly idempotent: true;
122
128
  readonly inputSchema: {
123
129
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
124
130
  memberId: import("zod").ZodString;
@@ -130,6 +136,7 @@ export declare const memberToolDefinitions: readonly [{
130
136
  readonly description: "Permanently removes member and all associated data. Cannot be undone. Use for GDPR compliance or member requests.";
131
137
  readonly readOnly: false;
132
138
  readonly destructive: true;
139
+ readonly idempotent: true;
133
140
  readonly inputSchema: {
134
141
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
135
142
  memberId: import("zod").ZodString;
@@ -140,6 +147,7 @@ export declare const memberToolDefinitions: readonly [{
140
147
  readonly title: "Check Tier Progress";
141
148
  readonly description: "Get member tier progression status showing current tier and progress to next. Returns currentValue, requiredValue, and progressPercent.";
142
149
  readonly readOnly: true;
150
+ readonly idempotent: true;
143
151
  readonly inputSchema: {
144
152
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
145
153
  memberId: import("zod").ZodString;
@@ -150,6 +158,7 @@ export declare const memberToolDefinitions: readonly [{
150
158
  readonly title: "Assign Tier to Member";
151
159
  readonly description: "Manually assign a tier level to a member, overriding automatic tier calculation. Use tierset_get_tiers to find available levelId values.";
152
160
  readonly readOnly: false;
161
+ readonly idempotent: true;
153
162
  readonly inputSchema: {
154
163
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
155
164
  memberId: import("zod").ZodString;
@@ -161,6 +170,7 @@ export declare const memberToolDefinitions: readonly [{
161
170
  readonly title: "Remove Manual Tier Assignment";
162
171
  readonly description: "Remove manually assigned tier from member, restoring automatic tier calculation based on conditions.";
163
172
  readonly readOnly: false;
173
+ readonly idempotent: true;
164
174
  readonly inputSchema: {
165
175
  storeCode: import("zod").ZodOptional<import("zod").ZodString>;
166
176
  memberId: import("zod").ZodString;
@@ -14,6 +14,7 @@ export const memberToolDefinitions = [
14
14
  "REQUIRED: email (must be unique). OPTIONAL: firstName, lastName, phone, birthDate (YYYY-MM-DD), gender, loyaltyCardNumber. " +
15
15
  "NOTE: agreement1 (terms) and agreement2 (marketing consent) are consent decisions -- never default these to true without explicit user approval.",
16
16
  readOnly: false,
17
+ idempotent: false,
17
18
  inputSchema: MemberCreateInputSchema,
18
19
  handler: memberCreate,
19
20
  },
@@ -22,6 +23,7 @@ export const memberToolDefinitions = [
22
23
  title: "Get Member Profile",
23
24
  description: "Get member details including profile, points balance, and tier status. Use memberId from member_create or member_list.",
24
25
  readOnly: true,
26
+ idempotent: true,
25
27
  inputSchema: MemberGetInputSchema,
26
28
  handler: memberGet,
27
29
  },
@@ -31,6 +33,7 @@ export const memberToolDefinitions = [
31
33
  description: "Search and list members with optional filters. Use member_get for full details of a specific member. " +
32
34
  "Supports cursor pagination: provide 'cursor' from previous response to get next page.",
33
35
  readOnly: true,
36
+ idempotent: true,
34
37
  inputSchema: MemberListInputSchema,
35
38
  handler: memberList,
36
39
  },
@@ -39,6 +42,7 @@ export const memberToolDefinitions = [
39
42
  title: "Update Member Profile",
40
43
  description: "Update member profile fields. Cannot change email. Use member_get first to see current values.",
41
44
  readOnly: false,
45
+ idempotent: true,
42
46
  inputSchema: MemberUpdateInputSchema,
43
47
  handler: memberUpdate,
44
48
  },
@@ -47,6 +51,7 @@ export const memberToolDefinitions = [
47
51
  title: "Activate Member Account",
48
52
  description: "Activate a member account. Inactive members cannot earn or spend points.",
49
53
  readOnly: false,
54
+ idempotent: true,
50
55
  inputSchema: MemberIdInputSchema,
51
56
  handler: memberActivate,
52
57
  },
@@ -55,6 +60,7 @@ export const memberToolDefinitions = [
55
60
  title: "Deactivate Member Account",
56
61
  description: "Deactivate a member account. Deactivated members cannot earn or spend points but retain their balance.",
57
62
  readOnly: false,
63
+ idempotent: true,
58
64
  inputSchema: MemberIdInputSchema,
59
65
  handler: memberDeactivate,
60
66
  },
@@ -64,6 +70,7 @@ export const memberToolDefinitions = [
64
70
  description: "Permanently removes member and all associated data. Cannot be undone. Use for GDPR compliance or member requests.",
65
71
  readOnly: false,
66
72
  destructive: true,
73
+ idempotent: true,
67
74
  inputSchema: MemberIdInputSchema,
68
75
  handler: memberDelete,
69
76
  },
@@ -72,6 +79,7 @@ export const memberToolDefinitions = [
72
79
  title: "Check Tier Progress",
73
80
  description: "Get member tier progression status showing current tier and progress to next. Returns currentValue, requiredValue, and progressPercent.",
74
81
  readOnly: true,
82
+ idempotent: true,
75
83
  inputSchema: MemberIdInputSchema,
76
84
  handler: memberGetTierProgress,
77
85
  },
@@ -80,6 +88,7 @@ export const memberToolDefinitions = [
80
88
  title: "Assign Tier to Member",
81
89
  description: "Manually assign a tier level to a member, overriding automatic tier calculation. Use tierset_get_tiers to find available levelId values.",
82
90
  readOnly: false,
91
+ idempotent: true,
83
92
  inputSchema: MemberAssignTierInputSchema,
84
93
  handler: memberAssignTier,
85
94
  },
@@ -88,6 +97,7 @@ export const memberToolDefinitions = [
88
97
  title: "Remove Manual Tier Assignment",
89
98
  description: "Remove manually assigned tier from member, restoring automatic tier calculation based on conditions.",
90
99
  readOnly: false,
100
+ idempotent: true,
91
101
  inputSchema: MemberIdInputSchema,
92
102
  handler: memberRemoveManualTier,
93
103
  },
@@ -6,17 +6,17 @@ export const MemberCreateInputSchema = {
6
6
  lastName: z.string().optional().describe("Member last name."),
7
7
  phone: z.string().optional().describe("Member phone number."),
8
8
  birthDate: z.string().optional().describe("Member birth date (YYYY-MM-DD format)."),
9
- gender: z.enum(["male", "female", "not_disclosed"]).optional().describe("Member gender."),
9
+ gender: z.enum(["male", "female", "not_disclosed"]).optional().describe("Member gender. Valid values: 'male', 'female', 'not_disclosed'."),
10
10
  loyaltyCardNumber: z.string().optional().describe("Loyalty card number. Auto-generated if not provided."),
11
11
  agreement1: z.boolean().optional().describe("Terms acceptance. BUSINESS DECISION: Legal requirement. Cannot default to true without explicit consent from the member."),
12
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
13
  agreement3: z.boolean().optional().describe("Additional agreement. Depends on store configuration."),
14
14
  address: z.object({
15
- street: z.string().optional(),
16
- city: z.string().optional(),
17
- postal: z.string().optional(),
18
- country: z.string().optional(),
19
- province: z.string().optional(),
15
+ street: z.string().optional().describe("Street address (e.g., '123 Main St')."),
16
+ city: z.string().optional().describe("City name."),
17
+ postal: z.string().optional().describe("Postal/ZIP code."),
18
+ country: z.string().optional().describe("Country name or ISO 3166-1 code (e.g., 'US', 'Poland')."),
19
+ province: z.string().optional().describe("State, province, or region."),
20
20
  }).optional().describe("Member address."),
21
21
  };
22
22
  export const MemberGetInputSchema = {
@@ -42,13 +42,13 @@ export const MemberUpdateInputSchema = {
42
42
  lastName: z.string().optional().describe("Member last name."),
43
43
  phone: z.string().optional().describe("Member phone number."),
44
44
  birthDate: z.string().optional().describe("Member birth date (YYYY-MM-DD format)."),
45
- gender: z.enum(["male", "female", "not_disclosed"]).optional().describe("Member gender."),
45
+ gender: z.enum(["male", "female", "not_disclosed"]).optional().describe("Member gender. Valid values: 'male', 'female', 'not_disclosed'."),
46
46
  address: z.object({
47
- street: z.string().optional(),
48
- city: z.string().optional(),
49
- postal: z.string().optional(),
50
- country: z.string().optional(),
51
- province: z.string().optional(),
47
+ street: z.string().optional().describe("Street address (e.g., '123 Main St')."),
48
+ city: z.string().optional().describe("City name."),
49
+ postal: z.string().optional().describe("Postal/ZIP code."),
50
+ country: z.string().optional().describe("Country name or ISO 3166-1 code (e.g., 'US', 'Poland')."),
51
+ province: z.string().optional().describe("State, province, or region."),
52
52
  }).optional().describe("Member address."),
53
53
  agreement1: z.boolean().optional().describe("Terms acceptance. BUSINESS DECISION: Legal requirement. Cannot default to true without explicit consent."),
54
54
  agreement2: z.boolean().optional().describe("Marketing consent. BUSINESS IMPACT: GDPR violation risk. Changing marketing consent requires explicit member approval."),
@@ -61,5 +61,5 @@ export const MemberIdInputSchema = {
61
61
  export const MemberAssignTierInputSchema = {
62
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
- levelId: z.string().describe("The tier level ID (UUID) to assign."),
64
+ levelId: z.string().describe("The tier level ID (UUID) to assign. Use ol_tierset_get_tiers(tierSetId) to find valid levelId values."),
65
65
  };