中文

Google Ads Skill & MCP Server for Claude Code, Codex, Cursor, Windsurf

The ADM Google Ads skill lets an AI coding tool read your Google Ads accounts and build Search campaigns from a written plan. It works with Claude Code, Codex, Cursor and Windsurf. The skill is one file, SKILL.md, that describes the workflow; the work itself runs through the ADM MCP server, which connects the AI tool to the Google Ads accounts linked to your ADM account.

  • Read on every plan: reports, search terms, keywords, ads and assets are available on every ADM plan, including Free.
  • Write on paid plans: creating campaigns, ad groups, ads, keywords, negative keywords and assets requires a paid plan (Starter, Professional or Enterprise).
  • Preview before every change: ADM validates each change and returns a preview. Nothing is written to Google Ads until you approve it.
  • New campaigns are paused: every campaign is created paused and is enabled only when you ask for it in a separate message.

Addresses:

  • Skill file: https://adm.cc/docs/google-ads-skill/SKILL.md (folder name adm-google-ads)
  • MCP server: https://app.adm.cc/mcp (Streamable HTTP), authenticated with an ADM API key sent as Authorization: Bearer adm_xxx
  • Contract version: 2026-09-30

1. Get started in three steps

Step 1. Create an API key. Sign in to ADM and open https://app.adm.cc/apikeys. Choose the access level:

  • Read only: reports and configuration only. Works on every plan, including Free.
  • Read & write: reports plus the write tools. Write tools require an active paid plan.

The full key is shown once. Copy it before closing the dialog.

Step 2. Ask the AI tool to install ADM. Copy the prompt below, replace adm_xxx with your key and send it to the AI tool. The AI tool follows the steps in section 5 to connect the ADM MCP server, save the skill file and test the connection. When it has finished, restart the AI tool if it asks you to.

Install the ADM MCP server and the ADM Google Ads skill by following https://adm.cc/docs/google-ads-skill#ai-install. My ADM API key is adm_xxx

The key remains in the conversation history. If that history may be seen by others, rotate the key in ADM (section 10). To configure the server by hand instead, see section 4.

Step 3. Give instructions in plain language. For example: "Which of my campaigns spent the most in the last 30 days?" or "Create the campaigns in plan.md." Section 3 has more examples.

2. Which ADM tools the skill calls

What you ask for ADM tools
Choose or switch the Google Ads account get_accounts
Reports and questions about performance get_campaigns, get_keywords, get_search_terms, get_ads, get_negative_keywords, get_assets
Build campaigns from a plan create_search_campaign, add_ad_groups, add_keywords, add_ads
Negative keywords add_account_negative_keywords, create_shared_negative_list, attach_shared_negative_list
Sitelinks, callouts, structured snippets and prices add_sitelinks, add_callouts, add_structured_snippets, add_prices
Image assets Upload endpoint POST https://app.adm.cc/mcp/uploads, then add_images
Ad groups drafted by ADM AI draft_ad_groups, get_ad_group_draft
Check a build and resume after an interruption get_campaign_setup, get_operation
Enable or pause a campaign set_campaign_status

Section 6 describes every tool and its parameters.

3. What you can say after setup

  • "Which of my campaigns spent the most in the last 30 days, and how many conversions did each get?"
  • "List search terms from the last 14 days that cost money without a conversion."
  • "Create the campaigns in plan.md. Preview first and wait for my approval."
  • "Draft ad groups for the photo-enhancer section of plan.md."
  • "Add banner.jpg as an image asset to the photo-enhancer@DE campaign."
  • "Switch to the MySecond ad account."

Default account

When several Google Ads accounts are connected, the skill asks once which one to use and saves the choice in .adm/account.json in the project directory (customer ID and account name only, never the API key). Later requests in the same project use that account, and each operation names the account it runs on. When only one account is connected, the skill uses it without asking. To change the account, say "Switch to the MySecond ad account". If the saved account is later disconnected in ADM, the skill stops before the next operation, reports that the saved account is no longer connected, and asks which account to use, even when only one remains.

Example: build a campaign from a plan

This example uses a fictional brand, PixelUp, and builds one campaign from a plan entry named photo-enhancer@DE: an online photo enhancement tool advertised in Germany in German.

