gads_budget_pacing
Monitor campaign budget pacing by comparing actual cost to budget, identifying over- and under-spending campaigns with utilization percentages for any date range.
Instructions
Cost vs budget per campaign for the specified period with utilization percentage. Flags over- and under-pacing campaigns. Default last 28 days.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| customer_id | No | Override GOOGLE_ADS_CUSTOMER_ID for this call | |
| start_date | No | Start date: YYYY-MM-DD, NdaysAgo, yesterday, or today | 28daysAgo |
| end_date | No | End date: YYYY-MM-DD, NdaysAgo, yesterday, or today | yesterday |
Implementation Reference
- src/tools/budget.ts:50-85 (handler)The budgetPacing async function that executes the tool logic. It queries Google Ads for campaign cost vs budget data, computes utilization percentages (spent/budget * 100), and returns enriched rows with pacing info.
export async function budgetPacing(args: z.infer<z.ZodObject<typeof budgetPacingSchema>>) { const customer = getCustomer(args.customer_id); const start = resolveDate(args.start_date); const end = resolveDate(args.end_date); const rows = await customer.query(` SELECT campaign.id, campaign.name, campaign.status, campaign_budget.id, campaign_budget.name, campaign_budget.amount_micros, campaign_budget.delivery_method, campaign_budget.period, metrics.cost_micros FROM campaign WHERE segments.date BETWEEN '${start}' AND '${end}' AND campaign.status = 'ENABLED' ORDER BY metrics.cost_micros DESC LIMIT 200 `); const enriched = rows.map((r: any) => { const budget = microsToDollars(r.campaign_budget?.amount_micros); const spent = microsToDollars(r.metrics?.cost_micros); const pct = budget > 0 ? (spent / budget) * 100 : null; return { ...r, pacing: { budget_dollars: budget, spent_dollars: spent, utilization_pct: pct !== null ? Number(pct.toFixed(1)) : null, }, }; }); return { rowCount: enriched.length, rows: enriched }; } - src/tools/budget.ts:44-48 (schema)Input schema for the budgetPacing tool, defining optional customer_id, start_date (default 28daysAgo), and end_date (default yesterday) with Zod validation.
export const budgetPacingSchema = { customer_id: z.string().optional().describe("Override GOOGLE_ADS_CUSTOMER_ID for this call"), start_date: z.string().default(DEFAULT_START).describe("Start date: YYYY-MM-DD, NdaysAgo, yesterday, or today"), end_date: z.string().default(DEFAULT_END).describe("End date: YYYY-MM-DD, NdaysAgo, yesterday, or today"), }; - src/index.ts:220-225 (registration)Registration of the 'gads_budget_pacing' tool on the MCP server with its description, schema, and handler wrapper.
server.tool( "gads_budget_pacing", "Cost vs budget per campaign for the specified period with utilization percentage. Flags over- and under-pacing campaigns. Default last 28 days.", budgetPacingSchema, async (args) => { try { return ok(await budgetPacing(args)); } catch (e) { return err(e); } } ); - src/tools/budget.ts:4-7 (helper)Helper function microsToDollars used to convert Google Ads micros (smallest currency unit) to dollars.
function microsToDollars(micros: number | string | undefined): number { const n = Number(micros ?? 0); return Number.isFinite(n) ? n / 1_000_000 : 0; } - src/client.ts:39-59 (helper)Default date constants (DEFAULT_START='28daysAgo', DEFAULT_END='yesterday') and resolveDate helper used by the handler to parse date strings.
export const DEFAULT_START = "28daysAgo"; export const DEFAULT_END = "yesterday"; export function resolveDate(d: string): string { if (/^\d{4}-\d{2}-\d{2}$/.test(d)) return d; if (d === "today") return toISO(new Date()); if (d === "yesterday") return toISO(offsetDays(new Date(), -1)); const m = d.match(/^(\d+)daysAgo$/); if (m) return toISO(offsetDays(new Date(), -parseInt(m[1], 10))); throw new GoogleAdsError(`Unrecognized date: ${d}. Use YYYY-MM-DD, today, yesterday, or NdaysAgo.`); } function offsetDays(d: Date, n: number): Date { const out = new Date(d); out.setUTCDate(out.getUTCDate() + n); return out; } function toISO(d: Date): string { return d.toISOString().slice(0, 10); }