activate_campaigns_batch
Batch activate multiple campaigns with a pre-flight ads volume check per ad account to warn if a Page is over capacity. Requires user confirmation before activation begins spending.
Instructions
WRITE (BULK): Activate many campaigns in one Batch API call. BEFORE activation we run a per-Page ads_volume pre-flight for every distinct ad account — warnings are returned in the response so the agent / user can abort if a Page is over capacity. Agents MUST confirm with the user before calling this (activation starts spend immediately).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| campaign_ids | Yes | ||
| skip_preflight | No | Skip the ads_volume check (not recommended) |
Implementation Reference
- src/tools/bulk.ts:95-136 (handler)The handler function for activate_campaigns_batch. It deduplicates campaign IDs, runs a pre-flight ads_volume check (unless skip_preflight is true) grouped by ad account, then sends batch POST requests to set status=ACTIVE. Returns {requested, preflight, results}.
handler: async (args) => { const ids = Array.from(new Set(args.campaign_ids as string[])); const preflight: Array<{ ad_account_id: string; campaigns: number; warnings: string[]; }> = []; if (!args.skip_preflight) { const groups = await groupCampaignsByAccount(ids); for (const [accountId, campaigns] of groups.entries()) { try { const { warnings } = await checkAdsVolume(accountId, campaigns.length); preflight.push({ ad_account_id: accountId, campaigns: campaigns.length, warnings, }); } catch (err) { preflight.push({ ad_account_id: accountId, campaigns: campaigns.length, warnings: [ `Could not fetch ads_volume: ${ err instanceof Error ? err.message : String(err) }`, ], }); } } } const requests: MetaBatchRequest[] = ids.map((id) => ({ method: "POST", relative_url: id, body: encodeBatchBody({ status: "ACTIVE" }), })); const results = await metaBatch(requests); return { requested: ids.length, preflight, results }; }, }, - src/tools/bulk.ts:88-94 (schema)Input schema for activate_campaigns_batch: campaign_ids (array of strings, min 1) and optional skip_preflight boolean.
inputSchema: { campaign_ids: z.array(z.string()).min(1), skip_preflight: z .boolean() .optional() .describe("Skip the ads_volume check (not recommended)"), }, - src/tools/bulk.ts:81-136 (registration)Tool definition object registered in the bulkTools array with name 'activate_campaigns_batch', description, inputSchema, and handler.
{ name: "activate_campaigns_batch", description: "WRITE (BULK): Activate many campaigns in one Batch API call. BEFORE activation we run " + "a per-Page ads_volume pre-flight for every distinct ad account — warnings are returned " + "in the response so the agent / user can abort if a Page is over capacity. Agents MUST " + "confirm with the user before calling this (activation starts spend immediately).", inputSchema: { campaign_ids: z.array(z.string()).min(1), skip_preflight: z .boolean() .optional() .describe("Skip the ads_volume check (not recommended)"), }, handler: async (args) => { const ids = Array.from(new Set(args.campaign_ids as string[])); const preflight: Array<{ ad_account_id: string; campaigns: number; warnings: string[]; }> = []; if (!args.skip_preflight) { const groups = await groupCampaignsByAccount(ids); for (const [accountId, campaigns] of groups.entries()) { try { const { warnings } = await checkAdsVolume(accountId, campaigns.length); preflight.push({ ad_account_id: accountId, campaigns: campaigns.length, warnings, }); } catch (err) { preflight.push({ ad_account_id: accountId, campaigns: campaigns.length, warnings: [ `Could not fetch ads_volume: ${ err instanceof Error ? err.message : String(err) }`, ], }); } } } const requests: MetaBatchRequest[] = ids.map((id) => ({ method: "POST", relative_url: id, body: encodeBatchBody({ status: "ACTIVE" }), })); const results = await metaBatch(requests); return { requested: ids.length, preflight, results }; }, }, - src/index.ts:65-90 (registration)The tool is registered on the MCP server via server.registerTool() in the registration loop that iterates allToolDefs (which includes bulkTools).
for (const tool of allTools) { server.registerTool( tool.name, { description: tool.description, inputSchema: tool.inputSchema, }, // The SDK's ToolCallback type infers the arg shape from inputSchema, but // our shared ToolDef uses a generic Record<string, unknown> signature for // portability. The cast here is intentional and isolated to the bridge. async (args: unknown) => { try { const result = await tool.handler(args as Record<string, unknown>); return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }], }; } catch (err) { const message = err instanceof Error ? err.message : String(err); return { content: [{ type: "text" as const, text: `Error: ${message}` }], isError: true, }; } }, ); } - src/tools/bulk.ts:29-53 (helper)groupCampaignsByAccount helper fetches each campaign's account_id in parallel to group campaigns by ad account for pre-flight ads_volume checks.
async function groupCampaignsByAccount( campaign_ids: string[], ): Promise<Map<string, string[]>> { const groups = new Map<string, string[]>(); const lookups = await Promise.all( campaign_ids.map(async (id) => { try { const camp = await metaGet<{ account_id?: string }>(`/${id}`, { fields: "account_id", }); return { id, account_id: camp.account_id }; } catch { return { id, account_id: undefined }; } }), ); for (const { id, account_id } of lookups) { if (!account_id) continue; const key = account_id.startsWith("act_") ? account_id : `act_${account_id}`; const list = groups.get(key) ?? []; list.push(id); groups.set(key, list); } return groups; }