The plan entry:

  • Campaign photo-enhancer@DE, location Germany, one ad group photo-enhancer-DE
  • Landing page https://www.example.com/de/photo-enhancer, display path foto / verbessern
  • 8 phrase-match keywords, 6 headlines, 2 descriptions, 4 campaign negative keywords
  • General settings: Google Search only, Maximize clicks with a CPC cap, no broad match

The plan does not state a daily budget or the CPC cap, so the AI tool asks for both before building anything. The example uses 10.00 per day and a 0.40 cap in the account currency.

What to ask:

Create the photo-enhancer@DE campaign from plan.md.
Daily budget 10.00, CPC cap 0.40. Preview first and wait for my approval.

The AI tool first calls create_search_campaign without confirm. ADM validates the request and returns a preview with every error, the display width of each text, the account currency and a monthly budget estimate (daily budget × 30.4). The preview includes a preview_id. After you approve, the AI tool sends the same arguments again with "confirm": true and that preview_id.

{
  "customer_id": "123-456-7890",
  "campaign": {
    "name": "photo-enhancer@DE",
    "daily_budget": 10.00,
    "bidding": { "type": "MAXIMIZE_CLICKS", "max_cpc": 0.40 },
    "network_settings": { "google_search": true, "search_partners": false, "display_network": false },
    "geo_targets": [ { "country_code": "DE" } ],
    "negative_keywords": [ "\"kamera\"", "\"handy\"", "\"bildschirm\"", "\"monitor\"" ],
    "ad_groups": [
      {
        "name": "photo-enhancer-DE",
        "language": "de",
        "keywords": [
          "\"foto verbessern\"", "\"bildqualität verbessern\"", "\"foto schärfen\"", "\"bild vergrößern\"",
          "\"foto qualität verbessern online\"", "\"unscharfes foto scharf machen\"",
          "\"bild hochskalieren\"", "\"foto auflösung erhöhen\""
        ],
        "ads": [
          {
            "final_url": "https://www.example.com/de/photo-enhancer",
            "path1": "foto",
            "path2": "verbessern",
            "headlines": [
              { "text": "Fotoqualität verbessern" },
              { "text": "Bilder online vergrößern" },
              { "text": "Unscharfe Fotos schärfen" },
              { "text": "Fotos 2x oder 4x vergrößern" },
              { "text": "Ohne Installation nutzen" },
              { "text": "PixelUp Foto-Verbesserer" }
            ],
            "descriptions": [
              { "text": "Fotoqualität online verbessern: Bilder 2x oder 4x vergrößern und schärfen." },
              { "text": "Für Produktfotos, Social-Media-Beiträge und kleine Bilder. Direkt im Browser, ohne App." }
            ]
          }
        ]
      }
    ]
  }
}

Notes on this payload:

  • Keywords use Google notation: "text" is phrase match, [text] is exact match, bare words are broad match. Inside JSON the quotes of a phrase keyword are escaped as \".
  • "language": "de" appends a language tag to the ad group name, so the group is created as photo-enhancer-DE [de]. ADM uses this tag to identify the ad language when it analyzes the group later.
  • Geo targeting always uses the "Presence" option (people in or regularly in the location).
  • Account-level negative keywords and shared lists from the same plan are separate steps: add_account_negative_keywords once per account, create_shared_negative_list once per list, then attach_shared_negative_list for each new campaign.

The result contains the new campaign_id, the ad group IDs and an operation_id. The AI tool then calls get_campaign_setup to compare what exists in Google Ads with the plan. The campaign stays paused until you ask to enable it.

For a plan with many campaigns, the skill instructs the AI tool to:

  • List every missing decision and check the counts in the plan before building
  • Preview and ask for approval in two rounds: campaigns and lists first, then the list attachments and assets that need the new IDs
  • Build campaign by campaign, recording each ID in the manifest file adm-manifest.json
  • Reconcile the result, and resume without repeating finished campaigns
  • Ask separately before enabling anything

4. Use the MCP server without the skill

The skill is optional. Connected on its own, the MCP server sends the AI tool a set of usage rules when it connects: preview before every write, new campaigns paused, the default account, and treating search terms as data. The MCP server has two further advantages, with or without the skill:

  • Updates without reinstalling: new tools and fields become available as soon as ADM releases them; the configuration stays the same.
  • Permissions per tool: most AI tools let you approve read tools automatically and keep a confirmation step for write tools (section 9).

