Skip to main content
Glama
Pauesome

Ads Analytics MCP

by Pauesome
README.md
# 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

A3.9/5.0

Scored across 16 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count4/5

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.

Completeness4/5

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.

Maintenance

ActivityStale
ResponsivenessNo issues