feat(google-ads): budget type (daily vs CUSTOM_PERIOD total) get and set (#366)#371
Merged
Conversation
…ds_ad_sets_update (#367) - Expose end_time (ISO 8601 string | UNIX timestamp | 0 to clear the end date per the Meta Marketing API convention) and lifetime_budget on the meta_ads_ad_sets_update tool inputSchema and forward them to update_ad_set. - Normalize the string "0" to the integer 0 before the contradiction guard so a string-typed zero cannot sidestep it; reject bool, negative, and blank end_time values. - Validate daily_budget/lifetime_budget mutual exclusion and refuse lifetime_budget combined with end_time=0 (a lifetime budget requires a schedule end). - Route lifetime_budget through _validate_positive_money. - StrategyPolicyGate: new max_lifetime_budget_per_campaign guardrail so lifetime-budget mutations cannot sidestep STRATEGY.md budget caps (fail-open contract preserved); documented in the strategy skill. Closes #367
…sset retrievals (#366) - list_demographic_criteria / list_audience_criteria on _TargetingMixin: GAQL reads over ad_group_criterion with whitelisted type filters, optional ad_group_id/campaign_id scoping via _validate_id, and an internal LIMIT 1000 cap for unscoped account-wide reads. - list_image_assets on _MediaMixin: asset read with name, dimensions, mime type, file size, and serving URL; integer limit validated 1..1000. - New MCP tools google_ads_demographic_targeting_list / google_ads_audience_targeting_list / google_ads_image_assets_list (tool count 83 -> 86). - mappers.py: auto-generated criterion/asset enum maps + map_enum_name handling raw proto ints (use_proto_plus=False production path), 'Enum.NAME' strings, and numeric strings, so real-API responses map to enum names instead of numeric codes. - Docs: AGENTS.md tool table and _mureo-google-ads SKILL.md catalog. Covers issue #366 tasks 1/2/4; task 3 (budget type get/set) ships separately given the guardrail interaction.
…/366-google-ads-budget-period
…set (#366) - get_budget now returns name, period (DAILY / CUSTOM_PERIOD), delivery_method, status (enum-mapped), total_budget / total_amount_micros (null unless CUSTOM_PERIOD), and reference_count. - update_budget accepts total_amount / total_amount_micros alongside the daily amount, with a dynamic update mask. period is create-only — the API marks CampaignBudget.period immutable. - create_budget accepts period (DAILY / CUSTOM_PERIOD whitelist) and total amounts; totals require CUSTOM_PERIOD and vice versa (fail fast). - All amount paths (daily + total, create + update) route through the shared _resolve_amount_micros validator: positive, catastrophe ceiling, bool/inf rejection, pair mutual exclusion. - StrategyPolicyGate: max_lifetime_budget_per_campaign now also catches Google total budgets in both spellings (total_amount, currency units; total_amount_micros, micros); the daily cap additionally reads amount_micros — closing parameter-form bypasses found in review. - BYOD get_budget shape parity; budget tool schemas + handlers updated; _mureo-google-ads and _mureo-strategy skills documented. Covers issue #366 task 3. Closes #366 together with #369. Claude-Session: https://claude.ai/code/session_0195WBK6YopTveq9RAQE2xMz
…udget-period # Conflicts: # mureo/_data/skills/_mureo-strategy/SKILL.md # mureo/google_ads/mappers.py # mureo/policy/strategy_gate.py # skills/_mureo-strategy/SKILL.md # tests/test_mcp_tools_google_ads.py # tests/test_strategy_gate.py
Merged
hyoshi
added a commit
that referenced
this pull request
Jul 9, 2026
Google Ads read-only demographic/audience criteria + image assets (#369), budget type get/set (#371); Meta Ads ad-set end_time + lifetime_budget (#368); Search Console cross-client isolation / site_url tenant allow-list (#375, security); About Installed-packages consistency (#365); Meta budget zero-decimal currency fix (#372).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #366 (task 3 — budget type get/set). Successor of #370, which GitHub auto-closed when its stacked base branch was deleted on the #369 merge; now based on
mainwith #368/#369 already merged. Closes #366.Summary
Google Ads budgets come in two types — DAILY and CUSTOM_PERIOD (campaign-lifetime total). mureo previously read/wrote only the daily amount. This PR:
google_ads_budget_getname,period(DAILY / CUSTOM_PERIOD),delivery_method, enum-mappedstatus,total_budget/total_amount_micros(null unless CUSTOM_PERIOD),reference_count— fixing the long-standing drift where the description promised these fieldsgoogle_ads_budget_updatetotal_amount/total_amount_microsalongside (or instead of) the daily amount, with a dynamic update maskgoogle_ads_budget_createperiod+ total amounts;requiredrelaxed to["name"]+ anyOf so a pure total budget needs no daily amountDesign notes
periodis create-only: the Google Ads API marksCampaignBudget.periodImmutable (verified from the proto docstrings), soupdate_budgetdeliberately does not accept it, despite the issue text suggesting both. Symmetric fail-fast guards: a total withoutperiod='CUSTOM_PERIOD'and CUSTOM_PERIOD without a total are bothValueErrors before any API call._resolve_amount_micros— positive,_MAX_BUDGET_AMOUNT_MICROSceiling, bool/inf rejection, pair mutual exclusion. The create path's legacy bareint(amount_micros)(no ceiling) is gone.max_lifetime_budget_per_campaignnow catches Google totals in both spellings (total_amountcurrency units,total_amount_micros÷1e6), and the daily cap additionally readsamount_micros— closing parameter-form bypasses found in security review.get_budgetshape parity (period=DAILY, null totals)._mureo-google-ads+_mureo-strategyskills (canonical + packaged byte-identical).Test plan
TDD (tests first). Reviews: security-reviewer (HIGH
total_amountgate bypass → fixed → CONFIRMED) + python-reviewer (MEDIUM schema wording + 2 LOW → fixed → CONFIRM).https://claude.ai/code/session_0195WBK6YopTveq9RAQE2xMz