Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

marketing-mcp

MCP server that exposes Google Ads, Google Analytics 4, and Meta Ads as tools for LLMs. Built in PHP. Supports two transport modes: stdio (local, single user) and HTTP (Docker, multi-user).


Table of contents


Server setup (Docker + HTTP)

The HTTP mode lets multiple users connect from any device using their own API credentials. SSL is handled by Nginx Proxy Manager or any other reverse proxy.

1 — Start the container

git clone https://github.com/and-ri/marketing-mcp.git
cd marketing-mcp
docker compose up -d

The server listens on port 8080. In Nginx Proxy Manager: add a Proxy Host → forward to localhost:8080 → enable SSL as usual.

2 — Create a user

docker compose exec mcp php users.php add alice

Prints a Bearer token. Keep it — this is the credential for Claude.

3 — Connect Claude

Add to ~/.claude.json (global) or .mcp.json (project-level):

{
  "mcpServers": {
    "marketing": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "headers": {
        "Authorization": "Bearer <token from step 2>"
      }
    }
  }
}

4 — Authorize platforms

Once connected, tell Claude to call auth_status — it will report what's missing and guide you through the setup. Or just ask: "set up Google Ads for me" and Claude will walk through each step.

See Authorizing platforms from Claude for details.

5 — Maintenance

docker compose logs -f mcp                  # live logs
docker compose up -d --build mcp            # rebuild after code changes
docker compose exec mcp php users.php list  # list all users

Local setup (stdio)

For single-user local use without Docker.

composer install
php auth.php         # interactive OAuth2 flow — opens browser, writes .env

MCP client config:

{
  "mcpServers": {
    "marketing": {
      "command": "php",
      "args": ["/path/to/marketing-mcp/app.php"]
    }
  }
}

Authorizing platforms from Claude

In HTTP mode, credentials are stored per-user in SQLite. Authorization happens through MCP tools — no terminal access needed.

How it works

  1. Call auth_start_google or auth_start_meta — returns an OAuth URL
  2. Open the URL in your browser and authorize access
  3. The browser shows "Authorized! You can close this tab."
  4. Call auth_finish_google or auth_finish_meta — saves the credentials

The server's OAuth callback endpoint is https://yourdomain.com/oauth/callback. This URL must be registered in both Google and Meta developer consoles (the tool descriptions contain exact instructions for where to add it).

Auth tools

Tool What it does
auth_status Shows which platforms are configured and what's missing
auth_start_google Starts Google OAuth2. Tool description contains full setup guide for Google Cloud Console
auth_finish_google Completes Google auth, saves refresh token
auth_start_meta Starts Meta OAuth2. Tool description contains full setup guide for the Meta developer portal
auth_finish_meta Completes Meta auth, saves long-lived token (~60 days)
auth_start_tiktok / auth_finish_tiktok TikTok Ads OAuth2 flow
auth_set_clarity Saves the Microsoft Clarity API token (single step — Clarity has no OAuth)
auth_set_rybbit Saves self-hosted Rybbit base_url + api_key (single step — Rybbit has no OAuth)

Token-based integrations (Microsoft Clarity, Rybbit) don't use OAuth — there's no browser step. Just call auth_set_clarity / auth_set_rybbit with the token(s) and you're done.

Google — what you'll need

Before calling auth_start_google:

  1. Google Cloud project — console.cloud.google.com
  2. APIs enabled — Google Ads API + Google Analytics Data API
  3. OAuth client — type: Web application, redirect URI: https://yourdomain.com/oauth/callback
  4. Developer token — ads.google.com/aw/apicenter

The auth_start_google tool description includes step-by-step instructions for all of the above. Claude can read them and guide you interactively.

Meta — what you'll need

Before calling auth_start_meta:

  1. Meta developer app — developers.facebook.com, type: Business
  2. Marketing API product added to the app
  3. Facebook Login product added, redirect URI https://yourdomain.com/oauth/callback registered
  4. App ID and App Secret — Settings → Basic

The auth_start_meta tool description includes step-by-step instructions. Meta tokens expire in ~60 days — re-authorize with auth_start_meta + auth_finish_meta when needed.


User management CLI

Run inside the Docker container:

# Create user — prints Bearer token
docker compose exec mcp php users.php add <name>

# List all users and tokens
docker compose exec mcp php users.php list

# Show a user's env vars (values masked)
docker compose exec mcp php users.php env:show <name>

# Set a single env var manually
docker compose exec mcp php users.php env:set <name> META_ACCESS_TOKEN EAAxxxxx

# Import all vars from a .env file
docker compose exec -T mcp php users.php env:import <name> < credentials.env

# Reset a compromised token
docker compose exec mcp php users.php token:reset <name>

# Remove a user and all their credentials
docker compose exec mcp php users.php remove <name>

Credentials stored per user (same key names as .env.example):

