dtc-mcp 0.1.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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +344 -0
  3. package/dist/config.d.ts +17 -0
  4. package/dist/config.js +71 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/cross-platform/correlator.d.ts +10 -0
  7. package/dist/cross-platform/correlator.js +166 -0
  8. package/dist/cross-platform/correlator.js.map +1 -0
  9. package/dist/cross-platform/tools.d.ts +2 -0
  10. package/dist/cross-platform/tools.js +30 -0
  11. package/dist/cross-platform/tools.js.map +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +13 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/platforms/klaviyo/client.d.ts +91 -0
  16. package/dist/platforms/klaviyo/client.js +389 -0
  17. package/dist/platforms/klaviyo/client.js.map +1 -0
  18. package/dist/platforms/klaviyo/tools.d.ts +2 -0
  19. package/dist/platforms/klaviyo/tools.js +368 -0
  20. package/dist/platforms/klaviyo/tools.js.map +1 -0
  21. package/dist/platforms/klaviyo/transforms.d.ts +59 -0
  22. package/dist/platforms/klaviyo/transforms.js +326 -0
  23. package/dist/platforms/klaviyo/transforms.js.map +1 -0
  24. package/dist/platforms/shopify/client.d.ts +51 -0
  25. package/dist/platforms/shopify/client.js +366 -0
  26. package/dist/platforms/shopify/client.js.map +1 -0
  27. package/dist/platforms/shopify/tools.d.ts +2 -0
  28. package/dist/platforms/shopify/tools.js +253 -0
  29. package/dist/platforms/shopify/tools.js.map +1 -0
  30. package/dist/platforms/shopify/transforms.d.ts +88 -0
  31. package/dist/platforms/shopify/transforms.js +191 -0
  32. package/dist/platforms/shopify/transforms.js.map +1 -0
  33. package/dist/server.d.ts +2 -0
  34. package/dist/server.js +15 -0
  35. package/dist/server.js.map +1 -0
  36. package/dist/shared/cache.d.ts +23 -0
  37. package/dist/shared/cache.js +62 -0
  38. package/dist/shared/cache.js.map +1 -0
  39. package/dist/shared/errors.d.ts +25 -0
  40. package/dist/shared/errors.js +96 -0
  41. package/dist/shared/errors.js.map +1 -0
  42. package/dist/shared/pagination.d.ts +21 -0
  43. package/dist/shared/pagination.js +36 -0
  44. package/dist/shared/pagination.js.map +1 -0
  45. package/dist/shared/types.d.ts +247 -0
  46. package/dist/shared/types.js +3 -0
  47. package/dist/shared/types.js.map +1 -0
  48. package/package.json +55 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Rafael Sztutman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,344 @@
