@storelayer/mcp-server 0.1.0 → 0.2.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 (3) hide show
  1. package/SKILL.md +276 -0
  2. package/dist/index.js +38 -1
  3. package/package.json +3 -2
package/SKILL.md ADDED
@@ -0,0 +1,276 @@
1
+ # Store Layer MCP Server — Skill Guide for Claude Desktop
2
+
3
+ You have access to the **Store Layer** MCP server, which lets you manage loyalty programs, promotions, wallets, users, events, referrals, and more for a specific project.
4
+
5
+ ## How It Works
6
+
7
+ All tools are dynamically loaded from the Store Layer API. Tool names use underscores (e.g., `promotions_evaluate_cart`). Tools that require a `userId` parameter operate on a specific external user's data.
8
+
9
+ ---
10
+
11
+ ## Available Tools by Domain
12
+
13
+ ### External Users (`external_users`)
14
+ | Tool | Description |
15
+ |------|-------------|
16
+ | `external_users_get_user` | Get a user by ID |
17
+ | `external_users_list_users` | List users with pagination |
18
+ | `external_users_lookup_user` | Smart lookup: tries ID match, then email fallback |
19
+ | `external_users_register` | Register a new user |
20
+ | `external_users_update` | Update a user |
21
+ | `external_users_remove` | Remove a user |
22
+
23
+ ### Wallet (`wallet`) — all require `userId`
24
+ | Tool | Description |
25
+ |------|-------------|
26
+ | `wallet_get_balance` | Get balance for all asset types |
27
+ | `wallet_list_transactions` | List transaction history with filters |
28
+ | `wallet_credit` | Add assets (points, tokens, etc.) |
29
+ | `wallet_debit` | Remove assets |
30
+
31
+ ### Loyalty Events (`events`)
32
+ | Tool | Description |
33
+ |------|-------------|
34
+ | `events_get` | Get a single event by ID |
35
+ | `events_list` | List events with filters (type, status, pagination) |
36
+ | `events_get_stats` | Aggregate stats: total, processed, pending |
37
+
38
+ ### Referral (`referral`)
39
+ | Tool | Description |
40
+ |------|-------------|
41
+ | `referral_get_config` | Get referral program config |
42
+ | `referral_list_codes` | List referral codes with filters |
43
+ | `referral_get_code_by_referrer` | Look up code by referrer ID |
44
+ | `referral_validate_code` | Check if a code is valid |
45
+ | `referral_list_referrals` | List referral records |
46
+ | `referral_list_events` | List referral audit events |
47
+ | `referral_get_stats` | Aggregate stats |
48
+ | `referral_get_leaderboard` | Leaderboard ranked by referrals |
49
+ | `referral_create_code` | Create a referral code |
50
+ | `referral_apply_code` | Apply a code for a referee |
51
+ | `referral_deactivate_code` | Deactivate a code |
52
+
53
+ ### Project (`project`)
54
+ | Tool | Description |
55
+ |------|-------------|
56
+ | `project_get_config` | Get project config (currency, timezone, settings) |
57
+ | `project_list_rules` | List loyalty rules |
58
+ | `project_get_rule` | Get a single rule by ID |
59
+ | `project_list_integrations` | List integrations |
60
+ | `project_list_resources` | List resource definitions |
61
+ | `project_add_rule` | Create a loyalty rule |
62
+ | `project_update_rule` | Update a rule |
63
+ | `project_remove_rule` | Delete a rule |
64
+ | `project_test_conditions` | Test conditions against sample data |
65
+
66
+ ### Promotions (`promotions`)
67
+ | Tool | Description |
68
+ |------|-------------|
69
+ | `promotions_list` | List promotions with filters |
70
+ | `promotions_get_active` | Get all active promotions |
71
+ | `promotions_list_coupons` | List coupons |
72
+ | `promotions_list_usage` | List usage/redemption records |
73
+ | `promotions_get_stats` | Stats for a specific promotion |
74
+ | `promotions_get_aggregate_stats` | Aggregate stats across all promotions |
75
+ | `promotions_create` | Create a promotion |
76
+ | `promotions_create_coupon` | Create a coupon for a promotion |
77
+ | `promotions_record_usage` | Record a redemption |
78
+ | `promotions_evaluate_cart` | **Evaluate a cart against active promotions** |
79
+
80
+ ### Support (`support`)
81
+ | Tool | Description |
82
+ |------|-------------|
83
+ | `support_get_ticket` | Get a ticket by ID |
84
+ | `support_list_tickets` | List tickets with filters |
85
+ | `support_get_stats` | Aggregate ticket stats |
86
+ | `support_create_ticket` | Create a ticket |
87
+ | `support_update_ticket` | Update a ticket |
88
+
89
+ ### Surveys (`surveys`)
90
+ | Tool | Description |
91
+ |------|-------------|
92
+ | `surveys_get` | Get a survey by ID |
93
+ | `surveys_list` | List surveys |
94
+ | `surveys_list_responses` | List responses for a survey |
95
+ | `surveys_get_stats` | Survey analytics |
96
+ | `surveys_create` | Create a survey |
97
+ | `surveys_submit_response` | Submit a response |
98
+
99
+ ### Workflows (`workflows`)
100
+ | Tool | Description |
101
+ |------|-------------|
102
+ | `workflows_list` | List workflow executions |
103
+ | `workflows_get` | Get a workflow execution with steps |
104
+
105
+ ### Stores (`stores`)
106
+ | Tool | Description |
107
+ |------|-------------|
108
+ | `stores_get_store` / `stores_list_stores` | Read stores |
109
+ | `stores_create_store` / `stores_update_store` / `stores_remove_store` | Manage stores |
110
+ | `stores_get_facility` / `stores_list_facilities` | Read facilities |
111
+ | `stores_create_facility` / `stores_update_facility` / `stores_remove_facility` | Manage facilities |
112
+
113
+ ### Resources (`resources`)
114
+ | Tool | Description |
115
+ |------|-------------|
116
+ | `resources_list` / `resources_get` | Read resource definitions |
117
+ | `resources_add` / `resources_update` / `resources_remove` | Manage resource definitions |
118
+
119
+ ---
120
+
121
+ ## Domain Knowledge
122
+
123
+ ### Promotions Engine
124
+
125
+ Every promotion has an `itemsDiscountComputation` script that computes per-item discounts. There are no predefined discount types — the script IS the discount logic.
126
+
127
+ **Script environment:**
128
+ - `$('cart')` — full cart object. `$('cart').items` is the items array. Each item: `{ id, price, quantity, category, tags, ...custom }`
129
+ - `$('cart').total` — cart total
130
+ - `$('user')` — current user (if userId provided)
131
+ - `$('couponCodes')` — applied coupon codes
132
+
133
+ **Return format:** Array of `{ id: string, amount: number }` where `id` = item ID and `amount` = discount to subtract.
134
+
135
+ **Condition system:**
136
+ - Structure: `{ conditions: [{ leftValue, operator, rightValue, rightType }], combinator: "AND" | "OR" }`
137
+ - Fields: `cart.total`, `cart.itemCount`, `cart.uniqueItemCount`, `cart.items[0].price`, etc.
138
+ - Operators: `equals`, `notEquals`, `gt`, `gte`, `lt`, `lte`, `contains`, `notContains`, `startsWith`, `endsWith`, `exists`, `notExists`, `regex`, `before`, `after`, `isEmpty`, `isNotEmpty`, `hasKey`
139
+
140
+ **Stacking modes:**
141
+ - `stackable` — combines with other promotions (default)
142
+ - `exclusive` — highest priority wins, all others excluded
143
+ - `exclusive_group` — highest priority within its group wins
144
+
145
+ **Cart formats:**
146
+ - **Standard:** Items have `quantity` field. Engine expands internally.
147
+ ```json
148
+ { "items": [{ "id": "muffin-001", "price": 3.50, "quantity": 2 }] }
149
+ ```
150
+ - **Sub-items (pre-expanded):** Each unit is a separate entry with a unique ID and a shared logical ID in another field. Pass `itemIdField` to specify which field to use.
151
+ ```json
152
+ { "items": [{ "id": "sub-1", "sku": "muffin-001", "price": 3.50 }, { "id": "sub-2", "sku": "muffin-001", "price": 3.50 }] }
153
+ ```
154
+
155
+ **Coupon system:** Promotions can require a coupon code (`requiresCoupon: true`). Create coupons with `promotions_create_coupon`, validate them during cart evaluation by passing `couponCodes`.
156
+
157
+ #### Promotion Examples
158
+
159
+ **10% off all items:**
160
+ ```json
161
+ {
162
+ "name": "10% Off Everything",
163
+ "status": "active",
164
+ "conditions": { "conditions": [], "combinator": "AND" },
165
+ "itemsDiscountComputation": {
166
+ "script": "const items = $('cart').items;\nreturn items.map(item => ({ id: item.id, amount: item.price * 0.10 }));",
167
+ "language": "javascript"
168
+ }
169
+ }
170
+ ```
171
+
172
+ **$5 off orders over $50:**
173
+ ```json
174
+ {
175
+ "name": "$5 Off Over $50",
176
+ "status": "active",
177
+ "conditions": {
178
+ "conditions": [{ "leftValue": "cart.total", "operator": "gte", "rightValue": 50, "rightType": "number" }],
179
+ "combinator": "AND"
180
+ },
181
+ "itemsDiscountComputation": {
182
+ "script": "const items = $('cart').items;\nconst total = items.reduce((s, i) => s + i.price, 0);\nreturn items.map(item => ({ id: item.id, amount: (item.price / total) * 5 }));",
183
+ "language": "javascript"
184
+ }
185
+ }
186
+ ```
187
+
188
+ **BOGO: buy 2+ shoes, cheapest free:**
189
+ ```json
190
+ {
191
+ "itemsDiscountComputation": {
192
+ "script": "const shoes = $('cart').items.filter(i => i.category === 'shoes');\nif (shoes.length < 2) return [];\nconst sorted = [...shoes].sort((a, b) => a.price - b.price);\nreturn [{ id: sorted[0].id, amount: sorted[0].price }];",
193
+ "language": "javascript"
194
+ }
195
+ }
196
+ ```
197
+
198
+ #### Cart Evaluation
199
+
200
+ Use `promotions_evaluate_cart` with:
201
+ ```json
202
+ {
203
+ "cart": {
204
+ "items": [
205
+ { "id": "item-1", "price": 25.00, "quantity": 2, "category": "shoes" },
206
+ { "id": "item-2", "price": 15.00, "quantity": 1, "category": "accessories" }
207
+ ]
208
+ },
209
+ "userId": "user_123",
210
+ "couponCodes": ["SAVE10"]
211
+ }
212
+ ```
213
+
214
+ Response includes: `summary` (originalTotal, totalDiscount, finalTotal), `items` (per-item discounts), `appliedPromotions`, `evaluatedPromotions` (with condition/computation match details), and `warnings`.
215
+
216
+ ---
217
+
218
+ ### Loyalty Rules
219
+
220
+ Rules trigger when events are ingested. Each rule has conditions (evaluated against runtime context) and actions (executed when conditions match).
221
+
222
+ **Condition expressions** use Handlebars syntax: `{{ event.type }}`, `{{ event.payload.amount }}`, `{{ user.email }}`, `{{ history.amount }}`
223
+
224
+ **Action types:**
225
+ - `reward` — `{ assetType: "points", amount: 10, description: "Purchase reward" }`. Dynamic: `amount: "{{ event.amount }}"`
226
+ - `integration` — `{ integrationId: "intg_xxx" }` — triggers webhook/Telegram/Slack
227
+ - `apply_referral` / `complete_referral` / `mark_code_used` — referral lifecycle actions
228
+
229
+ **Rule example — 1 point per dollar spent:**
230
+ ```json
231
+ {
232
+ "name": "Dollar-to-Points",
233
+ "eventType": "purchase",
234
+ "conditions": {
235
+ "conditions": [{ "leftValue": "{{ event.type }}", "operator": "equals", "rightValue": "purchase", "rightType": "string" }],
236
+ "combinator": "AND"
237
+ },
238
+ "actions": [{ "type": "reward", "config": { "assetType": "points", "amount": "{{ event.payload.amount }}", "description": "1 point per dollar" } }]
239
+ }
240
+ ```
241
+
242
+ ---
243
+
244
+ ### Events & Ingestion
245
+
246
+ Events are sent externally via `POST /api/ingest/events` with an API key. The MCP server can read events but not ingest them directly.
247
+
248
+ **Processing flow:** Event stored → queued → rules matched by `eventType` → resources resolved → conditions evaluated → actions executed → event marked processed.
249
+
250
+ **Event payload example:**
251
+ ```json
252
+ { "type": "purchase", "userId": "user123", "payload": { "orderId": "order_abc", "amount": 99.99, "items": [...] } }
253
+ ```
254
+
255
+ ---
256
+
257
+ ### Resources
258
+
259
+ Resources provide runtime context for rule evaluation. Types:
260
+ - **event** — incoming event data
261
+ - **http** — external REST API call
262
+ - **database** — PostgreSQL query via integration
263
+ - **internal** — Engine-X entity data (users, wallets, transactions via registry tools)
264
+ - **payload** — request body data
265
+
266
+ Resources are resolved before rule evaluation. Their data is accessible in condition expressions.
267
+
268
+ ---
269
+
270
+ ## Best Practices
271
+
272
+ 1. **Start by exploring** — use `promotions_get_aggregate_stats`, `project_get_config`, or list tools to understand the current state before making changes.
273
+ 2. **Test before activating** — create promotions in `draft` status, use `promotions_evaluate_cart` to test with sample carts, then set to `active`.
274
+ 3. **Use `project_test_conditions`** to validate rule conditions against sample data before saving.
275
+ 4. **For destructive actions** (delete, debit), always confirm with the user first.
276
+ 5. **Cart evaluation** returns detailed match info — check `evaluatedPromotions` for per-promotion condition/computation results and `warnings` for coupon validation issues.
package/dist/index.js CHANGED
@@ -6516,6 +6516,11 @@ var require_dist = __commonJS((exports, module) => {
6516
6516
  exports.default = formatsPlugin;
6517
6517
  });