Platform Keys
Google Ads GOOGLE_ADS_DEVELOPER_TOKEN, GOOGLE_ADS_CLIENT_ID, GOOGLE_ADS_CLIENT_SECRET, GOOGLE_ADS_REFRESH_TOKEN, GOOGLE_ADS_LOGIN_CUSTOMER_ID (MCC only)
Google Analytics 4 GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKEN
Meta Ads META_APP_ID, META_APP_SECRET, META_ACCESS_TOKEN

Tools reference

Finding account IDs

Platform How to find IDs
Google Ads customer_id 10-digit number, no dashes. Call google_ads_list_customers first.
GA4 property_id Numeric ID — Analytics → Admin → Property Settings. E.g. 123456789.
Meta account_id Numeric ID, with or without act_ prefix. Call meta_get_ad_accounts first.

Google Ads tools

date_range values: TODAY YESTERDAY LAST_7_DAYS LAST_14_DAYS LAST_30_DAYS THIS_MONTH LAST_MONTH THIS_QUARTER LAST_QUARTER THIS_YEAR LAST_YEAR

Monetary values are in micros — divide by 1,000,000 for the actual amount.


google_ads_list_customers

List all accessible Google Ads accounts. Call this first to get customer_id values.

Parameters: none

Returns: { customers: [{ customer_id, resource_name }], total }


google_ads_get_campaigns

Campaigns with spend, clicks, impressions, CTR, CPC, CPM, conversions, and conversion value.

Parameter Type Required Default
customer_id string yes —
date_range string no LAST_30_DAYS
status string no ALL — ENABLED | PAUSED | REMOVED | ALL

google_ads_get_ad_groups

Ad groups with bids and metrics. Optionally scoped to one campaign.

Parameter Type Required Default
customer_id string yes —
campaign_id string no —
date_range string no LAST_30_DAYS

google_ads_get_keywords

Keywords with quality score (1–10), match type, effective CPC bid, impression share.

Parameter Type Required Default
customer_id string yes —
campaign_id string no —
ad_group_id string no —
date_range string no LAST_30_DAYS
limit integer no 200

google_ads_get_search_terms

What users actually searched to trigger your ads. Use for negative keyword discovery.

Parameter Type Required Default
customer_id string yes —
campaign_id string no —
date_range string no LAST_30_DAYS
limit integer no 200

google_ads_get_ads

Individual ads with creative text (RSA headlines/descriptions, ETA fields) and approval status.

Parameter Type Required Default
customer_id string yes —
campaign_id string no —
ad_group_id string no —
date_range string no LAST_30_DAYS
limit integer no 100

google_ads_get_geo_performance

Performance by geographic location (country_criterion_id).

Parameter Type Required Default
customer_id string yes —
campaign_id string no —
date_range string no LAST_30_DAYS
limit integer no 100

google_ads_get_account_metrics

Top-line account summary: total spend, clicks, impressions, conversions, impression share, lost IS (budget and rank).

Parameter Type Required Default
customer_id string yes —
date_range string no LAST_30_DAYS

google_ads_query

Execute any GAQL query. Use when other tools don't cover the report you need (shopping, video, change history, audiences, etc.).

Parameter Type Required
customer_id string yes
query string yes
-- Change history
SELECT change_event.change_date_time, change_event.change_resource_type, change_event.user_email
FROM change_event WHERE change_event.change_date_time DURING LAST_14_DAYS
ORDER BY change_event.change_date_time DESC LIMIT 50

-- Shopping performance
SELECT shopping_performance_view.resource_name, metrics.clicks, metrics.cost_micros
FROM shopping_performance_view WHERE segments.date DURING LAST_30_DAYS

google_ads_get_change_history

Account change history — who changed what and when (campaigns, ad groups, bids, budgets, ads, criteria). Google retains only the last 30 days.

Parameter Type Required Default
customer_id string yes —
days integer no 14 (max 30)
resource_type string no — e.g. CAMPAIGN, AD_GROUP, AD_GROUP_AD, CAMPAIGN_BUDGET
limit integer no 500 (max 10000)

Returns: { changes: [...], total, days }


google_ads_get_negative_keyword_lists

Shared negative keyword lists (shared sets of type NEGATIVE_KEYWORDS): the lists, their keyword members, and which campaigns each is attached to.

Parameter Type Required Default
customer_id string yes —

Returns: { lists: [...], members: [...], attachments: [...], total_lists }


google_ads_get_negative_keywords

Negative keywords applied directly at the campaign and ad group levels (excludes shared lists).

Parameter Type Required Default
customer_id string yes —
campaign_id string no —
limit integer no 500

Returns: { campaign_level: [...], ad_group_level: [...], total }


google_ads_get_audiences