1
+ # dtc-mcp
2
+
3
+ Context-optimized MCP server for DTC e-commerce brands. Connect Claude (or any MCP client) to your Klaviyo and Shopify data with 16 pre-built analytics tools.
4
+
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
+
7
+ ## Features
8
+
9
+ - **8 Klaviyo tools** — campaign performance, flow breakdowns, subscriber health, profile search, event activity
10
+ - **7 Shopify tools** — sales summaries, daily/weekly/monthly trends, product performance, inventory alerts, customer cohorts, order search
11
+ - **2 cross-platform tools** — email revenue attribution, full DTC health dashboard
12
+ - **Dual revenue metrics** — both gross and net revenue on every sales query
13
+ - **Smart fallbacks** — ShopifyQL when available, GraphQL pagination when not
14
+ - **Aggressive caching** — respects Klaviyo's strict rate limits (1 req/s on reporting)
15
+
16
+ ## Quick Start
17
+
18
+ ```bash
19
+ npm install -g dtc-mcp
20
+ ```
21
+
22
+ Or run directly:
23
+
24
+ ```bash
25
+ npx dtc-mcp
26
+ ```
27
+
28
+ ## Setup with Claude Desktop
29
+
30
+ 1. Open Claude Desktop
31
+ 2. Go to **Settings** (gear icon) > **Developer** > **Edit Config**
32
+ 3. Add the following to your `claude_desktop_config.json`:
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "dtc-mcp": {
38
+ "command": "npx",
39
+ "args": ["-y", "dtc-mcp"],
40
+ "env": {
41
+ "KLAVIYO_API_KEY": "pk_your_private_key_here",
42
+ "SHOPIFY_STORE": "your-store.myshopify.com",
43
+ "SHOPIFY_CLIENT_ID": "your_client_id",
44
+ "SHOPIFY_CLIENT_SECRET": "shpss_your_secret"
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ 4. Restart Claude Desktop
52
+ 5. Look for the hammer icon in the chat input — that confirms the MCP tools are loaded
53
+
54
+ ### Klaviyo-only mode
55
+
56
+ If you only use Klaviyo (no Shopify), just omit the Shopify variables. The 8 Klaviyo tools and subscriber analytics will work standalone. Shopify tools will return a helpful "not configured" message.
57
+
58
+ ## Setup with ChatGPT
59
+
60
+ ChatGPT supports MCP servers via remote connections. Since dtc-mcp uses stdio transport (runs locally), you would need an MCP-to-HTTP bridge to expose it as a remote server. See [OpenAI's MCP documentation](https://platform.openai.com/docs/guides/tools-remote-mcp) for details on connecting remote MCP servers.
61
+
62
+ ## Getting Your API Credentials
63
+
64
+ ### Klaviyo API Key
65
+
66
+ 1. Log into [Klaviyo](https://www.klaviyo.com/login)
67
+ 2. Go to **Settings** (bottom-left) > **Account** > **Settings**
68
+ 3. Click **API Keys** in the left sidebar
69
+ 4. Click **Create Private API Key**
70
+ 5. Give it a name (e.g., "dtc-mcp")
71
+ 6. Select **Read-only** access for these scopes:
72
+ - `campaigns:read`
73
+ - `flows:read`
74
+ - `lists:read`
75
+ - `segments:read`
76
+ - `profiles:read`
77
+ - `metrics:read`
78
+ - `events:read`
79
+ 7. Copy the key (starts with `pk_`)
80
+
81
+ ### Shopify Credentials
82
+
83
+ There are two authentication methods. Use whichever matches your app type.
84
+
85
+ #### Option A: Dev Dashboard App (Recommended)
86
+
87
+ For apps created in the [Shopify Partners Dashboard](https://partners.shopify.com/) or Shopify CLI (required for new apps since January 2026):
88
+
89
+ 1. Go to your app in the Partners Dashboard
90
+ 2. Navigate to **Configuration** > **Client credentials**
91
+ 3. Copy the **Client ID** and **Client Secret**
92
+ 4. Your store URL is your `*.myshopify.com` domain
93
+
94
+ Set these environment variables:
95
+ ```
96
+ SHOPIFY_STORE=your-store.myshopify.com
97
+ SHOPIFY_CLIENT_ID=your_client_id
98
+ SHOPIFY_CLIENT_SECRET=shpss_your_secret
99
+ ```
100
+
101
+ **Required scopes:** `read_orders`, `read_products`, `read_customers`, `read_inventory`
102
+
103
+ #### Option B: Legacy Custom App
104
+
105
+ For custom apps created directly in Shopify Admin (apps created before January 2026):
106
+
107
+ 1. Go to **Shopify Admin** > **Settings** > **Apps and sales channels**
108
+ 2. Click **Develop apps** > select your app
109
+ 3. Go to **API credentials** and copy the **Admin API access token**
110
+
111
+ Set these environment variables:
112
+ ```
113
+ SHOPIFY_STORE=your-store.myshopify.com
114
+ SHOPIFY_ACCESS_TOKEN=shpat_your_token_here
115
+ ```
116
+
117
+ > Do not set both `SHOPIFY_ACCESS_TOKEN` and `SHOPIFY_CLIENT_ID`/`SHOPIFY_CLIENT_SECRET` at the same time. The server will error if both are present.
118
+
119
+ ## Environment Variables
120
+
121
+ | Variable | Required | Description |
122
+ |----------|----------|-------------|
123
+ | `KLAVIYO_API_KEY` | Yes | Klaviyo private API key (starts with `pk_`) |
124
+ | `SHOPIFY_STORE` | For Shopify | Your `*.myshopify.com` domain |
125
+ | `SHOPIFY_CLIENT_ID` | For Shopify (Dev Dashboard) | App client ID |
126
+ | `SHOPIFY_CLIENT_SECRET` | For Shopify (Dev Dashboard) | App client secret (starts with `shpss_`) |
127
+ | `SHOPIFY_ACCESS_TOKEN` | For Shopify (Legacy) | Admin API access token (starts with `shpat_`) |
128
+ | `SHOPIFY_API_VERSION` | No | Shopify API version (default: `2026-01`) |
129
+ | `KLAVIYO_CONVERSION_METRIC_ID` | No | Override auto-discovered "Placed Order" metric ID |
130
+ | `LOG_LEVEL` | No | `debug` \| `info` \| `warn` \| `error` (default: `info`) |
131
+
132
+ ## Tool Reference
133
+
134
+ ### Klaviyo Tools
135
+
136
+ #### `klaviyo_campaign_summary`
137
+ Top campaigns ranked by metric. Returns name, send date, opens, clicks, revenue.
138
+
139
+ | Parameter | Type | Default | Description |
140
+ |-----------|------|---------|-------------|
141
+ | `channel` | `"email"` \| `"sms"` | required | Channel filter |
142
+ | `metric` | `"revenue"` \| `"open_rate"` \| `"click_rate"` \| `"recipients"` | `"revenue"` | Rank by |
143
+ | `days` | 1-365 | 30 | Lookback period |
144
+ | `limit` | 1-25 | 10 | Max results |
145
+
146
+ > "Show me my top email campaigns by revenue this month"
147
+
148
+ #### `klaviyo_campaign_detail`
149
+ Deep dive on one campaign: full metrics, subject line, audiences, send time.
150
+
151
+ | Parameter | Type | Default | Description |
152
+ |-----------|------|---------|-------------|
153
+ | `campaign_id` | string | required | Klaviyo campaign ID |
154
+
155
+ > "Give me the full breakdown on my Black Friday campaign"
156
+
157
+ #### `klaviyo_flow_summary`
158
+ Top flows by metric. Returns name, status, trigger, message count, revenue.
159
+
160
+ | Parameter | Type | Default | Description |
161
+ |-----------|------|---------|-------------|
162
+ | `metric` | `"revenue"` \| `"click_rate"` \| `"conversion_rate"` \| `"recipients"` | `"revenue"` | Rank by |
163
+ | `days` | 1-365 | 30 | Lookback period |
164
+ | `status` | `"live"` \| `"draft"` \| `"manual"` \| `"all"` | `"live"` | Filter by status |
165
+ | `limit` | 1-25 | 10 | Max results |
166
+
167
+ > "Which of my flows generates the most revenue?"
168
+
169
+ #### `klaviyo_flow_detail`
170
+ Deep dive on one flow: per-message performance breakdown.
171
+
172
+ | Parameter | Type | Default | Description |
173
+ |-----------|------|---------|-------------|
174
+ | `flow_id` | string | required | Klaviyo flow ID |
175
+ | `days` | 1-365 | 30 | Lookback period |
176
+
177
+ > "Show me the per-email breakdown of my welcome flow"
178
+
179
+ #### `klaviyo_subscriber_health`
180
+ List growth and engagement tier breakdown.
181
+
182
+ | Parameter | Type | Default | Description |
183
+ |-----------|------|---------|-------------|
184
+ | `list_id` | string | optional | Specific list, or all lists |
185
+
186
+ > "What's the health of my email list?"
187
+
188
+ #### `klaviyo_list_segments`
189
+ All lists and segments with sizes.
190
+
191
+ | Parameter | Type | Default | Description |
192
+ |-----------|------|---------|-------------|
193
+ | `type` | `"lists"` \| `"segments"` \| `"all"` | `"all"` | Filter by type |
194
+ | `cursor` | string | optional | Pagination cursor |
195
+
196
+ > "List all my Klaviyo segments and their sizes"
197
+
198
+ #### `klaviyo_search_profiles`
199
+ Find profiles by email, phone, or name.
200
+
201
+ | Parameter | Type | Default | Description |
202
+ |-----------|------|---------|-------------|
203
+ | `query` | string | required | Email, phone, or name |
204
+ | `limit` | 1-10 | 5 | Max results |
205
+
206
+ > "Look up the profile for john@example.com"
207
+
208
+ #### `klaviyo_recent_activity`
209
+ Recent events for a metric (e.g., Placed Order, Opened Email).
210
+
211
+ | Parameter | Type | Default | Description |
212
+ |-----------|------|---------|-------------|
213
+ | `metric_name` | string | `"Placed Order"` | Metric name |
214
+ | `days` | 1-90 | 7 | Lookback period |
215
+ | `limit` | 1-25 | 10 | Max events |
216
+ | `profile_email` | string | optional | Filter to one profile |
217
+
218
+ > "Show me the last 10 orders placed"
219
+
220
+ ### Shopify Tools
221
+
222
+ #### `shopify_sales_summary`
223
+ Revenue (gross + net), orders, AOV for a period with comparison.
224
+
225
+ | Parameter | Type | Default | Description |
226
+ |-----------|------|---------|-------------|
227
+ | `days` | 1-90 | 30 | Lookback period |
228
+ | `compare_previous` | boolean | true | Include previous period comparison |
229
+
230
+ > "What were my sales last month compared to the month before?"
231
+
232
+ #### `shopify_sales_timeseries`
233
+ Revenue and orders broken down by day, week, or month.
234
+
235
+ | Parameter | Type | Default | Description |
236
+ |-----------|------|---------|-------------|
237
+ | `days` | 1-365 | 30 | Lookback period |
238
+ | `granularity` | `"daily"` \| `"weekly"` \| `"monthly"` | `"daily"` | Bucket size |
239
+
240
+ > "Show me daily revenue for this month"
241
+
242
+ #### `shopify_product_performance`
243
+ Top products by revenue or units sold.
244
+
245
+ | Parameter | Type | Default | Description |
246
+ |-----------|------|---------|-------------|
247
+ | `days` | 1-90 | 7 | Lookback period |
248
+ | `metric` | `"revenue"` \| `"units"` | `"revenue"` | Rank by |
249
+ | `limit` | 1-25 | 10 | Max results |
250
+
251
+ > "Which products sold the most units this week?"
252
+
253
+ #### `shopify_order_search`
254
+ Find orders by number, email, or status.
255
+
256
+ | Parameter | Type | Default | Description |
257
+ |-----------|------|---------|-------------|
258
+ | `query` | string | required | Order number, email, or `financial_status:paid` |
259
+ | `limit` | 1-25 | 10 | Max results |
260
+
261
+ > "Find order #1234"
262
+
263
+ #### `shopify_inventory_alerts`
264
+ Products with low or zero stock, sorted by most urgent.
265
+
266
+ | Parameter | Type | Default | Description |
267
+ |-----------|------|---------|-------------|
268
+ | `threshold` | number | 10 | Alert at or below this quantity |
269
+ | `limit` | 1-50 | 20 | Max results |
270
+
271
+ > "Which products are running low on stock?"
272
+
273
+ #### `shopify_customer_cohort`
274
+ New vs returning buyers. First-time vs repeat split.
275
+
276
+ | Parameter | Type | Default | Description |
277
+ |-----------|------|---------|-------------|
278
+ | `days` | 1-365 | 90 | Lookback period |
279
+ | `limit` | 1-500 | 250 | Max customers to analyze |
280
+
281
+ > "What percentage of orders this quarter came from new customers?"
282
+
283
+ #### `shopify_recent_orders`
284
+ Most recent orders. Quick snapshot of store activity.
285
+
286
+ | Parameter | Type | Default | Description |
287
+ |-----------|------|---------|-------------|
288
+ | `limit` | 1-25 | 10 | Max results |
289
+
290
+ > "Show me the last 10 orders"
291
+
292
+ ### Cross-Platform Tools
293
+
294
+ #### `dtc_email_revenue_attribution`
295
+ Email/SMS revenue vs total Shopify revenue. Shows email marketing contribution.
296
+
297
+ | Parameter | Type | Default | Description |
298
+ |-----------|------|---------|-------------|
299
+ | `days` | 1-365 | 30 | Lookback period |
300
+
301
+ > "What percentage of my revenue came from email?"
302
+
303
+ #### `dtc_dashboard`
304
+ Complete DTC health dashboard: sales + email + subscriber metrics in one call.
305
+
306
+ | Parameter | Type | Default | Description |
307
+ |-----------|------|---------|-------------|
308
+ | `days` | 7-90 | 30 | Lookback period |
309
+
310
+ > "Give me the full business dashboard for last month"
311
+
312
+ ## Example Queries
313
+
314
+ Here are questions you can ask Claude once dtc-mcp is connected:
315
+
316
+ - "How did my email campaigns perform this month?"
317
+ - "Which flow is generating the most revenue? Drill into the top one."
318
+ - "Show me daily revenue for this month so I can compare against my Shopify dashboard"
319
+ - "What's my gross vs net revenue for the past 30 days?"
320
+ - "Which products are my best sellers this week?"
321
+ - "Are any products running low on stock?"
322
+ - "What percentage of my revenue comes from email marketing?"
323
+ - "How many new vs returning customers did I have this quarter?"
324
+ - "Look up the customer profile for sarah@example.com"
325
+ - "Give me a complete health dashboard for my business"
326
+ - "Compare this month's sales to last month"
327
+ - "What are my top SMS campaigns by click rate?"
328
+
329
+ ## Development
330
+
331
+ ```bash
332
+ git clone https://github.com/rafaelsztutman/dtc-mcp.git
333
+ cd dtc-mcp
334
+ npm install
335
+ cp .env.example .env # Fill in your API credentials
336
+ npm run build # Compile TypeScript
337
+ npm test # Run tests (31 tests)
338
+ npm run dev # Watch mode
339
+ npm run inspect # Open MCP Inspector for interactive testing
340
+ ```
341
+
342
+ ## License
343
+
344
+ MIT
@@ -0,0 +1,17 @@
1
+ export type LogLevel = "debug" | "info" | "warn" | "error";
2
+ export type ShopifyAuthMode = "client_credentials" | "legacy" | null;
3
+ export interface Config {
4
+ readonly klaviyoApiKey: string;
5
+ readonly klaviyoRevision: string;
6
+ readonly shopifyStore: string | null;
7
+ readonly shopifyAccessToken: string | null;
8
+ readonly shopifyClientId: string | null;
9
+ readonly shopifyClientSecret: string | null;
10
+ readonly shopifyAuthMode: ShopifyAuthMode;
11
+ readonly shopifyApiVersion: string;
12
+ readonly klaviyoConversionMetricId: string | null;
13
+ readonly logLevel: LogLevel;
14
+ }
15
+ export declare const config: Config;
16
+ export declare function isShopifyConfigured(): boolean;
17
+ export declare function log(level: LogLevel, message: string, data?: Record<string, unknown>): void;
package/dist/config.js ADDED
@@ -0,0 +1,71 @@
1
+ function getRequired(name) {
2
+ const value = process.env[name];
3
+ if (!value) {
4
+ throw new Error(`Missing required environment variable: ${name}. ` +
5
+ `Set it in your environment: export ${name}=your_value_here`);
6
+ }
7
+ return value;
8
+ }
9
+ function getOptional(name) {
10
+ return process.env[name] || null;
11
+ }
12
+ function loadConfig() {
13
+ const klaviyoApiKey = getRequired("KLAVIYO_API_KEY");
14
+ const shopifyStore = getOptional("SHOPIFY_STORE");
15
+ const shopifyAccessToken = getOptional("SHOPIFY_ACCESS_TOKEN");
16
+ const shopifyClientId = getOptional("SHOPIFY_CLIENT_ID");
17
+ const shopifyClientSecret = getOptional("SHOPIFY_CLIENT_SECRET");
18
+ // Determine auth mode
19
+ const hasLegacy = shopifyAccessToken !== null;
20
+ const hasClientCredentials = shopifyClientId !== null && shopifyClientSecret !== null;
21
+ if (hasLegacy && hasClientCredentials) {
22
+ throw new Error("Ambiguous Shopify config: both SHOPIFY_ACCESS_TOKEN and SHOPIFY_CLIENT_ID/SHOPIFY_CLIENT_SECRET are set. Use one auth mode, not both.");
23
+ }
24
+ let shopifyAuthMode = null;
25
+ if (hasClientCredentials) {
26
+ shopifyAuthMode = "client_credentials";
27
+ }
28
+ else if (hasLegacy) {
29
+ shopifyAuthMode = "legacy";
30
+ }
31
+ // Validate that store is set when any auth is configured
32
+ if (shopifyAuthMode && !shopifyStore) {
33
+ throw new Error("SHOPIFY_STORE is required when Shopify credentials are configured.");
34
+ }
35
+ // Validate partial client credentials
36
+ if ((shopifyClientId && !shopifyClientSecret) ||
37
+ (!shopifyClientId && shopifyClientSecret)) {
38
+ throw new Error("Both SHOPIFY_CLIENT_ID and SHOPIFY_CLIENT_SECRET must be set together.");
39
+ }
40
+ const logLevel = (process.env.LOG_LEVEL || "info");
41
+ if (!["debug", "info", "warn", "error"].includes(logLevel)) {
42
+ throw new Error(`Invalid LOG_LEVEL: ${logLevel}. Must be one of: debug, info, warn, error`);
43
+ }
44
+ return Object.freeze({
45
+ klaviyoApiKey,
46
+ klaviyoRevision: "2026-01-15",
47
+ shopifyStore,
48
+ shopifyAccessToken,
49
+ shopifyClientId,
50
+ shopifyClientSecret,
51
+ shopifyAuthMode,
52
+ shopifyApiVersion: process.env.SHOPIFY_API_VERSION || "2026-01",
53
+ klaviyoConversionMetricId: getOptional("KLAVIYO_CONVERSION_METRIC_ID"),
54
+ logLevel,
55
+ });
56
+ }
57
+ export const config = loadConfig();
58
+ export function isShopifyConfigured() {
59
+ return config.shopifyStore !== null && config.shopifyAuthMode !== null;
60
+ }
61
+ export function log(level, message, data) {
62
+ const levels = ["debug", "info", "warn", "error"];
63
+ if (levels.indexOf(level) < levels.indexOf(config.logLevel))
64
+ return;
65
+ const entry = data
66
+ ? `[dtc-mcp] ${level}: ${message} ${JSON.stringify(data)}`
67
+ : `[dtc-mcp] ${level}: ${message}`;
68
+ // Always log to stderr — stdout is the MCP transport channel
69
+ console.error(entry);
70
+ }
71
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { RevenueAttribution, Dashboard } from "../shared/types.js";
2
+ /**
3
+ * Compare Klaviyo attributed revenue vs Shopify total revenue.
4
+ */
5
+ export declare function computeRevenueAttribution(days: number): Promise<RevenueAttribution>;
6
+ /**
7
+ * Orchestrate a complete DTC health dashboard.
8
+ * Uses cached results from sub-queries to minimize API calls.
9
+ */
10
+ export declare function computeDashboard(days: number): Promise<Dashboard>;
@@ -0,0 +1,166 @@
1
+ import { isShopifyConfigured } from "../config.js";
2
+ import { getCampaignReport, getFlowReport } from "../platforms/klaviyo/client.js";
3
+ import { getAggregatedSales } from "../platforms/shopify/client.js";
4
+ /**
5
+ * Compare Klaviyo attributed revenue vs Shopify total revenue.
6
+ */
7
+ export async function computeRevenueAttribution(days) {
8
+ // Fetch Klaviyo campaign + flow revenue (uses cached reporting data)
9
+ const [campaignRows, flowRows] = await Promise.all([
10
+ getCampaignReport(days),
11
+ getFlowReport(days),
12
+ ]);
13
+ // Sum campaign revenue
14
+ let campaignRevenue = 0;
15
+ const campaignRevenueMap = new Map();
16
+ for (const row of campaignRows) {
17
+ const revenue = row.statistics.conversion_value ?? 0;
18
+ const id = String(row.groupings.campaign_id ?? "unknown");
19
+ campaignRevenue += revenue;
20
+ const existing = campaignRevenueMap.get(id);
21
+ if (existing) {
22
+ existing.revenue += revenue;
23
+ }
24
+ else {
25
+ campaignRevenueMap.set(id, { name: id, revenue });
26
+ }
27
+ }
28
+ // Sum flow revenue
29
+ let flowRevenue = 0;
30
+ const flowRevenueMap = new Map();
31
+ for (const row of flowRows) {
32
+ const revenue = row.statistics.conversion_value ?? 0;
33
+ const id = String(row.groupings.flow_id ?? "unknown");
34
+ flowRevenue += revenue;
35
+ const existing = flowRevenueMap.get(id);
36
+ if (existing) {
37
+ existing.revenue += revenue;
38
+ }
39
+ else {
40
+ flowRevenueMap.set(id, { name: id, revenue });
41
+ }
42
+ }
43
+ const emailTotalRevenue = campaignRevenue + flowRevenue;
44
+ // Get Shopify total revenue
45
+ let totalRevenue = emailTotalRevenue;
46
+ if (isShopifyConfigured()) {
47
+ try {
48
+ const sales = await getAggregatedSales(days);
49
+ totalRevenue = sales.netRevenue || emailTotalRevenue;
50
+ }
51
+ catch {
52
+ // Shopify unavailable — use email revenue as total
53
+ }
54
+ }
55
+ const emailPct = totalRevenue > 0
56
+ ? Math.round((emailTotalRevenue / totalRevenue) * 10000) / 100
57
+ : 0;
58
+ const topCampaigns = Array.from(campaignRevenueMap.values())
59
+ .sort((a, b) => b.revenue - a.revenue)
60
+ .slice(0, 5);
61
+ const topFlows = Array.from(flowRevenueMap.values())
62
+ .sort((a, b) => b.revenue - a.revenue)
63
+ .slice(0, 5);
64
+ return {
65
+ period_days: days,
66
+ total_revenue: Math.round(totalRevenue * 100) / 100,
67
+ email_campaign_revenue: Math.round(campaignRevenue * 100) / 100,
68
+ flow_revenue: Math.round(flowRevenue * 100) / 100,
69
+ email_total_revenue: Math.round(emailTotalRevenue * 100) / 100,
70
+ email_pct_of_total: emailPct,
71
+ flow_vs_campaign_split: {
72
+ campaign_pct: emailTotalRevenue > 0
73
+ ? Math.round((campaignRevenue / emailTotalRevenue) * 10000) / 100
74
+ : 0,
75
+ flow_pct: emailTotalRevenue > 0
76
+ ? Math.round((flowRevenue / emailTotalRevenue) * 10000) / 100
77
+ : 0,
78
+ },
79
+ top_revenue_campaigns: topCampaigns,
80
+ top_revenue_flows: topFlows,
81
+ note: "Revenue attribution uses Klaviyo's send-date model. Actual overlap with Shopify revenue may vary.",
82
+ };
83
+ }
84
+ /**
85
+ * Orchestrate a complete DTC health dashboard.
86
+ * Uses cached results from sub-queries to minimize API calls.
87
+ */
88
+ export async function computeDashboard(days) {
89
+ // Run Klaviyo queries in parallel (these hit cache if recent calls were made)
90
+ const [campaignRows, flowRows] = await Promise.all([
91
+ getCampaignReport(days),
92
+ getFlowReport(days),
93
+ ]);
94
+ // Sum email revenue
95
+ let campaignRevenue = 0;
96
+ const topCampaigns = [];
97
+ const campaignMap = new Map();
98
+ for (const row of campaignRows) {
99
+ const id = String(row.groupings.campaign_id ?? "");
100
+ const existing = campaignMap.get(id) ?? { revenue: 0, opens: 0, recipients: 0 };
101
+ existing.revenue += row.statistics.conversion_value ?? 0;
102
+ existing.opens += row.statistics.opens_unique ?? row.statistics.opens ?? 0;
103
+ existing.recipients += row.statistics.recipients ?? 0;
104
+ campaignMap.set(id, existing);
105
+ campaignRevenue += row.statistics.conversion_value ?? 0;
106
+ }
107
+ for (const [name, stats] of campaignMap.entries()) {
108
+ topCampaigns.push({
109
+ name,
110
+ revenue: Math.round(stats.revenue * 100) / 100,
111
+ open_rate: stats.recipients > 0
112
+ ? Math.round((stats.opens / stats.recipients) * 10000) / 10000
113
+ : 0,
114
+ });
115
+ }
116
+ topCampaigns.sort((a, b) => b.revenue - a.revenue);
117
+ let flowRevenue = 0;
118
+ const topFlows = [];
119
+ const flowMap = new Map();
120
+ for (const row of flowRows) {
121
+ const id = String(row.groupings.flow_id ?? "");
122
+ flowMap.set(id, (flowMap.get(id) ?? 0) + (row.statistics.conversion_value ?? 0));
123
+ flowRevenue += row.statistics.conversion_value ?? 0;
124
+ }
125
+ for (const [name, revenue] of flowMap.entries()) {
126
+ topFlows.push({ name, revenue: Math.round(revenue * 100) / 100 });
127
+ }
128
+ topFlows.sort((a, b) => b.revenue - a.revenue);
129
+ const emailRevenue = campaignRevenue + flowRevenue;
130
+ // Shopify sales
131
+ let shopifyRevenue = emailRevenue;
132
+ let orderCount = 0;
133
+ let aov = 0;
134
+ if (isShopifyConfigured()) {
135
+ try {
136
+ const sales = await getAggregatedSales(days);
137
+ shopifyRevenue = sales.netRevenue || emailRevenue;
138
+ orderCount = sales.orderCount;
139
+ aov = orderCount > 0 ? Math.round((shopifyRevenue / orderCount) * 100) / 100 : 0;
140
+ }
141
+ catch {
142
+ // Shopify unavailable
143
+ }
144
+ }
145
+ return {
146
+ period_days: days,
147
+ sales: {
148
+ revenue: Math.round(shopifyRevenue * 100) / 100,
149
+ orders: orderCount,
150
+ aov,
151
+ },
152
+ email: {
153
+ email_revenue: Math.round(emailRevenue * 100) / 100,
154
+ email_pct_of_total: shopifyRevenue > 0
155
+ ? Math.round((emailRevenue / shopifyRevenue) * 10000) / 100
156
+ : 0,
157
+ top_campaigns: topCampaigns.slice(0, 5),
158
+ top_flows: topFlows.slice(0, 5),
159
+ },
160
+ subscribers: {
161
+ total: 0, // Would require a separate API call — omit to save rate limits
162
+ list_count: 0,
163
+ },
164
+ };
165
+ }
166
+ //# sourceMappingURL=correlator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlator.js","sourceRoot":"","sources":["../../src/cross-platform/correlator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCrossPlatformTools(server: McpServer): void;