npm.io
1.1.21 • Published 3d agoCLI

marketing-mcp

Licence
UNLICENSED
Version
1.1.21
Deps
9
Size
321 kB
Vulns
0
Weekly
0
Install scriptsThis package runs scripts during installation (preinstall/install/postinstall)

marketing-mcp

Read-only Marketing MCP server for agency workflows — Google Ads, GA4, Search Console, Meta Ads, and Ahrefs.

Current state

As of marketing-mcp@1.1.13, the MCP is now:

  • discovery-first for Google clients
  • Gmail-backed for Search Console and GA4 when a Google user account is connected
  • service-account fallback for Google reads when user OAuth is unavailable
  • optimized with multi-layer caching for faster startup and first client-directory access

In practice, this means:

  • users no longer need to manually create Firestore client rows before switching to discovered clients
  • agency_set_active_client can resolve discovered clients directly
  • Google Search Console and GA4 can prefer the connected Google account (for example kwsltdmarketing@gmail.com) over the service account
  • local npx MCP launches can reuse:
    • cached secrets in ~/.marketing-mcp/
    • cached Google user OAuth from secrets or local file
    • local disk discovery snapshots
    • shared Firestore discovery snapshots

What changed

1. Discovery-first client model

The old registry-first model has been replaced with a discovery-first directory:

  • Google Ads accounts
  • GA4 properties
  • GSC sites

are treated as first-class clients even if they have never been saved in Firestore.

Firestore is now an optional alias / override layer for:

  • preferred display names
  • canonical keys
  • notes / tags
  • Ahrefs domain overrides
2. Google auth model

Google platform access now works in this order:

  1. connected Google user account (preferred)
  2. service account fallback

This is especially important for Search Console, where many teams only have access through a shared Gmail / Workspace user and cannot add the service account to every property.

3. Shared token export/import

The admin UI can connect a Google account on the hosted app, and the resulting refresh token can be exported into the secrets .env so local npx MCP users inherit the same Google visibility through MOTHER_SECRET only.

4. Performance work

The MCP now includes several performance optimizations:

  • cached secrets reuse instead of refetching on every stdio launch
  • parallel tool imports
  • parallel Google discovery queries where safe
  • per-process Google discovery caches
  • persistent local disk client-directory snapshots
  • shared Firestore discovery snapshot cache keyed by auth identity
  • startup prewarm of the client directory
  • compact MCP JSON responses to reduce stdio payload size
5. Better matching

Brand-name matching now prefers the most canonical discovered entry, usually:

  • production GSC / website entry
  • main production site

over secondary variants such as:

  • filtered / unfiltered GA4 properties
  • forum / prismic / staging variants

So phrases like Bolsover Cruise Club now resolve to the main website/GSC client instead of throwing ambiguity in common cases.

Install (Cursor / Claude Desktop)

Only one env var is required. The server fetches .env, service-account.json, and fb-service-account.json from the secrets server at startup.

{
  "mcpServers": {
    "marketing-mcp": {
      "command": "npx",
      "args": ["-y", "marketing-mcp@latest"],
      "env": {
        "MOTHER_SECRET": "your-secret-here"
      }
    }
  }
}

Secrets are cached under ~/.marketing-mcp/ on first run.

The MCP can still be launched with:

{
  "mcpServers": {
    "marketing-mcp": {
      "command": "npx",
      "args": ["-y", "marketing-mcp@latest"],
      "env": {
        "MOTHER_SECRET": "your-secret-here"
      }
    }
  }
}

Using @latest adds some unavoidable npm startup overhead, but the MCP now hides most of the heavy Google discovery cost behind local + shared caches.

Remote MCP (Claude Cloud / Teams)

Claude Cloud uses OAuth — not API keys. Configure in secrets .env:

MCP_OAUTH_CLIENT_ID=marketing-mcp-claude
MCP_OAUTH_CLIENT_SECRET=generate-a-long-random-string

Legacy MCP_API_KEY / x-api-key still works for curl and scripts.

Claude custom connector form
Field Value
Name Marketing MCP
Remote MCP server URL https://marketing-mcp.vg-stuff.com/mcp
OAuth Client ID same as MCP_OAUTH_CLIENT_ID in secrets
OAuth Client Secret same as MCP_OAUTH_CLIENT_SECRET in secrets

Claude will OAuth → consent screen → connect. Callback: https://claude.ai/api/mcp/auth_callback.

Cursor / npx (stdio) still works with MOTHER_SECRET only — unchanged.

Verify OAuth metadata (after deploy)
curl https://marketing-mcp.vg-stuff.com/.well-known/oauth-authorization-server
curl https://marketing-mcp.vg-stuff.com/.well-known/oauth-protected-resource/mcp
curl -I -X POST https://marketing-mcp.vg-stuff.com/mcp   # should 401 with WWW-Authenticate