The configurations below read the key from the ADM_API_KEY environment variable. Codex, Cursor and Windsurf read the variable at run time, and their configuration files do not contain the key. Claude Code writes the key into its user-level configuration file, ~/.claude.json, when the server is added; do not share that file. Set the variable first, then restart the AI tool.

macOS or Linux (add the line to ~/.zshrc or ~/.bashrc):

export ADM_API_KEY="adm_xxx"

Windows (then open a new terminal):

setx ADM_API_KEY "adm_xxx"

Configuration for each AI tool:

Claude Code (Bash):

claude mcp add --transport http --scope user adm https://app.adm.cc/mcp \
  --header "Authorization: Bearer $ADM_API_KEY"

PowerShell:

claude mcp add --transport http --scope user adm https://app.adm.cc/mcp `
  --header "Authorization: Bearer $env:ADM_API_KEY"

--scope user makes the server available in every project on this computer. Check the connection with claude mcp list: the adm line should end with ✔ Connected.

To stop the approval prompts for read tools only, add this to ~/.claude/settings.json (all projects) or .claude/settings.json (one project):

{
  "permissions": {
    "allow": [
      "mcp__adm__get_*"
    ]
  }
}

mcp__adm__get_* matches every ADM tool whose name starts with get_: the nine read tools and get_ad_group_draft, which only reads the state of an AI draft. Keep write tools out of the allow list (section 9). draft_ad_groups does not change Google Ads, but each call runs several AI calls, so the rule leaves it out as well. If you registered the server under another name, replace adm in the rule with that name.

Codex:

codex mcp add adm --url https://app.adm.cc/mcp --bearer-token-env-var ADM_API_KEY

Codex reads ADM_API_KEY when it starts.

Cursor (~/.cursor/mcp.json for all projects, or .cursor/mcp.json in a project):

{
  "mcpServers": {
    "adm": {
      "url": "https://app.adm.cc/mcp",
      "headers": { "Authorization": "Bearer ${env:ADM_API_KEY}" }
    }
  }
}

Windsurf (~/.codeium/windsurf/mcp_config.json):

{
  "mcpServers": {
    "adm": {
      "serverUrl": "https://app.adm.cc/mcp",
      "headers": { "Authorization": "Bearer ${env:ADM_API_KEY}" }
    }
  }
}

The Codex, Cursor and Windsurf entries follow each tool's documented MCP configuration format. Restart the AI tool after changing its configuration, then ask it to list your Google Ads accounts. In Windsurf, open the file from the MCP settings with View raw config if it is not at the path above. ${env:ADM_API_KEY} is read from the environment of the AI tool's own process. Cursor and Windsurf are based on VS Code and, when opened from the Dock or an application launcher, also load variables set in ~/.zshrc or ~/.bashrc. After setting or changing the variable, quit the AI tool completely and open it again (on macOS, press Cmd+Q; closing the window does not quit the app); otherwise the header is sent without the key and the server returns HTTP 401.

Connection troubleshooting

Symptom Cause Fix
HTTP 401 The key is missing, mistyped, or has leading or trailing spaces Check ADM_API_KEY, restart the AI tool, or remove the server and add it again with the key pasted exactly as shown
HTTP 401 The key was revoked on the API keys page Create a new key and update the configuration (section 10)
HTTP 401 on Windows The header used a PowerShell variable inside single quotes, so the variable was not expanded Use double quotes, or paste the key directly into the command
HTTP 401 after setting the variable The AI tool was started before the variable was set Quit the AI tool completely and open it again (on macOS, press Cmd+Q); start a terminal-based tool from a new terminal. If the error persists, start the editor from a terminal where the variable is set, for example cursor .
HTTP 404 The URL is wrong The URL must be exactly https://app.adm.cc/mcp: https, the app. subdomain, and the /mcp path
Timeout or connection refused A corporate proxy or firewall blocks the request Set HTTPS_PROXY before starting the AI tool, or ask your network administrator to allow app.adm.cc

A key that worked before and now returns 401 has usually been revoked. The API keys page shows when each key was last used.

5. Installation steps (for AI tools to read)

This section is written for AI tools. You only need to complete step 2 of section 1; the AI tool then follows the steps below on its own.