Audience segments / user lists, including custom-uploaded Customer Match lists (user_list.type = CRM_BASED) with match rates, plus remarketing, similar, rule-based lists and combined audiences. Shows sizes, membership status, and eligibility.

Parameter Type Required Default
customer_id string yes —
list_type string no — e.g. CRM_BASED, REMARKETING, SIMILAR, RULE_BASED, LOGICAL

Returns: { user_lists: [...], audiences: [...], total_user_lists }


google_ads_get_audience_performance

Performance of audience segments attached to campaigns and ad groups (campaign_audience_view, ad_group_audience_view) with impressions, clicks, cost, and conversions per audience.

Parameter Type Required Default
customer_id string yes —
campaign_id string no —
date_range string no LAST_30_DAYS
limit integer no 200

Returns: { ad_group_level: [...], campaign_level: [...], total, date_range }


Google Analytics 4 tools

start_date / end_date: relative (today, yesterday, 7daysAgo, 30daysAgo) or absolute YYYY-MM-DD.


ga4_run_report

Custom report with any dimensions and metrics from the GA4 explorer.

Parameter Type Required Default
property_id string yes —
dimensions string[] yes —
metrics string[] yes —
start_date string no 30daysAgo
end_date string no today
limit integer no 100

ga4_run_realtime_report

Active users right now with optional dimension breakdown.

Parameter Type Required Default
property_id string yes —
dimensions string[] no ["country"]
metrics string[] no ["activeUsers"]

ga4_get_audience_overview

Daily breakdown: totalUsers, newUsers, sessions, bounceRate, averageSessionDuration, engagedSessions, engagementRate split by newVsReturning.

Parameter Type Required Default
property_id string yes —
start_date string no 30daysAgo
end_date string no today

ga4_get_traffic_sources

Sessions by channel group, source, and medium. Includes sessions, totalUsers, bounceRate, conversions, totalRevenue.

Parameter Type Required Default
property_id string yes —
start_date string no 30daysAgo
end_date string no today
limit integer no 50

ga4_get_top_pages

Top pages by views. Returns pagePath, pageTitle, totalUsers, bounceRate, engagementRate, conversions.

Parameter Type Required Default
property_id string yes —
start_date string no 30daysAgo
end_date string no today
limit integer no 50

ga4_get_conversions

Conversion events by eventName and channel group. Returns conversions, totalRevenue, sessions.

Parameter Type Required Default
property_id string yes —
start_date string no 30daysAgo
end_date string no today

ga4_get_ecommerce

Revenue, transactions, items sold, average order value. Returns empty if the property has no e-commerce events.

Parameter Type Required Default
property_id string yes —
start_date string no 30daysAgo
end_date string no today
limit integer no 50

ga4_get_geo_breakdown

Traffic by country and city.

Parameter Type Required Default
property_id string yes —
start_date string no 30daysAgo
end_date string no today
limit integer no 50

ga4_get_device_breakdown

Traffic by device category, OS, and browser.

Parameter Type Required Default
property_id string yes —
start_date string no 30daysAgo
end_date string no today

Meta Ads tools

account_id accepts numeric ID with or without act_ prefix.

date_preset: today yesterday last_3d last_7d last_14d last_28d last_30d last_90d this_month last_month this_quarter last_year lifetime

Monetary values are in account currency (not micros).


meta_get_ad_accounts

List all accessible ad accounts. Call this first to get account_id values.

Parameters: none

account_status codes: 1=Active, 2=Disabled, 3=Unsettled, 7=Pending review, 101=Closed.


meta_get_campaigns

Campaigns with budget, objective, and insights (spend, reach, impressions, clicks, CTR, CPC, ROAS, actions).

Parameter Type Required Default
account_id string yes —
date_preset string no last_30d
status string[] no ["ACTIVE","PAUSED"]
limit integer no 50

actions in insights is [{ action_type, value }]. Common types: link_click, lead, purchase, add_to_cart, initiate_checkout, complete_registration.


meta_get_adsets

Ad sets with targeting (age_min/max, genders, geo_locations, interests, custom_audiences, publisher_platforms) and insights.

Parameter Type Required Default
account_id string yes —
campaign_id string no —
date_preset string no last_30d
limit integer no 50

meta_get_ads

Individual ads with creative reference and insights. Use creative.id from results with meta_get_creatives to get image/copy.

Parameter Type Required Default
account_id string yes —
campaign_id string no —
adset_id string no —
date_preset string no last_30d
limit integer no 50

meta_get_insights

Aggregated insights with optional breakdowns. More flexible than the per-object insights on campaigns/adsets/ads.

Parameter Type Required Default
account_id string yes —
object_id string no —
object_type string no account — account | campaign | adset | ad
date_preset string no last_30d
breakdowns string[] no none
limit integer no 100

breakdowns: age gender country region impression_device placement platform_position publisher_platform

Note: some breakdown combinations are incompatible. Use one at a time when unsure.


