@open-loyalty/mcp-server 1.3.3 → 1.3.4
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.
- package/dist/tools/points.js +13 -3
- package/dist/tools/reward/handlers.js +9 -2
- package/dist/tools/tierset.d.ts +0 -27
- package/dist/tools/tierset.js +27 -20
- package/package.json +1 -1
package/dist/tools/points.js
CHANGED
|
@@ -80,9 +80,14 @@ export async function pointsSpend(input) {
|
|
|
80
80
|
return { transferId: response.transferId };
|
|
81
81
|
}
|
|
82
82
|
catch (error) {
|
|
83
|
-
// Check for insufficient points error
|
|
83
|
+
// Check for insufficient points error - API returns various formats:
|
|
84
|
+
// "NotEnoughPoints", "insufficient", "not enough points" (lowercase)
|
|
85
|
+
// Must check both error.message AND axios response data
|
|
84
86
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
85
|
-
|
|
87
|
+
const axiosError = error;
|
|
88
|
+
const responseMessage = axiosError.response?.data?.message || "";
|
|
89
|
+
const combinedMessage = `${errorMessage} ${responseMessage}`.toLowerCase();
|
|
90
|
+
if (combinedMessage.includes("notenoughpoints") || combinedMessage.includes("insufficient") || combinedMessage.includes("not enough points")) {
|
|
86
91
|
throw new OpenLoyaltyError({
|
|
87
92
|
code: "INSUFFICIENT_BALANCE",
|
|
88
93
|
message: "Member does not have enough points to complete this operation",
|
|
@@ -110,8 +115,13 @@ export async function pointsTransfer(input) {
|
|
|
110
115
|
return { transferId: response.transferId };
|
|
111
116
|
}
|
|
112
117
|
catch (error) {
|
|
118
|
+
// Check for insufficient points error - API returns various formats
|
|
119
|
+
// Must check both error.message AND axios response data
|
|
113
120
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
114
|
-
|
|
121
|
+
const axiosError = error;
|
|
122
|
+
const responseMessage = axiosError.response?.data?.message || "";
|
|
123
|
+
const combinedMessage = `${errorMessage} ${responseMessage}`.toLowerCase();
|
|
124
|
+
if (combinedMessage.includes("notenoughpoints") || combinedMessage.includes("insufficient") || combinedMessage.includes("not enough points")) {
|
|
115
125
|
throw new OpenLoyaltyError({
|
|
116
126
|
code: "INSUFFICIENT_BALANCE",
|
|
117
127
|
message: "Sender does not have enough points to transfer",
|
|
@@ -132,10 +132,11 @@ export async function rewardUpdate(input) {
|
|
|
132
132
|
visibilityPayload.to = existingVisibility.to;
|
|
133
133
|
payload.visibility = visibilityPayload;
|
|
134
134
|
}
|
|
135
|
-
// usageLimit:
|
|
135
|
+
// usageLimit: BOTH perUser AND general are REQUIRED by the API
|
|
136
136
|
// The GET response may include derived fields that PUT doesn't accept
|
|
137
137
|
payload.usageLimit = {
|
|
138
138
|
perUser: existingUsageLimit?.perUser ?? 1,
|
|
139
|
+
general: existingUsageLimit?.general ?? 10000,
|
|
139
140
|
};
|
|
140
141
|
// Coupon-specific fields (required for static_coupon, dynamic_coupon, conversion_coupon)
|
|
141
142
|
if (existing.couponValue !== undefined)
|
|
@@ -212,8 +213,14 @@ export async function rewardBuy(input) {
|
|
|
212
213
|
};
|
|
213
214
|
}
|
|
214
215
|
catch (error) {
|
|
216
|
+
// Check for insufficient points error - API returns various formats:
|
|
217
|
+
// "NotEnoughPoints", "insufficient", "not enough points" (lowercase)
|
|
218
|
+
// Must check both error.message AND axios response data
|
|
215
219
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
216
|
-
|
|
220
|
+
const axiosError = error;
|
|
221
|
+
const responseMessage = axiosError.response?.data?.message || "";
|
|
222
|
+
const combinedMessage = `${errorMessage} ${responseMessage}`.toLowerCase();
|
|
223
|
+
if (combinedMessage.includes("notenoughpoints") || combinedMessage.includes("insufficient") || combinedMessage.includes("not enough points")) {
|
|
217
224
|
throw new OpenLoyaltyError({
|
|
218
225
|
code: "INSUFFICIENT_BALANCE",
|
|
219
226
|
message: "Member does not have enough points to purchase this reward",
|
package/dist/tools/tierset.d.ts
CHANGED
|
@@ -19,17 +19,6 @@ export declare const TierSetCreateInputSchema: {
|
|
|
19
19
|
attribute: "activeUnits" | "totalEarnedUnits" | "totalSpending" | "monthsSinceJoiningProgram" | "cumulatedEarnedUnits";
|
|
20
20
|
walletType?: string | undefined;
|
|
21
21
|
}>, "many">;
|
|
22
|
-
downgrade: z.ZodOptional<z.ZodObject<{
|
|
23
|
-
mode: z.ZodEnum<["none", "automatic", "x_days"]>;
|
|
24
|
-
days: z.ZodOptional<z.ZodNumber>;
|
|
25
|
-
}, "strip", z.ZodTypeAny, {
|
|
26
|
-
mode: "none" | "automatic" | "x_days";
|
|
27
|
-
days?: number | undefined;
|
|
28
|
-
}, {
|
|
29
|
-
mode: "none" | "automatic" | "x_days";
|
|
30
|
-
days?: number | undefined;
|
|
31
|
-
}>>;
|
|
32
|
-
active: z.ZodOptional<z.ZodBoolean>;
|
|
33
22
|
};
|
|
34
23
|
export declare const TierSetGetInputSchema: {
|
|
35
24
|
storeCode: z.ZodOptional<z.ZodString>;
|
|
@@ -107,11 +96,6 @@ type TierSetCreateInput = {
|
|
|
107
96
|
attribute: string;
|
|
108
97
|
walletType?: string;
|
|
109
98
|
}[];
|
|
110
|
-
downgrade?: {
|
|
111
|
-
mode: string;
|
|
112
|
-
days?: number;
|
|
113
|
-
};
|
|
114
|
-
active?: boolean;
|
|
115
99
|
};
|
|
116
100
|
type TierSetGetInput = {
|
|
117
101
|
storeCode?: string;
|
|
@@ -196,17 +180,6 @@ export declare const tiersetToolDefinitions: readonly [{
|
|
|
196
180
|
attribute: "activeUnits" | "totalEarnedUnits" | "totalSpending" | "monthsSinceJoiningProgram" | "cumulatedEarnedUnits";
|
|
197
181
|
walletType?: string | undefined;
|
|
198
182
|
}>, "many">;
|
|
199
|
-
downgrade: z.ZodOptional<z.ZodObject<{
|
|
200
|
-
mode: z.ZodEnum<["none", "automatic", "x_days"]>;
|
|
201
|
-
days: z.ZodOptional<z.ZodNumber>;
|
|
202
|
-
}, "strip", z.ZodTypeAny, {
|
|
203
|
-
mode: "none" | "automatic" | "x_days";
|
|
204
|
-
days?: number | undefined;
|
|
205
|
-
}, {
|
|
206
|
-
mode: "none" | "automatic" | "x_days";
|
|
207
|
-
days?: number | undefined;
|
|
208
|
-
}>>;
|
|
209
|
-
active: z.ZodOptional<z.ZodBoolean>;
|
|
210
183
|
};
|
|
211
184
|
readonly handler: typeof tiersetCreate;
|
|
212
185
|
}, {
|
package/dist/tools/tierset.js
CHANGED
|
@@ -20,11 +20,7 @@ export const TierSetCreateInputSchema = {
|
|
|
20
20
|
walletType: z.string().optional().describe("Wallet type CODE (not UUID). Required for unit-based attributes (activeUnits, totalEarnedUnits, cumulatedEarnedUnits). " +
|
|
21
21
|
"Use wallet_type_list to find walletType.code (e.g., 'default')."),
|
|
22
22
|
})).describe("Array of conditions that define tier progression criteria. IMPORTANT: Use 'totalEarnedUnits' for lifetime points (NOT 'earnedUnits')."),
|
|
23
|
-
|
|
24
|
-
mode: DowngradeModeEnum.describe("Downgrade mode."),
|
|
25
|
-
days: z.number().optional().describe("Number of days for x_days mode (required if mode is x_days)."),
|
|
26
|
-
}).optional().describe("Downgrade configuration for the tier set."),
|
|
27
|
-
active: z.boolean().optional().describe("Whether the tier set is active. Defaults to true."),
|
|
23
|
+
// NOTE: downgrade and active are NOT supported at creation time - use tierset_update after creation
|
|
28
24
|
};
|
|
29
25
|
export const TierSetGetInputSchema = {
|
|
30
26
|
storeCode: z.string().optional().describe("Store code for multi-tenant routing. DO NOT pass this parameter - the configured default will be used automatically. Only provide a value if the user explicitly asks to work with a different store."),
|
|
@@ -87,25 +83,34 @@ export async function tiersetList(input) {
|
|
|
87
83
|
}
|
|
88
84
|
export async function tiersetCreate(input) {
|
|
89
85
|
const storeCode = getStoreCode(input.storeCode);
|
|
90
|
-
// API
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
86
|
+
// API QUIRK: Only accepts specific fields at creation time.
|
|
87
|
+
// Fields NOT supported at creation: tiers, active, downgrade
|
|
88
|
+
// These must be set via tierset_update or tierset_update_tiers after creation.
|
|
89
|
+
//
|
|
90
|
+
// REQUIRED fields:
|
|
91
|
+
// - translations.en.name (string)
|
|
92
|
+
// - conditions (array with attribute, optionally walletType)
|
|
93
|
+
const tierSetPayload = {
|
|
94
|
+
translations: {
|
|
95
|
+
en: {
|
|
96
|
+
name: input.name,
|
|
97
|
+
description: input.description || "",
|
|
99
98
|
},
|
|
100
|
-
conditions: input.conditions,
|
|
101
|
-
tiers: [], // Required by API - tiers are added separately via tierset_update_tiers
|
|
102
|
-
downgrade: input.downgrade,
|
|
103
|
-
active: input.active ?? true,
|
|
104
99
|
},
|
|
100
|
+
conditions: input.conditions,
|
|
105
101
|
};
|
|
102
|
+
const payload = { tierSet: tierSetPayload };
|
|
106
103
|
try {
|
|
107
104
|
const response = await apiPost(`/${storeCode}/tierSet`, payload);
|
|
108
|
-
|
|
105
|
+
// API QUIRK: Create endpoint only returns { tierSetId: string }
|
|
106
|
+
// We need to fetch the full tier set to get the conditionId values
|
|
107
|
+
const createResponse = response;
|
|
108
|
+
if (!createResponse.tierSetId) {
|
|
109
|
+
throw new Error(`Unexpected response format: ${JSON.stringify(response)}`);
|
|
110
|
+
}
|
|
111
|
+
// Fetch the created tier set to get condition IDs
|
|
112
|
+
const tierSetResponse = await apiGet(`/${storeCode}/tierSet/${createResponse.tierSetId}`);
|
|
113
|
+
const validated = TierSetSchema.parse(tierSetResponse);
|
|
109
114
|
return {
|
|
110
115
|
tierSetId: validated.tierSetId,
|
|
111
116
|
conditions: validated.conditions.map((c) => ({
|
|
@@ -228,7 +233,9 @@ export const tiersetToolDefinitions = [
|
|
|
228
233
|
description: "Create a new tier set (loyalty program structure). " +
|
|
229
234
|
"⚠️ LIMIT: Maximum 3 ACTIVE tier sets per store. ALWAYS call tierset_list FIRST to check existing tier sets - if one exists that matches your needs, REUSE IT instead of creating a new one. " +
|
|
230
235
|
"WORKFLOW: 1) tierset_list (check existing) → 2) tierset_create (only if needed) → 3) tierset_get (to get conditionId) → 4) tierset_update_tiers (to define thresholds). " +
|
|
231
|
-
"
|
|
236
|
+
"⚠️ API LIMITATION: Only 'name', 'description', and 'conditions' are accepted at creation time. " +
|
|
237
|
+
"Fields like 'active', 'downgrade', 'tiers' are NOT supported at creation - use tierset_update after creation to set these. " +
|
|
238
|
+
"Valid condition attributes: 'totalEarnedUnits' (lifetime points), 'activeUnits' (current balance), 'totalSpending', 'monthsSinceJoiningProgram'. " +
|
|
232
239
|
"COMMON MISTAKE: Use 'totalEarnedUnits' NOT 'earnedUnits' for lifetime points. " +
|
|
233
240
|
"For unit-based attributes, set walletType to 'default'.",
|
|
234
241
|
readOnly: false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-loyalty/mcp-server",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server for Open Loyalty API - enables AI agents to manage loyalty programs, members, points, rewards, and transactions",
|
|
6
6
|
"author": "Marcin Dyguda <md@openloyalty.io>",
|