The user has given you an ADM API key (it starts with adm_). Install ADM as follows, then report what you did.

  1. Add the MCP server at user level, so it works in all projects. Server name: adm. URL: https://app.adm.cc/mcp (Streamable HTTP). Header: Authorization: Bearer <key>. Write the key only into your user-level MCP configuration, never into a project file or a git repository. If a server named adm already exists, replace it.
AI tool How to add the server
Claude Code Run claude mcp add --transport http --scope user adm https://app.adm.cc/mcp --header "Authorization: Bearer <key>"
Codex In ~/.codex/config.toml add [mcp_servers.adm] with url = "https://app.adm.cc/mcp" and http_headers = { "Authorization" = "Bearer <key>" }
Cursor In ~/.cursor/mcp.json add "adm": { "url": "https://app.adm.cc/mcp", "headers": { "Authorization": "Bearer <key>" } } under mcpServers
Windsurf In ~/.codeium/windsurf/mcp_config.json add "adm": { "serverUrl": "https://app.adm.cc/mcp", "headers": { "Authorization": "Bearer <key>" } } under mcpServers

Keep any other servers already in the file. On Windows, ~ is the user profile folder.

  1. Save the skill. Download https://adm.cc/docs/google-ads-skill/SKILL.md to adm-google-ads/SKILL.md inside the skills directory you read for all projects. The folder name must be adm-google-ads. Create missing directories. If you do not know that directory, use the project-level one:
AI tool Skill file path
Claude Code ~/.claude/skills/adm-google-ads/SKILL.md
Codex .agents/skills/adm-google-ads/SKILL.md
Cursor .cursor/skills/adm-google-ads/SKILL.md
Windsurf .windsurf/skills/adm-google-ads/SKILL.md
  1. Test the connection. Call get_accounts on the adm server. If the tool is not available until you restart, say so.

  2. Report to the user: where the server configuration and the skill file were saved, whether a restart is needed, and the Google Ads accounts found. After a restart, the user can say "List my Google Ads accounts" to check.

6. Tool reference

Tools that act on an account take customer_id from get_accounts (format 123-456-7890); get_operation and get_ad_group_draft take only their own ID. Money is always in the account currency as major units, for example 5.00, never micros. Report windows (days) are 7, 14 or 30 complete days in the account time zone, excluding today (default 7). List tools return up to limit rows (1 to 200, default 50) and a next_cursor for the next page.

Read tools

Tool What it returns Key parameters
get_accounts Connected accounts (customer_id, name, currency, time zone), your plan, the key scope, whether writes are allowed, campaign creations left this month and write calls left today. Call it first. none
get_campaigns Campaigns with settings and performance, sorted by cost. Removed campaigns are never returned. days, status (ENABLED or PAUSED), limit, cursor
get_campaign_setup The full configuration of one campaign in the same shape as create_search_campaign, including paused items and campaign and ad group assets (images included), without metrics. Use it to verify a build. campaign_id
get_ads Responsive search ads with headlines, descriptions, pins, ad strength and performance. campaign_id, days, limit, cursor
get_keywords Keywords in Google notation with status, Quality Score, max CPC and performance. campaign_id, days, limit, cursor
get_search_terms Search queries that triggered your ads, with the matched keyword and performance. At most the top 10,000 rows by cost (truncated is true when capped). campaign_id, days, limit, cursor
get_negative_keywords Negative keywords by level: account, campaign, ad group and shared lists (with list IDs and attached campaigns). campaign_id
get_assets Sitelink, callout, structured snippet, price and image assets by level, with link status. Images include name, width, height, file size, image URL, approval status and review status. campaign_id
get_operation The recorded result of a write call: pending, completed, partial, failed or unknown, and the resources that were created. operation_id

Write tools

Every write tool previews first. Called without confirm, it validates and returns a preview without changing anything. Each preview returns a preview_id. Called again with identical arguments plus "confirm": true and that preview_id within 30 minutes of the preview, it performs the change. A confirmed call without preview_id returns invalid_argument; one whose preview_id is unknown, expired or was issued for different arguments returns preview_required. Retrying a confirm with the same preview_id returns the earlier result instead of writing twice. To make the same change again, preview again and confirm with the new preview_id after approval.

