npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

marketing-mcp

v1.1.20

Published

Read-only agency Marketing MCP — Google Ads, GA4, GSC, Meta, Ahrefs

Readme

marketing-mcp

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

Current state

As of [email protected], 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 [email protected]) 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.

Recommended behavior

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:3001 → GA4 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

GA4 — bulk grant (recommended)

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.