Apple Ads MCP Server
Apple Ads MCP 服务器
Apple Ads 平台 API,以 MCP 服务器形式提供。 覆盖所有已记录资源中的 99 个操作,通过 4 个高效 token 的工具 暴露给 Claude Code、Claude Desktop、Cursor 以及任何其他 Model Context Protocol 客户端。
apple-ads-mcp 是一个开源的 Apple Ads 平台 API 的 Model Context Protocol (MCP) 服务器 —— 即 Apple Search Ads 和 Apple Maps 品牌广告背后的 API。安装一次,你的 AI 代理即可读写广告系列、广告组、关键词、素材、预算、报告以及 Apple 为你的广告账户暴露的所有其他内容。
目录由 openapi/apple-ads.openapi.json 生成,该文件根据 Apple 官方 Node 客户端(apple/apple-ads-platform-api-node,规范标签 109)重建,并逐端点与 Apple 发布的文档交叉核对。
本仓库中不包含任何凭据。 你在运行时通过环境变量提供自己的 Apple Ads API 凭据。除 Apple 之外,不会捆绑、记录或传输任何内容。
目录
为什么是 4 个工具而不是 99 个
将全部 99 个 Apple Ads 操作注册为独立的 MCP 工具,意味着在模型做任何实际工作之前,就要把 99 个完整的 JSON Schema 倾倒进模型的上下文窗口。而其中大部分预算都浪费在了代理在单次对话中永远不会调用的操作上。
本服务器将完整目录 内部化,只暴露一个小而稳定的表面:
ads_search → find operations (names, methods, paths, tags — no schemas)
ads_schema → describe one operation (full input JSON Schema + call hint)
ads_call → execute one operation (real request, or _dryRun)
ads_tags → list resource tags (with operation counts, to narrow search)代理只为它即将使用的 schema 付费,不为其他任何东西付费。一次典型的 搜索 → schema → 调用 流程只需花费几千个 token,而不是预先加载所有 schema 所需的数万个 token —— 而且不会损失任何覆盖范围,因为完整的 99 个操作目录仍然可以通过 ads_search 和 ads_call 触达。
Related MCP server: mlg-meta-mcp
你能用它做什么
覆盖 28 个资源标签下的全部 99 个操作。随时运行 ads_tags 即可获取实时的精确列表及数量。
广告系列 — 创建、读取、更新、删除、按筛选/排序/分页查询(
Campaigns)广告组 — 创建、读取、更新、删除、查询(
AdGroups)关键词和否定关键词 — 创建、读取、更新、删除、查询,以及两者的批量创建/更新(
Keywords、NegativeKeywords)广告 — 创建、读取、更新、删除、查询(
Ads)素材 — 创建、读取、更新、删除、查询(
Creatives)资源 — 上传、读取、删除、查询 —— 用于素材和 Apple Maps 品牌广告的图片与视频(
Assets)产品页面 — 读取自定义产品页面及其本地化详情、查询(
ProductPages)共享预算 — 创建、读取、更新、删除、查询 —— 跨多个广告系列共享的预算(
SharedBudgets)地理位置 — 搜索和查询可投放位置、管理位置组(
Locations、LocationGroups、Search)Apple Maps 品牌广告 — 面向 Maps 广告系列的企业品牌和企业类别(
Brands、Categories)报告 — 按广告系列、广告组、广告、关键词和搜索词维度的应用及企业品牌效果报告(
Reports)洞察 — 展示份额和搜索词热度(
Insights)建议 — 每日预算和目标 CPA 建议:查询、应用、放弃(
Recommendations)关键词建议 — 用于扩展广告系列的关键词、词组、类别和目标 CPA 建议(
Suggestions)变更历史 — 广告账户的审计摘要和变更详情(
ChangeHistory)账户与访问管理 — 广告账户、组织信息、用户 ACL、已认证调用者的身份、广告主资源(
AdAccounts、Orgs、Acls、Me、AdvertiserResources)应用搜索、资格与元数据 — 搜索 App Store 目录、检查应用广告资格、查询应用本地化详情、支持的语言以及应用和品牌的拒绝原因(
Apps、Search、Eligibilities、Metadata、RejectionReasons)
环境要求
Bun 1.1 或更高版本
一个 具有 API 访问权限的 Apple Ads 账户 —— 来自 Apple Ads UI 的客户端 ID、团队 ID、密钥 ID 和私钥
一个支持 stdio 的 MCP 客户端:Claude Code、Claude Desktop、Cursor 或你自己的代理
获取你的凭据
登录 ads.apple.com。
在本地生成一个 EC 私钥(Apple 永远不会看到私钥本身):
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out private-key.pem openssl ec -in private-key.pem -pubout -out public-key.pem前往 账户设置 → API,将
public-key.pem的内容(包括BEGIN/END行)粘贴到公钥字段中,然后保存。保存后,页面会显示你的凭据块:一个 客户端 ID、一个 团队 ID 和一个 密钥 ID。将这三项全部复制。
将
private-key.pem放在仓库之外,例如~/.apple-ads/private-key.pem,并执行chmod 600。
后续工作由服务器完成:它签署 ES256 客户端密钥 JWT,将其兑换为访问令牌,并将令牌缓存在内存中。获取凭据后,如何找到你的广告账户 ID,请参阅 广告账户范围。
安装与配置
git clone https://github.com/imfaisii/apple-ads-mcp.git
cd apple-ads-mcp
bun install
bun run smoke # offline catalog check + server boot — no credentials needed将 .env.example 复制为 .env 并填入你的值,或者直接在 MCP 客户端的配置中传入。任何启动 stdio MCP 服务器的客户端都以相同方式工作 —— 本仓库中的 mcp.example.json 是一份可直接复制的模板:
{
"mcpServers": {
"apple-ads": {
"command": "bun",
"args": [
"run",
"/ABS/PATH/to/apple-ads-mcp/src/index.ts"
],
"env": {
"APPLE_ADS_CLIENT_ID": "SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"APPLE_ADS_TEAM_ID": "SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"APPLE_ADS_KEY_ID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"APPLE_ADS_PRIVATE_KEY_PATH": "/ABS/PATH/to/private-key.pem",
"APPLE_ADS_AD_ACCOUNT_ID": "1234567"
}
}
}
}Claude Code —— 全局注册:
claude mcp add apple-ads \
-s user \
-t stdio \
-e APPLE_ADS_CLIENT_ID=SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
-e APPLE_ADS_TEAM_ID=SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
-e APPLE_ADS_KEY_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
-e APPLE_ADS_PRIVATE_KEY_PATH=$HOME/.apple-ads/private-key.pem \
-e APPLE_ADS_AD_ACCOUNT_ID=1234567 \
-- bun run /ABS/PATH/to/apple-ads-mcp/src/index.ts
claude mcp get apple-ads启动一个新会话。工具会以 mcp__apple-ads__ads_search、…ads_schema、…ads_call 和 …ads_tags 的形式出现。
Claude Desktop —— 将上面所示的同一个 mcpServers 块添加到 claude_desktop_config.json:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
编辑后重启 Claude Desktop。
Cursor —— ~/.cursor/mcp.json(全局)或 .cursor/mcp.json(按项目),使用相同的 mcpServers 块。
你自己的代理 —— 运行 bun run src/index.ts 并通过 stdio 进行 MCP 通信。在原始终端中它看起来像是卡住了;那其实是一个正在等待客户端的 stdio 服务器,这是正常现象。
四个工具
ads_search —— 找到正确的操作
对 operationId、名称、路径、标签、方法和描述进行自由文本搜索。返回排序后的命中结果,不包含 JSON Schema。
参数 | 类型 | 必填 | 描述 |
| string | 可选* | 自由文本,例如 |
| string | 可选* | 精确的资源标签筛选,例如 |
| string | 可选 |
|
| integer | 可选 | 最大命中数,默认 |
* query、tag 或 method 至少提供其一。
{ "query": "create campaign", "method": "POST", "limit": 5 }ads_schema —— 描述一个操作
返回操作的完整输入 JSON Schema、其路径/查询/请求头参数列表,以及一个展示如何调用它的 callHint。
参数 | 类型 | 必填 | 描述 |
| string | 是 | OperationId 或生成的工具名称,例如 |
{ "operation": "campaigns_create" }ads_call —— 执行操作
参数 | 类型 | 必填 | 描述 |
| string | 是 | OperationId 或生成的工具名称 |
| object | 可选 | 路径参数、查询参数、 |
| boolean | 可选 | 解析方法/路径/查询/请求体,不调用 Apple |
路径和查询字段、adAccountId 和 _dryRun 都可以嵌套在 args 内,也可以作为 operation 旁边的顶层键。
// query campaigns
{
"operation": "campaigns_query",
"args": {
"body": {
"filters": [{ "field": "status", "operator": "EQUALS", "value": "ENABLED" }],
"pagination": { "pageSize": 10, "fetchTotalCount": true }
}
}
}
// create a campaign
{
"operation": "campaigns_create",
"args": {
"body": {
"name": "Away Finder — Brand",
"billingEvent": "TAPS",
"startTime": "2026-09-01T00:00:00Z",
"promotedObjectType": "APPSTORE_APP",
"promotedObjectId": "1234567890",
"status": "ENABLED",
"dailyBudget": { "value": { "amount": "50.00", "currency": "USD" } }
}
}
}ads_tags —— 在 API 中定位方向
参数 | 类型 | 必填 | 描述 |
| integer | 可选 | 最大标签数,默认全部 28 个,按操作数量排序 |
{}一个完整的示例
从零上下文到真实调用的完整流程:发现广告账户、找到操作、读取其 schema,然后执行。
1. ads_call { operation: "acls_list" }
→ { result: { acls: [ { adAccount: { id: 123456789, name: "Away Finder" }, roles: ["Admin"] } ] } }
Use adAccount.id as the ad account for every scoped call below.
2. ads_search { query: "list campaigns" }
→ hits: [ { name: "campaigns_query", operationId: "campaignsQueryPost", method: "POST", path: "/campaigns/query", tags: ["Campaigns"] }, ... ]
3. ads_schema { operation: "campaigns_query" }
→ full inputSchema (adAccountId, body.filters, body.sorting, body.pagination) + callHint
4. ads_call {
operation: "campaigns_query",
args: {
adAccountId: "123456789",
body: {
filters: [{ field: "status", operator: "EQUALS", value: "ENABLED" }],
sorting: [{ field: "name", order: "ASC" }],
pagination: { pageSize: 10, fetchTotalCount: true }
}
}
}
→ { status: 200, body: { result: [ { id: 542370549, name: "Away Finder — Brand", status: "ENABLED", ... } ], pagination: { offset: 0, pageSize: 10, totalCount: 1 } } }如果要创建而不是列出某个对象,请先以试运行模式运行同样的结构(参见 试运行),对照 ads_schema 检查解析后的请求,然后再真实调用。
广告账户范围
大多数 Apple Ads 调用都限定在单个广告账户内。服务器会以 X-Ap-Context 请求头发送此信息:
X-Ap-Context: adAccountId=123456789;你不需要自己构建这个请求头。两种方式任选其一:
在环境中设置一次
APPLE_ADS_AD_ACCOUNT_ID,这样每个限定范围的调用都会默认使用它,或者在
args内(或作为operation旁边的顶层字段)按调用传入adAccountId,该值会覆盖该次调用的默认值。
少数操作不需要广告账户,仅凭访问令牌即可工作:me_list(GET /me)和 acls_list(GET /acls)。先调用 acls_list —— 它会返回你的凭据可以访问的每个广告账户,以及你在每个账户上的角色,这样你就知道在将任何其他调用限定到某个账户之前,哪些 adAccountId 值是有效的。
查询、分页与排序
大多数列表式端点的结构都相同:POST /<resource>/query,选择器主体包含 filters、sorting 和 pagination。无论查询的是广告系列、广告组、关键词、创意素材还是报表,都是同样的模式。
// POST /campaigns/query
{
"filters": [
{ "field": "status", "operator": "EQUALS", "value": "ENABLED" }
],
"sorting": [
{ "field": "name", "order": "ASC" }
],
"pagination": {
"offset": 0,
"pageSize": 10,
"fetchTotalCount": true
}
}筛选器(field、operator、value,可选 ignoreCase)支持广泛的运算符集:EQUALS、NOT_EQUALS、IN、NOT_IN、CONTAINS_ANY、CONTAINS_ALL、NOT_CONTAINS_ANY、NOT_CONTAINS_ALL、STARTS_WITH、ENDS_WITH、LIKE、NOT_LIKE、BETWEEN、GREATER_THAN、GREATER_THAN_OR_EQUAL_TO、LESS_THAN、LESS_THAN_OR_EQUAL_TO、IS_NULL、IS_NOT_NULL。并非每个实体上的每个字段都支持每个运算符——请查看所调用操作的 ads_schema 中的字段列表。
排序接受 field 和 order(ASC 或 DESC)。省略时,结果按 id 升序排列。
分页接受 offset 和 pageSize,fetchTotalCount(默认 false)用于同时返回匹配总数。报表端点将 pageSize 上限设为 5000,省略时默认为 100;其他 /query 端点未记录固定上限——从一个适中的 pageSize 开始,用 offset 逐页翻取。
关键词和否定关键词还有批量端点(keywords_bulkCreate、keywords_bulkUpdate、negativeKeywords_bulkCreate、negativeKeywords_bulkUpdate),它们接受一个 items 数组,每个条目带有客户端提供的 correlationId 和 data 载荷,外加一个 allowPartialSuccess 标志。
报表
Reports 标签涵盖 10 个操作:按广告系列、广告组、广告、关键词和搜索词划分的应用级和品牌级效果报表(reports_appsCampaignsQuery、reports_appsAdgroupsQuery、reports_appsAdsQuery、reports_appsKeywordsQuery、reports_appsSearchtermsQuery,以及对应的 reports_businessBrandsCampaignsQuery / reports_businessBrandsAdgroupsQuery / reports_businessBrandsAdsQuery / reports_businessBrandsKeywordsQuery / reports_businessBrandsSearchtermsQuery,用于 Apple Maps 品牌广告)。
每个报表操作都是 POST /reports/.../query 调用,接受上述相同的 filters / sorting / pagination 选择器主体,并限定在某个广告账户范围内:
{
"operation": "reports_appsCampaignsQuery",
"args": {
"adAccountId": "123456789",
"body": {
"pagination": { "pageSize": 100 }
}
}
}在调用之前,先运行 ads_schema { operation: "reports_appsCampaignsQuery" } 查看该报表可筛选/可分组的精确字段——不同报表类型之间有所不同。
上传创意素材
assets_upload 是唯一发送 multipart/form-data 而非 JSON 的操作。MCP 客户端只能发送 JSON,因此文件部分被描述为一个对象,服务器会将其转换为真正的 multipart 上传:
{
"operation": "assets_upload",
"args": {
"adAccountId": "123456789",
"body": {
"file": { "path": "/Users/you/creative/hero.png", "contentType": "image/png" },
"promotedObjectId": "987654",
"promotedObjectType": "BUSINESS_BRAND"
}
}
}使用 { "path": "/abs/path" } 从运行服务器的机器上读取文件,或者当字节数据已在手边时使用 { "base64": "...", "filename": "hero.png", "contentType": "image/png" }。Apple 在此接受 PNG、JPG 和 HEIC 格式。
错误与速率限制
Apple 的响应体会原样透传。在
4xx/5xx时,代理会看到 Apple 自己的error.code、error.message和error.details,因此它可以准确读取被拒绝的内容并自行纠正。在
401时,此服务器会刷新访问令牌一次并自动重试——你不应看到过期的令牌错误浮出到代理层。在
429时,响应包含一个hint以及 Apple 发送的速率限制响应头(RateLimit-Limit、RateLimit-Remaining、RateLimit-Reset,以及存在时的Retry-After)。服务器不会代你重试——请根据这些响应头退避,优先使用Retry-After(当它存在时)。Apple 的文档未公布固定的数值限制,因此不要硬编码;每次响应时读取响应头即可。批量请求(
keywords_bulkCreate等)无论携带多少条目,都只计为一次速率限制调用——在规模化操作时,将变更批量合并到批量请求中。超过约 120,000 个字符的响应会被截断,并附带一条提示以缩小请求范围。对于列表/报表端点,请降低
pageSize或添加更具体的filters,而不是一次性拉取全部数据。
试运行
每个 ads_call 都接受 _dryRun: true。它会解析请求——方法、路径、路径参数、查询、请求头、主体以及广告账户上下文——而不向 Apple 发送任何内容:
{
"operation": "campaigns_update",
"_dryRun": true,
"args": {
"id": "542370549",
"adAccountId": "123456789",
"body": { "status": "PAUSED" }
}
}{
"dryRun": true,
"operationId": "campaignsIdPut",
"method": "PUT",
"path": "/campaigns/{id}",
"pathParams": { "id": "542370549" },
"query": {},
"headers": {},
"body": { "status": "PAUSED" },
"adAccountId": "123456789",
"xApContext": "adAccountId=123456789;"
}在写入请求触及真实广告账户之前,用它对照 ads_schema 检查该请求。
环境变量
变量 | 必需 | 用途 |
| 是 | 来自 Apple Ads UI 凭据块的客户端 ID |
| 是 | 来自同一凭据块的团队 ID |
| 是 | 来自同一凭据块的密钥 ID |
| 是* | EC 私钥文件的绝对路径 |
| 是* | 内联 PEM 内容,替代文件路径(在 CI 中很有用) |
| 否 |
|
| 否 | 默认为 |
| 否 | 默认为 |
| 否 |
|
* 在 APPLE_ADS_PRIVATE_KEY_PATH 和 APPLE_ADS_PRIVATE_KEY 中恰好提供一个。
将 .env.example 复制为 .env 供本地 shell 使用。.env*、*.p8 和 *.pem 已在 .gitignore 中。
目录是如何生成的
openapi/apple-ads.openapi.json → bun run generate → generated/tools.json
generated/manifest.jsonopenapi/apple-ads.openapi.json 是供应商提供的真实来源:根据 Apple 官方 Node 客户端(apple/apple-ads-platform-api-node,规范标签 109)重建,并逐端点与 Apple 发布的文档交叉核对。scripts/generate-tools.ts 读取它,为每个路径+方法生成一条目录条目——99 个操作、80 个路径、28 个标签——输出到 generated/tools.json,并将计数和来源信息输出到 generated/manifest.json。
工具描述根据 Apple 自己的文档进行了丰富。generated/docs.json 将每个 METHOD /path 映射到 developer.apple.com 上对应页面的标题、摘要和 URL,因此 ads_search 和 ads_schema 会返回 Apple 自己的措辞以及一个可打开的链接。
bun run generate # rebuild the catalog from openapi/apple-ads.openapi.json
bun run docs # refresh generated/docs.json from developer.apple.com
bun run smoke # regenerate, validate the catalog, boot the server覆盖率是经过验证的,而非假设的。Apple 发布了 99 个端点页面;此目录有 99 个操作,每个操作在方法和路径形态上都是一一对应的。完整的逐端点对比见 docs/endpoint-coverage.md。
generated/ 是构建输出。切勿手工编辑——请修改规范或生成器,然后重新生成。如果提交的目录与全新生成的结果不匹配,CI 会失败。
逃生舱:每个操作作为独立工具
export APPLE_ADS_EXPOSE_ALL_TOOLS=1 # also register all 99 operations as individual MCP tools在正常使用中保持未设置——它存在的目的是调试生成的目录,而非日常代理使用,并且它会把每个操作的完整模式重新放回上下文中。
安全
此仓库中不存储任何机密信息。 凭据仅来自环境变量。
.gitignore阻止*.p8、*.pem、PrivateKey_*.p8和.env*。客户端密钥是一个按策略短期有效的 ES256 JWT,你用私钥在本地签名(最长有效 180 天,即 Apple 自己的上限);Apple 永远不会收到私钥本身。生成的访问令牌仅缓存在内存中,并在 Apple 返回的
expires_in(当前为 3600 秒)之前以 60 秒的余量刷新——外加在401时的一次性重试。工具结果永远不会将凭据回显给模型。
唯一的网络目的地是
APPLE_ADS_BASE_URL(默认为 Apple 的 API 主机)和APPLE_ADS_AUTH_BASE_URL(默认为 Apple 的 OAuth 主机)。如果你意外提交了私钥,请立即在 Apple Ads UI 中轮换它。
报告漏洞请参阅 SECURITY.md。
贡献
欢迎提交 Issue 和 Pull Request。请参阅 CONTRIBUTING.md。有两条规则最重要:generated/ 是重新生成而非手工编辑的,且任何凭据都不得出现在 Pull Request 中。
许可证
MIT © imfaisii
与 Apple Inc. 无关联,亦未获得其认可。
Related MCP Connectors
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
Apple Search Ads MCP: campaign analytics, bid management, and attribution tracking.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
MCP server for building and testing AI agents with multi-model experimentation and insights.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA Model Context Protocol server that exposes the entire Apple App Store Connect API (1,200+ operations) as MCP tools, enabling AI assistants to query apps, manage builds, handle submissions, read analytics, and more.11 npmMIT
- AlicenseBqualityDmaintenanceMCP server for Meta Ads providing 30 tools for account discovery, campaign management, targeting research, and insights. Designed with LLM-friendly outputs and productivity features like cloning and bulk operations.311MIT
- FlicenseNot gradedqualityBmaintenanceAn MCP server that exposes the Apple App Store Connect API to AI agents, enabling management of apps, metadata, in-app purchases, subscriptions, TestFlight, provisioning, reviews, analytics, and more through 113 curated tools plus two generic JSON:API escape-hatch tools.2-
- AlicenseNot gradedqualityBmaintenanceComplete Google Ads API v21 MCP server with 40+ fully implemented tools for campaign, ad group, keyword, extension, and portfolio bidding management, enabling AI assistants to create, optimize, and manage Google Ads campaigns through natural language commands.MIT