Skip to content

Add write capabilities: campaigns, ad groups, ads, keywords, assets, and PMax support#36

Closed
SaiSatya16 wants to merge 26 commits intogoogleads:mainfrom
SaiSatya16:feature/write-tools
Closed

Add write capabilities: campaigns, ad groups, ads, keywords, assets, and PMax support#36
SaiSatya16 wants to merge 26 commits intogoogleads:mainfrom
SaiSatya16:feature/write-tools

Conversation

@SaiSatya16
Copy link
Copy Markdown

@SaiSatya16 SaiSatya16 commented Mar 21, 2026

Summary

This PR adds 33 new tools to the Google Ads MCP server, transforming it from read-only to a full campaign management platform. All tools follow the existing code patterns, include proper docstrings, and have been tested against live Google Ads accounts.

Highlights

  • 35 total tools (2 original + 33 new)
  • MCP Elicitation for destructive operations — prompts user for confirmation before deleting
  • login_customer_id parameter on all tools — switch between client accounts without restarting
  • Lazy client initialization — server starts without credentials (CI-friendly)
  • EU political advertising compliance — required since API v19.2

New tools by category

Campaign Management (4 tools)

  • `create_campaign` — Create Search/Display/Shopping campaigns with budgets and bidding strategies
  • `create_performance_max_campaign` — Create PMax campaigns with brand assets via batch mutate
  • `update_campaign` — Update campaign name, status, budget, and dates
  • `set_campaign_status` — Quick enable/pause toggle

Ad Group Management (2 tools)

  • `create_ad_group` — Create ad groups with CPC bids and type settings
  • `update_ad_group` — Update ad group name, status, and bids

Ads & Keywords (7 tools)

  • `create_responsive_search_ad` — Create RSAs with multiple headlines and descriptions
  • `add_keywords` — Add keywords with match types (exact, phrase, broad) and optional bids
  • `add_negative_keywords` — Add negative keywords to campaigns to block irrelevant traffic
  • `update_ad_status` — Enable, pause, or remove ads
  • `update_keyword` — Update keyword status or bid
  • `remove_ad` — Permanently remove ads (with MCP elicitation confirmation)
  • `remove_keyword` — Permanently remove keywords (with MCP elicitation confirmation)

Targeting (2 tools)

  • `set_geo_targets` — Set geographic targeting (countries/regions) for campaigns
  • `set_ad_schedule` — Set day/hour ad scheduling for campaigns

Asset Creation (10 tools)

  • `create_text_asset` — Text assets for PMax headlines/descriptions
  • `create_image_asset` — Upload images from URL or local file path
  • `create_youtube_video_asset` — YouTube video assets for PMax
  • `create_sitelink_asset` — Sitelink extensions
  • `create_callout_asset` — Callout extensions
  • `create_structured_snippet_asset` — Structured snippet extensions
  • `create_call_asset` — Call extensions
  • `create_promotion_asset` — Promotion extensions with discounts
  • `create_price_asset` — Price listing extensions
  • `create_lead_form_asset` — Lead generation form extensions

Asset Linking (4 tools)

  • `link_asset_to_campaign` — Attach assets to campaigns
  • `link_asset_to_ad_group` — Attach assets to ad groups
  • `link_assets_to_customer` — Attach assets at account level
  • `remove_campaign_asset` — Remove asset links (with MCP elicitation confirmation)

Asset Groups / Performance Max (4 tools)

  • `create_asset_group` — Create asset groups with all assets in single batch mutate
  • `add_assets_to_asset_group` — Add more assets to existing asset groups
  • `remove_asset_from_asset_group` — Remove assets (with MCP elicitation confirmation)

MCP Elicitation for Destructive Operations

All 4 destructive tools implement MCP Elicitation to prompt users for confirmation before proceeding:

```python
@mcp.tool()
async def remove_keyword(
customer_id: str,
ad_group_id: str,
criterion_id: str,
ctx: Context = None,
) -> Dict[str, str]:
result = await ctx.elicit(
message="DESTRUCTIVE ACTION: Permanently remove keyword?",
schema=Confirmation,
)
if result.action != "accept" or not result.data.confirm:
return {"message": "Keyword removal cancelled by user."}
```

  • Uses `ctx: Context` type annotation for proper SDK injection
  • Falls back gracefully with try/except when client doesn't support elicitation
  • Tested end-to-end on Claude Code CLI v2.1.81

Technical details

Test plan

  • `list_accessible_customers` — 3 accounts found
  • `search` — GAQL queries with PARAMETERS fix
  • `get_resource_metadata` — returns selectable/filterable/sortable fields
  • `create_campaign` — Search campaign with budget
  • `create_performance_max_campaign` — PMax with brand assets
  • `update_campaign` — updated budget
  • `set_campaign_status` — PAUSED/ENABLED toggle
  • `create_ad_group` — ad group with CPC bids
  • `update_ad_group` — updated bids
  • `create_responsive_search_ad` — RSA with 15 headlines + 4 descriptions
  • `add_keywords` — 55+ keywords (exact/phrase/broad)
  • `add_negative_keywords` — 60+ negative keywords blocking waste
  • `update_ad_status` — enabled/paused ads
  • `update_keyword` — updated keyword bids
  • `remove_ad` — removed disapproved ads (elicitation tested)
  • `remove_keyword` — elicitation confirmation on Claude Code CLI
  • `set_geo_targets` — US, UK, CA, AU, SG, UAE targeting
  • `set_ad_schedule` — business hours scheduling
  • `create_text_asset` — headlines/descriptions for PMax
  • `create_image_asset` — uploaded from local files
  • `create_youtube_video_asset` — YouTube video assets
  • `create_sitelink_asset` + `link_asset_to_campaign` — sitelinks
  • `create_callout_asset` — callout extensions
  • `create_structured_snippet_asset` — structured snippets
  • `create_call_asset` — call extension with phone number
  • `create_promotion_asset` — Black Friday promotion
  • `create_price_asset` — 3-tier pricing
  • `create_lead_form_asset` — lead gen form with 4 fields
  • `create_asset_group` — PMax asset group with batch mutate
  • `add_assets_to_asset_group` — added assets to existing group
  • `remove_asset_from_asset_group` — removed asset
  • `link_assets_to_customer` — account-level asset linking
  • `remove_campaign_asset` — removed campaign asset link

