mcp-meta-ads-incrementality 0.2.3 → 0.4.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 +30 -2
- package/dist/index.js +6 -597
- package/dist/index.js.map +1 -1
- package/dist/lib/graph.d.ts +2 -2
- package/dist/lib/graph.js +1 -1
- package/dist/lib/naming-standards.d.ts +12 -6
- package/dist/lib/naming-standards.d.ts.map +1 -1
- package/dist/lib/naming-standards.js +12 -6
- package/dist/lib/naming-standards.js.map +1 -1
- package/dist/toolDefinitions.d.ts +784 -0
- package/dist/toolDefinitions.d.ts.map +1 -0
- package/dist/toolDefinitions.js +611 -0
- package/dist/toolDefinitions.js.map +1 -0
- package/dist/tools/campaign-build.d.ts +12 -0
- package/dist/tools/campaign-build.d.ts.map +1 -1
- package/dist/tools/campaign-build.js +43 -16
- package/dist/tools/campaign-build.js.map +1 -1
- package/dist/writeGate.d.ts +25 -0
- package/dist/writeGate.d.ts.map +1 -0
- package/dist/writeGate.js +60 -0
- package/dist/writeGate.js.map +1 -0
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -27,8 +27,9 @@ import { createCampaign, createAdSet, createAdCreative, createAd, uploadImage, }
|
|
|
27
27
|
import { createLeadgenForm, createCustomAudience, uploadUsersToAudience, createLookalikeAudience, } from './tools/audiences-and-forms.js';
|
|
28
28
|
import { updateObjectStatus, updateBudget, deleteObject, } from './tools/lifecycle.js';
|
|
29
29
|
import { validateName } from './lib/naming-standards.js';
|
|
30
|
-
import { ALL_KNOWN_WINDOWS } from './lib/attribution.js';
|
|
31
30
|
import { resolveCredentials } from './credentials.js';
|
|
31
|
+
import { TOOLS } from './toolDefinitions.js';
|
|
32
|
+
import { filterTools, assertWriteAllowed } from './writeGate.js';
|
|
32
33
|
import { safeResponse } from './safe-response.js';
|
|
33
34
|
/* ------------------------------------------------------------------------- */
|
|
34
35
|
/* CLI flags */
|
|
@@ -79,608 +80,16 @@ catch (err) {
|
|
|
79
80
|
process.exit(1);
|
|
80
81
|
}
|
|
81
82
|
/* ------------------------------------------------------------------------- */
|
|
82
|
-
/* Tool definitions (JSONSchema) */
|
|
83
|
-
/* ------------------------------------------------------------------------- */
|
|
84
|
-
const TOOL_INSIGHTS_INCREMENTALITY = {
|
|
85
|
-
name: 'meta_ads_insights_incrementality',
|
|
86
|
-
description: 'Pull Meta Ads insights with a focus on incremental conversions. By default this returns ' +
|
|
87
|
-
'Meta\'s "incrementality" attribution window — the actual incremental conversion count — ' +
|
|
88
|
-
'rather than the default 7d-click + 1d-view number that systematically overstates ad impact. ' +
|
|
89
|
-
'When both incrementality and default windows are requested in the same call, the response ' +
|
|
90
|
-
'includes inflation_factor and overstatement_pct so an agency can quantify the gap between ' +
|
|
91
|
-
'reported and incremental performance.',
|
|
92
|
-
inputSchema: {
|
|
93
|
-
type: 'object',
|
|
94
|
-
properties: {
|
|
95
|
-
ad_account_id: {
|
|
96
|
-
type: 'string',
|
|
97
|
-
description: 'Meta ad account id (with or without the act_ prefix).',
|
|
98
|
-
},
|
|
99
|
-
date_range: {
|
|
100
|
-
type: 'object',
|
|
101
|
-
properties: {
|
|
102
|
-
since: { type: 'string', description: 'YYYY-MM-DD start date (inclusive)' },
|
|
103
|
-
until: { type: 'string', description: 'YYYY-MM-DD end date (inclusive)' },
|
|
104
|
-
},
|
|
105
|
-
required: ['since', 'until'],
|
|
106
|
-
},
|
|
107
|
-
conversion_event_name: {
|
|
108
|
-
type: 'string',
|
|
109
|
-
description: 'The action_type to extract, e.g. "offsite_conversion.fb_pixel_purchase", ' +
|
|
110
|
-
'"omni_purchase", or a custom conversion name.',
|
|
111
|
-
},
|
|
112
|
-
attribution_windows: {
|
|
113
|
-
type: 'array',
|
|
114
|
-
items: { type: 'string', enum: [...ALL_KNOWN_WINDOWS] },
|
|
115
|
-
description: 'Which attribution windows to fetch. Defaults to ["incrementality"]. Pass ' +
|
|
116
|
-
'["incrementality", "7d_click", "1d_view"] for the headline incrementality-vs-default ' +
|
|
117
|
-
'comparison. Add "dda" to also include Meta\'s data-driven attribution number.',
|
|
118
|
-
},
|
|
119
|
-
level: {
|
|
120
|
-
type: 'string',
|
|
121
|
-
enum: ['account', 'campaign', 'adset', 'ad'],
|
|
122
|
-
description: 'Aggregation level. Defaults to "campaign".',
|
|
123
|
-
},
|
|
124
|
-
campaign_id: { type: 'string', description: 'Optional: restrict to a single campaign.' },
|
|
125
|
-
adset_id: { type: 'string', description: 'Optional: restrict to a single ad set.' },
|
|
126
|
-
breakdowns: {
|
|
127
|
-
type: 'array',
|
|
128
|
-
items: { type: 'string' },
|
|
129
|
-
description: 'Optional Meta breakdowns, e.g. ["publisher_platform", "age"].',
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
required: ['ad_account_id', 'date_range', 'conversion_event_name'],
|
|
133
|
-
},
|
|
134
|
-
};
|
|
135
|
-
const TOOL_LIST_ADS_IN_CAMPAIGN = {
|
|
136
|
-
name: 'meta_ads_list_ads_in_campaign',
|
|
137
|
-
description: 'List all ads in a Meta campaign with their id, name, status, and current creative_id. ' +
|
|
138
|
-
'Use as the discovery step before reading or updating url_tags across a campaign.',
|
|
139
|
-
inputSchema: {
|
|
140
|
-
type: 'object',
|
|
141
|
-
properties: {
|
|
142
|
-
campaign_id: { type: 'string', description: 'Meta campaign id (numeric).' },
|
|
143
|
-
},
|
|
144
|
-
required: ['campaign_id'],
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
const TOOL_GET_AD_URL_TAGS = {
|
|
148
|
-
name: 'meta_ads_get_ad_url_tags',
|
|
149
|
-
description: 'Read the current url_tags (URL parameters) on a single Meta ad, plus the underlying ' +
|
|
150
|
-
'clickthrough link and creative_id. Use to inspect what utm tagging is in place today ' +
|
|
151
|
-
'before updating it.',
|
|
152
|
-
inputSchema: {
|
|
153
|
-
type: 'object',
|
|
154
|
-
properties: {
|
|
155
|
-
ad_id: { type: 'string', description: 'Meta ad id (numeric).' },
|
|
156
|
-
},
|
|
157
|
-
required: ['ad_id'],
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
const TOOL_UPDATE_AD_URL_TAGS = {
|
|
161
|
-
name: 'meta_ads_update_ad_url_tags',
|
|
162
|
-
description: 'Update the url_tags (URL parameters) on a single Meta ad. The value should be a query-' +
|
|
163
|
-
'string fragment WITHOUT a leading ?, e.g. "utm_source=facebook&utm_campaign=foo&' +
|
|
164
|
-
'campaignid={{campaign.id}}". Note Meta uses double-brace macros ({{campaign.id}}, ' +
|
|
165
|
-
'{{ad.id}}, {{ad.name}}, etc.), not Google-Ads-style single braces. ' +
|
|
166
|
-
'Mechanism: POSTs a creative override to the ad, which forks a new creative under the ' +
|
|
167
|
-
'hood while keeping the ad_id stable. Pass dry_run=true to preview without writing. ' +
|
|
168
|
-
'Recommend testing on a PAUSED ad before touching active ones — Meta may briefly re-review.',
|
|
169
|
-
inputSchema: {
|
|
170
|
-
type: 'object',
|
|
171
|
-
properties: {
|
|
172
|
-
ad_id: { type: 'string', description: 'Meta ad id (numeric).' },
|
|
173
|
-
url_tags: {
|
|
174
|
-
type: 'string',
|
|
175
|
-
description: 'New url_tags value. Query-string fragment, no leading ?. Use {{campaign.id}} / ' +
|
|
176
|
-
'{{ad.id}} / {{ad.name}} for Meta dynamic macros.',
|
|
177
|
-
},
|
|
178
|
-
dry_run: {
|
|
179
|
-
type: 'boolean',
|
|
180
|
-
description: 'If true, return a diff without writing. Default false.',
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
required: ['ad_id', 'url_tags'],
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
const TOOL_RENAME_AD = {
|
|
187
|
-
name: 'meta_ads_rename_ad',
|
|
188
|
-
description: 'Rename a Meta ad. Useful before applying url_tags with the {{ad.name}} macro — ad ' +
|
|
189
|
-
'names with spaces produce literal spaces or %20 in the resulting URL. The new name is ' +
|
|
190
|
-
'validated against shared naming standards (no spaces or URL-special characters); ' +
|
|
191
|
-
'violations block the write unless allow_naming_violations=true. Pass dry_run=true ' +
|
|
192
|
-
'to preview without writing.',
|
|
193
|
-
inputSchema: {
|
|
194
|
-
type: 'object',
|
|
195
|
-
properties: {
|
|
196
|
-
ad_id: { type: 'string', description: 'Meta ad id (numeric).' },
|
|
197
|
-
new_name: { type: 'string', description: 'New ad name.' },
|
|
198
|
-
dry_run: {
|
|
199
|
-
type: 'boolean',
|
|
200
|
-
description: 'If true, return a diff without writing. Default false.',
|
|
201
|
-
},
|
|
202
|
-
allow_naming_violations: {
|
|
203
|
-
type: 'boolean',
|
|
204
|
-
description: 'If true, bypass the naming-standards check. Default false.',
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
required: ['ad_id', 'new_name'],
|
|
208
|
-
},
|
|
209
|
-
};
|
|
210
|
-
/* ------------------------------------------------------------------------- */
|
|
211
|
-
/* Tier 1 — campaign / ad set / creative / ad / image */
|
|
212
|
-
/* ------------------------------------------------------------------------- */
|
|
213
|
-
const TOOL_CREATE_CAMPAIGN = {
|
|
214
|
-
name: 'meta_ads_create_campaign',
|
|
215
|
-
description: 'Create a Meta ad campaign. Defaults to PAUSED status so the MCP cannot accidentally take ' +
|
|
216
|
-
'spend live. Set buying_type=AUCTION (default) for the standard auction model. CBO callers ' +
|
|
217
|
-
'pass daily_budget at the campaign level; ABO callers omit it and budget at the ad set level.',
|
|
218
|
-
inputSchema: {
|
|
219
|
-
type: 'object',
|
|
220
|
-
properties: {
|
|
221
|
-
account_id: { type: 'string', description: 'Ad account id (with or without act_ prefix).' },
|
|
222
|
-
name: { type: 'string' },
|
|
223
|
-
objective: {
|
|
224
|
-
type: 'string',
|
|
225
|
-
description: 'Meta objective enum, e.g. OUTCOME_LEADS, OUTCOME_TRAFFIC, OUTCOME_SALES.',
|
|
226
|
-
},
|
|
227
|
-
status: { type: 'string', enum: ['PAUSED', 'ACTIVE'], description: 'Default PAUSED.' },
|
|
228
|
-
special_ad_categories: {
|
|
229
|
-
type: 'array',
|
|
230
|
-
items: { type: 'string' },
|
|
231
|
-
description: 'e.g. ["HOUSING"]. Default [].',
|
|
232
|
-
},
|
|
233
|
-
buying_type: { type: 'string', enum: ['AUCTION', 'RESERVED'], description: 'Default AUCTION.' },
|
|
234
|
-
daily_budget: { type: 'number', description: 'Cents. Set only for CBO campaigns.' },
|
|
235
|
-
lifetime_budget: { type: 'number', description: 'Cents. Alternative to daily_budget.' },
|
|
236
|
-
},
|
|
237
|
-
required: ['account_id', 'name', 'objective'],
|
|
238
|
-
},
|
|
239
|
-
};
|
|
240
|
-
const TOOL_CREATE_ADSET = {
|
|
241
|
-
name: 'meta_ads_create_adset',
|
|
242
|
-
description: 'Create a Meta ad set inside an existing campaign. Defaults to PAUSED. For ON_AD lead-gen ' +
|
|
243
|
-
'ad sets, pass destination_type="ON_AD" and promoted_object={page_id}. For OFFSITE ' +
|
|
244
|
-
'conversions, promoted_object={page_id, pixel_id, custom_event_type}. Pass is_incremental_' +
|
|
245
|
-
'attribution_enabled=true to switch to Meta\'s Incremental Conversions optimization (per ' +
|
|
246
|
-
'agency guidance). Budget required: daily_budget OR lifetime_budget (not both, not neither ' +
|
|
247
|
-
'unless inheriting from a CBO campaign).',
|
|
248
|
-
inputSchema: {
|
|
249
|
-
type: 'object',
|
|
250
|
-
properties: {
|
|
251
|
-
account_id: { type: 'string' },
|
|
252
|
-
campaign_id: { type: 'string' },
|
|
253
|
-
name: { type: 'string' },
|
|
254
|
-
optimization_goal: {
|
|
255
|
-
type: 'string',
|
|
256
|
-
description: 'e.g. LEAD_GENERATION, OFFSITE_CONVERSIONS, LINK_CLICKS, REACH.',
|
|
257
|
-
},
|
|
258
|
-
billing_event: {
|
|
259
|
-
type: 'string',
|
|
260
|
-
enum: ['IMPRESSIONS', 'LINK_CLICKS', 'POST_ENGAGEMENT', 'THRUPLAY'],
|
|
261
|
-
},
|
|
262
|
-
bid_strategy: {
|
|
263
|
-
type: 'string',
|
|
264
|
-
enum: ['LOWEST_COST_WITHOUT_CAP', 'LOWEST_COST_WITH_BID_CAP', 'COST_CAP'],
|
|
265
|
-
description: 'Default LOWEST_COST_WITHOUT_CAP (Highest volume).',
|
|
266
|
-
},
|
|
267
|
-
bid_amount: { type: 'number', description: 'Cents. Required for COST_CAP / WITH_BID_CAP.' },
|
|
268
|
-
targeting: { type: 'object', description: 'Full Meta targeting spec.' },
|
|
269
|
-
status: { type: 'string', enum: ['PAUSED', 'ACTIVE'] },
|
|
270
|
-
daily_budget: { type: 'number', description: 'Cents.' },
|
|
271
|
-
lifetime_budget: { type: 'number', description: 'Cents.' },
|
|
272
|
-
promoted_object: { type: 'object' },
|
|
273
|
-
destination_type: { type: 'string', description: 'e.g. ON_AD, WEBSITE, APP, MESSENGER.' },
|
|
274
|
-
attribution_spec: {
|
|
275
|
-
type: 'array',
|
|
276
|
-
items: {
|
|
277
|
-
type: 'object',
|
|
278
|
-
properties: {
|
|
279
|
-
event_type: { type: 'string' },
|
|
280
|
-
window_days: { type: 'number' },
|
|
281
|
-
},
|
|
282
|
-
required: ['event_type', 'window_days'],
|
|
283
|
-
},
|
|
284
|
-
},
|
|
285
|
-
is_incremental_attribution_enabled: { type: 'boolean' },
|
|
286
|
-
start_time: { type: 'string' },
|
|
287
|
-
end_time: { type: 'string' },
|
|
288
|
-
},
|
|
289
|
-
required: ['account_id', 'campaign_id', 'name', 'optimization_goal', 'billing_event', 'targeting'],
|
|
290
|
-
},
|
|
291
|
-
};
|
|
292
|
-
const TOOL_CREATE_AD_CREATIVE = {
|
|
293
|
-
name: 'meta_ads_create_ad_creative',
|
|
294
|
-
description: 'Create a Meta ad creative (single image). Two modes: (1) legacy object_story_spec ' +
|
|
295
|
-
'when message/headline/description are single strings; (2) asset_feed_spec / Flexible Ads ' +
|
|
296
|
-
'when any of messages[]/headlines[]/descriptions[] has >1 entry — Meta then mix-and-matches ' +
|
|
297
|
-
'variants per impression (cap 5 each). Pass lead_gen_form_id to auto-wire the SIGN_UP CTA. ' +
|
|
298
|
-
'Pair with meta_ads_upload_image to get an image_hash.',
|
|
299
|
-
inputSchema: {
|
|
300
|
-
type: 'object',
|
|
301
|
-
properties: {
|
|
302
|
-
account_id: { type: 'string' },
|
|
303
|
-
name: { type: 'string' },
|
|
304
|
-
page_id: { type: 'string' },
|
|
305
|
-
image_hash: { type: 'string', description: 'From meta_ads_upload_image. Required for both single and asset_feed_spec modes.' },
|
|
306
|
-
link: { type: 'string', description: 'Clickthrough URL.' },
|
|
307
|
-
message: { type: 'string', description: 'Single primary text. Mutually exclusive with messages[].' },
|
|
308
|
-
messages: {
|
|
309
|
-
type: 'array',
|
|
310
|
-
items: { type: 'string' },
|
|
311
|
-
description: 'Up to 5 primary text variants. Triggers asset_feed_spec mode if >1.',
|
|
312
|
-
},
|
|
313
|
-
headline: { type: 'string', description: 'Single headline below the image.' },
|
|
314
|
-
headlines: {
|
|
315
|
-
type: 'array',
|
|
316
|
-
items: { type: 'string' },
|
|
317
|
-
description: 'Up to 5 headline variants. Triggers asset_feed_spec mode if >1.',
|
|
318
|
-
},
|
|
319
|
-
description: { type: 'string', description: 'Single sub-description.' },
|
|
320
|
-
descriptions: {
|
|
321
|
-
type: 'array',
|
|
322
|
-
items: { type: 'string' },
|
|
323
|
-
description: 'Up to 5 description variants. Triggers asset_feed_spec mode if >1.',
|
|
324
|
-
},
|
|
325
|
-
call_to_action: {
|
|
326
|
-
type: 'object',
|
|
327
|
-
properties: {
|
|
328
|
-
type: { type: 'string', description: 'e.g. SIGN_UP, DOWNLOAD, LEARN_MORE, SHOP_NOW.' },
|
|
329
|
-
value: {
|
|
330
|
-
type: 'object',
|
|
331
|
-
properties: {
|
|
332
|
-
link: { type: 'string' },
|
|
333
|
-
lead_gen_form_id: { type: 'string' },
|
|
334
|
-
},
|
|
335
|
-
},
|
|
336
|
-
},
|
|
337
|
-
required: ['type'],
|
|
338
|
-
},
|
|
339
|
-
lead_gen_form_id: {
|
|
340
|
-
type: 'string',
|
|
341
|
-
description: 'Shortcut: builds CTA={type:SIGN_UP, value:{lead_gen_form_id}}.',
|
|
342
|
-
},
|
|
343
|
-
url_tags: {
|
|
344
|
-
type: 'string',
|
|
345
|
-
description: 'Query-string fragment for tracking, no leading ?. Use {{ad.name}} macros.',
|
|
346
|
-
},
|
|
347
|
-
instagram_actor_id: {
|
|
348
|
-
type: 'string',
|
|
349
|
-
description: 'Optional IG business account for cross-posting.',
|
|
350
|
-
},
|
|
351
|
-
},
|
|
352
|
-
required: ['account_id', 'name', 'page_id', 'link'],
|
|
353
|
-
},
|
|
354
|
-
};
|
|
355
|
-
const TOOL_CREATE_AD = {
|
|
356
|
-
name: 'meta_ads_create_ad',
|
|
357
|
-
description: 'Create a Meta ad attached to an ad set and creative. Defaults to PAUSED.',
|
|
358
|
-
inputSchema: {
|
|
359
|
-
type: 'object',
|
|
360
|
-
properties: {
|
|
361
|
-
account_id: { type: 'string' },
|
|
362
|
-
name: { type: 'string' },
|
|
363
|
-
adset_id: { type: 'string' },
|
|
364
|
-
creative_id: { type: 'string' },
|
|
365
|
-
status: { type: 'string', enum: ['PAUSED', 'ACTIVE'] },
|
|
366
|
-
},
|
|
367
|
-
required: ['account_id', 'name', 'adset_id', 'creative_id'],
|
|
368
|
-
},
|
|
369
|
-
};
|
|
370
|
-
const TOOL_UPLOAD_IMAGE = {
|
|
371
|
-
name: 'meta_ads_upload_image',
|
|
372
|
-
description: 'Upload an image to an ad account and return its image_hash + CDN URL. Pass either ' +
|
|
373
|
-
'image_bytes (base64-encoded) or image_path (local file path; this MCP reads + encodes it).',
|
|
374
|
-
inputSchema: {
|
|
375
|
-
type: 'object',
|
|
376
|
-
properties: {
|
|
377
|
-
account_id: { type: 'string' },
|
|
378
|
-
image_bytes: { type: 'string', description: 'Base64-encoded image data.' },
|
|
379
|
-
image_path: { type: 'string', description: 'Local file path to read.' },
|
|
380
|
-
},
|
|
381
|
-
required: ['account_id'],
|
|
382
|
-
},
|
|
383
|
-
};
|
|
384
|
-
/* ------------------------------------------------------------------------- */
|
|
385
|
-
/* Tier 2 — audiences + lead forms */
|
|
386
|
-
/* ------------------------------------------------------------------------- */
|
|
387
|
-
const TOOL_CREATE_LEADGEN_FORM = {
|
|
388
|
-
name: 'meta_ads_create_leadgen_form',
|
|
389
|
-
description: 'Create a Meta lead-gen Instant Form on a Facebook Page. Requires Page-scoped permissions on ' +
|
|
390
|
-
'the access token; calls that fail with Graph error code 3 ("Application does not have the ' +
|
|
391
|
-
'capability to make this API call") indicate the app lacks Lead Ads management capability ' +
|
|
392
|
-
'and need an App Review fix, not a client-code change. Also requires the Page to have ' +
|
|
393
|
-
'accepted Facebook\'s Lead Generation Terms of Service (Page Settings → Lead Ads Terms) — ' +
|
|
394
|
-
'fails with subcode 1815089 if not.',
|
|
395
|
-
inputSchema: {
|
|
396
|
-
type: 'object',
|
|
397
|
-
properties: {
|
|
398
|
-
page_id: { type: 'string' },
|
|
399
|
-
name: { type: 'string' },
|
|
400
|
-
locale: { type: 'string', description: 'Default en_US.' },
|
|
401
|
-
form_type: { type: 'string', enum: ['MORE_VOLUME', 'HIGHER_INTENT'] },
|
|
402
|
-
block_display_for_non_targeted_viewer: { type: 'boolean' },
|
|
403
|
-
privacy_policy: {
|
|
404
|
-
type: 'object',
|
|
405
|
-
properties: { url: { type: 'string' }, link_text: { type: 'string' } },
|
|
406
|
-
required: ['url'],
|
|
407
|
-
},
|
|
408
|
-
context_card: {
|
|
409
|
-
type: 'object',
|
|
410
|
-
properties: {
|
|
411
|
-
style: { type: 'string', enum: ['PARAGRAPH_STYLE', 'LIST_STYLE'] },
|
|
412
|
-
title: { type: 'string' },
|
|
413
|
-
content: { type: 'array', items: { type: 'string' } },
|
|
414
|
-
button_text: { type: 'string' },
|
|
415
|
-
},
|
|
416
|
-
required: ['style', 'title', 'content'],
|
|
417
|
-
},
|
|
418
|
-
questions: {
|
|
419
|
-
type: 'array',
|
|
420
|
-
items: {
|
|
421
|
-
type: 'object',
|
|
422
|
-
properties: {
|
|
423
|
-
type: { type: 'string' },
|
|
424
|
-
key: { type: 'string' },
|
|
425
|
-
label: { type: 'string' },
|
|
426
|
-
options: {
|
|
427
|
-
type: 'array',
|
|
428
|
-
items: {
|
|
429
|
-
type: 'object',
|
|
430
|
-
properties: { value: { type: 'string' }, key: { type: 'string' } },
|
|
431
|
-
required: ['value', 'key'],
|
|
432
|
-
},
|
|
433
|
-
},
|
|
434
|
-
},
|
|
435
|
-
required: ['type'],
|
|
436
|
-
},
|
|
437
|
-
},
|
|
438
|
-
thank_you_page: {
|
|
439
|
-
type: 'object',
|
|
440
|
-
properties: {
|
|
441
|
-
title: { type: 'string' },
|
|
442
|
-
body: { type: 'string' },
|
|
443
|
-
button_text: { type: 'string' },
|
|
444
|
-
button_type: { type: 'string', enum: ['VIEW_WEBSITE', 'CALL_BUSINESS', 'MESSAGE_BUSINESS'] },
|
|
445
|
-
website_url: { type: 'string' },
|
|
446
|
-
},
|
|
447
|
-
required: ['title', 'body', 'button_text', 'button_type'],
|
|
448
|
-
},
|
|
449
|
-
follow_up_action_url: { type: 'string' },
|
|
450
|
-
},
|
|
451
|
-
required: ['page_id', 'name', 'privacy_policy', 'questions', 'thank_you_page'],
|
|
452
|
-
},
|
|
453
|
-
};
|
|
454
|
-
const TOOL_CREATE_CUSTOM_AUDIENCE = {
|
|
455
|
-
name: 'meta_ads_create_custom_audience',
|
|
456
|
-
description: 'Create an empty Custom Audience on an ad account, ready to receive records via ' +
|
|
457
|
-
'meta_ads_upload_users_to_audience. Returns audience_id. Not usable in ad sets until at ' +
|
|
458
|
-
'least one user batch is uploaded and processed.',
|
|
459
|
-
inputSchema: {
|
|
460
|
-
type: 'object',
|
|
461
|
-
properties: {
|
|
462
|
-
account_id: { type: 'string' },
|
|
463
|
-
name: { type: 'string' },
|
|
464
|
-
description: { type: 'string' },
|
|
465
|
-
customer_file_source: {
|
|
466
|
-
type: 'string',
|
|
467
|
-
enum: ['USER_PROVIDED_ONLY', 'PARTNER_PROVIDED_ONLY', 'BOTH_USER_AND_PARTNER_PROVIDED'],
|
|
468
|
-
},
|
|
469
|
-
},
|
|
470
|
-
required: ['account_id', 'name'],
|
|
471
|
-
},
|
|
472
|
-
};
|
|
473
|
-
const TOOL_UPLOAD_USERS_TO_AUDIENCE = {
|
|
474
|
-
name: 'meta_ads_upload_users_to_audience',
|
|
475
|
-
description: 'Upload user records to a Custom Audience. Records are SHA-256 hashed locally before send ' +
|
|
476
|
-
'(emails lowercased+trimmed; phones stripped to digits; names normalized to alphanumeric) — ' +
|
|
477
|
-
'pass plaintext, do not pre-hash. Schema is inferred from the first record (EMAIL or PHONE) ' +
|
|
478
|
-
'if not specified. Returns num_received and num_invalid_entries.',
|
|
479
|
-
inputSchema: {
|
|
480
|
-
type: 'object',
|
|
481
|
-
properties: {
|
|
482
|
-
audience_id: { type: 'string' },
|
|
483
|
-
users: {
|
|
484
|
-
type: 'array',
|
|
485
|
-
items: {
|
|
486
|
-
type: 'object',
|
|
487
|
-
properties: {
|
|
488
|
-
email: { type: 'string' },
|
|
489
|
-
phone: { type: 'string' },
|
|
490
|
-
first_name: { type: 'string' },
|
|
491
|
-
last_name: { type: 'string' },
|
|
492
|
-
country: { type: 'string' },
|
|
493
|
-
zip: { type: 'string' },
|
|
494
|
-
city: { type: 'string' },
|
|
495
|
-
state: { type: 'string' },
|
|
496
|
-
},
|
|
497
|
-
},
|
|
498
|
-
},
|
|
499
|
-
schema: {
|
|
500
|
-
type: 'array',
|
|
501
|
-
items: {
|
|
502
|
-
type: 'string',
|
|
503
|
-
enum: ['EMAIL', 'PHONE', 'FN', 'LN', 'COUNTRY', 'ZIP', 'CT', 'ST', 'DOBY', 'GEN', 'MADID'],
|
|
504
|
-
},
|
|
505
|
-
},
|
|
506
|
-
session_id: { type: 'number' },
|
|
507
|
-
batch_size: { type: 'number', description: 'Default 1000, max 10000.' },
|
|
508
|
-
},
|
|
509
|
-
required: ['audience_id', 'users'],
|
|
510
|
-
},
|
|
511
|
-
};
|
|
512
|
-
const TOOL_CREATE_LOOKALIKE_AUDIENCE = {
|
|
513
|
-
name: 'meta_ads_create_lookalike_audience',
|
|
514
|
-
description: 'Create a Lookalike Audience seeded by an existing Custom Audience. Ratio is a fraction of ' +
|
|
515
|
-
'country population (0.01 = top 1%, highest similarity / smallest reach; 0.10 = broader). ' +
|
|
516
|
-
'Default 0.01. Range 0.01–0.20.',
|
|
517
|
-
inputSchema: {
|
|
518
|
-
type: 'object',
|
|
519
|
-
properties: {
|
|
520
|
-
account_id: { type: 'string' },
|
|
521
|
-
name: { type: 'string' },
|
|
522
|
-
origin_audience_id: { type: 'string' },
|
|
523
|
-
country: { type: 'string', description: 'ISO 3166-1 alpha-2.' },
|
|
524
|
-
ratio: { type: 'number' },
|
|
525
|
-
description: { type: 'string' },
|
|
526
|
-
},
|
|
527
|
-
required: ['account_id', 'name', 'origin_audience_id', 'country'],
|
|
528
|
-
},
|
|
529
|
-
};
|
|
530
|
-
/* ------------------------------------------------------------------------- */
|
|
531
|
-
/* Tier 3 + 4 — lifecycle */
|
|
532
|
-
/* ------------------------------------------------------------------------- */
|
|
533
|
-
const TOOL_UPDATE_OBJECT_STATUS = {
|
|
534
|
-
name: 'meta_ads_update_object_status',
|
|
535
|
-
description: 'Toggle the status of a campaign, ad set, or ad. Pass object_id of any of those types. ' +
|
|
536
|
-
'PAUSED stops spend; ACTIVE resumes; ARCHIVED removes from active views without deleting.',
|
|
537
|
-
inputSchema: {
|
|
538
|
-
type: 'object',
|
|
539
|
-
properties: {
|
|
540
|
-
object_id: { type: 'string', description: 'Campaign, ad set, or ad id.' },
|
|
541
|
-
status: { type: 'string', enum: ['PAUSED', 'ACTIVE', 'ARCHIVED'] },
|
|
542
|
-
},
|
|
543
|
-
required: ['object_id', 'status'],
|
|
544
|
-
},
|
|
545
|
-
};
|
|
546
|
-
const TOOL_UPDATE_BUDGET = {
|
|
547
|
-
name: 'meta_ads_update_budget',
|
|
548
|
-
description: 'Update the daily or lifetime budget on a campaign or ad set. Pass exactly one of ' +
|
|
549
|
-
'daily_budget or lifetime_budget (in cents). For CBO campaigns, set on the campaign; for ' +
|
|
550
|
-
'ABO ad sets, set on each ad set.',
|
|
551
|
-
inputSchema: {
|
|
552
|
-
type: 'object',
|
|
553
|
-
properties: {
|
|
554
|
-
object_id: { type: 'string', description: 'Campaign or ad set id.' },
|
|
555
|
-
daily_budget: { type: 'number', description: 'Cents.' },
|
|
556
|
-
lifetime_budget: { type: 'number', description: 'Cents.' },
|
|
557
|
-
},
|
|
558
|
-
required: ['object_id'],
|
|
559
|
-
},
|
|
560
|
-
};
|
|
561
|
-
const TOOL_DELETE_OBJECT = {
|
|
562
|
-
name: 'meta_ads_delete_object',
|
|
563
|
-
description: 'Permanently delete a Meta object (campaign, ad set, ad, creative, custom audience). ' +
|
|
564
|
-
'Irreversible — prefer meta_ads_update_object_status with status=ARCHIVED unless you truly ' +
|
|
565
|
-
'want to remove the object and its history.',
|
|
566
|
-
inputSchema: {
|
|
567
|
-
type: 'object',
|
|
568
|
-
properties: { object_id: { type: 'string' } },
|
|
569
|
-
required: ['object_id'],
|
|
570
|
-
},
|
|
571
|
-
};
|
|
572
|
-
const TOOL_SWAP_AD_LEAD_FORM = {
|
|
573
|
-
name: 'meta_ads_swap_ad_lead_form',
|
|
574
|
-
description: 'Swap the lead form on an existing Meta ad by rebuilding its creative with a new ' +
|
|
575
|
-
'lead_gen_form_id, then rebinding the ad. Ad_id stays stable. Handles both legacy ' +
|
|
576
|
-
'object_story_spec creatives and modern asset_feed_spec (Flexible Ads) creatives — ' +
|
|
577
|
-
'preserves all image/text content, only the SIGN_UP CTA form_id changes. Pass dry_run=true ' +
|
|
578
|
-
'to preview. Meta may briefly re-review the ad after the swap.',
|
|
579
|
-
inputSchema: {
|
|
580
|
-
type: 'object',
|
|
581
|
-
properties: {
|
|
582
|
-
ad_id: { type: 'string', description: 'Meta ad id (numeric).' },
|
|
583
|
-
new_form_id: { type: 'string', description: 'New lead_gen_form_id to bind.' },
|
|
584
|
-
cta_type: {
|
|
585
|
-
type: 'string',
|
|
586
|
-
description: 'Override the CTA button type (e.g. DOWNLOAD, SIGN_UP, LEARN_MORE, GET_OFFER). ' +
|
|
587
|
-
'If omitted, preserves the existing CTA type from the ad\'s current creative.',
|
|
588
|
-
},
|
|
589
|
-
dry_run: { type: 'boolean', description: 'If true, return a diff without writing. Default false.' },
|
|
590
|
-
},
|
|
591
|
-
required: ['ad_id', 'new_form_id'],
|
|
592
|
-
},
|
|
593
|
-
};
|
|
594
|
-
const TOOL_REBIND_AD_CREATIVE = {
|
|
595
|
-
name: 'meta_ads_rebind_ad_creative',
|
|
596
|
-
description: 'Rebind an ad to a different creative_id. ad_id stays stable. Use together with ' +
|
|
597
|
-
'meta_ads_create_ad_creative to do full ad rebuilds (e.g. swap form + rebuild text content) ' +
|
|
598
|
-
'without losing performance history on the ad object. SAFETY: refuses to rebind when the ' +
|
|
599
|
-
'old creative has asset_customization_rules (per-placement image rules) and the new one ' +
|
|
600
|
-
'does not — pass force=true to override. Pass dry_run=true to preview.',
|
|
601
|
-
inputSchema: {
|
|
602
|
-
type: 'object',
|
|
603
|
-
properties: {
|
|
604
|
-
ad_id: { type: 'string', description: 'Meta ad id (numeric).' },
|
|
605
|
-
creative_id: { type: 'string', description: 'New creative_id to bind to the ad.' },
|
|
606
|
-
dry_run: { type: 'boolean', description: 'If true, return a diff without writing. Default false.' },
|
|
607
|
-
force: {
|
|
608
|
-
type: 'boolean',
|
|
609
|
-
description: 'Bypass the asset_customization_rules safety check. Use only when intentionally replacing per-placement image rules.',
|
|
610
|
-
},
|
|
611
|
-
},
|
|
612
|
-
required: ['ad_id', 'creative_id'],
|
|
613
|
-
},
|
|
614
|
-
};
|
|
615
|
-
const TOOL_EDIT_CREATIVE_TEXT = {
|
|
616
|
-
name: 'meta_ads_edit_creative_text',
|
|
617
|
-
description: 'Edit the text fields (messages/headlines/descriptions) on an ad\'s creative while ' +
|
|
618
|
-
'PRESERVING images, videos, asset_customization_rules (per-placement image rules set ' +
|
|
619
|
-
'in Ads Manager UI), CTAs, and link_urls. Use this instead of create_ad_creative + ' +
|
|
620
|
-
'rebind_ad_creative when the ad has custom placements-by-image — the create+rebind ' +
|
|
621
|
-
'pattern silently flattens those rules. Refuses if asset_customization_rules reference ' +
|
|
622
|
-
'per-text labels (body_label/title_label/description_label) and the new text count ' +
|
|
623
|
-
'differs from the existing count — pass force=true to override. Multi-variant ads ' +
|
|
624
|
-
'(asset_feed_spec) accept up to 5 entries per field; legacy single-variant ads ' +
|
|
625
|
-
'(object_story_spec) accept ≤1 entry per field.',
|
|
626
|
-
inputSchema: {
|
|
627
|
-
type: 'object',
|
|
628
|
-
properties: {
|
|
629
|
-
ad_id: { type: 'string', description: 'Meta ad id (numeric).' },
|
|
630
|
-
messages: {
|
|
631
|
-
type: 'array',
|
|
632
|
-
items: { type: 'string' },
|
|
633
|
-
description: 'Up to 5 primary text variants. Replaces the entire bodies[] array.',
|
|
634
|
-
},
|
|
635
|
-
headlines: {
|
|
636
|
-
type: 'array',
|
|
637
|
-
items: { type: 'string' },
|
|
638
|
-
description: 'Up to 5 headline variants. Replaces the entire titles[] array.',
|
|
639
|
-
},
|
|
640
|
-
descriptions: {
|
|
641
|
-
type: 'array',
|
|
642
|
-
items: { type: 'string' },
|
|
643
|
-
description: 'Up to 5 description variants. Replaces the entire descriptions[] array.',
|
|
644
|
-
},
|
|
645
|
-
dry_run: { type: 'boolean', description: 'If true, return a diff without writing.' },
|
|
646
|
-
force: {
|
|
647
|
-
type: 'boolean',
|
|
648
|
-
description: 'Bypass the label-orphaning safety check (only for DCO-style ads with labeled text).',
|
|
649
|
-
},
|
|
650
|
-
},
|
|
651
|
-
required: ['ad_id'],
|
|
652
|
-
},
|
|
653
|
-
};
|
|
654
|
-
const TOOLS = [
|
|
655
|
-
TOOL_INSIGHTS_INCREMENTALITY,
|
|
656
|
-
TOOL_LIST_ADS_IN_CAMPAIGN,
|
|
657
|
-
TOOL_GET_AD_URL_TAGS,
|
|
658
|
-
TOOL_UPDATE_AD_URL_TAGS,
|
|
659
|
-
TOOL_RENAME_AD,
|
|
660
|
-
TOOL_SWAP_AD_LEAD_FORM,
|
|
661
|
-
TOOL_REBIND_AD_CREATIVE,
|
|
662
|
-
TOOL_EDIT_CREATIVE_TEXT,
|
|
663
|
-
TOOL_CREATE_CAMPAIGN,
|
|
664
|
-
TOOL_CREATE_ADSET,
|
|
665
|
-
TOOL_CREATE_AD_CREATIVE,
|
|
666
|
-
TOOL_CREATE_AD,
|
|
667
|
-
TOOL_UPLOAD_IMAGE,
|
|
668
|
-
TOOL_CREATE_LEADGEN_FORM,
|
|
669
|
-
TOOL_CREATE_CUSTOM_AUDIENCE,
|
|
670
|
-
TOOL_UPLOAD_USERS_TO_AUDIENCE,
|
|
671
|
-
TOOL_CREATE_LOOKALIKE_AUDIENCE,
|
|
672
|
-
TOOL_UPDATE_OBJECT_STATUS,
|
|
673
|
-
TOOL_UPDATE_BUDGET,
|
|
674
|
-
TOOL_DELETE_OBJECT,
|
|
675
|
-
];
|
|
676
|
-
/* ------------------------------------------------------------------------- */
|
|
677
83
|
/* Server */
|
|
678
84
|
/* ------------------------------------------------------------------------- */
|
|
679
85
|
const server = new Server({ name: pkg.name, version: pkg.version }, { capabilities: { tools: {} } });
|
|
680
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
86
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
87
|
+
tools: filterTools(TOOLS),
|
|
88
|
+
}));
|
|
681
89
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
682
90
|
const { name, arguments: args } = request.params;
|
|
683
91
|
try {
|
|
92
|
+
assertWriteAllowed(name);
|
|
684
93
|
switch (name) {
|
|
685
94
|
case 'meta_ads_insights_incrementality': {
|
|
686
95
|
const result = await runInsightsIncrementality(args, deps);
|