Apple Ads MCP
MCP server exposing the full Apple Ads (Search Ads) Campaign Management API v5 — 74 typed tools
README
apple-search-ads-mcp
A Model Context Protocol (MCP) server that wraps the full Apple Search Ads (now Apple Ads) Campaign Management API v5. 74 typed tools, 1:1 mapping to every documented v5 endpoint — campaigns, ad groups, ads, creatives, custom product pages, keywords, negative keywords, reports, impression-share reports, budget orders, ACLs, geo/app search, app metadata, rejection-reason audits — plus a raw passthrough for any future endpoints.
API lifecycle: Apple Ads (Search Ads) v5 is the current production API. v5 sunsets January 26, 2027 in favour of the new "Apple Ads Platform API" arriving Summer 2026. This server targets v5.0 → v5.5.
Quick install
git clone https://github.com/AppVisionOS/apple-search-ads-mcp.git
cd apple-search-ads-mcp
npm install
npm run build
Then register with Claude Code in one line:
claude mcp add apple-search-ads --scope user \
-e ASA_CLIENT_ID=SEARCHADS.xxxx \
-e ASA_TEAM_ID=SEARCHADS.xxxx \
-e ASA_KEY_ID=xxxx \
-e ASA_PRIVATE_KEY_PATH=/absolute/path/to/asa-private.p8 \
-e ASA_ORG_ID=1234567 \
-- node $(pwd)/dist/index.js
Setup
1. Get API credentials
The Apple Ads UI splits credentials across two screens. The API tab inside Account Settings only manages access for third-party service providers; for your own programmatic access, the flow goes through User Management first.
a) Invite an API user
In app-ads.apple.com → Account Settings → User Management → Invite User:
- Email: any address you control (can be your own; Apple requires a separate Apple ID for the API user)
- Role: pick one with API permissions (e.g. API Account Manager)
- Send the invite, then accept it from the invited inbox
b) Generate the key pair locally
While the invite is being processed, generate an ES256 key pair on your machine. Make sure it's PKCS#8 — Apple's .p8 examples and the older openssl ecparam output are not PKCS#8 and jose can't load them.
# CORRECT — produces PKCS#8 (-----BEGIN PRIVATE KEY-----)
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out asa-private.p8
openssl ec -in asa-private.p8 -pubout -out asa-public.pem
# If you already produced traditional EC (-----BEGIN EC PRIVATE KEY-----), convert it:
# openssl pkcs8 -topk8 -nocrypt -in asa-private.p8 -out asa-private-pkcs8.p8
Keep asa-private.p8 somewhere safe (e.g. ~/.apple-search-ads/, chmod 600). You'll only paste the public half into Apple.
c) Generate the API client
Sign out and sign back in as the invited API user (not the admin account). Go to Account Settings → API. You'll see a Client Credentials screen with a Public Key textarea — this only appears for users who hold the API role.
- Paste the contents of
asa-public.pem(with the-----BEGIN PUBLIC KEY-----/-----END PUBLIC KEY-----markers). - Click Generate API Client.
- Copy the three values Apple shows you: Client ID, Team ID, Key ID — these don't reappear later.
2. Install
npm install
npm run build
3. Configure
Copy .env.example to .env and fill it in, or pass env vars through your MCP client.
ASA_CLIENT_ID=SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ASA_TEAM_ID=SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ASA_KEY_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ASA_PRIVATE_KEY_PATH=/absolute/path/to/private-key.p8
ASA_ORG_ID=1234567 # optional default; can be overridden per call
ASA_PRIVATE_KEY (PEM contents inline, with \n escapes if injected via JSON) is supported as an alternative to ASA_PRIVATE_KEY_PATH.
4. Wire into your MCP client
{
"mcpServers": {
"apple-search-ads": {
"command": "node",
"args": ["/absolute/path/to/apple-search-ads-mcp/dist/index.js"],
"env": {
"ASA_CLIENT_ID": "SEARCHADS.xxxx...",
"ASA_TEAM_ID": "SEARCHADS.xxxx...",
"ASA_KEY_ID": "xxxx...",
"ASA_PRIVATE_KEY_PATH": "/absolute/path/to/private-key.p8",
"ASA_ORG_ID": "1234567"
}
}
}
}
Authentication
The server handles OAuth 2.0 client-credentials flow with an ES256 JWT client assertion:
- Sign a JWT with your
.p8private key (header:kid=Key ID,alg=ES256; payload:iss=Team ID,sub=Client ID,aud=https://appleid.apple.com). - POST it to
https://appleid.apple.com/auth/oauth2/tokenwithgrant_type=client_credentialsandscope=searchadsorg. - Use the returned 1-hour access token with
Authorization: Bearer …andX-AP-Context: orgId=…on every API call.
The token is cached in memory until ~30 s before expiry, so you sign one assertion and exchange one token per hour. On 401 the server force-refreshes and retries once. On 429/5xx it backs off (honouring Retry-After) up to 3 times.
Tool inventory (74 tools)
Account & access (2)
org_acls, me_user — call without an org context to discover what your token can do.
Discovery (3)
search_apps, search_geo, geo_lookup
App metadata (6)
apps_get, apps_locale_details, apps_eligibilities_find, apps_assets_find, creative_app_preview_devices, countries_or_regions_list
Custom Product Pages (3)
cpp_list, cpp_get, cpp_locale_details
Campaigns (6)
campaigns_create, campaigns_get, campaigns_list, campaigns_find, campaigns_update, campaigns_delete
Ad groups (7)
adgroups_create, adgroups_get, adgroups_list, adgroups_find_in_campaign, adgroups_find_org_wide, adgroups_update, adgroups_delete
Creatives (4)
creatives_create, creatives_list, creatives_get, creatives_find — creatives wrap a Custom Product Page, Default Product Page, or Creative Set reference. Ads bind to creatives via creativeId.
Ads (7)
ads_create, ads_get, ads_list, ads_find_in_campaign, ads_find_org_wide, ads_update, ads_delete
Targeting keywords (7)
targeting_keywords_create, targeting_keywords_get, targeting_keywords_list, targeting_keywords_find, targeting_keywords_update, targeting_keywords_delete (bulk), targeting_keywords_delete_single
Negative keywords — ad-group scope (6)
adgroup_negative_keywords_create, adgroup_negative_keywords_get, adgroup_negative_keywords_list, adgroup_negative_keywords_find, adgroup_negative_keywords_update, adgroup_negative_keywords_delete
Negative keywords — campaign scope (6)
campaign_negative_keywords_create, campaign_negative_keywords_get, campaign_negative_keywords_list, campaign_negative_keywords_find, campaign_negative_keywords_update, campaign_negative_keywords_delete
Reports (7)
| Tool | Endpoint |
|---|---|
reports_campaigns |
POST /reports/campaigns |
reports_adgroups |
POST /reports/campaigns/{id}/adgroups |
reports_keywords_in_campaign |
POST /reports/campaigns/{id}/keywords |
reports_keywords_in_adgroup |
POST /reports/campaigns/{id}/adgroups/{id}/keywords |
reports_search_terms_in_campaign |
POST /reports/campaigns/{id}/searchterms |
reports_search_terms_in_adgroup |
POST /reports/campaigns/{id}/adgroups/{id}/searchterms |
reports_ads_in_campaign |
POST /reports/campaigns/{id}/ads |
All accept startTime, endTime, optional granularity (HOURLY/DAILY/WEEKLY/MONTHLY), optional groupBy (adminArea / ageRange / countryCode / countryOrRegion / deviceClass / gender / locality), selector, returnRowTotals, returnGrandTotals, returnRecordsWithNoMetrics, timeZone (UTC | ORTZ).
Impression Share Reports (3) — async
custom_reports_create → returns reportId. Poll with custom_reports_get until state=COMPLETED. List with custom_reports_list.
Budget Orders (4) — LOC accounts only
budget_orders_create, budget_orders_get, budget_orders_list, budget_orders_update. v5 has no delete for budget orders.
Rejection-reason audit (2)
product_page_reasons_find, product_page_reasons_get — read-only inspection of why Apple's reviewers rejected creatives.
Escape hatch (1)
apple_search_ads_request — call any path with any method. Auth and org context are still handled for you.
Selectors
*_find tools accept Apple's selector grammar:
{
"conditions": [
{ "field": "status", "operator": "EQUALS", "values": ["ENABLED"] },
{ "field": "countriesOrRegions", "operator": "CONTAINS_ANY", "values": ["US", "GB"] }
],
"fields": ["id", "name", "status"],
"orderBy": [{ "field": "name", "sortOrder": "ASCENDING" }],
"pagination": { "limit": 100, "offset": 0 }
}
Operators: EQUALS, NOT_EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH, GREATER_THAN, LESS_THAN, IN, NOT_IN, CONTAINS_ALL, CONTAINS_ANY, BETWEEN. values is always an array.
End-to-end example
A workflow you can drive entirely through Claude:
org_acls→ pick theorgId.search_appsfor your app → grab theadamId.campaigns_createwith that adamId, daily budget, US targeting,adChannelType=SEARCH,supplySources=["APPSTORE_SEARCH_RESULTS"],billingEvent=TAPS.adgroups_createinside the campaign withdefaultBidAmount={amount:"1.00",currency:"USD"}andpricingModel=CPC.targeting_keywords_createwith a batch of{text, matchType, bidAmount}rows.cpp_list→ pick a productPageId →creatives_createwithtype=CUSTOM_PRODUCT_PAGEto mint a creativeId →ads_createto bind it to the ad group.- Wait a few days.
reports_campaignsfor top-line, thenreports_search_terms_in_campaignto harvest new keywords / negatives. custom_reports_createfor impression-share / share-of-voice on your top searches.
Known surface notes (v5 quirks)
- No legacy creative-set CRUD. Apple removed it in v5; create a
creativesrow instead and reference it fromads.creativeId. - No app categories endpoint. Use
apps_getand readprimaryGenre/secondaryGenre. - No postal-code geo targeting. Geo entities in v5 are Country / AdminArea / Locality only.
- No org-wide find for keywords or ad-group-scoped keyword find. Apple scopes targeting-keyword find at the campaign level (
/campaigns/{id}/adgroups/targetingkeywords/find) and rolls up across ad groups; filter byadGroupIdin the selector to narrow. - No DELETE on budget orders. Update them, don't delete them.
- Audiences, forecasting, conversion events are NOT in v5 — those live in separate Apple APIs (AdServices Attribution etc.).
Local development
npm run dev # tsc --watch
npm run typecheck # one-shot type check
npm run build # compile to dist/
Use apple_search_ads_request to debug any endpoint directly — it returns the raw envelope so you can see the exact response shape Apple returned.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。