@open-loyalty/mcp-server 1.0.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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +654 -0
  3. package/dist/client/http.d.ts +8 -0
  4. package/dist/client/http.js +69 -0
  5. package/dist/config.d.ts +17 -0
  6. package/dist/config.js +40 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +20 -0
  9. package/dist/server.d.ts +4 -0
  10. package/dist/server.js +334 -0
  11. package/dist/tools/achievement.d.ts +983 -0
  12. package/dist/tools/achievement.js +311 -0
  13. package/dist/tools/admin.d.ts +153 -0
  14. package/dist/tools/admin.js +193 -0
  15. package/dist/tools/analytics.d.ts +162 -0
  16. package/dist/tools/analytics.js +245 -0
  17. package/dist/tools/apikey.d.ts +72 -0
  18. package/dist/tools/apikey.js +78 -0
  19. package/dist/tools/audit.d.ts +107 -0
  20. package/dist/tools/audit.js +90 -0
  21. package/dist/tools/badge.d.ts +135 -0
  22. package/dist/tools/badge.js +165 -0
  23. package/dist/tools/campaign.d.ts +1775 -0
  24. package/dist/tools/campaign.js +724 -0
  25. package/dist/tools/export.d.ts +110 -0
  26. package/dist/tools/export.js +147 -0
  27. package/dist/tools/import.d.ts +110 -0
  28. package/dist/tools/import.js +126 -0
  29. package/dist/tools/index.d.ts +22 -0
  30. package/dist/tools/index.js +527 -0
  31. package/dist/tools/member.d.ts +345 -0
  32. package/dist/tools/member.js +358 -0
  33. package/dist/tools/member.test.d.ts +1 -0
  34. package/dist/tools/member.test.js +213 -0
  35. package/dist/tools/points.d.ts +188 -0
  36. package/dist/tools/points.js +306 -0
  37. package/dist/tools/points.test.d.ts +1 -0
  38. package/dist/tools/points.test.js +292 -0
  39. package/dist/tools/reward.d.ts +261 -0
  40. package/dist/tools/reward.js +371 -0
  41. package/dist/tools/reward.test.d.ts +1 -0
  42. package/dist/tools/reward.test.js +240 -0
  43. package/dist/tools/role.d.ts +161 -0
  44. package/dist/tools/role.js +160 -0
  45. package/dist/tools/segment.d.ts +797 -0
  46. package/dist/tools/segment.js +299 -0
  47. package/dist/tools/store.d.ts +101 -0
  48. package/dist/tools/store.js +117 -0
  49. package/dist/tools/tierset.d.ts +288 -0
  50. package/dist/tools/tierset.js +244 -0
  51. package/dist/tools/transaction.d.ts +357 -0
  52. package/dist/tools/transaction.js +242 -0
  53. package/dist/tools/transaction.test.d.ts +1 -0
  54. package/dist/tools/transaction.test.js +235 -0
  55. package/dist/tools/wallet-type.d.ts +32 -0
  56. package/dist/tools/wallet-type.js +58 -0
  57. package/dist/tools/webhook.d.ts +179 -0
  58. package/dist/tools/webhook.js +171 -0
  59. package/dist/types/schemas/achievement.d.ts +1116 -0
  60. package/dist/types/schemas/achievement.js +172 -0
  61. package/dist/types/schemas/admin.d.ts +263 -0
  62. package/dist/types/schemas/admin.js +99 -0
  63. package/dist/types/schemas/analytics.d.ts +542 -0
  64. package/dist/types/schemas/analytics.js +130 -0
  65. package/dist/types/schemas/badge.d.ts +131 -0
  66. package/dist/types/schemas/badge.js +48 -0
  67. package/dist/types/schemas/campaign.d.ts +2005 -0
  68. package/dist/types/schemas/campaign.js +189 -0
  69. package/dist/types/schemas/common.d.ts +52 -0
  70. package/dist/types/schemas/common.js +26 -0
  71. package/dist/types/schemas/export.d.ts +127 -0
  72. package/dist/types/schemas/export.js +43 -0
  73. package/dist/types/schemas/import.d.ts +344 -0
  74. package/dist/types/schemas/import.js +68 -0
  75. package/dist/types/schemas/member.d.ts +443 -0
  76. package/dist/types/schemas/member.js +92 -0
  77. package/dist/types/schemas/points.d.ts +188 -0
  78. package/dist/types/schemas/points.js +54 -0
  79. package/dist/types/schemas/reward.d.ts +278 -0
  80. package/dist/types/schemas/reward.js +69 -0
  81. package/dist/types/schemas/role.d.ts +260 -0
  82. package/dist/types/schemas/role.js +75 -0
  83. package/dist/types/schemas/segment.d.ts +592 -0
  84. package/dist/types/schemas/segment.js +114 -0
  85. package/dist/types/schemas/tierset.d.ts +552 -0
  86. package/dist/types/schemas/tierset.js +87 -0
  87. package/dist/types/schemas/transaction.d.ts +1022 -0
  88. package/dist/types/schemas/transaction.js +63 -0
  89. package/dist/types/schemas/wallet-type.d.ts +99 -0
  90. package/dist/types/schemas/wallet-type.js +17 -0
  91. package/dist/types/schemas/webhook.d.ts +195 -0
  92. package/dist/types/schemas/webhook.js +39 -0
  93. package/dist/utils/cursor.d.ts +84 -0
  94. package/dist/utils/cursor.js +117 -0
  95. package/dist/utils/errors.d.ts +12 -0
  96. package/dist/utils/errors.js +69 -0
  97. package/dist/utils/pagination.d.ts +39 -0
  98. package/dist/utils/pagination.js +77 -0
  99. package/package.json +65 -0