Tool What it does Key parameters
create_search_campaign Creates a complete Search campaign in one call: budget, bidding, networks, locations, negative keywords, shared lists, ad groups, keywords and responsive search ads. Always created paused. campaign (see the example in section 3)
add_ad_groups Adds ad groups with keywords, negatives and ads to an existing Search campaign. Created paused unless status is ENABLED. campaign_id, ad_groups, status
add_keywords Adds keywords, paused keywords and ad group negative keywords to one ad group. Existing keywords are reported as exists. ad_group_id, keywords, paused_keywords, negative_keywords
add_ads Adds 1 to 3 responsive search ads to an existing ad group (same ad shape as add_ad_groups, including path1 and path2). A group holds at most 3, paused ads included. An ad with the same headlines and descriptions as an existing ad is reported as exists. New ads are enabled and serve after Google review when the group and campaign are enabled. ad_group_id, ads
add_account_negative_keywords Adds negatives to the account-level list, which blocks them in every Search campaign, including running ones. keywords (up to 500)
create_shared_negative_list Creates a shared negative keyword list and optionally attaches it to campaigns in the same request. name, keywords (up to 1,000), campaign_ids
attach_shared_negative_list Attaches an existing shared list to campaigns. shared_list_id, campaign_ids
add_sitelinks Adds sitelinks: link text up to 25, two optional descriptions up to 35 each (both or neither), final URL. level, sitelinks, campaign_ids or ad_group_ids
add_callouts Adds callouts, up to 25 each. level, callouts, campaign_ids or ad_group_ids
add_structured_snippets Adds structured snippets: one of Google's predefined headers (English, or its official translation such as Marken) and 3 to 10 values up to 25 each. Translated headers are checked by Google when you confirm, not in the preview. level, snippets, campaign_ids or ad_group_ids
add_prices Adds price assets with 3 to 8 offerings; header and description up to 25 each. level, prices, campaign_ids or ad_group_ids
add_images Adds image assets to Search campaigns or ad groups from uploaded files (see Image uploads below). Results are per target. level (campaign or ad_group), images (upload_id, optional name), campaign_ids or ad_group_ids
set_campaign_status Pauses or enables a campaign. Once enabled, the campaign starts spending against its daily budget. campaign_id, status (ENABLED or PAUSED)

level is customer (all campaigns), campaign or ad_group; add_images accepts only campaign and ad_group. Asset tools accept up to 20 items and 20 targets per call; assets added to running campaigns start serving after Google reviews them.

AI ad group drafts

draft_ad_groups drafts ad groups for an existing Search campaign: ADM analyzes the landing page, groups the keywords by theme and generates responsive search ads for each group. It changes nothing in Google Ads and needs a read and write key and a paid plan. The draft runs in the background, so the call returns a draft_id at once.

Tool What it does Key parameters
draft_ad_groups Starts a draft and returns draft_id, status and poll_after_seconds. campaign_id, landing_page_url, keywords (1 to 200), language, paused_keywords, group_name_prefix (up to 100 characters), ads_per_group (1 to 3, default 1)
get_ad_group_draft The state of a draft: pending, running, done or failed. When done: ad_groups, primary_ad_group and warnings. When failed: error. draft_id

How a draft is used:

  1. Call draft_ad_groups, then call get_ad_group_draft every poll_after_seconds until status is done or failed. A draft usually finishes within 1 to 3 minutes.
  2. Pass ad_groups verbatim to add_ad_groups and preview it. Group names carry the prefix and the language tag.
  3. primary_ad_group names the group with the most keywords. It also receives the paused keywords, any keyword left unassigned by the draft, and the keywords of extra groups when the draft contains more than 20 groups. Add hand-written ads (add_ads) and ad group negative keywords (add_keywords) only to this group.

Drafts are kept for 24 hours. Each draft runs several AI calls; the limit is 60 drafts per hour per ADM account. Do not repeat a call for the same input while its draft is pending or running.

Image uploads

add_images links images that were uploaded to ADM first. Upload each file as multipart/form-data in the field file, with the same API key:

curl -F [email protected] -H "Authorization: Bearer adm_xxx" https://app.adm.cc/mcp/uploads

In PowerShell, use curl.exe with the same arguments.

The upload accepts JPG and PNG files up to 5120 KB; the format is detected from the file content, not the file name. The add_images preview then checks the dimensions: square 1:1 at least 300×300, or landscape 1.91:1 at least 600×314, with 1% tolerance on the aspect ratio. Uploads need a read and write key and a paid plan, and each upload counts as one write call.