6518
6518
 
6519
+ // src/index.ts
6520
+ import { readFileSync } from "node:fs";
6521
+ import { resolve, dirname } from "node:path";
6522
+ import { fileURLToPath } from "node:url";
6523
+
6519
6524
  // ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/core/core.js
6520
6525
  var NEVER = Object.freeze({
6521
6526
  status: "aborted"
@@ -13656,6 +13661,13 @@ class StdioServerTransport {
13656
13661
  }
13657
13662
 
13658
13663
  // src/index.ts
13664
+ var __dirname2 = dirname(fileURLToPath(import.meta.url));
13665
+ var SKILL_CONTENT;
13666
+ try {
13667
+ SKILL_CONTENT = readFileSync(resolve(__dirname2, "../SKILL.md"), "utf-8");
13668
+ } catch {
13669
+ SKILL_CONTENT = "Store Layer MCP Server — manage loyalty programs, promotions, wallets, users, events, and referrals. Use the available tools to interact with your project.";
13670
+ }
13659
13671
  var API_URL = (process.env.STORE_LAYER_API_URL || "https://api.storelayer.io").replace(/\/$/, "");
13660
13672
  var API_KEY = process.env.STORE_LAYER_API_KEY;
13661
13673
  var PROJECT_ID = process.env.STORE_LAYER_PROJECT_ID;
@@ -13736,7 +13748,7 @@ async function main() {
13736
13748
  mcpToRegistry.set(mcpName, tool.name);
13737
13749
  mcpToManifest.set(mcpName, tool);
13738
13750
  }
13739
- const server = new Server({ name: "store-layer", version: "0.1.0" }, { capabilities: { tools: {} } });
13751
+ const server = new Server({ name: "store-layer", version: "0.1.0" }, { capabilities: { tools: {}, prompts: {} } });
13740
13752
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
13741
13753
  tools: manifest.map((tool) => ({
13742
13754
  name: toMcpName(tool.name),
@@ -13783,6 +13795,31 @@ async function main() {
13783
13795
  };
13784
13796
  }
13785
13797
  });
13798
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
13799
+ prompts: [
13800
+ {
13801
+ name: "store-layer-guide",
13802
+ description: "Comprehensive guide for using Store Layer tools — covers all domains (promotions, wallets, users, events, referrals, rules), with examples and best practices."
13803
+ }
13804
+ ]
13805
+ }));
13806
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
13807
+ if (request.params.name !== "store-layer-guide") {
13808
+ throw new Error(`Unknown prompt: ${request.params.name}`);
13809
+ }
13810
+ return {
13811
+ description: "Store Layer MCP skill guide",
13812
+ messages: [
13813
+ {
13814
+ role: "user",
13815
+ content: {
13816
+ type: "text",
13817
+ text: SKILL_CONTENT
13818
+ }
13819
+ }
13820
+ ]
13821
+ };
13822
+ });
13786
13823
  const transport = new StdioServerTransport;
13787
13824
  await server.connect(transport);
13788
13825
  console.error("Store Layer MCP server running on stdio");
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@storelayer/mcp-server",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server for Store Layer — manage loyalty programs, promotions, wallets, and more from Claude Desktop, Claude Code, or Cursor.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "store-layer-mcp-server": "dist/index.js"
8
8
  },
9
9
  "files": [
10
- "dist"
10
+ "dist",
11
+ "SKILL.md"
11
12
  ],
12
13
  "scripts": {
13
14
  "build": "bun build src/index.ts --target=node --outdir=dist --format=esm",