MCP Apple Ads
Provides tools for interacting with the Apple Ads Platform API, enabling management of ad accounts, campaigns, ad groups, keywords, ads, shared budgets, and performance reporting.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MCP Apple Ads show me my campaign spend for the last 30 days"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@mgcrea/mcp-apple-ads
Model Context Protocol server for the Apple Ads Platform API — ad accounts, campaigns, ad groups, keywords, ads and reporting.
Writes are off by default, everything is created paused, and optional ceilings cap what any tool may spend.
Features
34 tools across accounts, apps, campaigns, ad groups, keywords, ads, shared budgets and reporting — plus a raw-request escape hatch for the rest of the API.
Reporting that fits in a context window. Totals by default, zero-valued metrics dropped, metadata trimmed to the fields that identify a row, and per-period rows opt-in and capped. A 90-day × 200-keyword report shapes down by more than 10×.
Money safety in four layers — see Spending safety.
No SDK dependency. Two runtime dependencies (
@modelcontextprotocol/server,zod); the ES256 client secret is signed withnode:crypto.
Related MCP server: apple-ads-mcp
API version and the 2027 sunset
This server targets the Apple Ads Platform API 1.0 (https://api.ads.apple.com/v1/), which Apple shipped in August 2026.
It does not target the older Campaign Management API v5 (api.searchads.apple.com/api/v5), which Apple sunsets on 26 January 2027. There is no version switch: a second shaping path for an API that will be gone is not worth the code, and anyone still on v5 has to migrate regardless.
Security
Your credentials. The private key is read once at startup and never leaves the process. It is never logged, never returned by a tool, and apple_ads_auth_status reports only whether it loaded. Prefer APPLE_ADS_PRIVATE_KEY (inline PEM, from a secret store) over a path on disk. If you do use a file, chmod 600 it — the server warns when it is group-readable.
Unlike App Store Connect, nothing here is downloadable-once: you generate the key pair and register only the public half, so a lost key is regenerable.
Blast radius. This server can spend money. That is why:
writes are unregistered, not merely refused, unless
APPLE_ADS_ALLOW_WRITES=1;every write — including creates — requires
confirm: true;everything is created
PAUSEDunlessactivate: true;APPLE_ADS_MAX_DAILY_BUDGETandAPPLE_ADS_MAX_BIDare checked locally before any request goes out.
Configure
Getting the key
Apple Ads credentials are not App Store Connect credentials, and not Apple Business Manager credentials. All three consoles issue similar-looking EC keys and none of them interoperate.
mkdir -p ~/.config/apple-ads && chmod 700 ~/.config/apple-ads
cd ~/.config/apple-ads
openssl ecparam -genkey -name prime256v1 -noout -out apple-ads-private.pem
openssl ec -in apple-ads-private.pem -pubout -out apple-ads-public.pem
chmod 600 apple-ads-private.pemThen, at ads.apple.com:
Account Settings → User Management — invite a user with an API role (
API Account Managerfor writes,API Account Read Onlyfor reads). Inviting yourself is fine.Sign in as that API user.
Account Settings → API — paste the contents of
apple-ads-public.pem. Apple then printsclientId,teamIdandkeyIdabove the field.
The trap that costs everyone an afternoon: an Account Admin has no Public Key field. That role sees a "Manage API Access" table instead, and the API page looks like it has nothing to create. You must invite an API-role user and sign in as that user before the field appears. Apple's own help page describes step 1 and step 3 as though they happen to the same person, which is why this is easy to miss.
A related wrong turn: Apple Business Manager (
business.apple.com) has an "Add API Account" dialog that looks almost identical and hands you a downloadable-once private key. It is a different service. ItsclientIdstartsBUSINESSAPI.; an Apple Ads one startsSEARCHADS..
The API user's access level is the real permission boundary — APPLE_ADS_ALLOW_WRITES is a second gate on top, not a substitute for picking the right role.
Environment
Variable | Required | Notes |
| yes |
|
| yes |
|
| yes | UUID of the registered public key |
| one of | EC P-256 private key, inline PEM |
| one of | path to the |
| no | default ad account; required when the login reaches several |
| no | registers the mutating tools |
| no | ceiling in major units on any daily budget a tool may set |
| no | ceiling in major units on any bid |
| no | config file path (default |
| no | default 3 |
| no | stderr debug logging |
Environment wins over the config file per field, so a one-off APPLE_ADS_ALLOW_WRITES=0 overrides a file that says true.
Quick start
// .mcp.json
{
"mcpServers": {
"apple-ads": {
"command": "npx",
"args": ["-y", "@mgcrea/mcp-apple-ads"],
"env": {
"APPLE_ADS_CLIENT_ID": "SEARCHADS.…",
"APPLE_ADS_TEAM_ID": "SEARCHADS.…",
"APPLE_ADS_KEY_ID": "…",
"APPLE_ADS_PRIVATE_KEY_PATH": "/Users/you/.config/apple-ads/apple-ads-private.pem",
},
},
},
}Inspect the tools
npx @modelcontextprotocol/inspector npx -y @mgcrea/mcp-apple-adsTools
Tool | What it does | Writes? |
| Credentials, reachable ad accounts, ceilings, setup steps | |
| Ad accounts this key can reach — the bootstrap call | |
| Find an app's adamId | |
| One app's advertising details | |
| Campaign configuration | |
| Ad group configuration | |
| Targeting keywords with bids | |
| Excluded terms | |
| Apple's keyword suggestions | |
| Creative surface | |
| Shared budgets | |
| The performance numbers, at five levels | |
| Headroom per search term | |
| Search demand per term | |
| Any endpoint no tool wraps | gated |
| Campaign lifecycle | ✓ |
| Ad group lifecycle | ✓ |
| Keyword management, bulk | ✓ |
| Exclusions | ✓ |
| Ads | ✓ |
| Pause or resume any of the four | ✓ |
| Change a shared budget | ✓ |
Listing cost, measured: 26 KB / ~6.5k tokens with writes off, 49 KB / ~12k tokens with writes on.
Deliberately not wrapped
Reachable through apple_ads_request: Maps advertising (/business-brands/* and its five report variants), asset upload and creative authoring, recommendations apply/dismiss, ad-account creation, location targeting, change history.
Worked example
Which keywords lost me money last month, and pause them.
apple_ads_list_accounts→ the ad account idapple_ads_get_reportatlevel: "keyword", last 30 days, no granularity → totals per keywordread
localSpendagainsttotalInstallsapple_ads_set_entity_statuswithentity: "keyword",status: "PAUSED"on each loser
Pausing, not deleting: a paused keyword stops spending and keeps its history.
Spending safety
Layer | What it does |
Write gate | Without |
| Required on every write, creates included — a campaign created ENABLED spends immediately |
Created PAUSED |
|
Ceilings |
|
The ceilings do not apply to apple_ads_request, which bypasses them by construction. Its description says so.
Apple Ads vs App Store Connect
Two APIs, two consoles, two keys — which is why they are two servers rather than one. They share exactly one thing: adamId is the same integer as an app's id in App Store Connect, and as the digits in its App Store URL.
If you run @mgcrea/mcp-appstore-connect too, app_store_connect_list_apps is the cheaper way to get an adamId than apple_ads_search_apps.
Traps worth knowing
A 403 is usually the ad-account context, not your credentials. If the token minted but calls fail, the
X-Ap-Contextheader is missing or names an account this login has no role on.apple_ads_auth_statuslists what is reachable./aclsand/meare the only calls that take no ad-account context — necessarily, since they are how you discover which accounts exist.Money amounts are strings upstream.
Money.amountis"50.00", not50. This server converts both ways; if you useapple_ads_requestyou are on your own.Keyword deletion is permanent and takes the reporting history with it. Pausing is nearly always what was meant.
A multi-account login must name an account. Tools refuse rather than guess, because guessing spends money in someone else's account.
Report granularity has windows. HOURLY needs a start within 7 days (and is unavailable for search-term and ad reports), DAILY within 90, WEEKLY within 365 with an end 14+ days old, MONTHLY with an end 90+ days old. These are checked locally, so you get the rule rather than
INVALID_PARAMETER.Search-term reports reject
timeZone: "UTC". UseORTZ, which is the default here.
Troubleshooting
Symptom | Cause |
Tool missing from the list | Writes disabled, or no credentials — run |
| Client/team/key id mismatch, or the private key is not the pair of the registered public key |
| Ad-account context — see Traps |
"This login can reach N ad accounts" | Pass |
Budget refused with no request sent | A ceiling is set — this is working as intended |
Develop
pnpm install
pnpm build
pnpm test
pnpm typecheck && pnpm lintTests are fully offline: fetch is mocked and the MCP client is in-memory. No credentials are needed or read.
License
MIT
Available Tools
19 toolsapple_ads_auth_statusApple Ads: Auth StatusARead-only
Report whether the server has working credentials, which ad accounts they reach, whether writes and spending ceilings are set, and — when it is not configured — exactly what to set to fix that. Call this first when a tool you expected is missing, or when a call fails with 403.
Inputs: no named parameters.
apple_ads_get_ad_groupApple Ads: Get Ad GroupARead-only
Get one ad group in full, including its bid, pricing model and targeting.
Inputs: adGroupId (required), savePath, adAccountId.
apple_ads_get_appApple Ads: Get AppARead-only
Get an app's advertising details by adamId — what Apple knows about it as an ad destination, including the locales its product page supports.
Inputs: adamId (required), savePath, adAccountId.
apple_ads_get_campaignApple Ads: Get CampaignARead-only
Get one campaign in full, including its targeting, budget and bid strategy. Use this before updating one, so the change is made against what is actually configured.
Inputs: campaignId (required), savePath, adAccountId.
apple_ads_get_reportApple Ads: Get ReportARead-only
The performance numbers: impressions, taps, installs, spend, and the rates between them, for any level of the account. This is where spend and conversion live — the list tools return configuration only. Returns TOTALS per row by default, which answers most questions; per-period rows are opt-in and much larger. For a wide report, pass savePath and read the file rather than raising maxRows.
Inputs: level (required), endDate (required), startDate (required), limit, +10 more.
apple_ads_get_search_term_popularityApple Ads: Get Search Term PopularityARead-only
The most-searched terms on the App Store, ranked, per country and genre — Apple's own demand data (terms with 500+ searches). The discovery side of keyword research: run it for your app's genre and market to see what people actually type, then pair with apple_ads_suggest_keywords. MONTHLY refreshes on the 5th; WEEKLY_SUN_SAT on Mondays.
Inputs: endDate (required), startDate (required), genre, limit, +6 more.
apple_ads_list_accountsApple Ads: List Ad AccountsARead-only
List the ad accounts this API key can reach, with the roles it holds on each. This is the bootstrap call: every other tool needs an ad account, and the id here is what to pass as adAccountId or set as APPLE_ADS_AD_ACCOUNT_ID. Also returns the API identity itself. Costs two requests.
Inputs: savePath.
apple_ads_list_ad_groupsApple Ads: List Ad GroupsARead-only
List ad groups, optionally filtered to one campaign. An ad group holds the keywords, the default bid and the audience refinement; the budget lives on its campaign. Spend and performance come from apple_ads_get_report, not from here.
Inputs: limit, offset, savePath, campaignId, +1 more.
apple_ads_list_adsApple Ads: List AdsARead-only
List the ads in an ad account, optionally filtered to one ad group. An ad pairs a creative with an ad group; what it cost and returned comes from apple_ads_get_report at level "ad".
Inputs: limit, offset, savePath, adGroupId, +1 more.
apple_ads_list_budgetsApple Ads: List Shared BudgetsARead-only
List shared budgets — spending pools several campaigns draw from, as opposed to the per-campaign daily budget set by apple_ads_create_campaign. Note Apple deprecated lifetime budgets in June 2026; shared budgets are what replaced them.
Inputs: limit, offset, savePath, adAccountId.
apple_ads_list_campaignsApple Ads: List CampaignsARead-only
List campaigns in an ad account, with their status, daily budget and the app each promotes. Returns the identifying fields, not the full targeting projection — use apple_ads_get_campaign for one campaign in full. Spend and performance are NOT here: those come from apple_ads_get_report.
Inputs: limit, offset, savePath, adAccountId.
apple_ads_list_creativesApple Ads: List CreativesARead-only
List the creatives available to use in ads. A creative is what the ad actually shows — usually a custom product page or the default App Store listing. Authoring creatives and uploading assets is not wrapped here; reach those through apple_ads_request.
Inputs: limit, offset, savePath, adAccountId.
apple_ads_list_keywordsApple Ads: List KeywordsARead-only
List targeting keywords, optionally filtered to one ad group, with their match type, bid and status. What each keyword actually cost and returned comes from apple_ads_get_report at level "keyword", not from here.
Inputs: limit, offset, savePath, adGroupId, +1 more.
apple_ads_list_negative_keywordsApple Ads: List Negative KeywordsARead-only
List negative keywords — terms that must NOT trigger the ad. These are how wasted spend on irrelevant searches is stopped without narrowing the positive targeting.
Inputs: limit, offset, savePath, adGroupId, +1 more.
apple_ads_list_product_pagesApple Ads: List Product PagesARead-only
List the custom product pages available as ad destinations. These are the SAME pages configured in App Store Connect — app_store_connect_list_versions and the screenshot tools manage their content; this only lists them for targeting.
Inputs: limit, adamId, offset, savePath, +1 more.
apple_ads_requestApple Ads: Raw API RequestARead-only
Call any Apple Ads Platform API endpoint that no dedicated tool wraps — Maps advertising (/business-brands/*), asset upload, recommendations, location targeting, change history. The ad-account context header is injected for you. You get no argument validation, no response shaping and no safety checks: prefer a dedicated tool where one exists. Writes are DISABLED: only GET is permitted. Set APPLE_ADS_ALLOW_WRITES=1 to allow mutations.
Inputs: path (required), body, method, savePath, +1 more.
apple_ads_search_appsApple Ads: Search AppsARead-only
Search the App Store for an app and get its adamId, which every campaign needs. The adamId is the SAME number as the app's id in App Store Connect — if the app_store_connect MCP server is loaded, app_store_connect_list_apps gives it directly and is both cheaper and unambiguous. Use this when it is not, or to find a competitor's app.
Inputs: query (required), limit, savePath, adAccountId.
apple_ads_suggest_keywordsApple Ads: Suggest KeywordsARead-only
Ask Apple for keyword suggestions for an app or a seed term. The research tool: use it before creating keywords, and pair it with apple_ads_get_report at level "search_term" to see which real searches already reach you.
Inputs: adamId (required), limit, savePath, adAccountId.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 19 tools
The list_ vs get_ pairs for campaigns and ad groups are cleanly separated, and the descriptions explicitly fence off overlapping concepts (configuration in list tools vs performance in get_report, suggest_keywords vs get_search_term_popularity, list_creatives vs list_product_pages vs list_ads). The only residual friction is between apple_ads_get_app and apple_ads_search_apps, and between the many 'list' lookups layered under the same account, but each description preempts the confusion with explicit 'use X instead' guidance.
A consistent apple_ads_ namespace with a predictable verb_noun pattern (list_campaigns, get_campaign, get_report, suggest_keywords) holds across nearly the whole set. Minor deviations are apple_ads_auth_status (noun phrase, no verb) and apple_ads_request (bare verb escape hatch), plus get_search_term_popularity's longer compound noun.
Nineteen tools is on the heavier side but each maps to a distinct Apple Ads resource (accounts, campaigns, ad groups, keywords, negatives, ads, creatives, product pages, budgets, reports, impression share, research, auth). Nothing looks like filler, though the count sits just inside the 'feels heavy' band.
The read surface is genuinely thorough end-to-end, but the set is essentially read-only: there are no create/update/delete operations for campaigns, ad groups, keywords, or negatives, and mutations are only reachable through the unvalidated apple_ads_request with writes disabled by default. Worse, apple_ads_list_budgets references 'apple_ads_create_campaign', a tool that is not in this set, which is a dangling lifecycle gap an agent will hit immediately.
Maintenance
Related MCP Connectors
Run Apple Search Ads from Claude: performance, recommendations, keywords, budgets, launches.
Manage Apple Ads campaigns and reporting in chat.
Analyze and manage Apple Ads from your AI assistant with RevenueCat insights and safety controls.
Manage Google, Meta, Amazon, TikTok, LinkedIn & ChatGPT ads. 430 tools for campaigns & analytics.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceAn MCP server for the Apple Ads Platform API that exposes 99 operations across all resources through four token-efficient tools. It enables AI agents to read and write campaigns, ad groups, keywords, creatives, budgets, reports, and more.78 npm2MIT
- AlicenseNot gradedqualityCmaintenanceEnables searching an offline catalog of Apple Ads Platform API documentation and issuing authenticated read and write calls to manage App Store and Apple Maps campaigns, reports, keywords, budgets, and recommendations. Read and write are split into separate tools, with read-only mode, pagination merging, and large-report file output.78 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables autonomous AI agents to safely inspect, optimize, and manage live search campaigns through 38 purpose-built tools covering bids, budgets, keywords, RSAs, assets, and negative keyword harvesting. Bundles two-layer Pydantic execution shields with bounded bid ceilings and fixed stepping, a preflight gatekeeper, and W3C distributed tracing that separates upstream Google Ads API latency from local execution overhead.MIT
- AlicenseBqualityBmaintenanceIt provides 31 read tools and 22 opt-in, preview-first write tools for inspecting campaigns, reporting, and targeting, and for preparing confirmed changes within explicitly allowlisted ad accounts.31316 npmApache 2.0