Response (HTTP 200):

{
  "upload_id": "upl_...",
  "width": 1200,
  "height": 628,
  "bytes": 184320,
  "sha256": "...",
  "content_type": "image/jpeg",
  "expires_at": "2026-10-01T09:30:00+00:00"
}

An upload_id is valid for 24 hours (expires_at) and only for the ADM account that uploaded it. After it expires, add_images returns not_found: upload the file again and preview again with the new upload_id.

Errors return {"error": {...}} with code, message, field_path, fix and request_id:

HTTP status code Cause
400 invalid_argument The request is not multipart/form-data, the file field is missing, or the file is empty
401 unauthorized The API key is missing or invalid
403 permission_denied The key is read-only, or the plan does not include write access
404 none Wrong URL: the endpoint is POST https://app.adm.cc/mcp/uploads on the app. subdomain
413 invalid_argument The file is larger than 5120 KB
415 invalid_argument The file is not a JPG or PNG image
429 quota_exceeded No write calls left today (UTC)

Google shows image assets only for eligible accounts, for example accounts that have been open for at least 60 days, have recent Search spend and a good policy history, and are not in a sensitive vertical. For other accounts Google may reject the link, or the images may not serve. Google also deduplicates images by content: an identical image already in the account is reused, and one already linked to the target is reported as exists.

Validation limits

Item Limit
Ad groups per call 20
Ads per ad group 3
Keywords 300 per ad group, 2,000 per call; each up to 80 characters and 10 words
Negative keywords 1,000 per list or campaign
Locations 50 per campaign; each is either country_code (ISO 3166-1 alpha-2) or location_id; at least one must be a positive target
Shared lists per create_search_campaign call 20
Headlines 3 to 15 per ad, display width up to 30
Descriptions 2 to 4 per ad, display width up to 90
Display path path1 and path2, display width up to 15 each
Names up to 255 characters
Images JPG or PNG up to 5120 KB; 1:1 at least 300×300 or 1.91:1 at least 600×314 (1% tolerance)
AI drafts 1 to 200 keywords per draft, 1 to 3 ads per group

Display width counts CJK characters as 2. Emoji are rejected. Pins use pinned_field values HEADLINE_1 to HEADLINE_3, DESCRIPTION_1 and DESCRIPTION_2.

Bidding type is one of MANUAL_CPC, MAXIMIZE_CLICKS, MAXIMIZE_CONVERSIONS, MAXIMIZE_CONVERSION_VALUE, TARGET_CPA and TARGET_ROAS. MANUAL_CPC requires max_cpc on the campaign or on each ad group; with MAXIMIZE_CLICKS, max_cpc is the CPC cap. network_settings.google_search must be true.

campaign.languages is accepted but not applied: Search campaigns are created without campaign-level language targeting. Use the ad group language field to record the ad language.

7. Error codes

Errors return one structure:

{
  "code": "invalid_argument",
  "message": "What went wrong",
  "field_path": "campaign.ad_groups[0].ads[0].headlines[3].text",
  "fix": "How to correct it",
  "retryable": false,
  "retry_after_seconds": null,
  "action_url": null,
  "request_id": "..."
}
Code Meaning What to do
invalid_argument A field is missing, out of range or too long Correct every field listed in field_path and preview again
account_not_found The customer_id is not connected to your ADM account Use a customer_id from get_accounts, or connect the account in ADM
not_found The campaign, ad group, list or operation does not exist Look up the ID again with a read tool
permission_denied The key is read-only, or the plan does not include write access Create a read and write key, or upgrade the plan (action_url)
quota_exceeded No campaign creations left this month, or no write calls left today Wait for the reset shown by get_accounts
preview_required confirm: true was sent with a preview_id that has no matching preview of these arguments in the last 30 minutes Preview with the same arguments, get approval, then confirm with the new preview_id
conflict A resource with the same name but different content already exists Check the existing resource; do not rename and recreate
busy Another write for your account is running Wait retry_after_seconds, then retry
reauth_required Google authorization for ADM has expired Reconnect Google Ads in ADM (action_url)
google_ads_error Google Ads rejected the change (policy or validation) Read the Google reason in message and adjust
result_unknown The call timed out or was interrupted and the outcome is not known Call get_operation or get_campaign_setup first; if the change is missing, preview again and confirm with the new preview_id after approval
internal Server error Retry once; contact support with request_id if it repeats

