Ads Analytics MCP
# Ads Analytics MCP
An open-source [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that gives an AI assistant (Claude Desktop, Claude Code, or any MCP client) **read access to campaign performance data** from three ad platforms:
- **Google Ads**
- **Meta Ads** (Facebook / Instagram)
- **TikTok Ads**
It exposes campaign, hourly, ad-level, and account-level insight tools. All data is fetched **live** from each platform's official API using credentials you supply. Nothing is bundled, cached to a database, or sent anywhere else.
On top of the raw tools, the repository ships a **Claude intelligence layer**: skills and subagents that turn those tools into opinionated, Spain-specific audits and recommendations (see [The Claude intelligence layer](#the-claude-intelligence-layer)).
> **No credentials are included in this repository.** Copy the example files and fill in your own. See [Configuration](#configuration).
## Table of contents
- [How it fits together](#how-it-fits-together)
- [Features](#features)
- [Available tools](#available-tools)
- [Requirements](#requirements)
- [Installation & build](#installation--build)
- [Configuration](#configuration)
- [Connecting to an MCP client](#connecting-to-an-mcp-client)
- [The Claude intelligence layer](#the-claude-intelligence-layer)
- [Skills](#skills)
- [Subagents](#subagents)
- [Reference knowledge base](#reference-knowledge-base)
- [How skills, agents, and tools play together](#how-skills-agents-and-tools-play-together)
- [Development](#development)
- [Architecture](#architecture)
- [Security](#security)
- [License](#license)
## How it fits together
There are two distinct layers in this repo:
```
┌──────────────────────────────────────────────────────────────┐
│ Claude intelligence layer (.claude/) │
│ │
│ Skills Subagents References │
│ google-ads ──► audit-google ──► benchmarks-spain.md │
│ meta-ads ──► audit-meta compliance-eu-spain.md│
│ tiktok-ads ──► audit-tiktok platform-specs.md │
│ ads-budget-review ─► audit-budget │
│ ads-pacing-monitor business-analyst │
│ ads-strategy-plan │
│ ab-test-design │
│ competitor-teardown │
└───────────────────────────────┬──────────────────────────────┘
│ calls MCP tools
▼
┌──────────────────────────────────────────────────────────────┐
│ MCP server (src/ → dist/) │
│ │
│ tools/ ──► services/ ──► adapters/ ──► Platform APIs │
│ (16 read-only tools) Google / Meta / │
│ TikTok │
└──────────────────────────────────────────────────────────────┘
```
- **The MCP server** (`src/`) is plain, provider-agnostic infrastructure: 16 read-only tools that any MCP client can call. It has no opinions. It returns normalized metrics.
- **The Claude layer** (`.claude/`) is optional and Claude-specific. Skills are the entry points users trigger by phrase; they pull data with the tools and delegate deep work to subagents; subagents score against the reference benchmarks and write reports. You can run the server with no Claude layer at all and still use every tool.
## Features
- 🔌 Live API access to Google Ads, Meta Marketing API, and TikTok Marketing API
- 📊 Campaign performance, hourly breakdowns, ad-level, search terms, keywords, impression share
- 🧠 Account-level insight tools: opportunity score, auction rankings, anomaly signals
- 👥 Multi-account: configure as many accounts as you like, one JSON file each
- 🔒 Secrets stay local. `.env` + `clients/*.json` are git-ignored by default
- 🤖 Optional Claude skills + subagents for Spain-specific audits and budget strategy
## Available tools
All 16 tools are **read-only**. Every metric is normalized into a common shape (spend, impressions, clicks, CTR, CPC, CPM, conversions, CPA, conversion value, ROAS) before it leaves the server, so an agent sees the same vocabulary across platforms.
| Tool | Platform | Description |
|------|----------|-------------|
| `list_clients` | n/a | List configured accounts and their platforms. **Call this first** to discover `client_id`s |
| `get_google_ads_campaign_performance` | Google | Campaign metrics; `aggregation` = `campaign` / `campaign_day` / `day` |
| `get_google_ads_hourly_performance` | Google | Hour-of-day × day-of-week breakdown (dayparting) |
| `get_google_ads_search_terms` | Google | Search-terms report; surfaces `wasted_spend` (spend with zero conversions) |
| `get_google_ads_keywords` | Google | Keyword performance + Quality Score distribution |
| `get_google_ads_impression_share` | Google | Impression share, split into budget-lost vs rank-lost |
| `get_meta_campaign_performance` | Meta | Campaign metrics at campaign × day granularity |
| `get_meta_hourly_performance` | Meta | Hour-of-day × day-of-week breakdown (dayparting) |
| `get_meta_opportunity_score` | Meta | Account opportunity score (0–100) + recommendations queue |
| `get_meta_auction_rankings` | Meta | quality / engagement / conversion rankings + `below_average_count` |
| `get_meta_anomaly_signal` | Meta | Rolling-baseline Z-score anomaly detection per campaign × metric |
| `get_tiktok_campaign_performance` | TikTok | Campaign metrics at campaign × day granularity |
| `get_tiktok_hourly_performance` | TikTok | Hour-of-day × day-of-week breakdown (dayparting) |
| `get_tiktok_ad_performance` | TikTok | Ad-level metrics + `video_watched_2s` / `_6s` (hook / hold rate) |
| `get_tiktok_auction_rankings` | TikTok | video_quality_score + engagement / conversion rankings |
| `get_tiktok_anomaly_signal` | TikTok | Rolling-baseline Z-score anomaly detection per campaign × metric |
## Requirements
- Node.js >= 20
- API access on each platform you want to use:
- **Google Ads:** a Google Cloud OAuth app + a Google Ads API developer token
- **Meta:** a Marketing API app and a (preferably long-lived System User) access token with `ads_read`
- **TikTok:** a TikTok Marketing API app
## Installation & build
```bash
git clone <your-fork-url>
cd ads-analytics-mcp
npm install # install dependencies
npm run build # compile TypeScript src/ → dist/
```
`npm run build` runs `tsc` and emits the runnable server to `dist/index.js`. That compiled file is what every MCP client launches. Always rebuild after changing anything under `src/`.
Verify the build booted correctly (lists all tools over stdio without needing credentials):
```bash
npm start # node dist/index.js, should log "server connected via stdio"
```
The server starts even with **zero** accounts configured (it just exposes `list_clients` returning an empty list). Credentials are only needed when you actually call a platform tool.
## Configuration
Credentials are split in two:
1. **App-level credentials** → `.env` (your OAuth apps / developer tokens)
2. **Account-level credentials** → `clients/<id>.json` (per-account IDs + tokens)
### 1. App-level `.env`
```bash
cp .env.example .env
```
Fill in your Google Ads OAuth app + developer token, and (if using TikTok) your TikTok app id/secret. See the comments in `.env.example`.
### 2. Account files
```bash
cp clients/_example.json clients/my-account.json
```
Edit it with the account IDs and tokens for the platforms you use. Any platform block you omit is simply unavailable for that account. A minimal Google-only account:
```json
{
"id": "my-account",
"name": "My Account",
"google_ads": {
"customer_id": "1234567890",
"refresh_token": "1//0e...",
"currency": "EUR"
}
}
```
Add as many `clients/*.json` files as you have accounts. They are git-ignored (only `_example.json` is tracked). The `id` field is the `client_id` you pass to every tool.
### Getting tokens
```bash
# Google Ads OAuth refresh token (uses GOOGLE_ADS_CLIENT_ID/SECRET from .env)
npm run auth:google
# TikTok access token (uses TIKTOK_APP_ID/SECRET from .env)
npm run auth:tiktok
```
For Meta, generate a long-lived System User token in Business Settings and paste it into your account file's `meta_ads.access_token`.
## Connecting to an MCP client
### Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"ads-analytics": {
"command": "node",
"args": ["/absolute/path/to/ads-analytics-mcp/dist/index.js"]
}
}
}
```
### Claude Code
```bash
claude mcp add ads-analytics -- node /absolute/path/to/ads-analytics-mcp/dist/index.js
```
Then ask things like *"List my accounts"* or *"Show last 30 days Google Ads campaign performance for my-account."* When more than one account is configured, pass `client_id` (the account's `id`).
## The Claude intelligence layer
Everything under `.claude/` is an opinionated layer that sits **on top of** the raw tools. It is what turns *"here are the numbers"* into *"here is what's wrong and what to do about it,"* tuned for Spanish (EU) advertisers. If you use a non-Claude MCP client, you can ignore this section. The tools work without it.
It has three parts: **skills** (entry points), **subagents** (deep workers), and **references** (the shared knowledge base they both cite).
### Skills
Skills live in `.claude/skills/<name>/SKILL.md`. Each one activates on natural-language triggers, resolves inputs (`client_id`, date range), pulls data with the MCP tools, and either answers inline or hands off to a subagent for a full scored audit.
**Per-platform skills** (one entry point per channel):
| Skill | Triggers on | Pulls these tools | Delegates to |
|-------|-------------|-------------------|--------------|
| `google-ads` | "google ads", "Quality Score", "search terms waste", "PMax", "impression share" | the 5 `get_google_ads_*` tools | `audit-google` |
| `meta-ads` | "meta ads", "facebook ads", "instagram ads", "pixel", "CAPI", "EMQ", "learning limited" | the 5 `get_meta_*` tools | `audit-meta` |
| `tiktok-ads` | "tiktok ads", "hook rate", "2s hold", "creative fatigue", "Smart Performance Campaign" | the 5 `get_tiktok_*` tools | `audit-tiktok` |
**Cross-platform skills** (work across all three channels at once):
| Skill | Purpose |
|-------|---------|
| `ads-budget-review` | Strategic allocation. Applies the 70/20/10 rule, 3× Kill Rule, 20% Scaling Rule; produces a kill list and a scale list. Delegates to `audit-budget`. |
| `ads-pacing-monitor` | Tactical mid-month spend pacing. Projects end-of-month spend at the current run rate and flags over/under-pacing campaigns. |
| `ads-strategy-plan` | Interactive kickoff plan for a new client: platform selection, campaign architecture, budget, creative, tracking, rollout roadmap. |
| `ab-test-design` | Structured A/B-test design: hypothesis, sample-size calculator, duration estimate, platform-specific split-test setup. |
| `competitor-teardown` | Competitive landing-page analysis: positioning, messaging, objections, trust signals, CTA strategy. |
### Subagents
Subagents live in `.claude/agents/<name>.md`. They are the deep workers a skill hands off to. Each runs a self-contained, scored audit and writes a Markdown report to `./reports/<client_id>/<date>-<platform>-audit/`. The first line of every report is a machine-readable score line:
```
SCORE: <0-100>/100 GRADE: <A|B|C|D|F> CLIENT: <client_id> PLATFORM: <platform> PERIOD: <start>..<end>
```
| Agent | What it audits | Tools it pulls |
|-------|----------------|----------------|
| `audit-google` | Conversion tracking, wasted spend, structure, Quality Score, PMax, bidding | the 5 `get_google_ads_*` tools |
| `audit-meta` | Pixel/CAPI health, EMQ, learning state, auction competitiveness, anomalies | the 5 `get_meta_*` tools |
| `audit-tiktok` | Hook strength (2s/6s hold), creative performance, SPC, auction, anomalies | the 5 `get_tiktok_*` tools |
| `audit-budget` | Allocation, bidding strategy, scaling readiness, budget sufficiency | campaign + impression-share tools across all platforms |
| `business-analyst` | CMO-level synthesis: blended CPL/CPA/ROAS, where budget works vs is wasted | none directly; **reads the other agents' reports** |
`business-analyst` is the capstone: it does not call the platform tools itself. It consumes the reports written by the four audit agents and produces a unified cross-channel executive narrative with budget-reallocation recommendations.
### Reference knowledge base
Both skills and agents are forbidden from inventing thresholds. They must cite these shared files in `.claude/references/`:
| File | Contains |
|------|----------|
| `benchmarks-spain.md` | Spain (EUR) guidance ranges for CPC/CPM/CTR/CPA/ROAS per platform. ROAS and CPA always outweigh raw rate metrics. |
| `compliance-eu-spain.md` | GDPR / LOPDGDD / AEPD / Consent Mode v2, Meta EU Consent Policy, TikTok restricted categories. US privacy law is explicitly out of scope. |
| `platform-specs.md` | Creative dimensions, safe zones, and format-compliance specs (universal, not localized). |
This is why the audits are tuned for Spain: a campaign sitting below an average but with healthy ROAS is scored WARN, not FAIL, and US-specific rules (CCPA, ECPC-only logic, Offline Conversions API) are flagged as out-of-date or out-of-scope.
### How skills, agents, and tools play together
A typical full request flows top-to-bottom through all three layers:
```
User: "Audit my TikTok account for client maganda, last 14 days"
│
▼
Skill tiktok-ads ── resolves client_id via list_clients
│ reads benchmarks-spain + compliance + platform-specs
│ decides: full audit → hand off
▼
Agent audit-tiktok ── parallel MCP calls:
│ get_tiktok_campaign_performance
│ get_tiktok_ad_performance (2s/6s hold)
│ get_tiktok_hourly_performance
│ get_tiktok_auction_rankings
│ get_tiktok_anomaly_signal
│ ── scores each check vs benchmarks-spain.md
▼
Tools get_tiktok_* ── services/ normalize → adapters/ → TikTok Marketing API
│
▼
Report ./reports/maganda/2026-06-28-tiktok-audit/TIKTOK-ADS-REPORT.md
SCORE: 72/100 GRADE: C CLIENT: maganda PLATFORM: tiktok_ads PERIOD: ...
```
Run all three platform audits plus the budget review, then ask `business-analyst` to synthesize. It reads those four reports and produces the blended cross-channel view. A **quick** question ("what's my Meta CPA this week?") never leaves the skill: it pulls one tool and answers inline, no report written.
The contract between layers is deliberately thin: skills and agents only ever touch the platform through the 16 documented tool names. Add a new tool in `src/tools/`, rebuild, and reference it by name in a skill or agent. No other wiring required.
## Development
```bash
npm run dev # tsx watch, hot-reloads src/ on save
npm run typecheck # tsc --noEmit, type-check without emitting
npm run build # compile to dist/
npm start # run the compiled server (node dist/index.js)
```
## Architecture
```
src/
index.ts entry point (stdio transport)
server.ts MCP server wiring (ListTools / CallTool)
config/ app-level config (env) + account registry (clients/*.json)
schemas/ zod schemas for inputs, normalized output, account config
tools/ MCP tool definitions + handlers (one file per platform area)
services/ per-platform orchestration + normalization
adapters/ the only layer that knows each platform's API shapes
utils/ dates, logging, metric math
.claude/
skills/ natural-language entry points (per-platform + cross-platform)
agents/ deep audit subagents + the cross-channel business-analyst
references/ Spain benchmarks, EU compliance, creative specs
```
Data flows **tools → services → adapters → platform API** on the way out, and the reverse, normalized, on the way back. `adapters/` is the only layer that knows a platform's raw API shape; everything above it speaks the normalized vocabulary.
## Security
- Never commit `.env` or real `clients/*.json` files, both are git-ignored.
- The repository ships only `*.example` placeholders.
- Tokens are read at runtime and used solely to call the official platform APIs.
- All 16 tools are **read-only**. The server has no write path to any ad account.
## License
[MIT](./LICENSE)
TDQS
Scored across 16 tools
Each tool is scoped by a platform prefix (google_ads/meta/tiktok) plus a specific entity or metric, so even parallel tools like get_meta_hourly_performance and get_tiktok_hourly_performance or the two anomaly signals are clearly distinguishable by platform and purpose. There is no meaningful overlap where an agent could reasonably misselect.
All data tools follow the consistent get_{platform}_{metric_or_entity} pattern (e.g. get_google_ads_keywords, get_meta_auction_rankings, get_tiktok_anomaly_signal). The single discovery tool list_clients deviates slightly but is a natural, predictable exception with no competing convention.
16 tools sits at the upper edge of the ideal range, but the count is justified: three ad platforms each get parallel coverage of performance, hourly, and diagnostics. It is slightly heavy but every tool maps to a concrete analytical need rather than being filler.
The surface covers a strong analytics lifecycle across all three platforms: campaign performance, dayparting/hourly, auction rankings, anomaly detection, plus discovery. Minor asymmetries exist (Google Ads has no auction-rankings or anomaly tool, and Meta lacks ad-level performance), but these are workaroundable gaps rather than dead ends.