Export Budget Recommendations (Bulk-Edit Ready)
ads_export_recommendationsExport budget optimization recommendations as platform-specific CSVs for Google Ads Editor or Meta Ads Manager, JSON for automation, or Markdown for review. Paste bulk edits to reallocate budget based on performance.
Instructions
Export budget_analyze recommendations as a Google Ads Editor CSV, Meta Ads Manager CSV, JSON, or Markdown — so you can take action in one bulk paste instead of editing campaigns one by one. AdOps does not call Google/Meta write APIs (that would require OAuth + developer token approval for every user); instead, this tool hands you the exact CSV rows those dashboards expect, and you paste them in. Perfect for the "pause anything with CPA over 50 and reallocate budget" workflow. Use platform=google with format=google_ads_csv (import into Google Ads Editor). Use platform=meta with format=meta_ads_csv (bulk edit in Meta Ads Manager Power Editor). Use json to pipe into n8n or your own automation. Use markdown for human review before acting. min_delta_pct filters out noise (e.g. 0.15 = only show changes >=15%).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| format | Yes | Output format. google_ads_csv and meta_ads_csv produce platform-specific bulk-edit CSVs ready to paste into Google Ads Editor or Meta Ads Manager. json is for piping into automations (n8n, Zapier). markdown is for standup docs or tickets. | |
| output_path | No | Absolute file path to write the output to. If omitted, returns the content inline in the tool response. | |
| optimization_goal | No | Goal fed to budget_analyze. | maximize_roas |
| platform | No | Restrict to a single platform. Required for google_ads_csv or meta_ads_csv to avoid mixed output. | |
| min_delta_pct | No | Only include recommendations whose budget change is at least this fraction (e.g. 0.1 = 10%). Filters out minor adjustments. | |
| limit | No | Max recommendations to export. Default 50. |
Implementation Reference
- src/index.ts:148-155 (schema)Input schema for ads_export_recommendations – defines format, output_path, optimization_goal, platform, min_delta_pct, and limit.
const ExportRecommendationsInputSchema = z.object({ format: z.enum(['google_ads_csv', 'meta_ads_csv', 'json', 'markdown']).describe('Output format. google_ads_csv and meta_ads_csv produce platform-specific bulk-edit CSVs ready to paste into Google Ads Editor or Meta Ads Manager. json is for piping into automations (n8n, Zapier). markdown is for standup docs or tickets.'), output_path: z.string().optional().describe('Absolute file path to write the output to. If omitted, returns the content inline in the tool response.'), optimization_goal: z.enum(['maximize_roas', 'maximize_conversions', 'minimize_cpa']).default('maximize_roas').describe('Goal fed to budget_analyze.'), platform: z.enum(['google', 'meta']).optional().describe('Restrict to a single platform. Required for google_ads_csv or meta_ads_csv to avoid mixed output.'), min_delta_pct: z.number().min(0).max(1).optional().describe('Only include recommendations whose budget change is at least this fraction (e.g. 0.1 = 10%). Filters out minor adjustments.'), limit: z.number().int().min(1).max(100).default(50).describe('Max recommendations to export. Default 50.'), }); - src/index.ts:157-191 (registration)Registration of the 'ads_export_recommendations' tool with title, description, and handler that calls exportRecommendations().
server.registerTool( 'ads_export_recommendations', { title: 'Export Budget Recommendations (Bulk-Edit Ready)', description: 'Export budget_analyze recommendations as a Google Ads Editor CSV, Meta Ads Manager CSV, JSON, or Markdown — so you can take action in one bulk paste instead of editing campaigns one by one. AdOps does not call Google/Meta write APIs (that would require OAuth + developer token approval for every user); instead, this tool hands you the exact CSV rows those dashboards expect, and you paste them in. Perfect for the "pause anything with CPA over 50 and reallocate budget" workflow. Use platform=google with format=google_ads_csv (import into Google Ads Editor). Use platform=meta with format=meta_ads_csv (bulk edit in Meta Ads Manager Power Editor). Use json to pipe into n8n or your own automation. Use markdown for human review before acting. min_delta_pct filters out noise (e.g. 0.15 = only show changes >=15%).', inputSchema: ExportRecommendationsInputSchema, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }, }, async (input) => { try { const reject = await ensureProOrReject(LICENSE_CONFIG, 'ads_export_recommendations'); if (reject) return reject; const result = await exportRecommendations(input); if (result.file_written) { return { content: [{ type: 'text' as const, text: JSON.stringify({ message: `Exported ${result.rows_exported} recommendations to ${result.file_written}`, format: result.format, summary: result.summary, next_steps: result.next_steps, }, null, 2), }], }; } return { content: [{ type: 'text' as const, text: `# Export (${result.format}) — ${result.rows_exported} rows\n\n${result.content}\n\n---\nNext steps:\n${result.next_steps.map((s) => `- ${s}`).join('\n')}`, }], }; } catch (e) { return handleToolError(e); } }, ); - Main exportRecommendations function – applies filters (min_delta_pct, limit), serializes to requested format (google_ads_csv, meta_ads_csv, json, markdown), optionally writes to disk, and returns result with next steps.
export async function exportRecommendations( options: ExportOptions, store?: Storage, ): Promise<ExportResult> { const s = store ?? defaultStorage; const goal = options.optimization_goal ?? 'maximize_roas'; const analysis = await analyzeBudget(goal, options.platform, s); const totalRecs = analysis.recommendations.length; let filtered = analysis.recommendations; // Filter by minimum delta percentage if (options.min_delta_pct !== undefined) { const minPct = options.min_delta_pct; filtered = filtered.filter((r) => { if (r.current_budget === 0) return true; // always include new allocations const deltaPct = Math.abs(r.suggested_budget - r.current_budget) / r.current_budget; return deltaPct >= minPct; }); } // Limit to top N const limit = Math.min(options.limit ?? 50, 100); filtered = filtered.slice(0, limit); const filteredOut = totalRecs - filtered.length; // Serialize let content: string; switch (options.format) { case 'google_ads_csv': content = toGoogleAdsEditorCsv(filtered); break; case 'meta_ads_csv': content = toMetaAdsCsv(filtered); break; case 'json': content = JSON.stringify( { generated_at: new Date().toISOString(), optimization_goal: goal, platform: options.platform ?? 'all', total_recommendations: totalRecs, exported: filtered.length, recommendations: filtered, }, null, 2, ); break; case 'markdown': content = toMarkdown(analysis, filtered, goal); break; default: throw new Error(`Unknown export format: ${options.format}`); } // Optionally write to disk let fileWritten: string | undefined; if (options.output_path) { const dir = path.dirname(options.output_path); await fs.mkdir(dir, { recursive: true }); await fs.writeFile(options.output_path, content, 'utf-8'); fileWritten = options.output_path; } const nextSteps: string[] = []; if (options.format === 'google_ads_csv') { nextSteps.push( 'Open Google Ads Editor → File → Import → Paste from clipboard OR upload CSV', 'Review proposed changes in the diff view before posting', 'Commit changes to your Google Ads account', ); } else if (options.format === 'meta_ads_csv') { nextSteps.push( 'Open Meta Ads Manager → Campaigns → select rows → Edit → Bulk Edit', 'Or use Meta Ads Manager Import CSV (Power Editor)', 'Review and publish changes', ); } else if (options.format === 'json') { nextSteps.push('Pipe this JSON into n8n, Zapier, or your own script for automated execution.'); } else { nextSteps.push('Paste this markdown into a standup document or ticket for team review.'); } return { format: options.format, rows_exported: filtered.length, content: fileWritten ? undefined : content, file_written: fileWritten, summary: { total_recommendations: totalRecs, filtered_out: filteredOut, by_type: countByType(filtered), }, next_steps: nextSteps, }; } - Helper to convert recommendations to Google Ads Editor CSV format with 'Campaign', 'Action', 'Status', 'Budget', 'Reason', 'Expected Impact' columns, filtering for google platform only.
function toGoogleAdsEditorCsv( recommendations: BudgetAnalysis['recommendations'], ): string { // Google Ads Editor bulk upload format — "Campaign", "Status", "Budget" columns // match the same headers Google exports, so the CSV re-imports cleanly. const header = ['Campaign', 'Action', 'Status', 'Budget', 'Reason', 'Expected Impact']; const rows = recommendations .filter((r) => r.platform === 'google') .map((r) => [ csvCell(r.campaign_name), csvCell(r.type), csvCell(r.type === 'pause' ? 'Paused' : ''), csvCell(r.suggested_budget), csvCell(r.reason), csvCell(r.expected_impact), ].join(',')); return [header.join(','), ...rows].join('\n'); } - Helper to convert recommendations to Meta Ads Manager CSV format with 'Campaign name', 'Action', 'Campaign status', 'Campaign budget', 'Reason', 'Expected impact' columns, filtering for meta platform only.
function toMetaAdsCsv( recommendations: BudgetAnalysis['recommendations'], ): string { // Meta Ads Manager bulk-edit CSV has its own column names const header = ['Campaign name', 'Action', 'Campaign status', 'Campaign budget', 'Reason', 'Expected impact']; const rows = recommendations .filter((r) => r.platform === 'meta') .map((r) => [ csvCell(r.campaign_name), csvCell(r.type), csvCell(r.type === 'pause' ? 'Paused' : ''), csvCell(r.suggested_budget), csvCell(r.reason), csvCell(r.expected_impact), ].join(',')); return [header.join(','), ...rows].join('\n'); }