HTTP 401 (unauthorized) is returned before any tool runs when the key is missing or invalid. See the troubleshooting table in section 4.

Every tool call returns within 45 seconds. Large reads can be narrowed with campaign_id.

8. Quotas

Quota Free Starter Professional Enterprise
Read tools Yes Yes Yes Yes
Write tools No Yes Yes Yes
Campaign creations per month 1 (web only) 10 30 100
Write calls per day (UTC) Not available 300 300 300
AI drafts per hour Not available 60 60 60
  • The monthly creation count is shared with campaigns created in the ADM web wizard and resets on your billing date. get_accounts shows the remaining count and the reset time.
  • A write call is any call with confirm: true, plus each image upload. Previews do not count.
  • AI drafts (draft_ad_groups) have their own hourly limit and do not count as write calls.
  • Write access needs both a read and write key and an active paid plan.

9. Why write tools should not be auto-approved

Several read tools return text written by other people: search terms are typed by anyone who sees your ads, and ad text, asset text and names may come from colleagues or agencies. A search term can contain words that look like an instruction to the AI tool. ADM labels this content as data and asks the AI tool to ignore instructions inside it, but no AI model is guaranteed to follow that rule every time.

The approval prompt for write tools is the step where you see exactly what will change. Keep it in place:

  • Approve only read tools automatically. In Claude Code, allow only mcp__adm__get_*; do not add mcp__adm__* or individual write tools to the allow list.
  • Do not run the AI tool with approval prompts turned off while the ADM server is connected.
  • Read each preview before approving, especially budgets and set_campaign_status calls.

10. Key rotation

  1. Create a new key on the API keys page.
  2. Replace the key: update ADM_API_KEY, or the key in the AI tool's MCP configuration, then restart the AI tool. Claude Code stores the header when the server is added, so run claude mcp remove adm and add the server again (section 4).
  3. Repeat step 2 on each computer that uses the old key.
  4. Watch the "Last used" time of the old key. When it no longer changes, revoke the old key.

Treat a key like a password. Do not commit it to a repository or write it into project files. A key pasted into a chat remains in the conversation history; rotate it if that history may be seen by others. If a key may have leaked, revoke it immediately.

11. Changes you make in Google Ads

The tools create and add; apart from pausing and enabling campaigns, they do not edit or remove existing items. Make these changes in Google Ads directly:

  • Edit or remove existing ads, keywords, negative keywords or assets
  • Change the budget or bidding of an existing campaign
  • Turn off auto-apply recommendations (Recommendations, then Auto-apply)
  • Set up conversion tracking
  • Add logo, business name, call and lead form assets
  • Set the ad schedule, device bid adjustments and audience segments
  • Create campaign types other than Search, such as Performance Max
  • Manage billing and complete advertiser verification

12. Changelog

2026-09-30

  • First release of the ADM MCP server, contract version 2026-09-30.
  • 9 read tools: get_accounts, get_campaigns, get_campaign_setup, get_ads, get_keywords, get_search_terms, get_negative_keywords, get_assets, get_operation.
  • 11 write tools: create_search_campaign, add_ad_groups, add_keywords, add_account_negative_keywords, create_shared_negative_list, attach_shared_negative_list, add_sitelinks, add_callouts, add_structured_snippets, add_prices, set_campaign_status.
  • ADM skill adm-google-ads for building campaigns from a plan (section 3).
  • Added in the same contract version (new tools and fields only, no breaking change): write tools add_ads and add_images, AI draft tools draft_ad_groups and get_ad_group_draft, the image upload endpoint POST /mcp/uploads, and image assets in get_assets and get_campaign_setup. The skill adds the AI draft and image workflows.
  • Documentation moved to /docs/google-ads-skill. The skill adds reports and questions, a default account saved per project, and setup for Codex, Cursor and Windsurf; the server instructions add the same default account rule. On first use in a project, the skill suggests prompts the user can copy.

Our site uses cookies. By continuing to use our site, you agree to the use of cookies. For more information about the use of cookies on our website, please see our Cookie Policy.