@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,69 @@
1
+ import { AxiosError } from "axios";
2
+ export class OpenLoyaltyError extends Error {
3
+ code;
4
+ hint;
5
+ relatedTool;
6
+ constructor(options) {
7
+ super(options.message);
8
+ this.name = "OpenLoyaltyError";
9
+ this.code = options.code;
10
+ this.hint = options.hint;
11
+ this.relatedTool = options.relatedTool;
12
+ }
13
+ }
14
+ export function formatApiError(error, relatedTool) {
15
+ if (error instanceof OpenLoyaltyError) {
16
+ return error;
17
+ }
18
+ if (error instanceof AxiosError) {
19
+ const status = error.response?.status;
20
+ const data = error.response?.data;
21
+ if (status === 404) {
22
+ return new OpenLoyaltyError({
23
+ code: "NOT_FOUND",
24
+ message: data?.message
25
+ ? String(data.message)
26
+ : "Resource not found",
27
+ hint: "Check that the ID or storeCode is correct",
28
+ relatedTool,
29
+ });
30
+ }
31
+ if (status === 400) {
32
+ const errors = data?.errors;
33
+ const errorDetails = errors?.map(e => `${e.path || ''}: ${e.message}`).join('; ') || '';
34
+ // If API returns "Validation failed" with no details, it's likely an MCP implementation bug
35
+ const hasNoDetails = !errorDetails && data?.message === "Validation failed";
36
+ const hint = hasNoDetails
37
+ ? "The API rejected the request without details. This is likely a bug in the MCP tool implementation (e.g., missing required fields like translations). Please report this issue."
38
+ : "Check the input parameters match the expected format";
39
+ return new OpenLoyaltyError({
40
+ code: "VALIDATION_ERROR",
41
+ message: data?.message
42
+ ? `${String(data.message)}${errorDetails ? ` (${errorDetails})` : ''}`
43
+ : "Invalid request data",
44
+ hint,
45
+ relatedTool,
46
+ });
47
+ }
48
+ return new OpenLoyaltyError({
49
+ code: `HTTP_${status || "UNKNOWN"}`,
50
+ message: error.message,
51
+ hint: "Check the API response for more details",
52
+ relatedTool,
53
+ });
54
+ }
55
+ if (error instanceof Error) {
56
+ return new OpenLoyaltyError({
57
+ code: "UNKNOWN_ERROR",
58
+ message: error.message,
59
+ hint: "An unexpected error occurred",
60
+ relatedTool,
61
+ });
62
+ }
63
+ return new OpenLoyaltyError({
64
+ code: "UNKNOWN_ERROR",
65
+ message: String(error),
66
+ hint: "An unexpected error occurred",
67
+ relatedTool,
68
+ });
69
+ }
@@ -0,0 +1,39 @@
1
+ export interface PaginationParams {
2
+ page?: number;
3
+ perPage?: number;
4
+ }
5
+ export interface ScrollPaginationParams extends PaginationParams {
6
+ cursor?: string;
7
+ }
8
+ /**
9
+ * Build pagination query string using Open Loyalty API parameter names.
10
+ * API uses _page (1-indexed) and _itemsOnPage for pagination.
11
+ */
12
+ export declare function buildPaginationQuery(params: PaginationParams): string;
13
+ /**
14
+ * Build pagination params for URLSearchParams, supporting both traditional
15
+ * page-based pagination and cursor-based scroll pagination.
16
+ *
17
+ * When cursor is provided (including empty string for first scroll request),
18
+ * scroll pagination is used and page is ignored.
19
+ *
20
+ * API scroll behavior:
21
+ * - First request: ?_scroll (optionally with _itemsOnPage)
22
+ * - Subsequent requests: ?_scroll=<base64_cursor>
23
+ * - Response includes `scroll` field with next page cursor
24
+ */
25
+ export declare function buildPaginationParams(params: ScrollPaginationParams, urlParams: URLSearchParams): void;
26
+ /**
27
+ * Normalize a date string to full ISO datetime format.
28
+ * The Open Loyalty API requires full ISO datetime format (YYYY-MM-DDTHH:mm:ssZ).
29
+ *
30
+ * - If input is just a date (YYYY-MM-DD), converts based on position:
31
+ * - "from" dates: adds T00:00:00Z (start of day)
32
+ * - "to" dates: adds T23:59:59Z (end of day)
33
+ * - If input already has time component, returns as-is
34
+ *
35
+ * @param dateStr - Date string (YYYY-MM-DD or full ISO datetime)
36
+ * @param position - Whether this is a "from" (start) or "to" (end) date
37
+ * @returns Full ISO datetime string
38
+ */
39
+ export declare function normalizeDateToISO(dateStr: string, position: "from" | "to"): string;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Build pagination query string using Open Loyalty API parameter names.
3
+ * API uses _page (1-indexed) and _itemsOnPage for pagination.
4
+ */
5
+ export function buildPaginationQuery(params) {
6
+ const queryParams = [];
7
+ if (params.page !== undefined && params.page > 0) {
8
+ queryParams.push(`_page=${params.page}`);
9
+ }
10
+ if (params.perPage !== undefined && params.perPage > 0) {
11
+ queryParams.push(`_itemsOnPage=${params.perPage}`);
12
+ }
13
+ return queryParams.length > 0 ? `?${queryParams.join("&")}` : "";
14
+ }
15
+ /**
16
+ * Build pagination params for URLSearchParams, supporting both traditional
17
+ * page-based pagination and cursor-based scroll pagination.
18
+ *
19
+ * When cursor is provided (including empty string for first scroll request),
20
+ * scroll pagination is used and page is ignored.
21
+ *
22
+ * API scroll behavior:
23
+ * - First request: ?_scroll (optionally with _itemsOnPage)
24
+ * - Subsequent requests: ?_scroll=<base64_cursor>
25
+ * - Response includes `scroll` field with next page cursor
26
+ */
27
+ export function buildPaginationParams(params, urlParams) {
28
+ if (params.cursor !== undefined) {
29
+ // Scroll pagination - cursor takes precedence over page
30
+ // Empty string means first scroll request, non-empty is subsequent request
31
+ urlParams.set("_scroll", params.cursor);
32
+ if (params.perPage && params.perPage > 0) {
33
+ urlParams.set("_itemsOnPage", String(params.perPage));
34
+ }
35
+ }
36
+ else {
37
+ // Traditional page-based pagination
38
+ if (params.page && params.page > 0) {
39
+ urlParams.set("_page", String(params.page));
40
+ }
41
+ if (params.perPage && params.perPage > 0) {
42
+ urlParams.set("_itemsOnPage", String(params.perPage));
43
+ }
44
+ }
45
+ }
46
+ /**
47
+ * Normalize a date string to full ISO datetime format.
48
+ * The Open Loyalty API requires full ISO datetime format (YYYY-MM-DDTHH:mm:ssZ).
49
+ *
50
+ * - If input is just a date (YYYY-MM-DD), converts based on position:
51
+ * - "from" dates: adds T00:00:00Z (start of day)
52
+ * - "to" dates: adds T23:59:59Z (end of day)
53
+ * - If input already has time component, returns as-is
54
+ *
55
+ * @param dateStr - Date string (YYYY-MM-DD or full ISO datetime)
56
+ * @param position - Whether this is a "from" (start) or "to" (end) date
57
+ * @returns Full ISO datetime string
58
+ */
59
+ export function normalizeDateToISO(dateStr, position) {
60
+ // Check if it's already a full datetime (has 'T' in it)
61
+ if (dateStr.includes("T")) {
62
+ return dateStr;
63
+ }
64
+ // Validate basic date format YYYY-MM-DD
65
+ const datePattern = /^\d{4}-\d{2}-\d{2}$/;
66
+ if (!datePattern.test(dateStr)) {
67
+ // Return as-is and let the API validate
68
+ return dateStr;
69
+ }
70
+ // Convert to full ISO datetime based on position
71
+ if (position === "from") {
72
+ return `${dateStr}T00:00:00Z`;
73
+ }
74
+ else {
75
+ return `${dateStr}T23:59:59Z`;
76
+ }
77
+ }
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@open-loyalty/mcp-server",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "MCP server for Open Loyalty API - enables AI agents to manage loyalty programs, members, points, rewards, and transactions",
6
+ "author": "Marcin Dyguda <md@openloyalty.io>",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/openloyalty/mcp-server.git"
11
+ },
12
+ "homepage": "https://github.com/openloyalty/mcp-server#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/openloyalty/mcp-server/issues"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "open-loyalty",
20
+ "loyalty",
21
+ "loyalty-program",
22
+ "claude",
23
+ "ai",
24
+ "anthropic"
25
+ ],
26
+ "main": "dist/index.js",
27
+ "bin": {
28
+ "openloyalty-mcp": "./dist/index.js"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "README.md",
33
+ "LICENSE"
34
+ ],
35
+ "scripts": {
36
+ "build": "tsc",
37
+ "prepublishOnly": "npm run build",
38
+ "start": "node dist/index.js",
39
+ "dev": "tsx src/index.ts",
40
+ "typecheck": "tsc --noEmit",
41
+ "test": "vitest",
42
+ "test:run": "vitest run",
43
+ "test:coverage": "vitest run --coverage",
44
+ "test:watch": "vitest --watch"
45
+ },
46
+ "dependencies": {
47
+ "@modelcontextprotocol/sdk": "^1.0.0",
48
+ "axios": "^1.6.0",
49
+ "zod": "^3.22.0"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^20.10.0",
53
+ "@vitest/coverage-v8": "^4.0.17",
54
+ "axios-mock-adapter": "^2.1.0",
55
+ "tsx": "^4.7.0",
56
+ "typescript": "^5.3.0",
57
+ "vitest": "^4.0.17"
58
+ },
59
+ "engines": {
60
+ "node": ">=18"
61
+ },
62
+ "publishConfig": {
63
+ "access": "public"
64
+ }
65
+ }