meta_get_custom_audiences

Custom and lookalike audiences with size estimates and data source.

Parameter Type Required Default
account_id string yes —
limit integer no 50

subtype values: CUSTOM WEBSITE APP LOOKALIKE ENGAGEMENT VIDEO OFFLINE_CONVERSION


meta_get_pixels

Meta Pixels with last fire time.

Parameter Type Required
account_id string yes

meta_get_creatives

Ad creative assets: title, body, image URL, CTA type, destination URL.

Parameter Type Required Default
account_id string yes —
limit integer no 50

Microsoft Clarity tools

Behavioral analytics that complement GA4 — Clarity surfaces UX friction (rage clicks, dead clicks, quickbacks, scroll depth, JS errors) that GA4 doesn't capture. Auth is a static project token (CLARITY_API_TOKEN), generated in Clarity → Settings → Data Export → Generate new API token. No OAuth flow.

Constraints (Clarity API): data covers only the last 1–3 days, max 10 requests per project per day, response capped at 1000 rows (no pagination), results in UTC.

clarity_get_insights

Live insights for the project: traffic, scroll depth, engagement time, dead/rage/quickback clicks, excessive scroll, and script errors. Optionally broken down by up to three dimensions.

Parameter Type Required Default
num_of_days integer no 1 — 1 | 2 | 3
dimension1 string no — Browser | Device | Country/Region | OS | Source | Medium | Campaign | Channel | URL
dimension2 string no — second breakdown
dimension3 string no — third breakdown

Returns: { num_of_days, metrics: [{ metricName, information: [...] }] }


Rybbit tools

Read-only analytics from a self-hosted Rybbit instance. Set RYBBIT_BASE_URL (your instance, no trailing slash) and RYBBIT_API_KEY (Settings → Account → API Keys). All tools take a site_id plus an optional time range (start_date, end_date, time_zone) and filters (JSON-encoded array).

rybbit_get_overview

Top-line metrics: sessions, pageviews, users, pages per session, bounce rate, session duration.

rybbit_get_overview_timeseries

Overview metrics bucketed over time. Adds bucket (minute | five_minutes | hour | day | week | month, default day).

rybbit_get_sessions

Paginated session list with geo, device, browser, source, pages, duration. Adds page, limit, optional user_id.

rybbit_get_event_names

Custom event names with occurrence counts — track conversions and key actions.

rybbit_get_performance

Core Web Vitals (LCP, CLS, INP, FCP, TTFB) at p50/p75/p90/p99. Useful for landing page quality.

rybbit_get_errors

JavaScript errors grouped by name with counts, session counts, and percentages. Adds page, limit.

rybbit_get_goals

Conversion goals with total conversions, sessions, and conversion rate. Adds page, page_size.

Common Parameter Type Required Default
site_id string yes —
start_date string no — ISO date
end_date string no — ISO date
time_zone string no UTC
filters string no — JSON array

Common workflows

"What's my Google Ads performance this month?"

  1. google_ads_list_customers → get customer_id
  2. google_ads_get_account_metrics with date_range: THIS_MONTH
  3. google_ads_get_campaigns for breakdown by campaign

"Which Meta campaigns have the best ROAS?"

  1. meta_get_ad_accounts → get account_id
  2. meta_get_campaigns → look at insights.action_values[purchase] / insights.spend

"Where is my website traffic coming from?"

  1. ga4_get_traffic_sources with your property_id

"What search terms are wasting budget?"

  1. google_ads_get_search_terms → find terms with high spend and 0 conversions

"Full cross-platform audit"

  1. google_ads_get_account_metrics — top-line spend/conversions
  2. google_ads_get_campaigns + google_ads_get_keywords — quality scores, impression share
  3. google_ads_get_search_terms — irrelevant queries
  4. meta_get_campaigns + meta_get_insights with breakdowns: ["age"] and ["placement"]
  5. ga4_get_traffic_sources — paid vs organic split
  6. ga4_get_conversions — which events actually convert

Error reference

All tools return errors inside the MCP content array (with isError: true). Common causes:

Error Cause Fix
PERMISSION_DENIED OAuth token can't access that account Check account access in Google Ads or use google_ads_list_customers to see what's accessible
INVALID_CUSTOMER_ID Wrong format or account doesn't exist Use 10-digit format without dashes
quota exceeded Google Ads API daily quota hit Wait 24 hours or request a higher quota
(#100) Meta: invalid parameter or missing permission Check scope — ensure ads_read, ads_management, business_management were granted
OAuth2 token expired Google refresh token revoked or expired Call auth_start_google + auth_finish_google
Error validating access token Meta token expired (~60 days) Call auth_start_meta + auth_finish_meta
Developer token is not approved Google Ads dev token has Basic Access Apply for Standard Access at ads.google.com/aw/apicenter

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages