google-ads-agent
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| ADS_AGENT_STATE | No | State directory used by agent/run.sh. | ~/.ads-agent |
| ADS_AGENT_POLICY | No | Path to the policy file. | ~/.ads-agent/policy.yaml |
| ADS_AGENT_JOURNAL | No | Path to the audit journal. | ~/.ads-agent/journal.jsonl |
| GOOGLE_ADS_CLIENT_ID | No | OAuth client ID. Required if not using a credentials file. | |
| GOOGLE_ADS_CLIENT_SECRET | No | OAuth client secret. Required if not using a credentials file. | |
| GOOGLE_ADS_REFRESH_TOKEN | No | OAuth refresh token. Required if not using a credentials file. | |
| ADS_AGENT_MAX_DAILY_BUDGET | No | Refuse to create a campaign above this daily budget. | 100 |
| GOOGLE_ADS_DEVELOPER_TOKEN | No | Google Ads developer token. Required if not using a credentials file. | |
| GOOGLE_ADS_LOGIN_CUSTOMER_ID | No | Optional manager account ID (digits only) used as the login customer ID. | |
| ADS_AGENT_ALLOWED_CUSTOMER_IDS | No | Comma-separated allowlist of customer IDs. Unset means any accessible account. | |
| GOOGLE_ADS_CONFIGURATION_FILE_PATH | No | Path to the Google Ads credentials file. A config file takes precedence if it exists. | ~/google-ads.yaml |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| list_accountsA | List the Google Ads accounts these credentials can reach, with currency, time zone, and whether each is a test account. Call this first to get the customer_id for other tools. |
| suggest_geo_targetsA | Resolve location names to the numeric geo target IDs that campaign specs need. Pass names like ['Spain', 'Madrid', 'United States']. Returns each match with its ID, canonical name, and target type. |
| suggest_keywordsA | Generate keyword ideas from seed terms and/or a URL, with average monthly search volume, competition, and top-of-page bid range. Requires the geo and language IDs you intend to target so the volumes are relevant. |
| preview_campaignA | Dry-run a campaign spec: lint it locally, then submit it to the Google Ads API with validate_only so nothing is written. Returns the rendered plan, any errors, and — if valid — a confirm_token to pass to create_campaign. Always call this before create_campaign; show the plan to the user. |
| create_campaignA | Create the campaign for real, as a single atomic mutate. Requires the exact spec and confirm_token from a successful preview_campaign. The campaign is always created PAUSED — it cannot spend until a human enables it in the Google Ads UI. Confirm with the user before calling this. |
| preview_rsaA | Dry-run a new responsive search ad for an EXISTING ad group: lint it locally, then submit it to the API with validate_only so nothing is written. Returns the plan and, if valid, a confirm_token for add_rsa. Google Ads ads are immutable once created — there is no in-place edit — so this is how you act on an audit_campaign finding like 'RSA has 3 headlines' or 'ad strength is POOR': add a stronger RSA, then pause the old one yourself once the new one is confirmed live. |
| add_rsaA | Create the previewed responsive search ad for real, in the existing ad group. Requires the exact spec and confirm_token from a successful preview_rsa. The ad is created ENABLED — Google Ads has no paused-on-create option for ads the way this server forces for new campaigns — so it starts serving immediately into what is presumably a live ad group. Confirm with the user before calling this. |
| list_campaignsB | List campaigns in an account with status, budget, and bidding strategy. |
| get_campaignA | Read back a campaign in full — settings, targeting, ad groups, keywords, and responsive search ad assets with their ad strength. Use this after create_campaign to verify what actually landed. |
| performanceA | Performance metrics at campaign, ad group, or ad level over a date range. Campaign level includes impression share and where it is being lost (budget vs rank); ad level includes ad strength. Needs Basic access and a live campaign — test accounts serve no ads and return no metrics. |
| keyword_performanceA | Per-keyword performance with quality score, its three components, and effective bid. Sorted by cost, so the biggest spenders come first. Use this to find keywords spending without converting — and AdRel/LPExp/ExpCTR to find out why a low quality score is low, since the single QS number alone does not say which of the three to fix. |
| search_termsA | The real search queries that triggered your ads, with metrics and whether each is already added as a keyword or excluded as a negative. This is the primary source for negative keyword decisions. |
| ad_asset_performanceA | Per-asset performance for responsive search ads: Google's own performance_label (PENDING/LEARNING/LOW/GOOD/BEST) for each individual headline and description. ad_strength is one label for the whole ad; this is the useful signal underneath it — which specific line is dragging the ad down, so you know what to replace with add_rsa rather than guessing from the aggregate label. |
| geo_performanceA | Performance by the geographic location a user actually was in or searching from — distinct from geo_target_ids, which is what you targeted. Answers 'is this converting where I think it is', which targeting alone cannot: a campaign can be fully within its targeted country and still be spending almost entirely in one weak region. |
| audit_campaignA | Structural review of a campaign: targeting gaps, Display expansion left on, thin RSAs, poor ad strength, all-broad-match ad groups, keywords duplicated across ad groups, budget-to-bid mismatch. Uses no performance data, so it works on test accounts and brand-new campaigns. |
| run_gaqlA | Run an arbitrary read-only GAQL query and return the raw rows. Escape hatch for anything the other tools do not cover. Only SELECT is accepted; GAQL has no mutation capability so this cannot write. |
| conversion_actionsA | Inspect conversion tracking: every conversion action with its status, category, counting type, whether it feeds bidding, and how many conversions it actually recorded. Use this to decide whether a low conversion count means a weak funnel or broken tracking — the whole autonomy gate depends on that answer. |
| change_historyA | Who changed what, when, and from which client (web UI, Editor, API, scripts), with the fields that changed. Answers 'why did performance shift' and 'was this setting deliberate'. Google retains at most 30 days of change history. |
| check_setupA | Check what this installation can actually do: credentials, API version, and which API methods the developer token's access level permits. Run this first when something is refused, or right after setup. Read-only — the one write probe uses validate_only and writes nothing. |
| keyword_volumesA | Average monthly search volume, competition, and bid range for a list of keywords you already have. Unlike suggest_keywords this does not expand or invent anything — it reports on exactly the terms you pass, which is what you want for auditing an existing keyword list. |
| forecast_campaignA | Forecast a campaign before building it: projected clicks, impressions, cost, average CPC, conversions and CPA for a keyword set at a given daily budget and max CPC. Run this before preview_campaign to find out whether the budget and bid actually buy enough traffic to learn anything. |
| assign_ad_groupsA | Map a list of keywords across ad groups that ALREADY EXIST, using Google's own themeing. It assigns and refines rather than inventing themes from scratch: pass a campaign whose ad groups are already created, and it returns which ad group each keyword belongs in, with a suggested match type. To split one big ad group, create the themed ad groups first, then run this to distribute the keywords. |
| policy_statusA | Show the active policy: which actions are autonomous, propose-only, or forbidden; the evidence thresholds each autonomous action must clear; and how much of the change budget is left. Call this before attempting any change so you know what will be permitted. |
| set_networksA | Turn Display expansion and/or search partners OFF for a search campaign. Can only switch networks off, never on — a request to enable one is refused. Autonomous when the network's own measured spend clears the policy threshold with no conversions. |
| add_negative_keywordsA | Add campaign-level negative keywords. Autonomous when each term's own measured search-term data clears the policy threshold with no conversions. Terms that do not clear it are reported, not added. |
| add_shared_negative_keywordsA | Add negative keywords to a SHARED list — one list of terms attached to many campaigns at once, for junk that applies account-wide rather than to a single campaign. Autonomous when each term's measured search-term evidence, aggregated across every campaign the list reaches, clears the policy threshold with no conversions — the same evidence rule as add_negative_keywords, since suppressing a term account-wide needs at least the same bar as suppressing it in one campaign. The list must already exist and be attached to at least one campaign: create and attach it via mutate first (shared_set create, then campaign_shared_set create) — list_shared_negative_lists shows what exists. |
| list_shared_negative_listsA | List every shared negative-keyword list in the account: how many terms each holds and which campaigns it is attached to. A shared list applies one set of negatives to many campaigns at once, unlike add_negative_keywords which is per-campaign. |
| pause_keywordsA | Pause keywords that are spending without converting. Pauses, never removes, so it is reversible. Evidence is measured here; keywords that do not clear the policy threshold are kept and reported. |
| pause_ad_groupsA | Pause whole ad groups that are spending without converting. Pauses, never removes. Thresholds are higher than for keywords, since an ad group is a much bigger unit to switch off. |
| revert_last_runA | Undo a run's changes using the inverse operations stored in the journal. Defaults to the most recent run. This is the escape hatch when the agent gets something wrong; it re-enables what was paused and removes negatives the agent itself added. |
| remove_campaignA | Permanently remove a campaign. IRREVERSIBLE — Google Ads has no un-remove, and a removed campaign can never be re-enabled (its historical stats do stay queryable). Call with no confirm_token to see exactly what will be destroyed and get a token; call again with that token to execute. The campaign must already be PAUSED. Prefer leaving a campaign paused unless there is a real reason to remove it. |
| create_campaign_draftA | Create a draft copy of an existing campaign: an inert shadow campaign you can safely edit — via get_campaign, mutate, add_rsa, and the rest, pointed at the draft's own campaign ID — without touching what is actually live. Nothing about the base campaign changes until you call promote_campaign_draft. |
| list_campaign_draftsA | List campaign drafts: status, which campaign each is a draft of, and the draft's own campaign ID — the target for get_campaign / mutate / add_rsa while editing it. |
| remove_campaign_draftB | Discard a campaign draft without promoting it. The base (live) campaign is untouched either way — this only deletes the shadow copy. |
| promote_campaign_draftA | Promote a campaign draft's edits back into the live base campaign — the one action in the draft workflow that touches live traffic: the base campaign starts running whatever was built in the draft. Call with no confirm_token to preview (shows which campaign it would overwrite); call again with the token to execute. Promotion is asynchronous — this kicks it off and returns immediately without waiting for it to finish. Check list_campaign_drafts afterward: status moves PROPOSED -> PROMOTING -> PROMOTED (or PROMOTE_FAILED). |
| list_capabilitiesA | Show the capability table: every resource, operation and field the generic mutate tool is allowed to touch, and at which tier. Anything absent from this table is refused, so this is also the list of what is NOT possible. Read-only. |
| explain_capabilityA | Explain what would happen if the generic mutate tool were asked to change one field: which rule matches, at what tier, and what else was considered. Use this before mutate to find out whether a change is permitted. Read-only. |
| list_proposalsA | List open proposals recorded by the generic mutate tool's propose-only findings — the exact diffs a human can execute with apply_proposal instead of manually re-deriving the mutate() call from a run's report. |
| apply_proposalA | Execute a previously recorded proposal from the generic mutate tool — the exact call a human is now explicitly approving, rather than the agent re-deciding on its own to act on a propose-only finding. Refused when this run is unattended (ADS_AGENT_UNATTENDED=1): applying a proposal is precisely the human step the |
| dismiss_proposalA | Dismiss an open proposal without executing it, so it stops showing up in list_proposals. Proposing the identical change again later reopens it — dismissing is a decision about this instance, not a permanent ban on the change. |
| mutateA | Change any mutable Google Ads resource. This is the general write path: it reaches every resource the API exposes, and is governed entirely by the capability table rather than by hand-written logic. Call it once with no confirm_token to see the exact diff, the tier that governs it, and a token. Call it again with that token to execute. Anything with no rule in the table is refused, as are forbidden rules however they are confirmed. Every update reads the current values first, so the journal can undo it. Prefer the specific tools where they exist — pause_keywords and the rest measure evidence this path cannot. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 41 tools
Most tools target a distinct resource or action, and the descriptions clearly separate pairs like pause_keywords vs pause_ad_groups and add_negative_keywords vs add_shared_negative_keywords. However, the four performance-variant tools, the preview_campaign/forecast_campaign pair, and the governance tools (policy_status, list_capabilities, explain_capability) require careful reading to pick correctly, and the generic mutate/run_gaql escape hatches broadly overlap with the specific tools.
Almost all tool names are snake_case with an imperative verb first, following a list_/create_/preview_/pause_/remove_/promote_ pattern. The main deviations are the noun-style report tools like keyword_performance, ad_asset_performance, geo_performance, search_terms, and conversion_actions, which are consistent in style but not verb_noun. Overall the naming is predictable and readable.
At 41 tools, this is well over the 25-tool threshold and makes for a heavy selection surface that will consume significant agent context. The broad Google Ads scope justifies much of the breadth, but clusters like the four performance reports, the capability/proposal machinery, and the generic mutate path could be consolidated. It is capable but oversized.
The toolset covers the campaign lifecycle, keyword/ad group/ad operations, negative keywords, drafts, audits, forecasting, performance analysis, and governance, with mutate and run_gaql as escape hatches so there are no dead ends. A few common operations such as pausing/toggling campaigns or creating ad groups are not first-class tools and must go through mutate, and shared-list creation is left to raw API calls. These are workable gaps rather than blocking ones.