@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 @@
1
+ export {};
@@ -0,0 +1,213 @@
1
+ // src/tools/member.test.ts
2
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
3
+ import { setupMockAxios, teardownMockAxios, getMockAxios } from '../../tests/mocks/http.mock.js';
4
+ import { memberFixtures } from '../../tests/fixtures/member.fixtures.js';
5
+ import { memberCreate, memberGet, memberList, memberUpdate, memberActivate, memberDeactivate, memberDelete, memberGetTierProgress, memberAssignTier, memberRemoveManualTier, } from './member.js';
6
+ describe('Member Operations', () => {
7
+ beforeEach(() => {
8
+ setupMockAxios();
9
+ });
10
+ afterEach(() => {
11
+ teardownMockAxios();
12
+ });
13
+ describe('memberCreate', () => {
14
+ it('should create member with required fields only', async () => {
15
+ const mockAxios = getMockAxios();
16
+ mockAxios.onPost('/default/member').reply(200, memberFixtures.createResponse);
17
+ const result = await memberCreate({
18
+ email: 'test@example.com',
19
+ });
20
+ expect(result).toEqual({
21
+ memberId: '550e8400-e29b-41d4-a716-446655440000',
22
+ loyaltyCardNumber: 'CARD123456789',
23
+ email: 'test@example.com',
24
+ });
25
+ // Verify request body format with customer wrapper
26
+ const requestData = JSON.parse(mockAxios.history.post[0].data);
27
+ expect(requestData).toEqual({
28
+ customer: {
29
+ email: 'test@example.com',
30
+ },
31
+ });
32
+ });
33
+ it('should create member with all optional fields', async () => {
34
+ const mockAxios = getMockAxios();
35
+ mockAxios.onPost('/default/member').reply(200, {
36
+ customerId: 'full-uuid',
37
+ loyaltyCardNumber: 'CUSTOM123',
38
+ email: 'full@example.com',
39
+ });
40
+ const result = await memberCreate({
41
+ email: 'full@example.com',
42
+ firstName: 'John',
43
+ lastName: 'Doe',
44
+ phone: '+1234567890',
45
+ birthDate: '1990-01-15',
46
+ gender: 'male',
47
+ loyaltyCardNumber: 'CUSTOM123',
48
+ agreement1: true,
49
+ agreement2: false,
50
+ agreement3: true,
51
+ address: {
52
+ street: '123 Main St',
53
+ city: 'New York',
54
+ postal: '10001',
55
+ country: 'US',
56
+ },
57
+ });
58
+ expect(result.memberId).toBe('full-uuid');
59
+ // Verify all fields in request
60
+ const requestData = JSON.parse(mockAxios.history.post[0].data);
61
+ expect(requestData.customer.firstName).toBe('John');
62
+ expect(requestData.customer.lastName).toBe('Doe');
63
+ expect(requestData.customer.address.city).toBe('New York');
64
+ expect(requestData.customer.agreement1).toBe(true);
65
+ expect(requestData.customer.agreement2).toBe(false);
66
+ });
67
+ it('should use custom storeCode when provided', async () => {
68
+ const mockAxios = getMockAxios();
69
+ mockAxios.onPost('/custom-store/member').reply(200, {
70
+ customerId: 'uuid',
71
+ email: 'test@example.com',
72
+ });
73
+ await memberCreate({
74
+ storeCode: 'custom-store',
75
+ email: 'test@example.com',
76
+ });
77
+ expect(mockAxios.history.post[0].url).toBe('/custom-store/member');
78
+ });
79
+ it('should throw formatted error on API error', async () => {
80
+ const mockAxios = getMockAxios();
81
+ mockAxios.onPost('/default/member').reply(400, {
82
+ message: 'Email already exists',
83
+ code: 400,
84
+ });
85
+ await expect(memberCreate({ email: 'duplicate@example.com' }))
86
+ .rejects.toThrow();
87
+ });
88
+ });
89
+ describe('memberGet', () => {
90
+ it('should get member details and map customerId to memberId', async () => {
91
+ const mockAxios = getMockAxios();
92
+ mockAxios.onGet('/default/member/member-uuid').reply(200, memberFixtures.getResponse);
93
+ const result = await memberGet({ memberId: 'member-uuid' });
94
+ expect(result.memberId).toBe('550e8400-e29b-41d4-a716-446655440000');
95
+ expect(result.email).toBe('test@example.com');
96
+ expect(result.firstName).toBe('John');
97
+ expect(result.levelName).toBe('Silver');
98
+ });
99
+ it('should use custom storeCode', async () => {
100
+ const mockAxios = getMockAxios();
101
+ mockAxios.onGet('/other-store/member/id123').reply(200, memberFixtures.getResponse);
102
+ await memberGet({ storeCode: 'other-store', memberId: 'id123' });
103
+ expect(mockAxios.history.get[0].url).toBe('/other-store/member/id123');
104
+ });
105
+ });
106
+ describe('memberList', () => {
107
+ it('should list members and map customerId to memberId', async () => {
108
+ const mockAxios = getMockAxios();
109
+ mockAxios.onGet('/default/member').reply(200, memberFixtures.listResponse);
110
+ const result = await memberList({});
111
+ expect(result.members).toHaveLength(2);
112
+ expect(result.members[0].memberId).toBe('uuid-1');
113
+ expect(result.members[0].email).toBe('user1@example.com');
114
+ expect(result.total.all).toBe(100);
115
+ expect(result.total.filtered).toBe(2);
116
+ });
117
+ it('should include pagination params with correct names', async () => {
118
+ const mockAxios = getMockAxios();
119
+ mockAxios.onGet(/\/default\/member/).reply(200, memberFixtures.listResponse);
120
+ await memberList({ page: 2, perPage: 25 });
121
+ const url = mockAxios.history.get[0].url;
122
+ expect(url).toContain('_page=2');
123
+ expect(url).toContain('_itemsOnPage=25');
124
+ });
125
+ it('should include filter params', async () => {
126
+ const mockAxios = getMockAxios();
127
+ mockAxios.onGet(/\/default\/member/).reply(200, memberFixtures.listResponse);
128
+ await memberList({
129
+ email: 'test@example.com',
130
+ firstName: 'John',
131
+ active: true,
132
+ });
133
+ const url = mockAxios.history.get[0].url;
134
+ expect(url).toContain('email=test%40example.com');
135
+ expect(url).toContain('firstName=John');
136
+ expect(url).toContain('active=true');
137
+ });
138
+ });
139
+ describe('memberUpdate', () => {
140
+ it('should update member with customer wrapper', async () => {
141
+ const mockAxios = getMockAxios();
142
+ mockAxios.onPut('/default/member/member-uuid').reply(200);
143
+ await memberUpdate({
144
+ memberId: 'member-uuid',
145
+ firstName: 'Jane',
146
+ lastName: 'Smith',
147
+ });
148
+ const requestData = JSON.parse(mockAxios.history.put[0].data);
149
+ expect(requestData).toEqual({
150
+ customer: {
151
+ firstName: 'Jane',
152
+ lastName: 'Smith',
153
+ },
154
+ });
155
+ });
156
+ });
157
+ describe('memberActivate', () => {
158
+ it('should call activate endpoint', async () => {
159
+ const mockAxios = getMockAxios();
160
+ mockAxios.onPost('/default/member/member-uuid/activate').reply(200);
161
+ await memberActivate({ memberId: 'member-uuid' });
162
+ expect(mockAxios.history.post[0].url).toBe('/default/member/member-uuid/activate');
163
+ });
164
+ });
165
+ describe('memberDeactivate', () => {
166
+ it('should call deactivate endpoint', async () => {
167
+ const mockAxios = getMockAxios();
168
+ mockAxios.onPost('/default/member/member-uuid/deactivate').reply(200);
169
+ await memberDeactivate({ memberId: 'member-uuid' });
170
+ expect(mockAxios.history.post[0].url).toBe('/default/member/member-uuid/deactivate');
171
+ });
172
+ });
173
+ describe('memberDelete', () => {
174
+ it('should call delete endpoint', async () => {
175
+ const mockAxios = getMockAxios();
176
+ mockAxios.onDelete('/default/member/member-uuid').reply(200);
177
+ await memberDelete({ memberId: 'member-uuid' });
178
+ expect(mockAxios.history.delete[0].url).toBe('/default/member/member-uuid');
179
+ });
180
+ });
181
+ describe('memberGetTierProgress', () => {
182
+ it('should get tier progress information', async () => {
183
+ const mockAxios = getMockAxios();
184
+ mockAxios.onGet('/default/member/member-uuid/tier').reply(200, memberFixtures.tierProgressResponse);
185
+ const result = await memberGetTierProgress({ memberId: 'member-uuid' });
186
+ expect(result.currentTier?.name).toBe('Silver');
187
+ expect(result.nextTier?.name).toBe('Gold');
188
+ expect(result.currentValue).toBe(750);
189
+ expect(result.requiredValue).toBe(1000);
190
+ expect(result.progressPercent).toBe(75);
191
+ });
192
+ });
193
+ describe('memberAssignTier', () => {
194
+ it('should assign tier to member', async () => {
195
+ const mockAxios = getMockAxios();
196
+ mockAxios.onPost('/default/member/member-uuid/tier').reply(200);
197
+ await memberAssignTier({
198
+ memberId: 'member-uuid',
199
+ levelId: 'gold-level-uuid',
200
+ });
201
+ const requestData = JSON.parse(mockAxios.history.post[0].data);
202
+ expect(requestData.levelId).toBe('gold-level-uuid');
203
+ });
204
+ });
205
+ describe('memberRemoveManualTier', () => {
206
+ it('should remove manual tier assignment', async () => {
207
+ const mockAxios = getMockAxios();
208
+ mockAxios.onDelete('/default/member/member-uuid/tier').reply(200);
209
+ await memberRemoveManualTier({ memberId: 'member-uuid' });
210
+ expect(mockAxios.history.delete[0].url).toBe('/default/member/member-uuid/tier');
211
+ });
212
+ });
213
+ });
@@ -0,0 +1,188 @@
1
+ import { z } from "zod";
2
+ export declare const PointsAddInputSchema: {
3
+ storeCode: z.ZodOptional<z.ZodString>;
4
+ memberId: z.ZodString;
5
+ points: z.ZodNumber;
6
+ walletCode: z.ZodOptional<z.ZodString>;
7
+ comment: z.ZodOptional<z.ZodString>;
8
+ expiresInDays: z.ZodOptional<z.ZodNumber>;
9
+ lockedUntilDays: z.ZodOptional<z.ZodNumber>;
10
+ };
11
+ export declare const PointsSpendInputSchema: {
12
+ storeCode: z.ZodOptional<z.ZodString>;
13
+ memberId: z.ZodString;
14
+ points: z.ZodNumber;
15
+ walletCode: z.ZodOptional<z.ZodString>;
16
+ comment: z.ZodOptional<z.ZodString>;
17
+ };
18
+ export declare const PointsTransferInputSchema: {
19
+ storeCode: z.ZodOptional<z.ZodString>;
20
+ senderId: z.ZodString;
21
+ receiverId: z.ZodString;
22
+ points: z.ZodNumber;
23
+ };
24
+ export declare const PointsBalanceInputSchema: {
25
+ storeCode: z.ZodOptional<z.ZodString>;
26
+ memberId: z.ZodString;
27
+ walletCode: z.ZodOptional<z.ZodString>;
28
+ };
29
+ export declare const PointsHistoryInputSchema: {
30
+ storeCode: z.ZodOptional<z.ZodString>;
31
+ memberId: z.ZodString;
32
+ cursor: z.ZodOptional<z.ZodString>;
33
+ page: z.ZodOptional<z.ZodNumber>;
34
+ perPage: z.ZodOptional<z.ZodNumber>;
35
+ type: z.ZodOptional<z.ZodEnum<["adding", "spending", "p2p_spending", "p2p_adding", "blocked", "expired"]>>;
36
+ };
37
+ export declare const PointsHistogramInputSchema: {
38
+ storeCode: z.ZodOptional<z.ZodString>;
39
+ memberId: z.ZodString;
40
+ pointType: z.ZodEnum<["spent", "earned", "expired", "pending"]>;
41
+ walletCode: z.ZodOptional<z.ZodString>;
42
+ interval: z.ZodOptional<z.ZodEnum<["day", "week", "month"]>>;
43
+ dateFrom: z.ZodOptional<z.ZodString>;
44
+ dateTo: z.ZodOptional<z.ZodString>;
45
+ };
46
+ export declare function pointsAdd(input: {
47
+ storeCode?: string;
48
+ memberId: string;
49
+ points: number;
50
+ walletCode?: string;
51
+ comment?: string;
52
+ expiresInDays?: number;
53
+ lockedUntilDays?: number;
54
+ }): Promise<{
55
+ transferId: string;
56
+ }>;
57
+ export declare function pointsSpend(input: {
58
+ storeCode?: string;
59
+ memberId: string;
60
+ points: number;
61
+ walletCode?: string;
62
+ comment?: string;
63
+ }): Promise<{
64
+ transferId: string;
65
+ }>;
66
+ export declare function pointsTransfer(input: {
67
+ storeCode?: string;
68
+ senderId: string;
69
+ receiverId: string;
70
+ points: number;
71
+ }): Promise<{
72
+ transferId: string;
73
+ }>;
74
+ export declare function pointsGetBalance(input: {
75
+ storeCode?: string;
76
+ memberId: string;
77
+ walletCode?: string;
78
+ }): Promise<{
79
+ activeUnits: number;
80
+ earnedUnits: number;
81
+ spentUnits: number;
82
+ lockedUnits: number;
83
+ expiredUnits: number;
84
+ }>;
85
+ export declare function pointsGetHistory(input: {
86
+ storeCode?: string;
87
+ memberId: string;
88
+ cursor?: string;
89
+ page?: number;
90
+ perPage?: number;
91
+ type?: string;
92
+ }): Promise<{
93
+ transfers: Array<{
94
+ transferId: string;
95
+ type: string;
96
+ points: number;
97
+ comment?: string;
98
+ createdAt: string;
99
+ }>;
100
+ total: {
101
+ all?: number;
102
+ filtered?: number;
103
+ };
104
+ cursor?: string;
105
+ }>;
106
+ export declare function pointsGetHistogram(input: {
107
+ storeCode?: string;
108
+ memberId: string;
109
+ pointType: string;
110
+ walletCode?: string;
111
+ interval?: string;
112
+ dateFrom?: string;
113
+ dateTo?: string;
114
+ }): Promise<Array<{
115
+ date: string;
116
+ earned: number;
117
+ spent: number;
118
+ balance: number;
119
+ }>>;
120
+ export declare const pointsToolDefinitions: readonly [{
121
+ readonly name: "openloyalty_points_add";
122
+ readonly description: string;
123
+ readonly inputSchema: {
124
+ storeCode: z.ZodOptional<z.ZodString>;
125
+ memberId: z.ZodString;
126
+ points: z.ZodNumber;
127
+ walletCode: z.ZodOptional<z.ZodString>;
128
+ comment: z.ZodOptional<z.ZodString>;
129
+ expiresInDays: z.ZodOptional<z.ZodNumber>;
130
+ lockedUntilDays: z.ZodOptional<z.ZodNumber>;
131
+ };
132
+ readonly handler: typeof pointsAdd;
133
+ }, {
134
+ readonly name: "openloyalty_points_spend";
135
+ readonly description: string;
136
+ readonly inputSchema: {
137
+ storeCode: z.ZodOptional<z.ZodString>;
138
+ memberId: z.ZodString;
139
+ points: z.ZodNumber;
140
+ walletCode: z.ZodOptional<z.ZodString>;
141
+ comment: z.ZodOptional<z.ZodString>;
142
+ };
143
+ readonly handler: typeof pointsSpend;
144
+ }, {
145
+ readonly name: "openloyalty_points_transfer";
146
+ readonly description: string;
147
+ readonly inputSchema: {
148
+ storeCode: z.ZodOptional<z.ZodString>;
149
+ senderId: z.ZodString;
150
+ receiverId: z.ZodString;
151
+ points: z.ZodNumber;
152
+ };
153
+ readonly handler: typeof pointsTransfer;
154
+ }, {
155
+ readonly name: "openloyalty_points_get_balance";
156
+ readonly description: string;
157
+ readonly inputSchema: {
158
+ storeCode: z.ZodOptional<z.ZodString>;
159
+ memberId: z.ZodString;
160
+ walletCode: z.ZodOptional<z.ZodString>;
161
+ };
162
+ readonly handler: typeof pointsGetBalance;
163
+ }, {
164
+ readonly name: "openloyalty_points_get_history";
165
+ readonly description: string;
166
+ readonly inputSchema: {
167
+ storeCode: z.ZodOptional<z.ZodString>;
168
+ memberId: z.ZodString;
169
+ cursor: z.ZodOptional<z.ZodString>;
170
+ page: z.ZodOptional<z.ZodNumber>;
171
+ perPage: z.ZodOptional<z.ZodNumber>;
172
+ type: z.ZodOptional<z.ZodEnum<["adding", "spending", "p2p_spending", "p2p_adding", "blocked", "expired"]>>;
173
+ };
174
+ readonly handler: typeof pointsGetHistory;
175
+ }, {
176
+ readonly name: "openloyalty_points_get_histogram";
177
+ readonly description: string;
178
+ readonly inputSchema: {
179
+ storeCode: z.ZodOptional<z.ZodString>;
180
+ memberId: z.ZodString;
181
+ pointType: z.ZodEnum<["spent", "earned", "expired", "pending"]>;
182
+ walletCode: z.ZodOptional<z.ZodString>;
183
+ interval: z.ZodOptional<z.ZodEnum<["day", "week", "month"]>>;
184
+ dateFrom: z.ZodOptional<z.ZodString>;
185
+ dateTo: z.ZodOptional<z.ZodString>;
186
+ };
187
+ readonly handler: typeof pointsGetHistogram;
188
+ }];