First steps in chat

  1. agency_list_clients — see discovered clients + any saved alias/override data
  2. agency_set_active_client — e.g. "Bolsover Cruise Club", "https://www.bolsovercruiseclub.com/", a GA4 property ID, or a client key
  3. Platform read tools default to the active client

Local development

Create .env.local with MOTHER_SECRET=... and run pnpm install (postinstall pulls secrets) or pnpm start.

Google user OAuth via secrets

If you want local stdio MCP users to inherit the same Google user access as the hosted admin, add these values to the secrets .env fetched by MOTHER_SECRET:

GOOGLE_USER_OAUTH_REFRESH_TOKEN=
GOOGLE_USER_OAUTH_EMAIL=
GOOGLE_USER_OAUTH_SCOPES=
GOOGLE_USER_OAUTH_UPDATED_AT=

Then the local npx MCP can use the connected Google account without any extra local config beyond MOTHER_SECRET.

GA4 bulk access grant (Andrew / team)

Grant the MCP service account Viewer on every GA4 property in one go:

Admin UI (recommended):

pnpm admin

Open http://localhost:3001GA4 service account access → sign in with Google → done.

CLI:

pnpm ga4:grant-access          # live
pnpm ga4:grant-access:dry      # preview only

One-time Google Cloud setup:

  • Enable Google Analytics Admin API
  • Add this OAuth redirect URI to your existing OAuth client (same one used for Google Ads):
    • http://localhost:3001/oauth2callback (local)
    • https://marketing-mcp.vg-stuff.com/oauth2callback (production)

Andrew must sign in with a Google account that has Manage Users on the client GA4 accounts. Grants are at account level — one click covers all properties in each account.

Deploy (Docker — admin + MCP)

One container serves both the admin UI and the remote MCP on the same host:

Host Purpose
marketing-mcp.vg-stuff.com Admin dashboard and remote MCP at /mcp
cp .env.docker.example .env
# Set MOTHER_SECRET, ADMIN_PASSWORD, MCP_API_KEY

pnpm docker:build
pnpm docker:up

The container listens on 3001 and exposes:

  • / — client registry admin UI
  • /mcp — Streamable HTTP MCP (requires x-api-key)
  • /api/ga4-grant/* — bulk GA4 service account grant
  • /api/health — health check
  • /api/config — public URLs (from src/config/app-url.ts)

Required env vars:

Variable Purpose
MOTHER_SECRET Fetches .env, service accounts from secrets server
ADMIN_PASSWORD Login password for the dashboard
MCP_API_KEY Auth key for remote MCP clients
NODE_ENV=production Admin + MCP → marketing-mcp.vg-stuff.com

Reverse proxy (Caddy):

marketing-mcp.vg-stuff.com {
  reverse_proxy localhost:3001
}

Google OAuth — production redirect URI: https://marketing-mcp.vg-stuff.com/oauth2callback

All URLs derive from src/config/app-url.ts (getAppBaseUrl(), getMcpEndpointUrl()).

GA4 & Search Console access

Google does not offer org-wide service account access. The workaround:

  1. Use the admin “Grant Viewer on all my GA4 accounts” button (or pnpm ga4:grant-access).
  2. Sign in with a human Google account that has Manage Users on client GA4 accounts.
  3. The tool adds the MCP service account as Viewer at the account level — that covers every property in each account, including new ones.

You still need one grant per GA4 account (usually one per client). You cannot skip this entirely — Google requires an explicit invite.

Search Console — preferred user OAuth model

Search Console now works best through the connected Google user account.

If the team only has access through a Gmail / Workspace user and cannot add the service account to many client properties, connect that Google account in the admin UI and export its refresh token into secrets. The MCP will then prefer that Google account automatically for:

  • gsc_list_sites
  • gsc_search_analytics
  • GSC discovery used by agency_list_clients
Search Console — no bulk user-management API

GSC has no API to add users to properties. There is no org-level service account access. Each site must add the service account manually:

  1. GSC → Settings → Users and permissions → Add user → service account email → Full access.
  2. Direct link: https://search.google.com/search-console/users?resource_id=<site_url>

There is no Google-supported way to automate this across all client sites without the property owner adding the email.

Caching model

Client discovery now uses multiple cache layers:

  1. in-memory cache inside the running MCP process
  2. local disk snapshot at ~/.marketing-mcp/.client-directory-cache.json
  3. shared Firestore snapshot cache keyed by Google auth identity
  4. live Google discovery as the final fallback / refresh source

Behavior:

  • return cached snapshot fast
  • refresh stale snapshots in the background
  • update local disk + shared Firestore after live discovery completes

This makes repeated usage and even fresh npx launches much faster after the first heavy Google discovery run.

Notes

  • Google Ads: use a client account customer ID in the registry, not your MCC/manager ID, for campaign and performance reads.
  • GA4 / GSC: prefer connected Google user auth when available; service account remains fallback.
  • Current package version at the time of this update: 1.1.13.

Keywords