Queries without WHERE/ORDER BY/LIMIT clauses would produce invalid GAQL
like "FROM resourcePARAMETERS..." instead of "FROM resource PARAMETERS..."
- Add get_googleads_client() and get_googleads_type() helpers
- Add create_field_mask() for update operations using protobuf_helpers
- Update scope comment to reflect read-write capability
- create_campaign: Create Search/Display/Shopping campaigns with budgets and bidding
- create_performance_max_campaign: Create PMax campaigns with brand assets via batch mutate
- update_campaign: Update campaign name, status, budget, and dates
- set_campaign_status: Quick enable/pause/remove toggle

Includes EU political advertising compliance (required since API v19.2)
and supports all major bidding strategies (Manual CPC, Maximize Conversions,
Maximize Conversion Value, Target CPA, Target ROAS, Target Spend).
- create_ad_group: Create ad groups with CPC bids and type settings
- update_ad_group: Update ad group name, status, and bids
- create_responsive_search_ad: Create RSAs with multiple headlines and descriptions
- add_keywords: Add keywords with match types (exact, phrase, broad) and optional bids
- update_ad_status: Enable, pause, or remove ads
- update_keyword: Update keyword status or bid
- create_sitelink_asset: Create sitelink extensions
- create_callout_asset: Create callout extensions
- create_structured_snippet_asset: Create structured snippet extensions
- create_call_asset: Create call extensions
- create_image_asset: Upload images from URL or local file path
- create_promotion_asset: Create promotion extensions with discounts
- create_price_asset: Create price listing extensions
- create_lead_form_asset: Create lead generation form extensions
- create_text_asset: Create text assets for PMax headlines/descriptions
- create_youtube_video_asset: Create YouTube video assets for PMax
- link_asset_to_campaign: Attach assets to campaigns
- link_asset_to_ad_group: Attach assets to ad groups
- link_assets_to_customer: Attach assets at account level
- remove_campaign_asset: Remove asset links from campaigns
- create_asset_group: Create asset groups with all assets in single batch mutate
- add_assets_to_asset_group: Add more assets to existing asset groups
- remove_asset_from_asset_group: Remove assets from asset groups

Handles PMax requirement of creating asset group + asset links atomically.
Import campaigns, ad_groups, ads_keywords, assets, asset_links,
and asset_groups modules to register their tools with the MCP server.
@google-cla
Copy link
Copy Markdown

google-cla bot commented Mar 21, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Regenerated golden_tools_list.json to include all new write tools
so the smoke test passes.
Enables per-query login_customer_id parameter across all 28 tools,
allowing users to access client accounts through a Manager Account
without changing the MCP server config. Falls back to the
GOOGLE_ADS_LOGIN_CUSTOMER_ID env var when not specified.
- Promotion asset: add required final_url, fix percent_off micros conversion,
  skip NONE enum values for occasion and discount_modifier
- Lead form asset: add required call_to_action_description and final_urls fields
- Remove asset from asset group: use field type string name in resource path
  instead of numeric enum value

All 29 tools tested and verified against live Google Ads API.
The default client was eagerly created at import time, causing
crashes when credentials are not available (e.g., in CI).
Now uses lazy initialization on first use.
Full JSON schema comparison breaks across pydantic/Python versions
due to different nullable type representations. Compare tool names
and required parameters instead for stable cross-version testing.
@Raibaz
Copy link
Copy Markdown
Collaborator

Raibaz commented Mar 23, 2026

Hi, thanks for looking into this and for all the work that you have been putting in this PR!

We plan to release write capabilities in steps, so there's a chance that this PR will not be merged as we may prefer doing gradual releases for write features.

I'll leave it open for now in case we change our plans.

- Add remove_ad and remove_keyword tools with elicitation confirmation
- Add elicitation to remove_campaign_asset and remove_asset_from_asset_group
- Use ctx: Context type annotation for proper SDK injection
- Falls back gracefully when client doesn't support elicitation
- Tested end-to-end on Claude Code CLI v2.1.81
- Update golden tools list with all 32 tools
@SaiSatya16
Copy link
Copy Markdown
Author

Thanks for the feedback @Raibaz! Makes total sense.

I needed write tools to manage my google ads, I will be enhancing it further, if you guys change your plans I am happy to split these into a separate small PR if helpful. And if you want to cherry-pick specific write tool categories for gradual release, everything is modular — each tool file is independent.

- Add get_resource_metadata tool using GoogleAdsFieldService for on-demand field discovery
- Replace gaql_resources.json (8276 lines) with gaql_resources.txt (178 lines)
- Search tool description reduced from 300K+ chars to ~2K chars
- Update search description to reference get_resource_metadata for field lookup
- Fixes context window overflow issue (github.com/googleads/issues/15)
- Update update_references.py to generate flat text format
- Update search tests for new description format
@SaiSatya16
Copy link
Copy Markdown
Author

Closing in favor of a rebased PR on top of the latest main (post PR #37 merge).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants