Skip to main content
Glama

Export Budget Recommendations (Bulk-Edit Ready)

ads_export_recommendations
Read-onlyIdempotent

Export 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

TableJSON Schema
NameRequiredDescriptionDefault
formatYesOutput 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_pathNoAbsolute file path to write the output to. If omitted, returns the content inline in the tool response.
optimization_goalNoGoal fed to budget_analyze.maximize_roas
platformNoRestrict to a single platform. Required for google_ads_csv or meta_ads_csv to avoid mixed output.
min_delta_pctNoOnly include recommendations whose budget change is at least this fraction (e.g. 0.1 = 10%). Filters out minor adjustments.
limitNoMax recommendations to export. Default 50.

Implementation Reference

  • 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');
    }
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true and idempotentHint=true. The description reinforces that the tool does not call Google/Meta write APIs and explains that output can be inline or to a file. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single paragraph of 6-7 sentences, front-loaded with the core purpose, followed by specific details. Every sentence adds value without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no output schema, the description fully explains output formats and usage. It references sibling tools (budget_analyze) and covers all parameters. An agent gets complete guidance for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, providing baseline 3. The description adds extra meaning by explaining the workflow for each format (e.g., 'import into Google Ads Editor') and providing an example for min_delta_pct, exceeding baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool exports budget_analyze recommendations in multiple formats (Google Ads CSV, Meta Ads CSV, JSON, Markdown) for bulk editing. It distinguishes from sibling tools like budget_analyze and budget_reallocate.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit when-to-use guidance (bulk editing, 'pause anything with CPA over 50' workflow) and when-not-to-use (no direct API calls). It specifies which format works with which platform and explains alternatives for automation or human review.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/enzoemir1/adops-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server