mcp-meta-ads-incrementality 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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * mcp-meta-ads-incrementality — MCP server entrypoint.
4
+ *
5
+ * Exposes Meta Marketing API insights with a focus on incrementality reporting.
6
+ * Defaults to Meta's `incrementality` attribution window so agencies can lead
7
+ * with the incremental conversion number rather than the inflated default
8
+ * 7d-click + 1d-view that platform reports surface today.
9
+ *
10
+ * Auth: reads META_ACCESS_TOKEN from the environment. Recommended: System User
11
+ * token from Business Manager (long-lived, scoped to ad accounts the System
12
+ * User has access to). User access tokens also work but expire.
13
+ *
14
+ * Transport: stdio. Configure inside your MCP client (Claude Desktop, Claude
15
+ * Code, etc.) by pointing it at this binary.
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG"}
package/dist/index.js ADDED
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * mcp-meta-ads-incrementality — MCP server entrypoint.
4
+ *
5
+ * Exposes Meta Marketing API insights with a focus on incrementality reporting.
6
+ * Defaults to Meta's `incrementality` attribution window so agencies can lead
7
+ * with the incremental conversion number rather than the inflated default
8
+ * 7d-click + 1d-view that platform reports surface today.
9
+ *
10
+ * Auth: reads META_ACCESS_TOKEN from the environment. Recommended: System User
11
+ * token from Business Manager (long-lived, scoped to ad accounts the System
12
+ * User has access to). User access tokens also work but expire.
13
+ *
14
+ * Transport: stdio. Configure inside your MCP client (Claude Desktop, Claude
15
+ * Code, etc.) by pointing it at this binary.
16
+ */
17
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
18
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
19
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
20
+ import { readFileSync } from 'node:fs';
21
+ import { dirname, join } from 'node:path';
22
+ import { fileURLToPath } from 'node:url';
23
+ import { MetaInsightsClient, createDefaultMetaSdk } from './lib/meta-client.js';
24
+ import { runInsightsIncrementality, } from './tools/insights-incrementality.js';
25
+ import { ALL_KNOWN_WINDOWS } from './lib/attribution.js';
26
+ import { resolveCredentials } from './credentials.js';
27
+ /* ------------------------------------------------------------------------- */
28
+ /* CLI flags */
29
+ /* ------------------------------------------------------------------------- */
30
+ const __dirname = dirname(fileURLToPath(import.meta.url));
31
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
32
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
33
+ process.stderr.write(`${pkg.name} v${pkg.version}\n\n`);
34
+ process.stderr.write(`MCP server for Meta Marketing API incrementality reporting.\n`);
35
+ process.stderr.write(`Communicates over stdio. Configure in your MCP client.\n\n`);
36
+ process.stderr.write(`Auth (priority order):\n`);
37
+ process.stderr.write(` 1. META_ACCESS_TOKEN env var (System User token, recommended)\n`);
38
+ process.stderr.write(` 2. Cached OAuth token from: npx -p mcp-meta-ads-incrementality mcp-meta-ads-auth\n\n`);
39
+ process.stderr.write(`Options:\n`);
40
+ process.stderr.write(` --help, -h Show this help\n`);
41
+ process.stderr.write(` --version, -v Show version\n`);
42
+ process.exit(0);
43
+ }
44
+ if (process.argv.includes('--version') || process.argv.includes('-v')) {
45
+ process.stderr.write(`${pkg.version}\n`);
46
+ process.exit(0);
47
+ }
48
+ /* ------------------------------------------------------------------------- */
49
+ /* Auth & client construction */
50
+ /* ------------------------------------------------------------------------- */
51
+ let accessToken;
52
+ try {
53
+ const resolved = resolveCredentials();
54
+ accessToken = resolved.access_token;
55
+ process.stderr.write(`[startup] Auth: ${resolved.source === 'env' ? 'META_ACCESS_TOKEN env var' : 'cached OAuth token'}\n`);
56
+ if (resolved.expires_at) {
57
+ const daysLeft = Math.round((resolved.expires_at - Date.now() / 1000) / 86400);
58
+ process.stderr.write(`[startup] Token expires in ~${daysLeft} days\n`);
59
+ }
60
+ }
61
+ catch (err) {
62
+ process.stderr.write(`[fatal] ${err.message}\n`);
63
+ process.exit(1);
64
+ }
65
+ let deps;
66
+ try {
67
+ const sdk = await createDefaultMetaSdk();
68
+ const metaClient = new MetaInsightsClient(sdk, accessToken);
69
+ deps = { metaClient };
70
+ }
71
+ catch (err) {
72
+ process.stderr.write(`[fatal] Failed to initialize Meta SDK: ${err.message}\n`);
73
+ process.exit(1);
74
+ }
75
+ /* ------------------------------------------------------------------------- */
76
+ /* Tool definitions (JSONSchema) */
77
+ /* ------------------------------------------------------------------------- */
78
+ const TOOL_INSIGHTS_INCREMENTALITY = {
79
+ name: 'meta_ads_insights_incrementality',
80
+ description: 'Pull Meta Ads insights with a focus on incremental conversions. By default this returns ' +
81
+ 'Meta\'s "incrementality" attribution window — the actual incremental conversion count — ' +
82
+ 'rather than the default 7d-click + 1d-view number that systematically overstates ad impact. ' +
83
+ 'When both incrementality and default windows are requested in the same call, the response ' +
84
+ 'includes inflation_factor and overstatement_pct so an agency can quantify the gap between ' +
85
+ 'reported and incremental performance.',
86
+ inputSchema: {
87
+ type: 'object',
88
+ properties: {
89
+ ad_account_id: {
90
+ type: 'string',
91
+ description: 'Meta ad account id (with or without the act_ prefix).',
92
+ },
93
+ date_range: {
94
+ type: 'object',
95
+ properties: {
96
+ since: { type: 'string', description: 'YYYY-MM-DD start date (inclusive)' },
97
+ until: { type: 'string', description: 'YYYY-MM-DD end date (inclusive)' },
98
+ },
99
+ required: ['since', 'until'],
100
+ },
101
+ conversion_event_name: {
102
+ type: 'string',
103
+ description: 'The action_type to extract, e.g. "offsite_conversion.fb_pixel_purchase", ' +
104
+ '"omni_purchase", or a custom conversion name.',
105
+ },
106
+ attribution_windows: {
107
+ type: 'array',
108
+ items: { type: 'string', enum: [...ALL_KNOWN_WINDOWS] },
109
+ description: 'Which attribution windows to fetch. Defaults to ["incrementality"]. Pass ' +
110
+ '["incrementality", "7d_click", "1d_view"] for the headline incrementality-vs-default ' +
111
+ 'comparison. Add "dda" to also include Meta\'s data-driven attribution number.',
112
+ },
113
+ level: {
114
+ type: 'string',
115
+ enum: ['account', 'campaign', 'adset', 'ad'],
116
+ description: 'Aggregation level. Defaults to "campaign".',
117
+ },
118
+ campaign_id: { type: 'string', description: 'Optional: restrict to a single campaign.' },
119
+ adset_id: { type: 'string', description: 'Optional: restrict to a single ad set.' },
120
+ breakdowns: {
121
+ type: 'array',
122
+ items: { type: 'string' },
123
+ description: 'Optional Meta breakdowns, e.g. ["publisher_platform", "age"].',
124
+ },
125
+ },
126
+ required: ['ad_account_id', 'date_range', 'conversion_event_name'],
127
+ },
128
+ };
129
+ const TOOLS = [TOOL_INSIGHTS_INCREMENTALITY];
130
+ /* ------------------------------------------------------------------------- */
131
+ /* Server */
132
+ /* ------------------------------------------------------------------------- */
133
+ const server = new Server({ name: pkg.name, version: pkg.version }, { capabilities: { tools: {} } });
134
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
135
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
136
+ const { name, arguments: args } = request.params;
137
+ try {
138
+ switch (name) {
139
+ case 'meta_ads_insights_incrementality': {
140
+ const result = await runInsightsIncrementality(args, deps);
141
+ return {
142
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
143
+ };
144
+ }
145
+ default:
146
+ return {
147
+ isError: true,
148
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
149
+ };
150
+ }
151
+ }
152
+ catch (err) {
153
+ const message = err instanceof Error ? err.message : String(err);
154
+ return {
155
+ isError: true,
156
+ content: [{ type: 'text', text: `Error: ${message}` }],
157
+ };
158
+ }
159
+ });
160
+ const transport = new StdioServerTransport();
161
+ await server.connect(transport);
162
+ process.stderr.write(`[ready] ${pkg.name} v${pkg.version} listening on stdio\n`);
163
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * All Meta attribution window values this MCP recognizes.
4
+ *
5
+ * Includes:
6
+ * - `incrementality` — the differentiator. Meta's incremental conversion attribution.
7
+ * This is not exhaustively documented in Meta's public Marketing API reference but
8
+ * is referenced in the Breakdowns documentation. We default to it because the entire
9
+ * purpose of this MCP is to surface incremental performance over the inflated
10
+ * default 7d-click + 1d-view number that agencies report today.
11
+ * - `1d_click`, `7d_click`, `28d_click` — documented click windows still supported
12
+ * after the Jan 12, 2026 deprecation of `7d_view` and `28d_view`.
13
+ * - `1d_view` — the only view window remaining post-deprecation.
14
+ * - `1d_ev` — engaged view (video views ≥10s within 1 day).
15
+ * - `dda` — Meta's data-driven attribution. Documented; less click-biased than the
16
+ * default. Useful as a "less inflated" comparison number.
17
+ * - `skan_view`, `skan_click` — SKAdNetwork attribution for iOS post-ATT.
18
+ *
19
+ * Deliberately excluded:
20
+ * - `7d_view`, `28d_view` — deprecated by Meta on Jan 12, 2026 and no longer returned.
21
+ * Surfacing them here would invite confused bug reports.
22
+ */
23
+ export declare const ALL_KNOWN_WINDOWS: readonly ["incrementality", "1d_click", "7d_click", "28d_click", "1d_view", "1d_ev", "dda", "skan_view", "skan_click"];
24
+ export type AttributionWindow = (typeof ALL_KNOWN_WINDOWS)[number];
25
+ /**
26
+ * The default windows requested when the caller does not specify any.
27
+ *
28
+ * Set to `['incrementality']` because this MCP is built around the thesis that
29
+ * agencies should lead with incremental conversions, not Meta's default
30
+ * 7d-click + 1d-view number which systematically overstates ad impact.
31
+ */
32
+ export declare const DEFAULT_ATTRIBUTION_WINDOWS: readonly AttributionWindow[];
33
+ /**
34
+ * The industry-default attribution that agencies currently report. Exposed as a
35
+ * named preset so callers can ask for "what would Ads Manager show by default?"
36
+ * for comparison against `incrementality`.
37
+ */
38
+ export declare const STANDARD_AGENCY_WINDOWS: readonly AttributionWindow[];
39
+ /**
40
+ * Convenience preset that fetches all four numbers in one API call so a caller
41
+ * can show the complete gap: incremental, default-inflated, and Meta's own
42
+ * better-but-still-attributed `dda` model.
43
+ */
44
+ export declare const PRESET_COMPARE_ALL: readonly AttributionWindow[];
45
+ /**
46
+ * Type guard. Returns true only for exact string matches against the known
47
+ * window enum. Case-sensitive — Meta's API is case-sensitive on these values.
48
+ */
49
+ export declare function isValidAttributionWindow(value: unknown): value is AttributionWindow;
50
+ /**
51
+ * Zod schema for a single attribution window. Uses `z.enum` so error messages
52
+ * include the full list of accepted values.
53
+ */
54
+ export declare const AttributionWindowSchema: z.ZodEnum<["incrementality", "1d_click", "7d_click", "28d_click", "1d_view", "1d_ev", "dda", "skan_view", "skan_click"]>;
55
+ /**
56
+ * Normalize caller-supplied attribution windows to a validated, deduplicated array.
57
+ *
58
+ * - undefined / null / empty array → DEFAULT_ATTRIBUTION_WINDOWS (incrementality)
59
+ * - any other input is validated; invalid windows throw with a clear error
60
+ * that names the offending value
61
+ * - duplicates are removed, but first-occurrence order is preserved
62
+ * - returns a fresh mutable array (not a reference to a frozen constant)
63
+ */
64
+ export declare function parseAttributionWindows(input: readonly string[] | null | undefined): AttributionWindow[];
65
+ //# sourceMappingURL=attribution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../../src/lib/attribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,wHAUpB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAInE;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,EAAE,SAAS,iBAAiB,EAEzD,CAAC;AAEZ;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,iBAAiB,EAGrD,CAAC;AAEZ;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,iBAAiB,EAKhD,CAAC;AAEZ;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAEnF;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,0HAA4B,CAAC;AAEjE;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,GAC1C,iBAAiB,EAAE,CAmBrB"}
@@ -0,0 +1,103 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * All Meta attribution window values this MCP recognizes.
4
+ *
5
+ * Includes:
6
+ * - `incrementality` — the differentiator. Meta's incremental conversion attribution.
7
+ * This is not exhaustively documented in Meta's public Marketing API reference but
8
+ * is referenced in the Breakdowns documentation. We default to it because the entire
9
+ * purpose of this MCP is to surface incremental performance over the inflated
10
+ * default 7d-click + 1d-view number that agencies report today.
11
+ * - `1d_click`, `7d_click`, `28d_click` — documented click windows still supported
12
+ * after the Jan 12, 2026 deprecation of `7d_view` and `28d_view`.
13
+ * - `1d_view` — the only view window remaining post-deprecation.
14
+ * - `1d_ev` — engaged view (video views ≥10s within 1 day).
15
+ * - `dda` — Meta's data-driven attribution. Documented; less click-biased than the
16
+ * default. Useful as a "less inflated" comparison number.
17
+ * - `skan_view`, `skan_click` — SKAdNetwork attribution for iOS post-ATT.
18
+ *
19
+ * Deliberately excluded:
20
+ * - `7d_view`, `28d_view` — deprecated by Meta on Jan 12, 2026 and no longer returned.
21
+ * Surfacing them here would invite confused bug reports.
22
+ */
23
+ export const ALL_KNOWN_WINDOWS = [
24
+ 'incrementality',
25
+ '1d_click',
26
+ '7d_click',
27
+ '28d_click',
28
+ '1d_view',
29
+ '1d_ev',
30
+ 'dda',
31
+ 'skan_view',
32
+ 'skan_click',
33
+ ];
34
+ const KNOWN_WINDOW_SET = new Set(ALL_KNOWN_WINDOWS);
35
+ /**
36
+ * The default windows requested when the caller does not specify any.
37
+ *
38
+ * Set to `['incrementality']` because this MCP is built around the thesis that
39
+ * agencies should lead with incremental conversions, not Meta's default
40
+ * 7d-click + 1d-view number which systematically overstates ad impact.
41
+ */
42
+ export const DEFAULT_ATTRIBUTION_WINDOWS = Object.freeze([
43
+ 'incrementality',
44
+ ]);
45
+ /**
46
+ * The industry-default attribution that agencies currently report. Exposed as a
47
+ * named preset so callers can ask for "what would Ads Manager show by default?"
48
+ * for comparison against `incrementality`.
49
+ */
50
+ export const STANDARD_AGENCY_WINDOWS = Object.freeze([
51
+ '7d_click',
52
+ '1d_view',
53
+ ]);
54
+ /**
55
+ * Convenience preset that fetches all four numbers in one API call so a caller
56
+ * can show the complete gap: incremental, default-inflated, and Meta's own
57
+ * better-but-still-attributed `dda` model.
58
+ */
59
+ export const PRESET_COMPARE_ALL = Object.freeze([
60
+ 'incrementality',
61
+ '7d_click',
62
+ '1d_view',
63
+ 'dda',
64
+ ]);
65
+ /**
66
+ * Type guard. Returns true only for exact string matches against the known
67
+ * window enum. Case-sensitive — Meta's API is case-sensitive on these values.
68
+ */
69
+ export function isValidAttributionWindow(value) {
70
+ return typeof value === 'string' && KNOWN_WINDOW_SET.has(value);
71
+ }
72
+ /**
73
+ * Zod schema for a single attribution window. Uses `z.enum` so error messages
74
+ * include the full list of accepted values.
75
+ */
76
+ export const AttributionWindowSchema = z.enum(ALL_KNOWN_WINDOWS);
77
+ /**
78
+ * Normalize caller-supplied attribution windows to a validated, deduplicated array.
79
+ *
80
+ * - undefined / null / empty array → DEFAULT_ATTRIBUTION_WINDOWS (incrementality)
81
+ * - any other input is validated; invalid windows throw with a clear error
82
+ * that names the offending value
83
+ * - duplicates are removed, but first-occurrence order is preserved
84
+ * - returns a fresh mutable array (not a reference to a frozen constant)
85
+ */
86
+ export function parseAttributionWindows(input) {
87
+ if (input == null || input.length === 0) {
88
+ return [...DEFAULT_ATTRIBUTION_WINDOWS];
89
+ }
90
+ const seen = new Set();
91
+ const result = [];
92
+ for (const raw of input) {
93
+ if (!isValidAttributionWindow(raw)) {
94
+ throw new Error(`Invalid attribution window: "${raw}". Valid values are: ${ALL_KNOWN_WINDOWS.join(', ')}.`);
95
+ }
96
+ if (!seen.has(raw)) {
97
+ seen.add(raw);
98
+ result.push(raw);
99
+ }
100
+ }
101
+ return result;
102
+ }
103
+ //# sourceMappingURL=attribution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attribution.js","sourceRoot":"","sources":["../../src/lib/attribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,gBAAgB;IAChB,UAAU;IACV,UAAU;IACV,WAAW;IACX,SAAS;IACT,OAAO;IACP,KAAK;IACL,WAAW;IACX,YAAY;CACJ,CAAC;AAIX,MAAM,gBAAgB,GAAwB,IAAI,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAEzE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAiC,MAAM,CAAC,MAAM,CAAC;IACrF,gBAAgB;CACR,CAAC,CAAC;AAEZ;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAiC,MAAM,CAAC,MAAM,CAAC;IACjF,UAAU;IACV,SAAS;CACD,CAAC,CAAC;AAEZ;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAiC,MAAM,CAAC,MAAM,CAAC;IAC5E,gBAAgB;IAChB,UAAU;IACV,SAAS;IACT,KAAK;CACG,CAAC,CAAC;AAEZ;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;AAEjE;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAA2C;IAE3C,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxC,OAAO,CAAC,GAAG,2BAA2B,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC1C,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CACb,gCAAgC,GAAG,wBAAwB,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC3F,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,75 @@
1
+ import type { AttributionWindow } from './attribution.js';
2
+ /**
3
+ * A single entry in the `actions` (or `action_values`) array on a Meta /insights row.
4
+ *
5
+ * Per-window numbers appear as additional string-keyed properties whose values are
6
+ * usually strings (Meta returns numbers as strings in the JSON wire format), but we
7
+ * tolerate raw numbers and unparseable values defensively.
8
+ */
9
+ export interface MetaActionEntry {
10
+ action_type: string;
11
+ value?: string | number;
12
+ [window: string]: string | number | undefined;
13
+ }
14
+ /**
15
+ * A row from Meta's /insights endpoint. Most fields are optional because shape
16
+ * varies by `breakdowns` and `level` (account / campaign / adset / ad).
17
+ */
18
+ export interface MetaInsightsRow {
19
+ date_start?: string;
20
+ date_stop?: string;
21
+ spend?: string | number;
22
+ campaign_id?: string;
23
+ campaign_name?: string;
24
+ adset_id?: string;
25
+ adset_name?: string;
26
+ ad_id?: string;
27
+ ad_name?: string;
28
+ actions?: MetaActionEntry[];
29
+ action_values?: MetaActionEntry[];
30
+ }
31
+ export interface ExtractedConversion {
32
+ date_start?: string;
33
+ date_stop?: string;
34
+ campaign_id?: string;
35
+ campaign_name?: string;
36
+ adset_id?: string;
37
+ adset_name?: string;
38
+ ad_id?: string;
39
+ ad_name?: string;
40
+ spend: number | null;
41
+ /** Per-window conversion counts. Always populated for every requested window. */
42
+ conversions: Record<string, number>;
43
+ }
44
+ /**
45
+ * Extract a single named conversion across the requested attribution windows from
46
+ * one /insights row.
47
+ *
48
+ * - If the action_type is not present in the row, every requested window returns 0.
49
+ * - If a window value is missing for an otherwise-present action, that window returns 0.
50
+ * - When Meta returns multiple entries for the same action_type (multi-currency rows,
51
+ * pixel + offline merges), the values are summed across entries.
52
+ * - String→number coercion is defensive: NaN becomes 0.
53
+ */
54
+ export declare function extractConversion(row: MetaInsightsRow, conversionEventName: string, windows: readonly AttributionWindow[]): ExtractedConversion;
55
+ /**
56
+ * Multi-row variant. Maps `extractConversion` over each row in input order.
57
+ * Use when the underlying /insights call had `breakdowns` set, returning one
58
+ * row per breakdown bucket.
59
+ */
60
+ export declare function extractConversionRows(rows: readonly MetaInsightsRow[], conversionEventName: string, windows: readonly AttributionWindow[]): ExtractedConversion[];
61
+ /**
62
+ * Compute the inflation factor: how much larger is the comparison window
63
+ * (typically default 7d_click+1d_view attribution) than the incrementality number?
64
+ *
65
+ * - Returns the ratio comparison / incrementality.
66
+ * - Returns null when incrementality is 0 (no defined ratio) or both are 0.
67
+ *
68
+ * Example: incrementality=40, comparison=100 → 2.5 (the default number is 2.5×
69
+ * the incremental number, i.e. 60% of reported conversions are not incremental).
70
+ */
71
+ export declare function computeInflationFactor(input: {
72
+ incrementality: number;
73
+ comparison: number;
74
+ }): number | null;
75
+ //# sourceMappingURL=insights.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.d.ts","sourceRoot":"","sources":["../../src/lib/insights.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAExB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,aAAa,CAAC,EAAE,eAAe,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAcD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,eAAe,EACpB,mBAAmB,EAAE,MAAM,EAC3B,OAAO,EAAE,SAAS,iBAAiB,EAAE,GACpC,mBAAmB,CA0BrB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,SAAS,eAAe,EAAE,EAChC,mBAAmB,EAAE,MAAM,EAC3B,OAAO,EAAE,SAAS,iBAAiB,EAAE,GACpC,mBAAmB,EAAE,CAEvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,GAAG,IAAI,CAGhB"}
@@ -0,0 +1,73 @@
1
+ function toNumberOrZero(input) {
2
+ if (input == null)
3
+ return 0;
4
+ const n = typeof input === 'number' ? input : Number(input);
5
+ return Number.isFinite(n) ? n : 0;
6
+ }
7
+ function toNumberOrNull(input) {
8
+ if (input == null)
9
+ return null;
10
+ const n = typeof input === 'number' ? input : Number(input);
11
+ return Number.isFinite(n) ? n : null;
12
+ }
13
+ /**
14
+ * Extract a single named conversion across the requested attribution windows from
15
+ * one /insights row.
16
+ *
17
+ * - If the action_type is not present in the row, every requested window returns 0.
18
+ * - If a window value is missing for an otherwise-present action, that window returns 0.
19
+ * - When Meta returns multiple entries for the same action_type (multi-currency rows,
20
+ * pixel + offline merges), the values are summed across entries.
21
+ * - String→number coercion is defensive: NaN becomes 0.
22
+ */
23
+ export function extractConversion(row, conversionEventName, windows) {
24
+ const conversions = {};
25
+ for (const w of windows)
26
+ conversions[w] = 0;
27
+ if (Array.isArray(row.actions)) {
28
+ for (const action of row.actions) {
29
+ if (action?.action_type !== conversionEventName)
30
+ continue;
31
+ for (const w of windows) {
32
+ const current = conversions[w] ?? 0;
33
+ conversions[w] = current + toNumberOrZero(action[w]);
34
+ }
35
+ }
36
+ }
37
+ return {
38
+ date_start: row.date_start,
39
+ date_stop: row.date_stop,
40
+ campaign_id: row.campaign_id,
41
+ campaign_name: row.campaign_name,
42
+ adset_id: row.adset_id,
43
+ adset_name: row.adset_name,
44
+ ad_id: row.ad_id,
45
+ ad_name: row.ad_name,
46
+ spend: toNumberOrNull(row.spend),
47
+ conversions,
48
+ };
49
+ }
50
+ /**
51
+ * Multi-row variant. Maps `extractConversion` over each row in input order.
52
+ * Use when the underlying /insights call had `breakdowns` set, returning one
53
+ * row per breakdown bucket.
54
+ */
55
+ export function extractConversionRows(rows, conversionEventName, windows) {
56
+ return rows.map((r) => extractConversion(r, conversionEventName, windows));
57
+ }
58
+ /**
59
+ * Compute the inflation factor: how much larger is the comparison window
60
+ * (typically default 7d_click+1d_view attribution) than the incrementality number?
61
+ *
62
+ * - Returns the ratio comparison / incrementality.
63
+ * - Returns null when incrementality is 0 (no defined ratio) or both are 0.
64
+ *
65
+ * Example: incrementality=40, comparison=100 → 2.5 (the default number is 2.5×
66
+ * the incremental number, i.e. 60% of reported conversions are not incremental).
67
+ */
68
+ export function computeInflationFactor(input) {
69
+ if (input.incrementality === 0)
70
+ return null;
71
+ return input.comparison / input.incrementality;
72
+ }
73
+ //# sourceMappingURL=insights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.js","sourceRoot":"","sources":["../../src/lib/insights.ts"],"names":[],"mappings":"AAgDA,SAAS,cAAc,CAAC,KAAkC;IACxD,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,CAAC,CAAC;IAC5B,MAAM,CAAC,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,cAAc,CAAC,KAAkC;IACxD,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,CAAC,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC/B,GAAoB,EACpB,mBAA2B,EAC3B,OAAqC;IAErC,MAAM,WAAW,GAA2B,EAAE,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAE5C,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YACjC,IAAI,MAAM,EAAE,WAAW,KAAK,mBAAmB;gBAAE,SAAS;YAC1D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACxB,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACpC,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC;QAChC,WAAW;KACZ,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAgC,EAChC,mBAA2B,EAC3B,OAAqC;IAErC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAGtC;IACC,IAAI,KAAK,CAAC,cAAc,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,OAAO,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,cAAc,CAAC;AACjD,CAAC"}
@@ -0,0 +1,69 @@
1
+ import type { AttributionWindow } from './attribution.js';
2
+ import type { MetaInsightsRow } from './insights.js';
3
+ /**
4
+ * The minimal subset of `facebook-nodejs-business-sdk`'s surface that this MCP
5
+ * actually depends on. Defining it as an interface lets us inject a fake in
6
+ * unit tests without having to mock the SDK module wholesale.
7
+ *
8
+ * The real SDK adapter is created by `createDefaultMetaSdk()` in this same module.
9
+ */
10
+ export interface MetaSdkLike {
11
+ init(accessToken: string): void;
12
+ createAdAccount(accountId: string): SdkAdAccount;
13
+ }
14
+ export interface SdkAdAccount {
15
+ getInsights(fields: string[], params: Record<string, unknown>): Promise<SdkCursor>;
16
+ }
17
+ /**
18
+ * Matches `Cursor` from facebook-nodejs-business-sdk: an Array-like that holds
19
+ * the current page's items and exposes `hasNext()` / `next()` for pagination.
20
+ *
21
+ * Items in the cursor are SDK-wrapped objects whose raw payload sits on `_data`;
22
+ * we unwrap during iteration so callers always see plain `MetaInsightsRow`.
23
+ * Plain object literals (no `_data`) are also accepted and used directly, which
24
+ * keeps unit-test fakes simple.
25
+ */
26
+ export interface SdkCursor extends ReadonlyArray<SdkCursorItem> {
27
+ hasNext(): boolean;
28
+ next(): Promise<unknown>;
29
+ }
30
+ export type SdkCursorItem = {
31
+ _data?: MetaInsightsRow;
32
+ } & Partial<MetaInsightsRow>;
33
+ export type InsightsLevel = 'account' | 'campaign' | 'adset' | 'ad';
34
+ export interface DateRange {
35
+ /** Inclusive start date in YYYY-MM-DD form. */
36
+ since: string;
37
+ /** Inclusive end date in YYYY-MM-DD form. */
38
+ until: string;
39
+ }
40
+ export interface GetInsightsParams {
41
+ /** Meta ad account id. May be passed with or without the `act_` prefix. */
42
+ accountId: string;
43
+ dateRange: DateRange;
44
+ attributionWindows: readonly AttributionWindow[];
45
+ level: InsightsLevel;
46
+ /** Optional Meta-supported breakdowns (e.g. `publisher_platform`, `age`). */
47
+ breakdowns?: readonly string[];
48
+ /** Restrict to a single campaign by id. */
49
+ campaignId?: string;
50
+ /** Restrict to a single ad set by id. */
51
+ adsetId?: string;
52
+ }
53
+ /**
54
+ * Normalize a Meta ad account id into the `act_<numeric>` form Meta requires.
55
+ * Accepts strings or numbers, with or without the `act_` prefix.
56
+ */
57
+ export declare function normalizeAccountId(input: string | number | null | undefined): string;
58
+ export declare class MetaInsightsClient {
59
+ private readonly sdk;
60
+ constructor(sdk: MetaSdkLike, accessToken: string);
61
+ getInsights(input: GetInsightsParams): Promise<MetaInsightsRow[]>;
62
+ }
63
+ /**
64
+ * Create the production `MetaSdkLike` adapter that delegates to the real
65
+ * `facebook-nodejs-business-sdk`. Imported lazily so unit tests that inject
66
+ * fakes never have to load the SDK.
67
+ */
68
+ export declare function createDefaultMetaSdk(): Promise<MetaSdkLike>;
69
+ //# sourceMappingURL=meta-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta-client.d.ts","sourceRoot":"","sources":["../../src/lib/meta-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAMrD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CAAC;CAClD;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CACpF;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAU,SAAQ,aAAa,CAAC,aAAa,CAAC;IAC7D,OAAO,IAAI,OAAO,CAAC;IACnB,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,KAAK,CAAC,EAAE,eAAe,CAAA;CAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAMnF,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,IAAI,CAAC;AAEpE,MAAM,WAAW,SAAS;IACxB,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,kBAAkB,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,KAAK,EAAE,aAAa,CAAC;IACrB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAMD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAmBpF;AA4BD,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,WAAW,EACjC,WAAW,EAAE,MAAM;IAQf,WAAW,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CA6CxE;AAMD;;;;GAIG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,WAAW,CAAC,CAkBjE"}