dtc-mcp 0.1.3 → 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.
- package/README.md +228 -97
- package/dist/bootstrap.cjs +53 -0
- package/dist/bootstrap.cjs.map +1 -0
- package/dist/bootstrap.d.cts +2 -0
- package/dist/config.js +26 -14
- package/dist/config.js.map +1 -1
- package/dist/index.js +15 -21
- package/dist/index.js.map +1 -1
- package/dist/platforms/klaviyo/tools.js +10 -15
- package/dist/platforms/klaviyo/tools.js.map +1 -1
- package/dist/platforms/shopify/client.d.ts +1 -1
- package/dist/platforms/shopify/client.js +13 -27
- package/dist/platforms/shopify/client.js.map +1 -1
- package/dist/platforms/shopify/tools.js +153 -38
- package/dist/platforms/shopify/tools.js.map +1 -1
- package/dist/platforms/shopify/transforms.d.ts +7 -12
- package/dist/platforms/shopify/transforms.js +168 -51
- package/dist/platforms/shopify/transforms.js.map +1 -1
- package/dist/server.js +1 -1
- package/dist/shared/types.d.ts +84 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# dtc-mcp
|
|
2
2
|
|
|
3
|
-
Context-optimized MCP server for DTC e-commerce brands. Connect Claude (or any MCP client) to your Klaviyo and Shopify data with
|
|
3
|
+
Context-optimized MCP server for DTC e-commerce brands. Connect Claude (or any MCP client) to your Klaviyo and Shopify data with 22 pre-built analytics tools.
|
|
4
4
|
|
|
5
5
|
Unlike raw API wrappers, dtc-mcp pre-aggregates data server-side and returns only actionable fields — using ~80% less context than dumping raw API responses into your conversation.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **8 Klaviyo tools** — campaign performance, flow breakdowns, subscriber health, profile search, event activity
|
|
10
|
-
- **
|
|
10
|
+
- **12 Shopify tools** — sales summaries, time series, product performance, inventory alerts, customer cohorts & segments, sales breakdowns by country/channel/vendor, traffic sources, returns analysis, order search
|
|
11
11
|
- **2 cross-platform tools** — email revenue attribution, full DTC health dashboard
|
|
12
12
|
- **Dual revenue metrics** — both gross and net revenue on every sales query
|
|
13
|
-
- **
|
|
13
|
+
- **ShopifyQL-powered analytics** — fast aggregated queries for sales, customers, and sessions
|
|
14
14
|
- **Aggressive caching** — respects Klaviyo's strict rate limits (1 req/s on reporting)
|
|
15
15
|
|
|
16
16
|
## Quick Start
|
|
@@ -32,7 +32,7 @@ npx dtc-mcp
|
|
|
32
32
|
1. Download the latest `dtc-mcp.mcpb` from [GitHub Releases](https://github.com/rafaelsztutman/dtc-mcp/releases)
|
|
33
33
|
2. Double-click the `.mcpb` file — Claude Desktop will open an install dialog
|
|
34
34
|
3. Enter your API credentials when prompted (Klaviyo key required, Shopify optional)
|
|
35
|
-
4. The
|
|
35
|
+
4. The 22 tools will appear in the hammer menu automatically
|
|
36
36
|
|
|
37
37
|
### Option B: Manual Configuration
|
|
38
38
|
|
|
@@ -57,8 +57,8 @@ npx dtc-mcp
|
|
|
57
57
|
}
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
1. Restart Claude Desktop
|
|
61
|
+
2. Look for the hammer icon in the chat input — that confirms the MCP tools are loaded
|
|
62
62
|
|
|
63
63
|
### Klaviyo-only mode
|
|
64
64
|
|
|
@@ -78,13 +78,13 @@ ChatGPT supports MCP servers via remote connections. Since dtc-mcp uses stdio tr
|
|
|
78
78
|
4. Click **Create Private API Key**
|
|
79
79
|
5. Give it a name (e.g., "dtc-mcp")
|
|
80
80
|
6. Select **Read-only** access for these scopes:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
- `campaigns:read`
|
|
82
|
+
- `flows:read`
|
|
83
|
+
- `lists:read`
|
|
84
|
+
- `segments:read`
|
|
85
|
+
- `profiles:read`
|
|
86
|
+
- `metrics:read`
|
|
87
|
+
- `events:read`
|
|
88
88
|
7. Copy the key (starts with `pk_`)
|
|
89
89
|
|
|
90
90
|
### Shopify Credentials
|
|
@@ -101,13 +101,14 @@ For apps created in the [Shopify Partners Dashboard](https://partners.shopify.co
|
|
|
101
101
|
4. Your store URL is your `*.myshopify.com` domain
|
|
102
102
|
|
|
103
103
|
Set these environment variables:
|
|
104
|
+
|
|
104
105
|
```
|
|
105
106
|
SHOPIFY_STORE=your-store.myshopify.com
|
|
106
107
|
SHOPIFY_CLIENT_ID=your_client_id
|
|
107
108
|
SHOPIFY_CLIENT_SECRET=shpss_your_secret
|
|
108
109
|
```
|
|
109
110
|
|
|
110
|
-
**Required scopes:** `read_orders`, `read_products`, `read_customers`, `read_inventory`
|
|
111
|
+
**Required scopes:** `read_orders`, `read_products`, `read_customers`, `read_inventory`, `read_reports`
|
|
111
112
|
|
|
112
113
|
#### Option B: Legacy Custom App
|
|
113
114
|
|
|
@@ -118,6 +119,7 @@ For custom apps created directly in Shopify Admin (apps created before January 2
|
|
|
118
119
|
3. Go to **API credentials** and copy the **Admin API access token**
|
|
119
120
|
|
|
120
121
|
Set these environment variables:
|
|
122
|
+
|
|
121
123
|
```
|
|
122
124
|
SHOPIFY_STORE=your-store.myshopify.com
|
|
123
125
|
SHOPIFY_ACCESS_TOKEN=shpat_your_token_here
|
|
@@ -127,194 +129,318 @@ SHOPIFY_ACCESS_TOKEN=shpat_your_token_here
|
|
|
127
129
|
|
|
128
130
|
## Environment Variables
|
|
129
131
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
132
|
+
|
|
133
|
+
| Variable | Required | Description |
|
|
134
|
+
| ------------------------------ | --------------------------- | ----------------------------------------------------- |
|
|
135
|
+
| `KLAVIYO_API_KEY` | Yes | Klaviyo private API key (starts with `pk_`) |
|
|
136
|
+
| `SHOPIFY_STORE` | For Shopify | Your `*.myshopify.com` domain |
|
|
137
|
+
| `SHOPIFY_CLIENT_ID` | For Shopify (Dev Dashboard) | App client ID |
|
|
138
|
+
| `SHOPIFY_CLIENT_SECRET` | For Shopify (Dev Dashboard) | App client secret (starts with `shpss_`) |
|
|
139
|
+
| `SHOPIFY_ACCESS_TOKEN` | For Shopify (Legacy) | Admin API access token (starts with `shpat_`) |
|
|
140
|
+
| `SHOPIFY_API_VERSION` | No | Shopify API version (default: `2026-01`) |
|
|
141
|
+
| `KLAVIYO_CONVERSION_METRIC_ID` | No | Override auto-discovered "Placed Order" metric ID |
|
|
142
|
+
| `LOG_LEVEL` | No | `debug` | `info` | `warn` | `error` (default: `info`) |
|
|
143
|
+
|
|
140
144
|
|
|
141
145
|
## Tool Reference
|
|
142
146
|
|
|
143
147
|
### Klaviyo Tools
|
|
144
148
|
|
|
145
149
|
#### `klaviyo_campaign_summary`
|
|
150
|
+
|
|
146
151
|
Top campaigns ranked by metric. Returns name, send date, opens, clicks, revenue.
|
|
147
152
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
|
151
|
-
| `
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
153
|
+
|
|
154
|
+
| Parameter | Type | Default | Description |
|
|
155
|
+
| --------- | ------------------------------------------------------------- | ----------- | --------------- |
|
|
156
|
+
| `channel` | `"email"` | `"sms"` | required | Channel filter |
|
|
157
|
+
| `metric` | `"revenue"` | `"open_rate"` | `"click_rate"` | `"recipients"` | `"revenue"` | Rank by |
|
|
158
|
+
| `days` | 1-365 | 30 | Lookback period |
|
|
159
|
+
| `limit` | 1-25 | 10 | Max results |
|
|
160
|
+
|
|
154
161
|
|
|
155
162
|
> "Show me my top email campaigns by revenue this month"
|
|
156
163
|
|
|
157
164
|
#### `klaviyo_campaign_detail`
|
|
165
|
+
|
|
158
166
|
Deep dive on one campaign: full metrics, subject line, audiences, send time.
|
|
159
167
|
|
|
160
|
-
|
|
161
|
-
|
|
168
|
+
|
|
169
|
+
| Parameter | Type | Default | Description |
|
|
170
|
+
| ------------- | ------ | -------- | ------------------- |
|
|
162
171
|
| `campaign_id` | string | required | Klaviyo campaign ID |
|
|
163
172
|
|
|
173
|
+
|
|
164
174
|
> "Give me the full breakdown on my Black Friday campaign"
|
|
165
175
|
|
|
166
176
|
#### `klaviyo_flow_summary`
|
|
177
|
+
|
|
167
178
|
Top flows by metric. Returns name, status, trigger, message count, revenue.
|
|
168
179
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
180
|
+
|
|
181
|
+
| Parameter | Type | Default | Description |
|
|
182
|
+
| --------- | ------------------------------------------------------------------- | ----------- | ---------------- |
|
|
183
|
+
| `metric` | `"revenue"` | `"click_rate"` | `"conversion_rate"` | `"recipients"` | `"revenue"` | Rank by |
|
|
184
|
+
| `days` | 1-365 | 30 | Lookback period |
|
|
185
|
+
| `status` | `"live"` | `"draft"` | `"manual"` | `"all"` | `"live"` | Filter by status |
|
|
186
|
+
| `limit` | 1-25 | 10 | Max results |
|
|
187
|
+
|
|
175
188
|
|
|
176
189
|
> "Which of my flows generates the most revenue?"
|
|
177
190
|
|
|
178
191
|
#### `klaviyo_flow_detail`
|
|
192
|
+
|
|
179
193
|
Deep dive on one flow: per-message performance breakdown.
|
|
180
194
|
|
|
181
|
-
|
|
182
|
-
|
|
195
|
+
|
|
196
|
+
| Parameter | Type | Default | Description |
|
|
197
|
+
| --------- | ------ | -------- | --------------- |
|
|
183
198
|
| `flow_id` | string | required | Klaviyo flow ID |
|
|
184
|
-
| `days`
|
|
199
|
+
| `days` | 1-365 | 30 | Lookback period |
|
|
200
|
+
|
|
185
201
|
|
|
186
202
|
> "Show me the per-email breakdown of my welcome flow"
|
|
187
203
|
|
|
188
204
|
#### `klaviyo_subscriber_health`
|
|
205
|
+
|
|
189
206
|
List growth and engagement tier breakdown.
|
|
190
207
|
|
|
191
|
-
|
|
192
|
-
|
|
208
|
+
|
|
209
|
+
| Parameter | Type | Default | Description |
|
|
210
|
+
| --------- | ------ | -------- | --------------------------- |
|
|
193
211
|
| `list_id` | string | optional | Specific list, or all lists |
|
|
194
212
|
|
|
213
|
+
|
|
195
214
|
> "What's the health of my email list?"
|
|
196
215
|
|
|
197
216
|
#### `klaviyo_list_segments`
|
|
217
|
+
|
|
198
218
|
All lists and segments with sizes.
|
|
199
219
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
|
203
|
-
| `
|
|
220
|
+
|
|
221
|
+
| Parameter | Type | Default | Description |
|
|
222
|
+
| --------- | ---------------------------------- | -------- | ----------------- |
|
|
223
|
+
| `type` | `"lists"` | `"segments"` | `"all"` | `"all"` | Filter by type |
|
|
224
|
+
| `cursor` | string | optional | Pagination cursor |
|
|
225
|
+
|
|
204
226
|
|
|
205
227
|
> "List all my Klaviyo segments and their sizes"
|
|
206
228
|
|
|
207
229
|
#### `klaviyo_search_profiles`
|
|
230
|
+
|
|
208
231
|
Find profiles by email, phone, or name.
|
|
209
232
|
|
|
210
|
-
| Parameter | Type | Default | Description |
|
|
211
|
-
|-----------|------|---------|-------------|
|
|
212
|
-
| `query` | string | required | Email, phone, or name |
|
|
213
|
-
| `limit` | 1-10 | 5 | Max results |
|
|
214
233
|
|
|
215
|
-
|
|
234
|
+
| Parameter | Type | Default | Description |
|
|
235
|
+
| --------- | ------ | -------- | --------------------- |
|
|
236
|
+
| `query` | string | required | Email, phone, or name |
|
|
237
|
+
| `limit` | 1-10 | 5 | Max results |
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
> "Look up the profile for [john@example.com](mailto:john@example.com)"
|
|
216
241
|
|
|
217
242
|
#### `klaviyo_recent_activity`
|
|
243
|
+
|
|
218
244
|
Recent events for a metric (e.g., Placed Order, Opened Email).
|
|
219
245
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
|
223
|
-
| `
|
|
224
|
-
| `
|
|
225
|
-
| `
|
|
246
|
+
|
|
247
|
+
| Parameter | Type | Default | Description |
|
|
248
|
+
| --------------- | ------ | ---------------- | --------------------- |
|
|
249
|
+
| `metric_name` | string | `"Placed Order"` | Metric name |
|
|
250
|
+
| `days` | 1-90 | 7 | Lookback period |
|
|
251
|
+
| `limit` | 1-25 | 10 | Max events |
|
|
252
|
+
| `profile_email` | string | optional | Filter to one profile |
|
|
253
|
+
|
|
226
254
|
|
|
227
255
|
> "Show me the last 10 orders placed"
|
|
228
256
|
|
|
229
257
|
### Shopify Tools
|
|
230
258
|
|
|
231
259
|
#### `shopify_sales_summary`
|
|
260
|
+
|
|
232
261
|
Revenue (gross + net), orders, AOV for a period with comparison.
|
|
233
262
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
|
237
|
-
| `
|
|
263
|
+
|
|
264
|
+
| Parameter | Type | Default | Description |
|
|
265
|
+
| ------------------ | ------- | ------- | ---------------------------------- |
|
|
266
|
+
| `days` | 1-90 | 30 | Lookback period |
|
|
267
|
+
| `compare_previous` | boolean | true | Include previous period comparison |
|
|
268
|
+
|
|
238
269
|
|
|
239
270
|
> "What were my sales last month compared to the month before?"
|
|
240
271
|
|
|
241
272
|
#### `shopify_sales_timeseries`
|
|
273
|
+
|
|
242
274
|
Revenue and orders broken down by day, week, or month.
|
|
243
275
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
|
247
|
-
| `
|
|
276
|
+
|
|
277
|
+
| Parameter | Type | Default | Description |
|
|
278
|
+
| ------------- | ------------------------------------ | --------- | --------------- |
|
|
279
|
+
| `days` | 1-365 | 30 | Lookback period |
|
|
280
|
+
| `granularity` | `"daily"` | `"weekly"` | `"monthly"` | `"daily"` | Bucket size |
|
|
281
|
+
|
|
248
282
|
|
|
249
283
|
> "Show me daily revenue for this month"
|
|
250
284
|
|
|
251
285
|
#### `shopify_product_performance`
|
|
286
|
+
|
|
252
287
|
Top products by revenue or units sold.
|
|
253
288
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
|
257
|
-
| `
|
|
258
|
-
| `
|
|
289
|
+
|
|
290
|
+
| Parameter | Type | Default | Description |
|
|
291
|
+
| --------- | ----------------------- | ----------- | --------------- |
|
|
292
|
+
| `days` | 1-90 | 7 | Lookback period |
|
|
293
|
+
| `metric` | `"revenue"` | `"units"` | `"revenue"` | Rank by |
|
|
294
|
+
| `limit` | 1-25 | 10 | Max results |
|
|
295
|
+
|
|
259
296
|
|
|
260
297
|
> "Which products sold the most units this week?"
|
|
261
298
|
|
|
262
299
|
#### `shopify_order_search`
|
|
300
|
+
|
|
263
301
|
Find orders by number, email, or status.
|
|
264
302
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
|
268
|
-
| `
|
|
303
|
+
|
|
304
|
+
| Parameter | Type | Default | Description |
|
|
305
|
+
| --------- | ------ | -------- | ----------------------------------------------- |
|
|
306
|
+
| `query` | string | required | Order number, email, or `financial_status:paid` |
|
|
307
|
+
| `limit` | 1-25 | 10 | Max results |
|
|
308
|
+
|
|
269
309
|
|
|
270
310
|
> "Find order #1234"
|
|
271
311
|
|
|
272
312
|
#### `shopify_inventory_alerts`
|
|
313
|
+
|
|
273
314
|
Products with low or zero stock, sorted by most urgent.
|
|
274
315
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
|
278
|
-
| `
|
|
316
|
+
|
|
317
|
+
| Parameter | Type | Default | Description |
|
|
318
|
+
| ----------- | ------ | ------- | ------------------------------- |
|
|
319
|
+
| `threshold` | number | 10 | Alert at or below this quantity |
|
|
320
|
+
| `limit` | 1-50 | 20 | Max results |
|
|
321
|
+
|
|
279
322
|
|
|
280
323
|
> "Which products are running low on stock?"
|
|
281
324
|
|
|
282
|
-
#### `
|
|
283
|
-
|
|
325
|
+
#### `shopify_customer_cohorts`
|
|
326
|
+
|
|
327
|
+
Monthly or quarterly acquisition cohorts with LTV and retention signals.
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
| Parameter | Type | Default | Description |
|
|
331
|
+
| ------------- | --------------------------- | ----------- | --------------- |
|
|
332
|
+
| `granularity` | `"monthly"` | `"quarterly"` | `"monthly"` | Cohort grouping |
|
|
333
|
+
| `months` | 1-24 | 12 | Lookback period |
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
> "Show me customer cohorts by month — which cohort has the best LTV?"
|
|
337
|
+
|
|
338
|
+
#### `shopify_customer_segments`
|
|
339
|
+
|
|
340
|
+
Customer distribution by RFM group, spend tier, country, or tags.
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
| Parameter | Type | Default | Description |
|
|
344
|
+
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------- |
|
|
345
|
+
| `dimension` | `"rfm_group"` | `"predicted_spend_tier"` | `"customer_email_subscription_status"` | `"customer_sms_subscription_status"` | `"customer_country"` | `"customer_tag"` | `"customer_number_of_orders"` | required | Segmentation dimension |
|
|
346
|
+
| `months` | 1-24 | 12 | Lookback period |
|
|
347
|
+
| `limit` | 1-50 | 20 | Max segments |
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
> "Break down my customers by RFM group — who are my champions vs at-risk?"
|
|
351
|
+
|
|
352
|
+
#### `shopify_sales_breakdown`
|
|
353
|
+
|
|
354
|
+
Revenue and orders broken down by country, channel, vendor, or traffic source.
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
| Parameter | Type | Default | Description |
|
|
358
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------- |
|
|
359
|
+
| `dimension` | `"billing_country"` | `"billing_region"` | `"channel_name"` | `"product_vendor"` | `"referrer_source"` | `"referring_channel"` | `"referring_platform"` | `"traffic_type"` | `"shipping_country"` | required | Breakdown dimension |
|
|
360
|
+
| `days` | 1-365 | 30 | Lookback period |
|
|
361
|
+
| `metric` | `"total_sales"` | `"net_sales"` | `"orders"` | `"average_order_value"` | `"gross_profit"` | `"net_sales"` | Metric to rank by |
|
|
362
|
+
| `limit` | 1-50 | 10 | Max results |
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
> "What are my top countries by revenue?" or "Break down sales by channel"
|
|
366
|
+
|
|
367
|
+
#### `shopify_product_analytics`
|
|
368
|
+
|
|
369
|
+
Product performance with margins, returns, and quantities via ShopifyQL.
|
|
284
370
|
|
|
285
|
-
| Parameter | Type | Default | Description |
|
|
286
|
-
|-----------|------|---------|-------------|
|
|
287
|
-
| `days` | 1-365 | 90 | Lookback period |
|
|
288
|
-
| `limit` | 1-500 | 250 | Max customers to analyze |
|
|
289
371
|
|
|
290
|
-
|
|
372
|
+
| Parameter | Type | Default | Description |
|
|
373
|
+
| --------- | --------------------------------------------------------------- | ------------- | ---------------- |
|
|
374
|
+
| `days` | 1-365 | 30 | Lookback period |
|
|
375
|
+
| `metric` | `"net_sales"` | `"gross_sales"` | `"orders"` | `"gross_profit"` | `"net_sales"` | Sort products by |
|
|
376
|
+
| `limit` | 1-50 | 10 | Max results |
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
> "Which products have the best margins?" or "Show product performance with return rates"
|
|
380
|
+
|
|
381
|
+
#### `shopify_traffic_sources`
|
|
382
|
+
|
|
383
|
+
Session analytics by source, landing page, or daily trend.
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
| Parameter | Type | Default | Description |
|
|
387
|
+
| --------- | ------------------------------------------- | ----------- | --------------- |
|
|
388
|
+
| `mode` | `"sources"` | `"landing_pages"` | `"trend"` | `"sources"` | Analysis mode |
|
|
389
|
+
| `days` | 1-365 | 30 | Lookback period |
|
|
390
|
+
| `limit` | 1-50 | 10 | Max results |
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
> "Where is my traffic coming from?" or "What are my top landing pages?"
|
|
394
|
+
|
|
395
|
+
#### `shopify_returns_analysis`
|
|
396
|
+
|
|
397
|
+
Return rates, costs, and most-returned products.
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
| Parameter | Type | Default | Description |
|
|
401
|
+
| --------- | ---------------------------- | ----------- | --------------------------------------- |
|
|
402
|
+
| `mode` | `"summary"` | `"by_product"` | `"summary"` | Summary totals or per-product breakdown |
|
|
403
|
+
| `days` | 1-365 | 30 | Lookback period |
|
|
404
|
+
| `limit` | 1-50 | 10 | Max results (by_product mode) |
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
> "What's my return rate?" or "Which products get returned the most?"
|
|
291
408
|
|
|
292
409
|
#### `shopify_recent_orders`
|
|
410
|
+
|
|
293
411
|
Most recent orders. Quick snapshot of store activity.
|
|
294
412
|
|
|
413
|
+
|
|
295
414
|
| Parameter | Type | Default | Description |
|
|
296
|
-
|
|
297
|
-
| `limit`
|
|
415
|
+
| --------- | ---- | ------- | ----------- |
|
|
416
|
+
| `limit` | 1-25 | 10 | Max results |
|
|
417
|
+
|
|
298
418
|
|
|
299
419
|
> "Show me the last 10 orders"
|
|
300
420
|
|
|
301
421
|
### Cross-Platform Tools
|
|
302
422
|
|
|
303
423
|
#### `dtc_email_revenue_attribution`
|
|
424
|
+
|
|
304
425
|
Email/SMS revenue vs total Shopify revenue. Shows email marketing contribution.
|
|
305
426
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
|
427
|
+
|
|
428
|
+
| Parameter | Type | Default | Description |
|
|
429
|
+
| --------- | ----- | ------- | --------------- |
|
|
430
|
+
| `days` | 1-365 | 30 | Lookback period |
|
|
431
|
+
|
|
309
432
|
|
|
310
433
|
> "What percentage of my revenue came from email?"
|
|
311
434
|
|
|
312
435
|
#### `dtc_dashboard`
|
|
436
|
+
|
|
313
437
|
Complete DTC health dashboard: sales + email + subscriber metrics in one call.
|
|
314
438
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
|
439
|
+
|
|
440
|
+
| Parameter | Type | Default | Description |
|
|
441
|
+
| --------- | ---- | ------- | --------------- |
|
|
442
|
+
| `days` | 7-90 | 30 | Lookback period |
|
|
443
|
+
|
|
318
444
|
|
|
319
445
|
> "Give me the full business dashboard for last month"
|
|
320
446
|
|
|
@@ -330,7 +456,12 @@ Here are questions you can ask Claude once dtc-mcp is connected:
|
|
|
330
456
|
- "Are any products running low on stock?"
|
|
331
457
|
- "What percentage of my revenue comes from email marketing?"
|
|
332
458
|
- "How many new vs returning customers did I have this quarter?"
|
|
333
|
-
- "
|
|
459
|
+
- "Show me customer cohorts by month — which has the best LTV?"
|
|
460
|
+
- "Break down my customers by RFM group"
|
|
461
|
+
- "What are my top countries by revenue?"
|
|
462
|
+
- "Where is my traffic coming from?"
|
|
463
|
+
- "What's my return rate? Which products get returned the most?"
|
|
464
|
+
- "Which products have the best margins?"
|
|
334
465
|
- "Give me a complete health dashboard for my business"
|
|
335
466
|
- "Compare this month's sales to last month"
|
|
336
467
|
- "What are my top SMS campaigns by click rate?"
|
|
@@ -343,11 +474,11 @@ cd dtc-mcp
|
|
|
343
474
|
npm install
|
|
344
475
|
cp .env.example .env # Fill in your API credentials
|
|
345
476
|
npm run build # Compile TypeScript
|
|
346
|
-
npm test # Run tests (
|
|
477
|
+
npm test # Run tests (44 tests)
|
|
347
478
|
npm run dev # Watch mode
|
|
348
479
|
npm run inspect # Open MCP Inspector for interactive testing
|
|
349
480
|
```
|
|
350
481
|
|
|
351
482
|
## License
|
|
352
483
|
|
|
353
|
-
MIT
|
|
484
|
+
MIT
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// CommonJS bootstrap — runs BEFORE any ESM module evaluation.
|
|
4
|
+
// Catches errors during ESM module loading and writes to a debug log file.
|
|
5
|
+
// This exists because Claude Desktop's built-in Node.js does not capture stderr.
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const os = require("os");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const LOG = path.join(os.tmpdir(), "dtc-mcp-debug.log");
|
|
11
|
+
function debugLog(msg) {
|
|
12
|
+
try {
|
|
13
|
+
fs.appendFileSync(LOG, `${new Date().toISOString()} ${msg}\n`);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// can't write to log file
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
fs.writeFileSync(LOG, "");
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// ignore
|
|
24
|
+
}
|
|
25
|
+
debugLog("=== dtc-mcp bootstrap ===");
|
|
26
|
+
debugLog(`node: ${process.version}`);
|
|
27
|
+
debugLog(`platform: ${process.platform} ${process.arch}`);
|
|
28
|
+
debugLog(`argv: ${JSON.stringify(process.argv)}`);
|
|
29
|
+
debugLog(`cwd: ${process.cwd()}`);
|
|
30
|
+
debugLog(`env KLAVIYO_API_KEY set: ${!!process.env.KLAVIYO_API_KEY} (length: ${process.env.KLAVIYO_API_KEY?.length ?? 0})`);
|
|
31
|
+
debugLog(`env SHOPIFY_STORE set: ${!!process.env.SHOPIFY_STORE}`);
|
|
32
|
+
debugLog(`env SHOPIFY_CLIENT_ID set: ${!!process.env.SHOPIFY_CLIENT_ID}`);
|
|
33
|
+
debugLog(`env SHOPIFY_CLIENT_SECRET set: ${!!process.env.SHOPIFY_CLIENT_SECRET}`);
|
|
34
|
+
debugLog(`env SHOPIFY_ACCESS_TOKEN set: ${!!process.env.SHOPIFY_ACCESS_TOKEN}`);
|
|
35
|
+
process.on("uncaughtException", (err) => {
|
|
36
|
+
debugLog(`uncaughtException: ${err?.stack || err}`);
|
|
37
|
+
});
|
|
38
|
+
process.on("unhandledRejection", (err) => {
|
|
39
|
+
debugLog(`unhandledRejection: ${err instanceof Error ? err.stack : String(err)}`);
|
|
40
|
+
});
|
|
41
|
+
process.on("exit", (code) => {
|
|
42
|
+
debugLog(`process exit: code=${code}`);
|
|
43
|
+
});
|
|
44
|
+
debugLog("loading ESM module...");
|
|
45
|
+
import("./index.js")
|
|
46
|
+
.then(() => {
|
|
47
|
+
debugLog("ESM module loaded OK");
|
|
48
|
+
})
|
|
49
|
+
.catch((err) => {
|
|
50
|
+
debugLog(`ESM module load FAILED: ${err?.stack || err}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=bootstrap.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.cjs","sourceRoot":"","sources":["../src/bootstrap.cts"],"names":[],"mappings":"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"}
|
package/dist/config.js
CHANGED
|
@@ -10,7 +10,14 @@ function getRequired(name) {
|
|
|
10
10
|
return value;
|
|
11
11
|
}
|
|
12
12
|
function getOptional(name) {
|
|
13
|
-
|
|
13
|
+
const raw = process.env[name];
|
|
14
|
+
if (!raw)
|
|
15
|
+
return null;
|
|
16
|
+
// Claude Desktop may pass placeholder values for unfilled optional fields
|
|
17
|
+
const trimmed = raw.trim();
|
|
18
|
+
if (!trimmed || trimmed === "undefined" || trimmed === "null")
|
|
19
|
+
return null;
|
|
20
|
+
return trimmed;
|
|
14
21
|
}
|
|
15
22
|
function loadConfig() {
|
|
16
23
|
const klaviyoApiKey = getRequired("KLAVIYO_API_KEY");
|
|
@@ -21,36 +28,41 @@ function loadConfig() {
|
|
|
21
28
|
// Determine auth mode
|
|
22
29
|
const hasLegacy = shopifyAccessToken !== null;
|
|
23
30
|
const hasClientCredentials = shopifyClientId !== null && shopifyClientSecret !== null;
|
|
31
|
+
let shopifyAuthMode = null;
|
|
24
32
|
if (hasLegacy && hasClientCredentials) {
|
|
25
|
-
|
|
33
|
+
// Both set — prefer client_credentials (recommended), ignore legacy token
|
|
34
|
+
console.error("[dtc-mcp] warn: Both SHOPIFY_ACCESS_TOKEN and SHOPIFY_CLIENT_ID/SECRET are set. Using Client Credentials (recommended). Remove ACCESS_TOKEN to silence this warning.");
|
|
35
|
+
shopifyAuthMode = "client_credentials";
|
|
26
36
|
}
|
|
27
|
-
|
|
28
|
-
if (hasClientCredentials) {
|
|
37
|
+
else if (hasClientCredentials) {
|
|
29
38
|
shopifyAuthMode = "client_credentials";
|
|
30
39
|
}
|
|
31
40
|
else if (hasLegacy) {
|
|
32
41
|
shopifyAuthMode = "legacy";
|
|
33
42
|
}
|
|
34
|
-
//
|
|
43
|
+
// Warn (don't throw) if store is missing when auth is configured
|
|
35
44
|
if (shopifyAuthMode && !shopifyStore) {
|
|
36
|
-
|
|
45
|
+
console.error("[dtc-mcp] warn: Shopify credentials are set but SHOPIFY_STORE is missing. Shopify tools will be disabled.");
|
|
46
|
+
shopifyAuthMode = null;
|
|
37
47
|
}
|
|
38
|
-
//
|
|
48
|
+
// Warn (don't throw) for partial client credentials
|
|
39
49
|
if ((shopifyClientId && !shopifyClientSecret) ||
|
|
40
50
|
(!shopifyClientId && shopifyClientSecret)) {
|
|
41
|
-
|
|
51
|
+
console.error("[dtc-mcp] warn: Only one of SHOPIFY_CLIENT_ID/SHOPIFY_CLIENT_SECRET is set. Both are required. Shopify client credentials auth will be disabled.");
|
|
42
52
|
}
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
const rawLogLevel = (process.env.LOG_LEVEL || "info");
|
|
54
|
+
const validLevels = ["debug", "info", "warn", "error"];
|
|
55
|
+
const logLevel = validLevels.includes(rawLogLevel) ? rawLogLevel : "info";
|
|
56
|
+
if (!validLevels.includes(rawLogLevel)) {
|
|
57
|
+
console.error(`[dtc-mcp] warn: Invalid LOG_LEVEL "${rawLogLevel}", defaulting to "info".`);
|
|
46
58
|
}
|
|
47
59
|
return Object.freeze({
|
|
48
60
|
klaviyoApiKey,
|
|
49
61
|
klaviyoRevision: "2026-01-15",
|
|
50
62
|
shopifyStore,
|
|
51
|
-
shopifyAccessToken,
|
|
52
|
-
shopifyClientId,
|
|
53
|
-
shopifyClientSecret,
|
|
63
|
+
shopifyAccessToken: shopifyAuthMode === "legacy" ? shopifyAccessToken : null,
|
|
64
|
+
shopifyClientId: shopifyAuthMode === "client_credentials" ? shopifyClientId : null,
|
|
65
|
+
shopifyClientSecret: shopifyAuthMode === "client_credentials" ? shopifyClientSecret : null,
|
|
54
66
|
shopifyAuthMode,
|
|
55
67
|
shopifyApiVersion: process.env.SHOPIFY_API_VERSION || "2026-01",
|
|
56
68
|
klaviyoConversionMetricId: getOptional("KLAVIYO_CONVERSION_METRIC_ID"),
|