@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,235 @@
1
+ // src/tools/transaction.test.ts
2
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
3
+ import { setupMockAxios, teardownMockAxios, getMockAxios } from '../../tests/mocks/http.mock.js';
4
+ import { transactionFixtures } from '../../tests/fixtures/transaction.fixtures.js';
5
+ import { transactionCreate, transactionGet, transactionList, transactionAssignMember, } from './transaction.js';
6
+ describe('Transaction Operations', () => {
7
+ beforeEach(() => {
8
+ setupMockAxios();
9
+ });
10
+ afterEach(() => {
11
+ teardownMockAxios();
12
+ });
13
+ describe('transactionCreate', () => {
14
+ it('should create transaction with transaction wrapper', async () => {
15
+ const mockAxios = getMockAxios();
16
+ mockAxios.onPost('/default/transaction').reply(200, transactionFixtures.createResponse);
17
+ const result = await transactionCreate({
18
+ header: {
19
+ documentNumber: 'DOC-001',
20
+ purchasedAt: '2026-01-15T10:00:00Z',
21
+ },
22
+ items: [
23
+ {
24
+ sku: 'PROD-001',
25
+ name: 'Test Product',
26
+ grossValue: 100,
27
+ category: 'Electronics',
28
+ },
29
+ ],
30
+ });
31
+ expect(result.transactionId).toBe('txn-uuid-123');
32
+ // Verify request body format with transaction wrapper
33
+ const requestData = JSON.parse(mockAxios.history.post[0].data);
34
+ expect(requestData.transaction).toBeDefined();
35
+ expect(requestData.transaction.header.documentNumber).toBe('DOC-001');
36
+ expect(requestData.transaction.header.documentType).toBe('sell');
37
+ expect(requestData.transaction.items).toHaveLength(1);
38
+ expect(requestData.transaction.items[0].sku).toBe('PROD-001');
39
+ });
40
+ it('should include customerData for member matching', async () => {
41
+ const mockAxios = getMockAxios();
42
+ mockAxios.onPost('/default/transaction').reply(200, {
43
+ transactionId: 'txn-uuid',
44
+ pointsEarned: 100,
45
+ });
46
+ const result = await transactionCreate({
47
+ header: {
48
+ documentNumber: 'DOC-002',
49
+ purchasedAt: '2026-01-15T10:00:00Z',
50
+ },
51
+ items: [
52
+ {
53
+ sku: 'PROD-001',
54
+ name: 'Product',
55
+ grossValue: 100,
56
+ category: 'Category',
57
+ },
58
+ ],
59
+ customerData: {
60
+ email: 'customer@example.com',
61
+ },
62
+ });
63
+ expect(result.pointsEarned).toBe(100);
64
+ const requestData = JSON.parse(mockAxios.history.post[0].data);
65
+ expect(requestData.transaction.customerData.email).toBe('customer@example.com');
66
+ });
67
+ it('should handle return transactions', async () => {
68
+ const mockAxios = getMockAxios();
69
+ mockAxios.onPost('/default/transaction').reply(200, transactionFixtures.createResponse);
70
+ await transactionCreate({
71
+ header: {
72
+ documentNumber: 'RETURN-001',
73
+ purchasedAt: '2026-01-15T10:00:00Z',
74
+ documentType: 'return',
75
+ linkedDocumentNumber: 'DOC-001',
76
+ },
77
+ items: [
78
+ {
79
+ sku: 'PROD-001',
80
+ name: 'Product',
81
+ grossValue: -50,
82
+ category: 'Category',
83
+ },
84
+ ],
85
+ });
86
+ const requestData = JSON.parse(mockAxios.history.post[0].data);
87
+ expect(requestData.transaction.header.documentType).toBe('return');
88
+ expect(requestData.transaction.header.linkedDocumentNumber).toBe('DOC-001');
89
+ });
90
+ it('should use highPrecisionQuantity', async () => {
91
+ const mockAxios = getMockAxios();
92
+ mockAxios.onPost('/default/transaction').reply(200, transactionFixtures.createResponse);
93
+ await transactionCreate({
94
+ header: {
95
+ documentNumber: 'DOC-003',
96
+ purchasedAt: '2026-01-15T10:00:00Z',
97
+ },
98
+ items: [
99
+ {
100
+ sku: 'PROD-001',
101
+ name: 'Product',
102
+ grossValue: 100,
103
+ category: 'Category',
104
+ highPrecisionQuantity: 1.5,
105
+ },
106
+ ],
107
+ });
108
+ const requestData = JSON.parse(mockAxios.history.post[0].data);
109
+ expect(requestData.transaction.items[0].highPrecisionQuantity).toBe(1.5);
110
+ });
111
+ it('should use custom storeCode', async () => {
112
+ const mockAxios = getMockAxios();
113
+ mockAxios.onPost('/custom-store/transaction').reply(200, transactionFixtures.createResponse);
114
+ await transactionCreate({
115
+ storeCode: 'custom-store',
116
+ header: {
117
+ documentNumber: 'DOC-001',
118
+ purchasedAt: '2026-01-15T10:00:00Z',
119
+ },
120
+ items: [
121
+ {
122
+ sku: 'PROD-001',
123
+ name: 'Product',
124
+ grossValue: 100,
125
+ category: 'Category',
126
+ },
127
+ ],
128
+ });
129
+ expect(mockAxios.history.post[0].url).toBe('/custom-store/transaction');
130
+ });
131
+ });
132
+ describe('transactionGet', () => {
133
+ it('should get transaction details', async () => {
134
+ const mockAxios = getMockAxios();
135
+ mockAxios.onGet('/default/transaction/txn-uuid').reply(200, transactionFixtures.getResponse);
136
+ const result = await transactionGet({ transactionId: 'txn-uuid' });
137
+ expect(result.transactionId).toBe('txn-uuid-123');
138
+ expect(result.documentNumber).toBe('DOC-001');
139
+ expect(result.documentType).toBe('sell');
140
+ expect(result.matched).toBe(true);
141
+ expect(result.grossValue).toBe(150);
142
+ expect(result.pointsEarned).toBe(150);
143
+ });
144
+ it('should extract documentNumber from header', async () => {
145
+ const mockAxios = getMockAxios();
146
+ mockAxios.onGet('/default/transaction/txn-uuid').reply(200, {
147
+ transactionId: 'txn-uuid',
148
+ header: {
149
+ documentNumber: 'DOC-FROM-HEADER',
150
+ documentType: 'sell',
151
+ purchasedAt: '2026-01-15T10:00:00Z',
152
+ },
153
+ items: [],
154
+ matched: true,
155
+ grossValue: 100,
156
+ });
157
+ const result = await transactionGet({ transactionId: 'txn-uuid' });
158
+ expect(result.documentNumber).toBe('DOC-FROM-HEADER');
159
+ });
160
+ });
161
+ describe('transactionList', () => {
162
+ it('should list transactions from items array', async () => {
163
+ const mockAxios = getMockAxios();
164
+ mockAxios.onGet('/default/transaction').reply(200, transactionFixtures.listResponse);
165
+ const result = await transactionList({});
166
+ expect(result.transactions).toHaveLength(2);
167
+ expect(result.transactions[0].transactionId).toBe('txn-uuid-1');
168
+ expect(result.transactions[0].documentNumber).toBe('DOC-001');
169
+ expect(result.transactions[0].matched).toBe(true);
170
+ });
171
+ it('should include pagination params', async () => {
172
+ const mockAxios = getMockAxios();
173
+ mockAxios.onGet(/\/default\/transaction/).reply(200, transactionFixtures.listResponse);
174
+ await transactionList({ page: 2, perPage: 25 });
175
+ const url = mockAxios.history.get[0].url;
176
+ expect(url).toContain('_page=2');
177
+ expect(url).toContain('_itemsOnPage=25');
178
+ });
179
+ it('should include filter params', async () => {
180
+ const mockAxios = getMockAxios();
181
+ mockAxios.onGet(/\/default\/transaction/).reply(200, transactionFixtures.listResponse);
182
+ await transactionList({
183
+ customerId: 'member-uuid',
184
+ documentNumber: 'DOC-001',
185
+ documentType: 'sell',
186
+ matched: true,
187
+ });
188
+ const url = mockAxios.history.get[0].url;
189
+ expect(url).toContain('customerId=member-uuid');
190
+ expect(url).toContain('header%3AdocumentNumber=DOC-001');
191
+ expect(url).toContain('header%3AdocumentType=sell');
192
+ expect(url).toContain('matched=true');
193
+ });
194
+ });
195
+ describe('transactionAssignMember', () => {
196
+ it('should assign transaction to member by customerId', async () => {
197
+ const mockAxios = getMockAxios();
198
+ mockAxios.onPost('/default/transaction/customer/assign').reply(200, transactionFixtures.assignResponse);
199
+ const result = await transactionAssignMember({
200
+ documentNumber: 'DOC-001',
201
+ customerId: 'member-uuid',
202
+ });
203
+ expect(result.transactionId).toBe('txn-uuid-123');
204
+ expect(result.customerId).toBe('member-uuid');
205
+ const requestData = JSON.parse(mockAxios.history.post[0].data);
206
+ expect(requestData.transactionDocumentNumber).toBe('DOC-001');
207
+ expect(requestData.customerId).toBe('member-uuid');
208
+ });
209
+ it('should assign by loyalty card number', async () => {
210
+ const mockAxios = getMockAxios();
211
+ mockAxios.onPost('/default/transaction/customer/assign').reply(200, transactionFixtures.assignResponse);
212
+ await transactionAssignMember({
213
+ documentNumber: 'DOC-001',
214
+ loyaltyCardNumber: 'CARD123456',
215
+ });
216
+ const requestData = JSON.parse(mockAxios.history.post[0].data);
217
+ expect(requestData.customerLoyaltyCardNumber).toBe('CARD123456');
218
+ });
219
+ it('should assign by phone number', async () => {
220
+ const mockAxios = getMockAxios();
221
+ mockAxios.onPost('/default/transaction/customer/assign').reply(200, transactionFixtures.assignResponse);
222
+ await transactionAssignMember({
223
+ documentNumber: 'DOC-001',
224
+ phone: '+1234567890',
225
+ });
226
+ const requestData = JSON.parse(mockAxios.history.post[0].data);
227
+ expect(requestData.customerPhoneNumber).toBe('+1234567890');
228
+ });
229
+ it('should throw error when no identifier provided', async () => {
230
+ await expect(transactionAssignMember({
231
+ documentNumber: 'DOC-001',
232
+ })).rejects.toThrow('At least one member identifier required');
233
+ });
234
+ });
235
+ });
@@ -0,0 +1,32 @@
1
+ import { z } from "zod";
2
+ import { WalletType } from "../types/schemas/wallet-type.js";
3
+ export declare const WalletTypeListInputSchema: {
4
+ storeCode: z.ZodOptional<z.ZodString>;
5
+ };
6
+ export declare const WalletTypeGetInputSchema: {
7
+ storeCode: z.ZodOptional<z.ZodString>;
8
+ walletTypeId: z.ZodString;
9
+ };
10
+ export declare function walletTypeList(input: {
11
+ storeCode?: string;
12
+ }): Promise<WalletType[]>;
13
+ export declare function walletTypeGet(input: {
14
+ storeCode?: string;
15
+ walletTypeId: string;
16
+ }): Promise<WalletType>;
17
+ export declare const walletTypeToolDefinitions: readonly [{
18
+ readonly name: "openloyalty_wallet_type_list";
19
+ readonly description: "List all available wallet types (point currencies). Use this to find walletCode values for tier conditions. Returns walletTypeId, code, and name for each wallet type.";
20
+ readonly inputSchema: {
21
+ storeCode: z.ZodOptional<z.ZodString>;
22
+ };
23
+ readonly handler: typeof walletTypeList;
24
+ }, {
25
+ readonly name: "openloyalty_wallet_type_get";
26
+ readonly description: "Get details for a specific wallet type by ID. Returns the full wallet type configuration including currency names.";
27
+ readonly inputSchema: {
28
+ storeCode: z.ZodOptional<z.ZodString>;
29
+ walletTypeId: z.ZodString;
30
+ };
31
+ readonly handler: typeof walletTypeGet;
32
+ }];
@@ -0,0 +1,58 @@
1
+ import { z } from "zod";
2
+ import { apiGet } from "../client/http.js";
3
+ import { WalletTypeSchema, WalletTypeListResponseSchema, } from "../types/schemas/wallet-type.js";
4
+ import { formatApiError } from "../utils/errors.js";
5
+ import { getConfig } from "../config.js";
6
+ export const WalletTypeListInputSchema = {
7
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
8
+ };
9
+ export const WalletTypeGetInputSchema = {
10
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
11
+ walletTypeId: z.string().describe("The wallet type ID to retrieve."),
12
+ };
13
+ export async function walletTypeList(input) {
14
+ const config = getConfig();
15
+ const storeCode = input.storeCode || config.defaultStoreCode;
16
+ try {
17
+ const response = await apiGet(`/${storeCode}/walletType`);
18
+ const validated = WalletTypeListResponseSchema.parse(response);
19
+ return validated.items.map((wt) => ({
20
+ walletTypeId: wt.walletTypeId,
21
+ code: wt.code,
22
+ name: wt.name,
23
+ unitSingularName: wt.unitSingularName,
24
+ unitPluralName: wt.unitPluralName,
25
+ active: wt.active,
26
+ isDefault: wt.isDefault,
27
+ }));
28
+ }
29
+ catch (error) {
30
+ throw formatApiError(error, "openloyalty_wallet_type_list");
31
+ }
32
+ }
33
+ export async function walletTypeGet(input) {
34
+ const config = getConfig();
35
+ const storeCode = input.storeCode || config.defaultStoreCode;
36
+ try {
37
+ const response = await apiGet(`/${storeCode}/walletType/${input.walletTypeId}`);
38
+ const validated = WalletTypeSchema.parse(response);
39
+ return validated;
40
+ }
41
+ catch (error) {
42
+ throw formatApiError(error, "openloyalty_wallet_type_get");
43
+ }
44
+ }
45
+ export const walletTypeToolDefinitions = [
46
+ {
47
+ name: "openloyalty_wallet_type_list",
48
+ description: "List all available wallet types (point currencies). Use this to find walletCode values for tier conditions. Returns walletTypeId, code, and name for each wallet type.",
49
+ inputSchema: WalletTypeListInputSchema,
50
+ handler: walletTypeList,
51
+ },
52
+ {
53
+ name: "openloyalty_wallet_type_get",
54
+ description: "Get details for a specific wallet type by ID. Returns the full wallet type configuration including currency names.",
55
+ inputSchema: WalletTypeGetInputSchema,
56
+ handler: walletTypeGet,
57
+ },
58
+ ];
@@ -0,0 +1,179 @@
1
+ import { z } from "zod";
2
+ export interface WebhookHeader {
3
+ headerName: string;
4
+ headerValue: string;
5
+ }
6
+ export interface WebhookSubscription {
7
+ webhookSubscriptionId: string;
8
+ eventName: string;
9
+ url: string;
10
+ headers?: WebhookHeader[];
11
+ createdAt?: string;
12
+ }
13
+ export interface WebhookSubscriptionListResponse {
14
+ items: WebhookSubscription[];
15
+ total?: {
16
+ all?: number | string;
17
+ filtered?: number | string;
18
+ estimated?: boolean;
19
+ };
20
+ }
21
+ export interface WebhookEventTypesResponse {
22
+ items: string[];
23
+ }
24
+ export declare const WebhookListInputSchema: {
25
+ storeCode: z.ZodOptional<z.ZodString>;
26
+ page: z.ZodOptional<z.ZodNumber>;
27
+ perPage: z.ZodOptional<z.ZodNumber>;
28
+ eventName: z.ZodOptional<z.ZodString>;
29
+ url: z.ZodOptional<z.ZodString>;
30
+ };
31
+ export declare const WebhookCreateInputSchema: {
32
+ storeCode: z.ZodOptional<z.ZodString>;
33
+ eventName: z.ZodString;
34
+ url: z.ZodString;
35
+ headers: z.ZodOptional<z.ZodArray<z.ZodObject<{
36
+ headerName: z.ZodString;
37
+ headerValue: z.ZodString;
38
+ }, "strip", z.ZodTypeAny, {
39
+ headerName: string;
40
+ headerValue: string;
41
+ }, {
42
+ headerName: string;
43
+ headerValue: string;
44
+ }>, "many">>;
45
+ };
46
+ export declare const WebhookGetInputSchema: {
47
+ storeCode: z.ZodOptional<z.ZodString>;
48
+ webhookSubscriptionId: z.ZodString;
49
+ };
50
+ export declare const WebhookUpdateInputSchema: {
51
+ storeCode: z.ZodOptional<z.ZodString>;
52
+ webhookSubscriptionId: z.ZodString;
53
+ eventName: z.ZodOptional<z.ZodString>;
54
+ url: z.ZodOptional<z.ZodString>;
55
+ headers: z.ZodOptional<z.ZodArray<z.ZodObject<{
56
+ headerName: z.ZodString;
57
+ headerValue: z.ZodString;
58
+ }, "strip", z.ZodTypeAny, {
59
+ headerName: string;
60
+ headerValue: string;
61
+ }, {
62
+ headerName: string;
63
+ headerValue: string;
64
+ }>, "many">>;
65
+ };
66
+ export declare const WebhookDeleteInputSchema: {
67
+ storeCode: z.ZodOptional<z.ZodString>;
68
+ webhookSubscriptionId: z.ZodString;
69
+ };
70
+ export declare const WebhookEventsInputSchema: {
71
+ storeCode: z.ZodOptional<z.ZodString>;
72
+ };
73
+ export declare function webhookList(input: {
74
+ storeCode?: string;
75
+ page?: number;
76
+ perPage?: number;
77
+ eventName?: string;
78
+ url?: string;
79
+ }): Promise<WebhookSubscriptionListResponse>;
80
+ export declare function webhookCreate(input: {
81
+ storeCode?: string;
82
+ eventName: string;
83
+ url: string;
84
+ headers?: WebhookHeader[];
85
+ }): Promise<{
86
+ webhookSubscriptionId: string;
87
+ }>;
88
+ export declare function webhookGet(input: {
89
+ storeCode?: string;
90
+ webhookSubscriptionId: string;
91
+ }): Promise<WebhookSubscription>;
92
+ export declare function webhookUpdate(input: {
93
+ storeCode?: string;
94
+ webhookSubscriptionId: string;
95
+ eventName?: string;
96
+ url?: string;
97
+ headers?: WebhookHeader[];
98
+ }): Promise<void>;
99
+ export declare function webhookDelete(input: {
100
+ storeCode?: string;
101
+ webhookSubscriptionId: string;
102
+ }): Promise<void>;
103
+ export declare function webhookEvents(input: {
104
+ storeCode?: string;
105
+ }): Promise<WebhookEventTypesResponse>;
106
+ export declare const webhookToolDefinitions: readonly [{
107
+ readonly name: "openloyalty_webhook_list";
108
+ readonly description: "List webhook subscriptions with optional filtering. Returns paginated list of subscriptions with webhookSubscriptionId, eventName, url, and createdAt.";
109
+ readonly inputSchema: {
110
+ storeCode: z.ZodOptional<z.ZodString>;
111
+ page: z.ZodOptional<z.ZodNumber>;
112
+ perPage: z.ZodOptional<z.ZodNumber>;
113
+ eventName: z.ZodOptional<z.ZodString>;
114
+ url: z.ZodOptional<z.ZodString>;
115
+ };
116
+ readonly handler: typeof webhookList;
117
+ }, {
118
+ readonly name: "openloyalty_webhook_create";
119
+ readonly description: "Create a new webhook subscription to receive event notifications. Returns webhookSubscriptionId on success. Use webhook_events to discover available event types before creating subscriptions.";
120
+ readonly inputSchema: {
121
+ storeCode: z.ZodOptional<z.ZodString>;
122
+ eventName: z.ZodString;
123
+ url: z.ZodString;
124
+ headers: z.ZodOptional<z.ZodArray<z.ZodObject<{
125
+ headerName: z.ZodString;
126
+ headerValue: z.ZodString;
127
+ }, "strip", z.ZodTypeAny, {
128
+ headerName: string;
129
+ headerValue: string;
130
+ }, {
131
+ headerName: string;
132
+ headerValue: string;
133
+ }>, "many">>;
134
+ };
135
+ readonly handler: typeof webhookCreate;
136
+ }, {
137
+ readonly name: "openloyalty_webhook_get";
138
+ readonly description: "Get full webhook subscription details including headers configuration.";
139
+ readonly inputSchema: {
140
+ storeCode: z.ZodOptional<z.ZodString>;
141
+ webhookSubscriptionId: z.ZodString;
142
+ };
143
+ readonly handler: typeof webhookGet;
144
+ }, {
145
+ readonly name: "openloyalty_webhook_update";
146
+ readonly description: "Update a webhook subscription. Can update eventName, url, and headers. Returns void on success (204 No Content).";
147
+ readonly inputSchema: {
148
+ storeCode: z.ZodOptional<z.ZodString>;
149
+ webhookSubscriptionId: z.ZodString;
150
+ eventName: z.ZodOptional<z.ZodString>;
151
+ url: z.ZodOptional<z.ZodString>;
152
+ headers: z.ZodOptional<z.ZodArray<z.ZodObject<{
153
+ headerName: z.ZodString;
154
+ headerValue: z.ZodString;
155
+ }, "strip", z.ZodTypeAny, {
156
+ headerName: string;
157
+ headerValue: string;
158
+ }, {
159
+ headerName: string;
160
+ headerValue: string;
161
+ }>, "many">>;
162
+ };
163
+ readonly handler: typeof webhookUpdate;
164
+ }, {
165
+ readonly name: "openloyalty_webhook_delete";
166
+ readonly description: "Delete a webhook subscription. Returns void on success (204 No Content). The subscription will stop receiving events immediately.";
167
+ readonly inputSchema: {
168
+ storeCode: z.ZodOptional<z.ZodString>;
169
+ webhookSubscriptionId: z.ZodString;
170
+ };
171
+ readonly handler: typeof webhookDelete;
172
+ }, {
173
+ readonly name: "openloyalty_webhook_events";
174
+ readonly description: "Get available webhook event types. Returns list of event names that can be used when creating webhook subscriptions. Use this to discover available events before creating subscriptions.";
175
+ readonly inputSchema: {
176
+ storeCode: z.ZodOptional<z.ZodString>;
177
+ };
178
+ readonly handler: typeof webhookEvents;
179
+ }];
@@ -0,0 +1,171 @@
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 WebhookListInputSchema = {
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: 25)."),
10
+ eventName: z.string().optional().describe("Filter by event name."),
11
+ url: z.string().optional().describe("Filter by URL."),
12
+ };
13
+ export const WebhookCreateInputSchema = {
14
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
15
+ eventName: z.string().describe("Event name to subscribe to. Use webhook_events to discover available events."),
16
+ url: z.string().describe("URL to receive webhook events."),
17
+ headers: z.array(z.object({
18
+ headerName: z.string().describe("Header name."),
19
+ headerValue: z.string().describe("Header value."),
20
+ })).optional().describe("Custom headers to include in webhook requests."),
21
+ };
22
+ export const WebhookGetInputSchema = {
23
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
24
+ webhookSubscriptionId: z.string().describe("The webhook subscription ID (UUID) to retrieve."),
25
+ };
26
+ export const WebhookUpdateInputSchema = {
27
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
28
+ webhookSubscriptionId: z.string().describe("The webhook subscription ID (UUID) to update."),
29
+ eventName: z.string().optional().describe("Event name to subscribe to."),
30
+ url: z.string().optional().describe("URL to receive webhook events."),
31
+ headers: z.array(z.object({
32
+ headerName: z.string().describe("Header name."),
33
+ headerValue: z.string().describe("Header value."),
34
+ })).optional().describe("Custom headers to include in webhook requests."),
35
+ };
36
+ export const WebhookDeleteInputSchema = {
37
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
38
+ webhookSubscriptionId: z.string().describe("The webhook subscription ID (UUID) to delete."),
39
+ };
40
+ export const WebhookEventsInputSchema = {
41
+ storeCode: z.string().optional().describe("Store code. If not provided, uses the default store code from configuration."),
42
+ };
43
+ // Handler functions
44
+ export async function webhookList(input) {
45
+ const config = getConfig();
46
+ const storeCode = input.storeCode || config.defaultStoreCode;
47
+ const params = new URLSearchParams();
48
+ if (input.page)
49
+ params.append("_page", String(input.page));
50
+ if (input.perPage)
51
+ params.append("_itemsOnPage", String(input.perPage));
52
+ if (input.eventName)
53
+ params.append("eventName", input.eventName);
54
+ if (input.url)
55
+ params.append("url", input.url);
56
+ const queryString = params.toString();
57
+ const url = `/${storeCode}/webhook/subscription${queryString ? `?${queryString}` : ""}`;
58
+ try {
59
+ const response = await apiGet(url);
60
+ return response;
61
+ }
62
+ catch (error) {
63
+ throw formatApiError(error, "openloyalty_webhook_list");
64
+ }
65
+ }
66
+ export async function webhookCreate(input) {
67
+ const config = getConfig();
68
+ const storeCode = input.storeCode || config.defaultStoreCode;
69
+ const payload = {
70
+ eventName: input.eventName,
71
+ url: input.url,
72
+ };
73
+ if (input.headers) {
74
+ payload.headers = input.headers;
75
+ }
76
+ try {
77
+ const response = await apiPost(`/${storeCode}/webhook/subscription`, { webhookSubscription: payload });
78
+ return response;
79
+ }
80
+ catch (error) {
81
+ throw formatApiError(error, "openloyalty_webhook_create");
82
+ }
83
+ }
84
+ export async function webhookGet(input) {
85
+ const config = getConfig();
86
+ const storeCode = input.storeCode || config.defaultStoreCode;
87
+ try {
88
+ const response = await apiGet(`/${storeCode}/webhook/subscription/${input.webhookSubscriptionId}`);
89
+ return response;
90
+ }
91
+ catch (error) {
92
+ throw formatApiError(error, "openloyalty_webhook_get");
93
+ }
94
+ }
95
+ export async function webhookUpdate(input) {
96
+ const config = getConfig();
97
+ const storeCode = input.storeCode || config.defaultStoreCode;
98
+ const payload = {};
99
+ if (input.eventName)
100
+ payload.eventName = input.eventName;
101
+ if (input.url)
102
+ payload.url = input.url;
103
+ if (input.headers)
104
+ payload.headers = input.headers;
105
+ try {
106
+ await apiPut(`/${storeCode}/webhook/subscription/${input.webhookSubscriptionId}`, { webhookSubscription: payload });
107
+ }
108
+ catch (error) {
109
+ throw formatApiError(error, "openloyalty_webhook_update");
110
+ }
111
+ }
112
+ export async function webhookDelete(input) {
113
+ const config = getConfig();
114
+ const storeCode = input.storeCode || config.defaultStoreCode;
115
+ try {
116
+ await apiDelete(`/${storeCode}/webhook/subscription/${input.webhookSubscriptionId}`);
117
+ }
118
+ catch (error) {
119
+ throw formatApiError(error, "openloyalty_webhook_delete");
120
+ }
121
+ }
122
+ export async function webhookEvents(input) {
123
+ const config = getConfig();
124
+ const storeCode = input.storeCode || config.defaultStoreCode;
125
+ try {
126
+ const response = await apiGet(`/${storeCode}/webhook/event`);
127
+ return response;
128
+ }
129
+ catch (error) {
130
+ throw formatApiError(error, "openloyalty_webhook_events");
131
+ }
132
+ }
133
+ // Tool definitions
134
+ export const webhookToolDefinitions = [
135
+ {
136
+ name: "openloyalty_webhook_list",
137
+ description: "List webhook subscriptions with optional filtering. Returns paginated list of subscriptions with webhookSubscriptionId, eventName, url, and createdAt.",
138
+ inputSchema: WebhookListInputSchema,
139
+ handler: webhookList,
140
+ },
141
+ {
142
+ name: "openloyalty_webhook_create",
143
+ description: "Create a new webhook subscription to receive event notifications. Returns webhookSubscriptionId on success. Use webhook_events to discover available event types before creating subscriptions.",
144
+ inputSchema: WebhookCreateInputSchema,
145
+ handler: webhookCreate,
146
+ },
147
+ {
148
+ name: "openloyalty_webhook_get",
149
+ description: "Get full webhook subscription details including headers configuration.",
150
+ inputSchema: WebhookGetInputSchema,
151
+ handler: webhookGet,
152
+ },
153
+ {
154
+ name: "openloyalty_webhook_update",
155
+ description: "Update a webhook subscription. Can update eventName, url, and headers. Returns void on success (204 No Content).",
156
+ inputSchema: WebhookUpdateInputSchema,
157
+ handler: webhookUpdate,
158
+ },
159
+ {
160
+ name: "openloyalty_webhook_delete",
161
+ description: "Delete a webhook subscription. Returns void on success (204 No Content). The subscription will stop receiving events immediately.",
162
+ inputSchema: WebhookDeleteInputSchema,
163
+ handler: webhookDelete,
164
+ },
165
+ {
166
+ name: "openloyalty_webhook_events",
167
+ description: "Get available webhook event types. Returns list of event names that can be used when creating webhook subscriptions. Use this to discover available events before creating subscriptions.",
168
+ inputSchema: WebhookEventsInputSchema,
169
+ handler: webhookEvents,
170
+ },
171
+ ];