@@ -0,0 +1,299 @@
1
+ import { z } from "zod";
2
+ import { apiGet, apiPost, apiPut, apiDelete } from "../client/http.js";
3
+ import { formatApiError } from "../utils/errors.js";
4
+ import { getConfig } from "../config.js";
5
+ // Input Schemas
6
+ export const SegmentListInputSchema = {
7
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
8
+ page: z.number().optional().describe("Page number (default: 1)."),
9
+ perPage: z.number().optional().describe("Items per page (default: 10)."),
10
+ active: z.boolean().optional().describe("Filter by active status."),
11
+ name: z.string().optional().describe("Filter by segment name."),
12
+ };
13
+ // Criterion input schema - flexible to support all criterion types
14
+ const SegmentCriterionInputSchema = z.object({
15
+ type: z.string().describe("Criterion type (e.g., 'tier', 'transaction_count', 'points_balance')."),
16
+ criterionId: z.string().optional().describe("Criterion ID (optional, generated if not provided)."),
17
+ // Common criterion data fields
18
+ days: z.number().optional().describe("Days for time-based criteria."),
19
+ fromDate: z.string().optional().describe("Start date (ISO format)."),
20
+ toDate: z.string().optional().describe("End date (ISO format)."),
21
+ min: z.number().optional().describe("Minimum value."),
22
+ max: z.number().optional().describe("Maximum value."),
23
+ posIds: z.array(z.string()).optional().describe("POS IDs."),
24
+ skus: z.array(z.string()).optional().describe("SKU codes."),
25
+ makers: z.array(z.string()).optional().describe("Maker/brand names."),
26
+ labels: z.array(z.object({ key: z.string(), value: z.string() })).optional().describe("Label key-value pairs."),
27
+ tierIds: z.array(z.string()).optional().describe("Tier level IDs."),
28
+ campaignId: z.string().optional().describe("Campaign ID for campaign_completion criterion."),
29
+ countries: z.array(z.string()).optional().describe("Country codes."),
30
+ anniversaryType: z.string().optional().describe("Anniversary type."),
31
+ customAttributeKey: z.string().optional().describe("Custom attribute key."),
32
+ customAttributeValue: z.string().optional().describe("Custom attribute value."),
33
+ }).passthrough();
34
+ // Part input schema
35
+ const SegmentPartInputSchema = z.object({
36
+ segmentPartId: z.string().optional().describe("Part ID (optional)."),
37
+ criteria: z.array(SegmentCriterionInputSchema).describe("Criteria for this part (AND logic)."),
38
+ });
39
+ export const SegmentCreateInputSchema = {
40
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
41
+ name: z.string().describe("Segment name (required)."),
42
+ description: z.string().optional().describe("Segment description."),
43
+ active: z.boolean().optional().describe("Whether segment is active (default: false)."),
44
+ parts: z.array(SegmentPartInputSchema).describe("Segment parts. Parts use OR logic (ANY part matches), criteria within parts use AND logic (ALL criteria must match)."),
45
+ };
46
+ export const SegmentGetInputSchema = {
47
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
48
+ segmentId: z.string().describe("The segment ID (UUID) to retrieve."),
49
+ };
50
+ export const SegmentUpdateInputSchema = {
51
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
52
+ segmentId: z.string().describe("The segment ID (UUID) to update."),
53
+ name: z.string().describe("Segment name."),
54
+ description: z.string().optional().describe("Segment description."),
55
+ active: z.boolean().optional().describe("Whether segment is active."),
56
+ parts: z.array(SegmentPartInputSchema).describe("Segment parts."),
57
+ };
58
+ export const SegmentDeleteInputSchema = {
59
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
60
+ segmentId: z.string().describe("The segment ID (UUID) to delete."),
61
+ };
62
+ export const SegmentGetMembersInputSchema = {
63
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
64
+ segmentId: z.string().describe("The segment ID (UUID) to get members for."),
65
+ page: z.number().optional().describe("Page number (default: 1)."),
66
+ perPage: z.number().optional().describe("Items per page (default: 25)."),
67
+ };
68
+ export const SegmentActivateInputSchema = {
69
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
70
+ segmentId: z.string().describe("The segment ID (UUID) to activate."),
71
+ };
72
+ export const SegmentDeactivateInputSchema = {
73
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
74
+ segmentId: z.string().describe("The segment ID (UUID) to deactivate."),
75
+ };
76
+ export const SegmentGetResourcesInputSchema = {
77
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
78
+ segmentId: z.string().describe("The segment ID (UUID) to get associated resources for."),
79
+ };
80
+ // Handler functions
81
+ export async function segmentList(input) {
82
+ const config = getConfig();
83
+ const storeCode = input.storeCode || config.defaultStoreCode;
84
+ const params = new URLSearchParams();
85
+ if (input.page)
86
+ params.append("_page", String(input.page));
87
+ if (input.perPage)
88
+ params.append("_itemsOnPage", String(input.perPage));
89
+ if (input.active !== undefined)
90
+ params.append("active", String(input.active));
91
+ if (input.name)
92
+ params.append("name", input.name);
93
+ const queryString = params.toString();
94
+ const url = `/${storeCode}/segment${queryString ? `?${queryString}` : ""}`;
95
+ try {
96
+ const response = await apiGet(url);
97
+ const segments = (response.items || []).map((item) => ({
98
+ segmentId: item.segmentId,
99
+ name: item.name,
100
+ description: item.description,
101
+ active: item.active,
102
+ customersCount: item.customersCount,
103
+ createdAt: item.createdAt,
104
+ }));
105
+ const total = response.total || {};
106
+ return {
107
+ segments,
108
+ total: {
109
+ all: typeof total.all === "number" ? total.all : undefined,
110
+ filtered: typeof total.filtered === "number" ? total.filtered : undefined,
111
+ },
112
+ };
113
+ }
114
+ catch (error) {
115
+ throw formatApiError(error, "openloyalty_segment_list");
116
+ }
117
+ }
118
+ export async function segmentCreate(input) {
119
+ const config = getConfig();
120
+ const storeCode = input.storeCode || config.defaultStoreCode;
121
+ const segmentPayload = {
122
+ name: input.name,
123
+ parts: input.parts,
124
+ };
125
+ if (input.description)
126
+ segmentPayload.description = input.description;
127
+ if (input.active !== undefined)
128
+ segmentPayload.active = input.active;
129
+ try {
130
+ // CRITICAL: Wrap body as { segment: {...} }
131
+ const response = await apiPost(`/${storeCode}/segment`, { segment: segmentPayload });
132
+ return { segmentId: response.segmentId };
133
+ }
134
+ catch (error) {
135
+ throw formatApiError(error, "openloyalty_segment_create");
136
+ }
137
+ }
138
+ export async function segmentGet(input) {
139
+ const config = getConfig();
140
+ const storeCode = input.storeCode || config.defaultStoreCode;
141
+ try {
142
+ const response = await apiGet(`/${storeCode}/segment/${input.segmentId}`);
143
+ return response;
144
+ }
145
+ catch (error) {
146
+ throw formatApiError(error, "openloyalty_segment_get");
147
+ }
148
+ }
149
+ export async function segmentUpdate(input) {
150
+ const config = getConfig();
151
+ const storeCode = input.storeCode || config.defaultStoreCode;
152
+ const segmentPayload = {
153
+ name: input.name,
154
+ parts: input.parts,
155
+ };
156
+ if (input.description !== undefined)
157
+ segmentPayload.description = input.description;
158
+ if (input.active !== undefined)
159
+ segmentPayload.active = input.active;
160
+ try {
161
+ // CRITICAL: Wrap body as { segment: {...} }
162
+ await apiPut(`/${storeCode}/segment/${input.segmentId}`, { segment: segmentPayload });
163
+ }
164
+ catch (error) {
165
+ throw formatApiError(error, "openloyalty_segment_update");
166
+ }
167
+ }
168
+ export async function segmentDelete(input) {
169
+ const config = getConfig();
170
+ const storeCode = input.storeCode || config.defaultStoreCode;
171
+ try {
172
+ await apiDelete(`/${storeCode}/segment/${input.segmentId}`);
173
+ }
174
+ catch (error) {
175
+ throw formatApiError(error, "openloyalty_segment_delete");
176
+ }
177
+ }
178
+ export async function segmentGetMembers(input) {
179
+ const config = getConfig();
180
+ const storeCode = input.storeCode || config.defaultStoreCode;
181
+ const params = new URLSearchParams();
182
+ if (input.page)
183
+ params.append("_page", String(input.page));
184
+ if (input.perPage)
185
+ params.append("_itemsOnPage", String(input.perPage));
186
+ const queryString = params.toString();
187
+ const url = `/${storeCode}/segment/${input.segmentId}/members${queryString ? `?${queryString}` : ""}`;
188
+ try {
189
+ const response = await apiGet(url);
190
+ const members = (response.items || []).map((item) => ({
191
+ customerId: item.customerId,
192
+ firstName: item.firstName,
193
+ lastName: item.lastName,
194
+ email: item.email,
195
+ loyaltyCardNumber: item.loyaltyCardNumber,
196
+ active: item.active,
197
+ }));
198
+ const total = response.total || {};
199
+ return {
200
+ members,
201
+ total: {
202
+ all: typeof total.all === "number" ? total.all : undefined,
203
+ filtered: typeof total.filtered === "number" ? total.filtered : undefined,
204
+ },
205
+ };
206
+ }
207
+ catch (error) {
208
+ throw formatApiError(error, "openloyalty_segment_get_members");
209
+ }
210
+ }
211
+ export async function segmentActivate(input) {
212
+ const config = getConfig();
213
+ const storeCode = input.storeCode || config.defaultStoreCode;
214
+ try {
215
+ await apiPost(`/${storeCode}/segment/${input.segmentId}/activate`, {});
216
+ }
217
+ catch (error) {
218
+ throw formatApiError(error, "openloyalty_segment_activate");
219
+ }
220
+ }
221
+ export async function segmentDeactivate(input) {
222
+ const config = getConfig();
223
+ const storeCode = input.storeCode || config.defaultStoreCode;
224
+ try {
225
+ await apiPost(`/${storeCode}/segment/${input.segmentId}/deactivate`, {});
226
+ }
227
+ catch (error) {
228
+ throw formatApiError(error, "openloyalty_segment_deactivate");
229
+ }
230
+ }
231
+ export async function segmentGetResources(input) {
232
+ const config = getConfig();
233
+ const storeCode = input.storeCode || config.defaultStoreCode;
234
+ try {
235
+ const response = await apiGet(`/${storeCode}/segment/${input.segmentId}/resources`);
236
+ const resources = response.items || response.resources || [];
237
+ return { resources };
238
+ }
239
+ catch (error) {
240
+ throw formatApiError(error, "openloyalty_segment_get_resources");
241
+ }
242
+ }
243
+ // Tool definitions
244
+ export const segmentToolDefinitions = [
245
+ {
246
+ name: "openloyalty_segment_list",
247
+ description: "List customer segments. Segments group members by criteria (purchase behavior, tier, location, etc). Use for campaign targeting and analytics.",
248
+ inputSchema: SegmentListInputSchema,
249
+ handler: segmentList,
250
+ },
251
+ {
252
+ name: "openloyalty_segment_create",
253
+ description: "Create segment to group members. Parts use OR logic (member matches if ANY part matches). Criteria within parts use AND logic (must match ALL criteria in that part). Common criteria: tier, transaction_count, points_balance, country. Example - VIP segment: parts: [{ criteria: [{ type: 'tier', tierIds: ['gold-level-id'] }] }]",
254
+ inputSchema: SegmentCreateInputSchema,
255
+ handler: segmentCreate,
256
+ },
257
+ {
258
+ name: "openloyalty_segment_get",
259
+ description: "Get full segment details including all parts and criteria configurations.",
260
+ inputSchema: SegmentGetInputSchema,
261
+ handler: segmentGet,
262
+ },
263
+ {
264
+ name: "openloyalty_segment_update",
265
+ description: "Update segment configuration. Requires full segment definition (name, parts with criteria). Use segment_get first to retrieve current configuration.",
266
+ inputSchema: SegmentUpdateInputSchema,
267
+ handler: segmentUpdate,
268
+ },
269
+ {
270
+ name: "openloyalty_segment_delete",
271
+ description: "Permanently delete a segment. Cannot be undone. Check segment_get_resources first to see what uses this segment.",
272
+ inputSchema: SegmentDeleteInputSchema,
273
+ handler: segmentDelete,
274
+ },
275
+ {
276
+ name: "openloyalty_segment_get_members",
277
+ description: "Get members belonging to a segment. Returns paginated list of member details. Use for verifying segment criteria or exporting member lists.",
278
+ inputSchema: SegmentGetMembersInputSchema,
279
+ handler: segmentGetMembers,
280
+ },
281
+ {
282
+ name: "openloyalty_segment_activate",
283
+ description: "Activate a segment. Active segments are used for campaign targeting and can be queried for members.",
284
+ inputSchema: SegmentActivateInputSchema,
285
+ handler: segmentActivate,
286
+ },
287
+ {
288
+ name: "openloyalty_segment_deactivate",
289
+ description: "Deactivate a segment. Deactivated segments are not used for campaign targeting but retain their configuration.",
290
+ inputSchema: SegmentDeactivateInputSchema,
291
+ handler: segmentDeactivate,
292
+ },
293
+ {
294
+ name: "openloyalty_segment_get_resources",
295
+ description: "Get resources (campaigns, rewards, etc.) that use this segment for targeting. Check before deleting a segment.",
296
+ inputSchema: SegmentGetResourcesInputSchema,
297
+ handler: segmentGetResources,
298
+ },
299
+ ];
@@ -0,0 +1,101 @@
1
+ import { z } from "zod";
2
+ export interface Store {
3
+ storeId: string;
4
+ code: string;
5
+ currency: string;
6
+ name: string;
7
+ active: boolean;
8
+ createdBy?: string;
9
+ createdAt?: string;
10
+ updatedBy?: string;
11
+ updatedAt?: string;
12
+ }
13
+ export interface StoreListResponse {
14
+ items: Store[];
15
+ total?: Record<string, unknown>;
16
+ }
17
+ export declare const StoreListInputSchema: {
18
+ page: z.ZodOptional<z.ZodNumber>;
19
+ perPage: z.ZodOptional<z.ZodNumber>;
20
+ active: z.ZodOptional<z.ZodBoolean>;
21
+ name: z.ZodOptional<z.ZodString>;
22
+ code: z.ZodOptional<z.ZodString>;
23
+ };
24
+ export declare const StoreCreateInputSchema: {
25
+ code: z.ZodString;
26
+ name: z.ZodString;
27
+ currency: z.ZodOptional<z.ZodString>;
28
+ active: z.ZodOptional<z.ZodBoolean>;
29
+ };
30
+ export declare const StoreGetInputSchema: {
31
+ storeId: z.ZodString;
32
+ };
33
+ export declare const StoreUpdateInputSchema: {
34
+ storeId: z.ZodString;
35
+ name: z.ZodOptional<z.ZodString>;
36
+ active: z.ZodOptional<z.ZodBoolean>;
37
+ currency: z.ZodOptional<z.ZodString>;
38
+ };
39
+ export declare function storeList(input: {
40
+ page?: number;
41
+ perPage?: number;
42
+ active?: boolean;
43
+ name?: string;
44
+ code?: string;
45
+ }): Promise<StoreListResponse>;
46
+ export declare function storeCreate(input: {
47
+ code: string;
48
+ name: string;
49
+ currency?: string;
50
+ active?: boolean;
51
+ }): Promise<{
52
+ storeId: string;
53
+ }>;
54
+ export declare function storeGet(input: {
55
+ storeId: string;
56
+ }): Promise<Store>;
57
+ export declare function storeUpdate(input: {
58
+ storeId: string;
59
+ name?: string;
60
+ active?: boolean;
61
+ currency?: string;
62
+ }): Promise<void>;
63
+ export declare const storeToolDefinitions: readonly [{
64
+ readonly name: "openloyalty_store_list";
65
+ readonly description: "List all stores with optional filtering. Returns paginated list of stores with storeId, code, name, currency, and active status. Stores enable multi-tenant loyalty programs. Each store has independent members, campaigns, and settings.";
66
+ readonly inputSchema: {
67
+ page: z.ZodOptional<z.ZodNumber>;
68
+ perPage: z.ZodOptional<z.ZodNumber>;
69
+ active: z.ZodOptional<z.ZodBoolean>;
70
+ name: z.ZodOptional<z.ZodString>;
71
+ code: z.ZodOptional<z.ZodString>;
72
+ };
73
+ readonly handler: typeof storeList;
74
+ }, {
75
+ readonly name: "openloyalty_store_create";
76
+ readonly description: "Create a new store for multi-tenant setup. Requires unique code and name. Returns storeId on success.";
77
+ readonly inputSchema: {
78
+ code: z.ZodString;
79
+ name: z.ZodString;
80
+ currency: z.ZodOptional<z.ZodString>;
81
+ active: z.ZodOptional<z.ZodBoolean>;
82
+ };
83
+ readonly handler: typeof storeCreate;
84
+ }, {
85
+ readonly name: "openloyalty_store_get";
86
+ readonly description: "Get full store configuration by ID. Returns storeId, code, name, currency, active status, and timestamps.";
87
+ readonly inputSchema: {
88
+ storeId: z.ZodString;
89
+ };
90
+ readonly handler: typeof storeGet;
91
+ }, {
92
+ readonly name: "openloyalty_store_update";
93
+ readonly description: "Update store configuration. Can update name, currency, and active status. Returns void on success (204 No Content).";
94
+ readonly inputSchema: {
95
+ storeId: z.ZodString;
96
+ name: z.ZodOptional<z.ZodString>;
97
+ active: z.ZodOptional<z.ZodBoolean>;
98
+ currency: z.ZodOptional<z.ZodString>;
99
+ };
100
+ readonly handler: typeof storeUpdate;
101
+ }];
@@ -0,0 +1,117 @@
1
+ import { z } from "zod";
2
+ import { apiGet, apiPost, apiPut } from "../client/http.js";
3
+ import { formatApiError } from "../utils/errors.js";
4
+ // Input Schemas
5
+ export const StoreListInputSchema = {
6
+ page: z.number().optional().describe("Page number (default: 1)."),
7
+ perPage: z.number().optional().describe("Items per page (default: 25)."),
8
+ active: z.boolean().optional().describe("Filter by active status."),
9
+ name: z.string().optional().describe("Filter by store name."),
10
+ code: z.string().optional().describe("Filter by store code."),
11
+ };
12
+ export const StoreCreateInputSchema = {
13
+ code: z.string().describe("Store code (required, unique identifier like 'US', 'EU')."),
14
+ name: z.string().describe("Store name (required)."),
15
+ currency: z.string().optional().describe("Currency code (e.g., 'USD', 'EUR')."),
16
+ active: z.boolean().optional().describe("Whether the store is active (default: true)."),
17
+ };
18
+ export const StoreGetInputSchema = {
19
+ storeId: z.string().describe("The store ID (UUID) to retrieve."),
20
+ };
21
+ export const StoreUpdateInputSchema = {
22
+ storeId: z.string().describe("The store ID (UUID) to update."),
23
+ name: z.string().optional().describe("Store name."),
24
+ active: z.boolean().optional().describe("Whether the store is active."),
25
+ currency: z.string().optional().describe("Currency code."),
26
+ };
27
+ // Handler functions
28
+ export async function storeList(input) {
29
+ const params = new URLSearchParams();
30
+ if (input.page)
31
+ params.append("_page", String(input.page));
32
+ if (input.perPage)
33
+ params.append("_itemsOnPage", String(input.perPage));
34
+ if (input.active !== undefined)
35
+ params.append("active[eq]", String(input.active));
36
+ if (input.name)
37
+ params.append("name[eq]", input.name);
38
+ if (input.code)
39
+ params.append("code", input.code);
40
+ const queryString = params.toString();
41
+ const url = `/store${queryString ? `?${queryString}` : ""}`;
42
+ try {
43
+ const response = await apiGet(url);
44
+ return response;
45
+ }
46
+ catch (error) {
47
+ throw formatApiError(error, "openloyalty_store_list");
48
+ }
49
+ }
50
+ export async function storeCreate(input) {
51
+ const payload = {
52
+ code: input.code,
53
+ name: input.name,
54
+ };
55
+ if (input.currency)
56
+ payload.currency = input.currency;
57
+ if (input.active !== undefined)
58
+ payload.active = input.active;
59
+ try {
60
+ const response = await apiPost("/store", { store: payload });
61
+ return response;
62
+ }
63
+ catch (error) {
64
+ throw formatApiError(error, "openloyalty_store_create");
65
+ }
66
+ }
67
+ export async function storeGet(input) {
68
+ try {
69
+ const response = await apiGet(`/store/${input.storeId}`);
70
+ return response;
71
+ }
72
+ catch (error) {
73
+ throw formatApiError(error, "openloyalty_store_get");
74
+ }
75
+ }
76
+ export async function storeUpdate(input) {
77
+ const payload = {};
78
+ if (input.name)
79
+ payload.name = input.name;
80
+ if (input.active !== undefined)
81
+ payload.active = input.active;
82
+ if (input.currency)
83
+ payload.currency = input.currency;
84
+ try {
85
+ await apiPut(`/store/${input.storeId}`, { store: payload });
86
+ }
87
+ catch (error) {
88
+ throw formatApiError(error, "openloyalty_store_update");
89
+ }
90
+ }
91
+ // Tool definitions
92
+ export const storeToolDefinitions = [
93
+ {
94
+ name: "openloyalty_store_list",
95
+ description: "List all stores with optional filtering. Returns paginated list of stores with storeId, code, name, currency, and active status. Stores enable multi-tenant loyalty programs. Each store has independent members, campaigns, and settings.",
96
+ inputSchema: StoreListInputSchema,
97
+ handler: storeList,
98
+ },
99
+ {
100
+ name: "openloyalty_store_create",
101
+ description: "Create a new store for multi-tenant setup. Requires unique code and name. Returns storeId on success.",
102
+ inputSchema: StoreCreateInputSchema,
103
+ handler: storeCreate,
104
+ },
105
+ {
106
+ name: "openloyalty_store_get",
107
+ description: "Get full store configuration by ID. Returns storeId, code, name, currency, active status, and timestamps.",
108
+ inputSchema: StoreGetInputSchema,
109
+ handler: storeGet,
110
+ },
111
+ {
112
+ name: "openloyalty_store_update",
113
+ description: "Update store configuration. Can update name, currency, and active status. Returns void on success (204 No Content).",
114
+ inputSchema: StoreUpdateInputSchema,
115
+ handler: storeUpdate,
116
+ },
117
+ ];