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

ga4-manager-mcp

v4.1.2

Published

MCP server for organic growth: Google Analytics 4, Search Console, AdSense, on-page SEO audits and DataForSEO, built for AI agents

Readme

GA4 Manager MCP Server

AI-powered GA4 and Google Search Console management through Model Context Protocol

A Model Context Protocol (MCP) server that exposes GA4 Manager CLI commands as 13 structured tools for AI assistants. Configure Google Analytics 4 properties, manage Search Console, and query analytics data using natural language with Claude Code, Claude Desktop, and other MCP clients.

Tests TypeScript MCP SDK Tools


What is This?

This MCP server bridges AI assistants like Claude Code with the GA4 Manager CLI, enabling:

  • Natural language analytics configuration - "Set up conversion tracking for purchases"
  • Automated Search Console management - "Submit my sitemap and check indexing status"
  • Data-driven insights - "Show me top search queries for the last 30 days"
  • Workflow automation - "Validate config, preview setup, then apply if it looks good"

What is MCP?

Model Context Protocol (MCP) is an open protocol that lets AI assistants interact with external tools and data sources. Think of it as an API designed for AI agents.

How it works:

  1. AI assistant (Claude Code) receives user request
  2. MCP client calls tool on MCP server (this project)
  3. MCP server executes GA4 CLI command
  4. Structured JSON response returned to AI assistant
  5. AI assistant uses data to help user

Features

  • ✅ 13 Production-Ready Tools - Complete GA4 and GSC operation coverage
  • ✅ Comprehensive Test Suite - All tools covered by unit and integration tests
  • ✅ Structured JSON Output - All CLI responses parsed to machine-readable format
  • ✅ Intelligent Parsing - Auto-detects and handles JSON, tables, CSV, markdown
  • ✅ Dry-Run Support - Preview changes before applying (safe operations)
  • ✅ Quota Tracking - Monitor Google Search Console API usage
  • ✅ Error Handling - Actionable error messages with suggestions
  • ✅ Multi-Client Support - Works with Claude Desktop, Claude CLI, VS Code, Cursor, Cline

Quick Start

Installation

From npm

claude mcp add ga4-manager \
  --env GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json \
  --env GOOGLE_CLOUD_PROJECT=your-gcp-project-id \
  -- npx -y ga4-manager-mcp

Native tools (GA4 reports, Search Console comparisons, AdSense, SEO audits, DataForSEO) work with just this. CLI-backed tools (ga4_setup, gsc_health, gsc_decay…) also need the ga4 binary: download it from GitHub Releases and put it on your PATH or set GA4_BINARY_PATH. Destructive tools stay disabled until you add --env GA4M_ALLOW_DESTRUCTIVE=1.

From source

# 1. Clone repository
git clone https://github.com/garbarok/ga4-manager.git
cd ga4-manager

# 2. Build CLI binary
make build

# 3. Build MCP server
cd mcp
npm install
npm run build

Configuration

Choose your client and follow the setup:

Claude CLI (Recommended)

claude mcp add \
  --name ga4-manager \
  --transport stdio \
  --command "node" \
  --args "/absolute/path/to/ga4-manager/mcp/dist/index.js" \
  --env "GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json" \
  --env "GOOGLE_CLOUD_PROJECT=your-gcp-project-id" \
  --env "GA4_BINARY_PATH=/absolute/path/to/ga4-manager/ga4" \
  --env "GA4_DEFAULT_PROPERTY_ID=123456789"

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "ga4-manager": {
      "command": "node",
      "args": ["/absolute/path/to/ga4-manager/mcp/dist/index.js"],
      "env": {
        "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/credentials.json",
        "GOOGLE_CLOUD_PROJECT": "your-gcp-project-id",
        "GA4_BINARY_PATH": "/absolute/path/to/ga4-manager/ga4",
        "GA4_DEFAULT_PROPERTY_ID": "123456789"
      }
    }
  }
}

Other Clients

See CONFIGURATION.md for setup guides:

  • VS Code (via MCP extension)
  • Cursor
  • Cline
  • Custom integrations

Verify Installation

Claude Desktop:

  • Restart Claude Desktop
  • Click 🔌 icon in bottom-left
  • Should show "ga4-manager" with 29 tools

Claude CLI:

claude mcp list
# Should show: ga4-manager (29 tools)

VS Code/Cursor:

  • Check MCP status indicator in status bar

Available Tools (29)

Tool Categories

Growth planning (1 tool) - Start here for SEO / monetization plans

  • site_growth_brief - One compact brief per site: GSC + GA4 traffic + AdSense-in-GA4 revenue KPIs with deltas, top pages by revenue, and the top 10 diagnostic findings ranked by estimated clicks

GA4 Analytics (8 tools) - Configuration, management and traffic

  • ga4_setup - Create conversions, dimensions, metrics
  • ga4_report - View current configuration
  • ga4_cleanup - Remove unused resources
  • ga4_link_list - List existing external-service links
  • ga4_link_create - Create a Search Console/BigQuery/Channels link
  • ga4_link_remove - Remove (unlink) a BigQuery/Channels link
  • ga4_validate - Configuration validation
  • ga4_traffic_report - Sessions, engagement, page views and ad revenue (when AdSense is linked) by page, landing page, channel, source, country, device or date

Search Console (14 tools) - SEO and indexing

  • gsc_sitemaps_list - List all sitemaps
  • gsc_sitemaps_submit - Submit new sitemap
  • gsc_sitemaps_delete - Remove sitemap
  • gsc_sitemaps_get - Get sitemap details
  • gsc_inspect_url - Check URL indexing status
  • gsc_analytics_run - Search performance data
  • gsc_monitor_urls - Batch URL monitoring
  • gsc_index_coverage - Index coverage report
  • gsc_cannibalization - Detect keyword cannibalization across URLs
  • gsc_opportunities - Surface ranking/CTR improvement opportunities (per query, or per page with granularity: "page")
  • gsc_ctr_anomaly - Flag pages with anomalous CTR for their position
  • gsc_decay - Flag pages that slipped in position and lost clicks vs the prior window
  • gsc_health - Search Console health overview
  • gsc_url_hygiene - URLs with impressions that should not be indexed (fragments, asset routes, malformed paths, query duplicates)
  • gsc_hreflang - hreflang pair integrity and cross-language ranking

Diagnostics & SEO (4 tools) - Traffic analysis and page auditing

  • gsc_traffic_compare - Diff GSC traffic between two date ranges per URL, or before/after a change date (around) with an optional pages filter
  • ga4_consent_health - Consent banner grant/deny rates and health score
  • seo_page_audit - On-page SEO audit with optional Core Web Vitals; render_js: true fetches the post-JavaScript DOM via Cloudflare Browser Rendering for JS-rendered/SPA pages
  • seo_audit_batch - Batch on-page SEO audit over a sitemap or URL list; supports render_js, include/exclude regex filters and SiteOne crawl discovery (discover.siteone)

AdSense (2 tools) - Publisher earnings reporting

  • adsense_accounts_list - List accessible AdSense publisher accounts
  • adsense_report - Earnings / page views / impressions / clicks / RPM by date, domain, country, ad unit

Keyword research (3 tools) - DataForSEO / ValueSERP, opt-in third-party credentials

  • keyword_volume - Search volume, CPC and competition for a list of keywords (DataForSEO)
  • keyword_serp_snapshot - Who currently ranks for a keyword, for content-gap/competitive research (DataForSEO)
  • keyword_rank_check - A domain's live Google rank for a keyword today, distinct from GSC's historical average position (ValueSERP)

Competitive & AI-visibility research (9 tools) - DataForSEO, opt-in; every result reports cost_usd

  • dataforseo_balance - Remaining DataForSEO balance (free)
  • llm_mentions - Whether Google AI answers / ChatGPT mention or cite a domain for given queries, and who they cite instead
  • keyword_gap - Keywords a competitor ranks for that you don't, with volume and difficulty
  • ranked_keywords - Every keyword and position for any domain, competitors included
  • keyword_ideas - Keyword ideas with volume, difficulty (KD) and search intent
  • google_trends - Interest over time (0–100) for up to 5 terms: rising/falling, seasonality, peak date
  • backlinks_overview - Domain rank, backlinks and referring domains, side by side
  • link_gap - Referring domains that link to competitors but not to you (outreach targets)
  • dataforseo_request - Gated escape hatch: any DataForSEO v3 endpoint by path; billed per call, off unless DATAFORSEO_ALLOW_RAW=1

Indexing (1 tool) - IndexNow, no account needed (just a key file on your site)

  • indexnow_submit - Notify Bing, Yandex, Seznam, Naver, Yep and others (not Google) about new or changed URLs. dry_run defaults to true; the key file is verified before submitting

Tool Operation Types

Read-Only (Safe):

  • ga4_report, ga4_validate, ga4_link_list, gsc_sitemaps_list, gsc_sitemaps_get, gsc_inspect_url, gsc_analytics_run, gsc_monitor_urls, gsc_index_coverage, gsc_cannibalization, gsc_opportunities, gsc_ctr_anomaly, gsc_health, gsc_traffic_compare, ga4_consent_health, seo_page_audit, seo_audit_batch, adsense_accounts_list, adsense_report, site_growth_brief, ga4_traffic_report, gsc_url_hygiene, gsc_hreflang, keyword_volume, keyword_serp_snapshot, keyword_rank_check, dataforseo_balance, llm_mentions, keyword_gap, ranked_keywords, keyword_ideas, google_trends, backlinks_overview, link_gap, dataforseo_request
  • ga4_report, ga4_validate, ga4_link_list, gsc_sitemaps_list, gsc_sitemaps_get, gsc_inspect_url, gsc_analytics_run, gsc_monitor_urls, gsc_index_coverage, gsc_cannibalization, gsc_opportunities, gsc_ctr_anomaly, gsc_decay, gsc_health, gsc_traffic_compare, ga4_consent_health, seo_page_audit, seo_audit_batch, adsense_accounts_list, adsense_report, site_growth_brief, ga4_traffic_report, gsc_url_hygiene, gsc_hreflang, keyword_volume, keyword_serp_snapshot, keyword_rank_check, dataforseo_balance, llm_mentions, keyword_gap, ranked_keywords, keyword_ideas, google_trends, backlinks_overview, link_gap

Modifying (Use with caution):

  • indexnow_submit (dry-run by default), ga4_setup, ga4_cleanup, ga4_link_create, ga4_link_remove, gsc_sitemaps_submit, gsc_sitemaps_delete

Always use dry-run first for modifying operations!


Tool Documentation

GA4 Tools

ga4_setup - Setup GA4/GSC Configuration

Creates conversions, dimensions, metrics, and Search Console configuration from YAML file.

Input Schema:

{
  config_path?: string;  // Path to YAML config file
  project_name?: string; // Project name (alternative to config_path)
  all?: boolean;         // Setup all configs in configs/ directory
  dry_run?: boolean;     // Preview without applying (recommended)
}

Example Request:

{
  "config_path": "configs/example-site.yaml",
  "dry_run": true
}

Example Response:

{
  "success": true,
  "operation": "setup",
  "dry_run": true,
  "project": {
    "name": "Example Site",
    "property_id": "123456789"
  },
  "results": {
    "ga4": {
      "conversions_created": 5,
      "conversions_skipped": 2,
      "dimensions_created": 8,
      "metrics_created": 3
    },
    "gsc": {
      "sitemaps_submitted": 2,
      "urls_monitored": 10
    }
  },
  "execution_time_ms": 2345,
  "message": "Dry-run complete. No changes applied."
}

Use Cases:

  • Initial GA4 property setup
  • Deploying analytics to new environment
  • Bulk configuration changes

ga4_report - View Configuration

Lists existing conversions, dimensions, and metrics for a GA4 property.

Input Schema:

{
  config_path?: string;  // Path to YAML config
  project_name?: string; // Project name
  all?: boolean;         // Report on all configs
}

Example Request:

{
  "config_path": "configs/example-site.yaml"
}

Example Response:

{
  "success": true,
  "operation": "report",
  "project": {
    "name": "Example Site",
    "property_id": "123456789"
  },
  "report": {
    "conversions": [
      {
        "name": "purchase",
        "counting_method": "ONCE_PER_EVENT",
        "status": "active"
      }
    ],
    "dimensions": [
      {
        "parameter": "user_tier",
        "display_name": "User Tier",
        "scope": "USER",
        "status": "active"
      }
    ],
    "metrics": [
      {
        "parameter": "cart_value",
        "display_name": "Cart Value",
        "unit": "CURRENCY",
        "scope": "EVENT"
      }
    ]
  }
}

Use Cases:

  • Audit existing configuration
  • Verify setup completed successfully
  • Document current state

ga4_cleanup - Remove Unused Resources

Archives unused conversions, dimensions, and metrics to free up quota.

Input Schema:

{
  config_path?: string;     // Path to YAML config
  project_name?: string;    // Project name
  all?: boolean;            // Cleanup all configs
  type?: string;            // "conversions" | "dimensions" | "metrics" | "all"
  dry_run?: boolean;        // Preview deletions (recommended)
  yes?: boolean;            // Auto-confirm (use with caution)
}

Example Request:

{
  "config_path": "configs/example-site.yaml",
  "type": "all",
  "dry_run": true
}

Example Response:

{
  "success": true,
  "operation": "cleanup",
  "dry_run": true,
  "items_to_remove": {
    "conversions": ["old_event_1", "deprecated_conversion"],
    "dimensions": ["unused_param"],
    "metrics": []
  },
  "quota_freed": {
    "conversions": 2,
    "dimensions": 1,
    "metrics": 0
  },
  "warning": "Archived parameters cannot be reused. Consider new names (e.g., user_type_v2)."
}

Important:

  • Archived dimension/metric parameters are permanently reserved (GA4 limitation)
  • Always use dry_run: true first
  • Consider using new parameter names instead of archiving

Use Cases:

  • Free up quota for new items
  • Remove deprecated tracking
  • Clean up test configurations

External Service Integration (ga4_link_list / ga4_link_create / ga4_link_remove)

Linking is split into three tools so that read, write, and delete operations are distinct (this is required for the Claude connector directory — a single tool may not mix safe and unsafe operations). All three dispatch to the same ga4 link CLI command.

ga4_link_list - List Links (read-only)

Lists existing BigQuery export links and custom Channel Groups. (Search Console links can't be listed via the GA4 Admin API and are reported as manual_check_required.)

{
  project_name: string;     // Required: Project name
}

Example Request:

{
  "project_name": "example-site"
}
ga4_link_create - Create a Link (modifying)

Creates a BigQuery export link, sets up default Channel Groups, or returns a Search Console setup guide.

{
  project_name: string;     // Required: Project name
  service: string;          // Required: "search-console" | "bigquery" | "channels"
  url?: string;             // Site URL (required for search-console)
  gcp_project?: string;     // GCP project (required for bigquery)
  dataset?: string;         // BigQuery dataset (required for bigquery)
}

Example Request (Link Search Console):

{
  "project_name": "example-site",
  "service": "search-console",
  "url": "https://example.com"
}
ga4_link_remove - Remove a Link (destructive)

Unlinks an existing BigQuery export or Channel Group. (Search Console links cannot be removed via the Admin API.)

{
  project_name: string;     // Required: Project name
  service: string;          // Required: "bigquery" | "channels"
}

Example Request:

{
  "project_name": "example-site",
  "service": "bigquery"
}

Use Cases:

  • Connect GA4 to Search Console
  • Setup BigQuery export
  • Configure custom channel groups

ga4_validate - Configuration Validation

Validates YAML configuration files against GA4 tier limits and naming rules.

Input Schema:

{
  config_file?: string;  // Path to config file
  all?: boolean;         // Validate all configs in configs/
  verbose?: boolean;     // Detailed validation output
}

Example Request:

{
  "config_file": "configs/example-site.yaml",
  "verbose": true
}

Example Response:

{
  "success": true,
  "operation": "validate",
  "file": "configs/example-site.yaml",
  "validation": {
    "valid": true,
    "errors": [],
    "warnings": [
      "Property tier 'standard' allows max 30 conversions (you have 5)"
    ],
    "details": {
      "conversions": 5,
      "dimensions": 8,
      "metrics": 3,
      "tier": "standard",
      "limits": {
        "conversions": 30,
        "dimensions": 50,
        "metrics": 50
      }
    }
  }
}

Use Cases:

  • Verify config before setup
  • Check quota usage
  • Identify configuration errors

Google Search Console Tools

gsc_sitemaps_list - List Sitemaps

Lists all sitemaps submitted for a Search Console property.

Input Schema:

{
  site: string;  // Required: "sc-domain:example.com" or "https://example.com/"
}

Example Response:

{
  "success": true,
  "operation": "sitemaps_list",
  "site": "sc-domain:example.com",
  "sitemaps": [
    {
      "path": "https://example.com/sitemap.xml",
      "type": "sitemapsIndex",
      "submitted": "2024-12-01T10:00:00Z",
      "last_downloaded": "2024-12-27T15:30:00Z",
      "is_pending": false,
      "is_sitemaps_index": true,
      "contents": {
        "submitted": 1500,
        "indexed": 1450
      }
    }
  ],
  "total": 1
}

gsc_sitemaps_submit - Submit Sitemap

Submits a new sitemap URL to Search Console for crawling.

Input Schema:

{
  site: string;  // Required: Site URL
  url: string;   // Required: Sitemap URL
}

Example Request:

{
  "site": "sc-domain:example.com",
  "url": "https://example.com/sitemap.xml"
}

Example Response:

{
  "success": true,
  "operation": "sitemap_submit",
  "site": "sc-domain:example.com",
  "sitemap_url": "https://example.com/sitemap.xml",
  "message": "Sitemap submitted successfully"
}

gsc_sitemaps_delete - Delete Sitemap

Removes a sitemap from Search Console (does not delete the actual file).

Input Schema:

{
  site: string;  // Required: Site URL
  url: string;   // Required: Sitemap URL to remove
}

gsc_sitemaps_get - Get Sitemap Details

Retrieves detailed information about a specific sitemap including errors and content breakdown.

Input Schema:

{
  site: string;  // Required: Site URL
  url: string;   // Required: Sitemap URL
}

Example Response:

{
  "success": true,
  "operation": "sitemap_get",
  "sitemap": {
    "path": "https://example.com/sitemap.xml",
    "type": "sitemapsIndex",
    "submitted": "2024-12-01T10:00:00Z",
    "last_downloaded": "2024-12-27T15:30:00Z",
    "errors": 0,
    "warnings": 2,
    "contents": {
      "web_pages": {
        "submitted": 1000,
        "indexed": 950
      },
      "images": {
        "submitted": 500,
        "indexed": 500
      }
    }
  }
}

gsc_inspect_url - URL Indexing Inspection

Inspects a URL's indexing status, mobile usability, rich results, and coverage state.

Input Schema:

{
  site: string;  // Required: "sc-domain:example.com" (domain property recommended)
  url: string;   // Required: Full URL to inspect
}

Example Request:

{
  "site": "sc-domain:example.com",
  "url": "https://example.com/products/widget"
}

Example Response:

{
  "success": true,
  "operation": "inspect_url",
  "url": "https://example.com/products/widget",
  "inspection_result": {
    "index_status": {
      "verdict": "PASS",
      "coverage_state": "Submitted and indexed",
      "crawled_as": "DESKTOP",
      "last_crawl_time": "2024-12-27T10:30:00Z",
      "page_fetch_state": "SUCCESSFUL",
      "robots_txt_state": "ALLOWED",
      "indexing_state": "INDEXING_ALLOWED"
    },
    "mobile_usability": {
      "verdict": "PASS",
      "issues": []
    },
    "rich_results": {
      "verdict": "PASS",
      "detected_items": [
        {
          "type": "Product",
          "name": "Widget",
          "issues": []
        }
      ]
    },
    "amp": {
      "verdict": "NOT_APPLICABLE"
    }
  },
  "quota": {
    "used": 1,
    "limit": 2000,
    "remaining": 1999,
    "percentage": 0.05
  }
}

Rate Limits:

  • 2,000 inspections per day per property
  • 600 inspections per minute per property

Use Cases:

  • Check why page isn't indexed
  • Verify mobile usability
  • Validate rich results markup
  • Monitor critical pages

gsc_analytics_run - Search Analytics Report

Queries Search Console for performance data: queries, pages, impressions, clicks, CTR, and position.

Input Schema:

{
  site?: string;            // Site URL (or from config)
  config?: string;          // Config file path (alternative)
  days?: number;            // Period: 1-180 days (default: 30)
  dimensions?: string;      // Comma-separated: "query,page,country,device"
  limit?: number;           // Max rows: 1-25000 (default: 100)
  format?: string;          // Output: "json" | "csv" | "table" | "markdown"
  dry_run?: boolean;        // Preview query parameters
}

Example Request:

{
  "site": "sc-domain:example.com",
  "days": 30,
  "dimensions": "query,page",
  "limit": 50,
  "format": "json"
}

Example Response:

{
  "success": true,
  "operation": "gsc_analytics",
  "site_url": "sc-domain:example.com",
  "period": "2024-11-27 to 2024-12-27",
  "total_rows": 50,
  "aggregates": {
    "total_clicks": 1250,
    "total_impressions": 15000,
    "average_ctr": 0.0833,
    "average_position": 8.5
  },
  "rows": [
    {
      "keys": ["best analytics tool", "https://example.com/"],
      "clicks": 45,
      "impressions": 500,
      "ctr": 0.09,
      "position": 3.2
    },
    {
      "keys": ["ga4 setup guide", "https://example.com/blog/ga4-guide"],
      "clicks": 38,
      "impressions": 420,
      "ctr": 0.09,
      "position": 4.1
    }
  ],
  "quota": {
    "used": 1,
    "limit": 2000,
    "remaining": 1999
  }
}

Available Dimensions:

  • query - Search query
  • page - Landing page URL
  • country - User country (ISO)
  • device - DESKTOP, MOBILE, TABLET
  • searchAppearance - How result appeared
  • date - Individual dates

Limits:

  • Max 3 dimensions per query
  • Data typically 2-3 days behind
  • 25,000 rows maximum

Use Cases:

  • Identify top-performing content
  • Find ranking opportunities
  • Track keyword performance
  • Analyze traffic patterns

gsc_monitor_urls - Batch URL Monitoring

Monitors multiple priority URLs for indexing issues using configuration file or direct URL array.

Input Schema (Config-based):

{
  config: string;     // Path to YAML config with priority_urls
  format?: string;    // "json" | "table" | "markdown"
  dry_run?: boolean;  // Preview URLs without inspecting
}

Input Schema (Direct URLs):

{
  site: string;       // Site URL
  urls: string[];     // Array of URLs to inspect (max 50)
  dry_run?: boolean;  // Preview without API calls
}

Example Request (Direct):

{
  "site": "sc-domain:example.com",
  "urls": [
    "https://example.com/",
    "https://example.com/products/",
    "https://example.com/blog/"
  ]
}

Example Response:

{
  "success": true,
  "operation": "monitor_urls",
  "site": "sc-domain:example.com",
  "summary": {
    "total": 3,
    "indexed": 3,
    "not_indexed": 0,
    "with_issues": 0
  },
  "results": [
    {
      "url": "https://example.com/",
      "label": "Homepage",
      "verdict": "PASS",
      "coverage_state": "Submitted and indexed",
      "mobile_usability": "PASS",
      "last_crawl": "2024-12-27T10:00:00Z",
      "issues": []
    }
  ],
  "quota": {
    "used": 3,
    "limit": 2000,
    "remaining": 1997
  }
}

Use Cases:

  • Monitor critical pages daily
  • Detect indexing issues early
  • Track mobile usability
  • Audit site after deployment

gsc_index_coverage - Index Coverage Report

Generates index coverage analysis showing indexed, excluded, and error pages based on Search Analytics impressions data.

Input Schema:

{
  site?: string;      // Site URL (or from config)
  config?: string;    // Config file path
  days?: number;      // Analysis period: 1-180 (default: 90)
  limit?: number;     // Sample size: 1-1000 (default: 1000)
  format?: string;    // "json" | "table" | "markdown"
}

Example Request:

{
  "site": "sc-domain:example.com",
  "days": 90,
  "limit": 1000,
  "format": "json"
}

Example Response:

{
  "success": true,
  "operation": "index_coverage",
  "site": "sc-domain:example.com",
  "period": "2024-09-28 to 2024-12-27",
  "analysis": {
    "total_pages": 1000,
    "indexed": 850,
    "low_impressions": 100,
    "no_impressions": 50
  },
  "classification": {
    "healthy": 850,
    "needs_review": 100,
    "excluded": 50
  },
  "top_pages": [
    {
      "url": "https://example.com/",
      "impressions": 5000,
      "clicks": 450,
      "status": "indexed"
    }
  ],
  "recommendations": [
    "50 pages with no impressions may need content improvement or removal",
    "100 pages with low impressions could benefit from SEO optimization"
  ]
}

How It Works:

  • Queries Search Analytics for page impressions
  • Classifies pages: ≥10 impressions = indexed, 1-9 = low, 0 = none
  • Samples up to 1,000 pages for performance
  • Estimates overall index health

Use Cases:

  • Monitor index health trends
  • Identify indexing issues
  • Track coverage after site updates
  • Estimate indexed page count

Diagnostics & SEO Tools

gsc_traffic_compare - Compare GSC Traffic Between Date Ranges

Diffs Google Search Console search analytics between two date ranges per URL to surface the biggest drops and gains.

Before/after mode: instead of the two periods, pass around: { date, window_days?, gap_days? } (window_days 7-90, default 28; gap_days 0-14, default 0) to compare the window ending the day before date with the window starting at date + gap_days. Use a GA4 annotation date as date for a SEOTesting-style test. If the after window would end within 3 days of today, both windows are shortened equally with a warning. Add pages: string[] (exact URL match after normalize) to target the changed pages; with around or pages the response includes a before_after block with total clicks, impressions, CTR and position and their deltas. Passing both around and period_a/period_b, or neither, is an error.

Input Schema:

{
  site: string;                     // Required: "sc-domain:example.com" or URL-prefix
  period_a_start: string;           // Required: ISO date "YYYY-MM-DD"
  period_a_end: string;             // Required: ISO date "YYYY-MM-DD"
  period_b_start: string;           // Required: ISO date "YYYY-MM-DD"
  period_b_end: string;             // Required: ISO date "YYYY-MM-DD"
  fetch_limit?: number;             // Max URLs to fetch per period (default 500)
  output_limit?: number;            // Max rows in output (default 50)
  sort_by?: "clicks" | "impressions" | "delta"; // Sort mode (default "clicks")
  min_clicks_a?: number;            // Minimum clicks in period A to include URL
  url_normalization?: "none" | "minimal" | "aggressive";
}

Example Response:

{
  "success": true,
  "site": "sc-domain:example.com",
  "period_a": { "start": "2024-12-01", "end": "2024-12-14" },
  "period_b": { "start": "2024-11-17", "end": "2024-11-30" },
  "summary": { "urls_analyzed": 120, "total_click_delta": -340 },
  "rows": [
    {
      "url": "https://example.com/pricing",
      "clicks_a": 45, "clicks_b": 120,
      "delta_clicks": -75, "delta_pct": -62.5
    }
  ]
}

ga4_consent_health - Consent Banner Health Report

Reports consent banner grant/deny rates and a composite health score from consent_granted / consent_denied custom events in GA4.

Input Schema:

{
  property_id: string;  // Required: GA4 property ID (e.g. "123456789")
  days?: number;        // Lookback window in days (default 30)
}

Example Response:

{
  "success": true,
  "property_id": "123456789",
  "period_days": 30,
  "consent_rate_pct": 72.4,
  "consent_visibility_pct": 89.1,
  "health_score": 64.5,
  "granted": 14821,
  "denied": 5641,
  "note": "Uses consent_granted/consent_denied custom events. Requires Consent Mode v2 event instrumentation."
}

seo_page_audit - On-Page SEO Audit

Single-URL on-page SEO audit: title/description length and pixel-width checks, canonical severity escalation, JSON-LD extraction, redirect chain tracing, robots.txt respect, and optional Core Web Vitals via PageSpeed Insights.

Input Schema:

{
  url: string;                    // Required: page URL to audit
  include_cwv?: boolean;          // Include Core Web Vitals via PSI (default false)
  psi_api_key?: string;           // PageSpeed Insights API key (required for CWV)
  strategy?: "mobile" | "desktop"; // PSI strategy (default "mobile")
}

Example Response:

{
  "success": true,
  "url": "https://example.com/pricing",
  "issues": [
    { "type": "title_too_long", "severity": "warning", "value": "62 chars / 498px" },
    { "type": "canonical_mismatch", "severity": "error", "canonical": "https://example.com/pricing/" }
  ],
  "meta": {
    "title": "Pricing - Example",
    "description": "Choose the right plan.",
    "canonical": "https://example.com/pricing/"
  },
  "redirects": [],
  "schema_types": ["Product", "BreadcrumbList"]
}

AdSense Tools

Publisher-side reporting via the AdSense Management API v2. These tools answer "how much do the ads on my own site earn" — the opposite of the advertiser-side Google Ads API. They are read-only and need no developer token or Manager account: just the adsense.readonly OAuth scope on the same credentials used for GA4/GSC (see PERMISSIONS.md).

Scope: Add https://www.googleapis.com/auth/adsense.readonly to your credential. For an ADC user credential, re-run gcloud auth application-default login --scopes=...,https://www.googleapis.com/auth/adsense.readonly. Note: a personal AdSense account cannot be read with a service-account key — use ADC user credentials.

adsense_accounts_list - List AdSense Accounts

Lists the AdSense publisher accounts the authenticated identity can access. Call this first — the returned name (accounts/pub-XXXXXXXXXXXXXXXX) is the account argument every other AdSense tool needs. No parameters.

Example Response:

{
  "success": true,
  "accounts": [
    { "name": "accounts/pub-1234567890123456", "displayName": "example.com", "state": "READY", "timeZone": { "id": "America/Los_Angeles" } }
  ]
}

adsense_report - Generate AdSense Report

Generates an earnings report for one account, returning each row as a header → value map plus totals.

Input Schema:

{
  account: string;        // Required: "accounts/pub-..." from adsense_accounts_list
  date_range?:            // Preset window (default "LAST_7_DAYS")
    | "CUSTOM" | "TODAY" | "YESTERDAY" | "MONTH_TO_DATE" | "YEAR_TO_DATE"
    | "LAST_7_DAYS" | "LAST_30_DAYS";
                          // Anything longer (e.g. last 3 months): use "CUSTOM".
                          // The AdSense API rejects LAST_MONTH / LAST_3_MONTHS /
                          // LAST_6_MONTHS / LAST_12_MONTHS / LAST_YEAR.
  start_date?: string;    // YYYY-MM-DD, required when date_range = "CUSTOM"
  end_date?: string;      // YYYY-MM-DD, required when date_range = "CUSTOM"
  metrics?: string[];     // default ESTIMATED_EARNINGS, PAGE_VIEWS, IMPRESSIONS, CLICKS, IMPRESSIONS_RPM
  dimensions?: string[];  // default ["DATE"]; e.g. DOMAIN_NAME, COUNTRY_NAME, AD_UNIT_NAME
  order_by?: string[];    // sort before limit; requested metric/dimension, "-" = desc,
                          // e.g. ["-ESTIMATED_EARNINGS"] for top earners first
  currency_code?: string; // ISO-4217, e.g. "USD" (defaults to account currency)
  limit?: number;         // max rows (default 100, max 1000)
}

Example Response:

{
  "success": true,
  "account": "accounts/pub-1234567890123456",
  "date_range": "LAST_7_DAYS",
  "start_date": "2026-06-19",
  "end_date": "2026-06-25",
  "headers": ["DATE", "ESTIMATED_EARNINGS", "PAGE_VIEWS", "IMPRESSIONS", "CLICKS", "IMPRESSIONS_RPM"],
  "rows": [
    { "DATE": "2026-06-19", "ESTIMATED_EARNINGS": "1.23", "PAGE_VIEWS": "842", "IMPRESSIONS": "910", "CLICKS": "7", "IMPRESSIONS_RPM": "1.35" }
  ],
  "totals": { "ESTIMATED_EARNINGS": "9.81", "PAGE_VIEWS": "5894" },
  "warnings": []
}

Growth, Traffic & Index-Quality Tools

site_growth_brief - Per-Site Growth Brief

The starting point for an SEO or monetization plan. For one config (or every configs/*.yaml with all: true, examples excluded) it joins Search Console, GA4 traffic and AdSense-in-GA4 revenue into a compact brief (≤ ~4 KB per site):

  • kpis — GSC clicks, impressions, CTR, position; GA4 sessions, engagement rate; ad revenue and revenue per 1k sessions
  • deltas — % change vs the preceding window of the same length
  • top_pages — up to 10 pages joined on path (GSC clicks/impressions, sessions, ad revenue), ranked by revenue then clicks
  • findings — top 10 from page-level opportunities, CTR anomalies, URL-hygiene warnings and (when hreflang_pairs are configured) hreflang warnings, ranked by estimated clicks at stake
  • data_gaps — sources that were not configured or failed; the brief still returns the rest
{ config?: string; all?: boolean; days?: number /* 7–240, default 28 */ }

Revenue is GA4-attributed (totalAdRevenue), so it needs AdSense linked to the GA4 property; use adsense_report for payout figures. The brief never calls URL Inspection or the AdSense API.

ga4_traffic_report - GA4 Traffic & Ad Revenue

GA4 Data API report by up to two of pagePath, landingPage, sessionDefaultChannelGroup, sessionSource, country, deviceCategory, date. Every row carries sessions, engaged_sessions, engagement_rate, average_session_duration_s, page_views, ad_revenue, ad_impressions, ad_clicks and revenue_per_1k_sessions, plus totals. The warning ad_revenue_unavailable means no AdSense impressions were attributed (not linked, or no ads served).

{
  property_id?: string;   // "123456789" or "properties/123456789"
  config?: string;        // alternative: uses the config's ga4.property_id
  days?: number;          // 1–365, default 28, ending yesterday
  dimensions?: string[];  // default ["pagePath"]
  order_by?: "sessions" | "page_views" | "ad_revenue" | "engagement_rate"; // desc, default sessions
  limit?: number;         // 1–1000, default 50
}

gsc_opportunities with granularity: "page"

Page mode ranks whole pages instead of query × page rows. It catches high-impression pages whose traffic is spread over anonymized long-tail queries, where no single query looks like an opportunity. The expected CTR is the higher of the bucket's site median (≥3 peer pages above min_impressions) and the industry baseline, so weak neighbours never excuse a page. Each result adds top_queries (up to 5) and anonymized_share (0–1). Costs 2 Search Analytics calls.

gsc_url_hygiene - Index-Polluting URLs

Classifies every URL with impressions (default 90 days) into at most one of malformed_path, fragment, asset_route, query_duplicate, utility_page (info). Each result has a one-line fix. Exit 2 / results non-empty on any warning.

gsc_hreflang - hreflang Integrity & Cross-Language Ranking

Uses search_console.hreflang_pairs from the config, or discovers pairs by fetching the top max_pages pages (default 50) and reading their <link rel="alternate" hreflang> tags. Reports missing_return_link, missing_self_reference, wrong_target, missing_x_default (info), and cross_language_ranking (a query in one pair language getting ≥ min_impressions on the other language's page). Fetches only the configured site's host, as ga4-manager/<version>, max 4 at a time.

search_console:
  site_url: "sc-domain:example.com"
  hreflang_pairs:
    - en: "https://www.example.com/calculator"
      es: "https://www.example.com/es/calculadora"

Configuration Files

YAML Config Structure

All tools support YAML configuration files for project settings:

project:
  name: Example Site
  description: Production analytics configuration
  version: 1.0.0

# GA4 Configuration
ga4:
  property_id: "123456789"
  tier: standard  # or "360"

conversions:
  - name: purchase
    counting_method: ONCE_PER_EVENT
    description: User completed purchase

  - name: signup
    counting_method: ONCE_PER_SESSION
    description: User created account

dimensions:
  - parameter: user_tier
    display_name: User Tier
    description: User subscription level
    scope: USER

  - parameter: product_category
    display_name: Product Category
    scope: EVENT

metrics:
  - parameter: cart_value
    display_name: Cart Value
    description: Total cart value
    unit: CURRENCY
    scope: EVENT

# Search Console Configuration
search_console:
  site_url: "https://example.com"

sitemaps:
  - url: "https://example.com/sitemap.xml"
    priority: true

monitoring:
  priority_urls:
    - url: "https://example.com/"
      label: "Homepage"
    - url: "https://example.com/products/"
      label: "Products Landing Page"

Example Templates

Ready-to-use templates in examples/:

  • minimal.yaml - Basic setup for learning
  • blog.yaml - Content sites (5 conversions, 5 dimensions, 3 metrics)
  • ecommerce.yaml - Online stores (7 conversions, 6 dimensions, 4 metrics)
  • saas.yaml - Web applications (6 conversions, 5 dimensions, 3 metrics)

See examples/README.md for template documentation.


Usage Workflows

Initial Setup Workflow

// 1. Validate configuration
await ga4_validate({
  config_file: "configs/example-site.yaml",
  verbose: true
});

// 2. Preview setup (dry-run)
await ga4_setup({
  config_path: "configs/example-site.yaml",
  dry_run: true
});

// 3. Review output, then apply
await ga4_setup({
  config_path: "configs/example-site.yaml",
  dry_run: false
});

// 4. Verify with report
await ga4_report({
  config_path: "configs/example-site.yaml"
});

// 5. Submit sitemaps
await gsc_sitemaps_submit({
  site: "sc-domain:example.com",
  url: "https://example.com/sitemap.xml"
});

Analytics Analysis Workflow

// 1. Check index coverage
const coverage = await gsc_index_coverage({
  site: "sc-domain:example.com",
  days: 90,
  limit: 1000
});

// 2. Get search performance
const analytics = await gsc_analytics_run({
  site: "sc-domain:example.com",
  days: 30,
  dimensions: "query,page",
  limit: 100
});

// 3. Inspect top pages
for (const page of analytics.rows.slice(0, 5)) {
  await gsc_inspect_url({
    site: "sc-domain:example.com",
    url: page.keys[1]  // page URL
  });
}

Monitoring Workflow

// 1. Monitor critical URLs
const monitoring = await gsc_monitor_urls({
  site: "sc-domain:example.com",
  urls: [
    "https://example.com/",
    "https://example.com/products/",
    "https://example.com/blog/"
  ]
});

// 2. If issues found, inspect details
if (monitoring.summary.with_issues > 0) {
  for (const result of monitoring.results) {
    if (result.issues.length > 0) {
      console.log(`Issues on ${result.url}:`, result.issues);
    }
  }
}

Best Practices for AI Assistants

1. Always Use Dry-Run First

Before any modifying operation (setup, cleanup, delete), preview changes:

// ❌ DON'T do this first
await ga4_setup({ config_path: "config.yaml" });

// ✅ DO this instead
await ga4_setup({ config_path: "config.yaml", dry_run: true });
// Review output, then apply:
await ga4_setup({ config_path: "config.yaml", dry_run: false });

2. Validate Configurations

Always validate before setup:

await ga4_validate({ config_file: "config.yaml", verbose: true });

3. Monitor Quota Usage

Check quota.remaining in responses:

const result = await gsc_inspect_url({ ... });
if (result.quota.remaining < 100) {
  console.warn("Low GSC quota remaining:", result.quota.remaining);
}

4. Use Specific Dimensions

Request only needed dimensions to reduce response size:

// ✅ Good - specific dimensions
await gsc_analytics_run({
  dimensions: "query,page",
  limit: 50
});

// ❌ Avoid - too many dimensions
await gsc_analytics_run({
  dimensions: "query,page,country,device,date",
  limit: 25000  // Also too many rows
});

5. Handle Errors Gracefully

All errors include actionable suggestions:

try {
  await ga4_setup({ config_path: "config.yaml" });
} catch (error) {
  console.error(error.code);        // e.g., "AUTH_ERROR"
  console.error(error.message);     // Human-readable message
  console.error(error.suggestion);  // How to fix
}

6. Provide Context to Users

When presenting results, explain what the data means:

const analytics = await gsc_analytics_run({ ... });

// ❌ Don't just dump raw data
console.log(analytics);

// ✅ Explain the insights
console.log(`Top query: "${analytics.rows[0].keys[0]}" with ${analytics.rows[0].clicks} clicks`);
console.log(`Average CTR: ${(analytics.aggregates.average_ctr * 100).toFixed(2)}%`);

Error Handling

Error Response Structure

All errors follow this format:

{
  "code": "AUTH_ERROR",
  "message": "Missing Google credentials",
  "details": {
    "suggestion": "Set GOOGLE_APPLICATION_CREDENTIALS environment variable",
    "stderr": "Error: could not find credentials file",
    "exit_code": 1
  }
}

Common Error Codes

| Code | Meaning | Solution | |------|---------|----------| | AUTH_ERROR | Missing/invalid credentials | Set GOOGLE_APPLICATION_CREDENTIALS | | VALIDATION_ERROR | Invalid input parameters | Check parameter types and values | | API_ERROR | Google API error | Check quota, permissions, property ID | | CLI_EXECUTION_FAILED | CLI command failed | Check GA4_BINARY_PATH, build binary | | TIMEOUT | Operation timed out | Increase timeout, check network | | FILE_NOT_FOUND | Config file missing | Verify file path is absolute |

Quota Errors

Search Console has strict quotas:

{
  "code": "API_ERROR",
  "message": "GSC quota exceeded",
  "details": {
    "quota": {
      "used": 2000,
      "limit": 2000,
      "remaining": 0
    },
    "suggestion": "Wait 24 hours for quota reset"
  }
}

Rate Limits

Google Search Console

  • Daily limit: 2,000 queries per site
  • Per-minute limit: 600 queries per site
  • Shared across: All GSC operations (inspect, analytics, sitemaps, coverage)

Google Analytics 4 Admin API

  • Requests: 50 per second per project
  • No daily limit (but stay within reasonable use)

MCP Server

  • No built-in rate limiting (relies on CLI rate limiting)
  • CLI has configurable rate limiter (default: 10 RPS)

Development

Build

npm run build        # Compile TypeScript to dist/
npm run dev          # Run with tsx (no compile)

Testing

npm test             # Run all tests
npm run test:watch   # Watch mode
npm run test:run     # Single run (CI)

Linting

npm run lint         # ESLint check

Architecture

mcp/
├── src/
│   ├── index.ts              # MCP server entry
│   ├── cli/
│   │   ├── executor.ts       # Subprocess execution
│   │   └── parser.ts         # Output parsing
│   ├── tools/                # 16 tool implementations
│   │   ├── ga4-setup.ts
│   │   ├── ga4-report.ts
│   │   ├── ga4-cleanup.ts
│   │   ├── ga4-link.ts
│   │   ├── ga4-validate.ts
│   │   ├── gsc-sitemaps.ts   # 4 sitemap tools
│   │   ├── gsc-inspect.ts
│   │   ├── gsc-analytics.ts
│   │   ├── gsc-monitor.ts
│   │   └── gsc-coverage.ts
│   ├── utils/
│   │   ├── ansi-strip.ts     # Terminal color removal
│   │   └── errors.ts         # Error mapping
│   └── types/
│       ├── cli.ts            # CLI response types
│       └── mcp.ts            # MCP schemas
└── tests/                     # comprehensive tests

Troubleshooting

Server Not Connecting

Symptom: Claude Desktop doesn't show ga4-manager

Solution:

  1. Verify node in PATH: which node
  2. Check config uses absolute paths (not relative)
  3. Test manually: node /path/to/mcp/dist/index.js
  4. Check Claude Desktop logs for errors

Binary Not Found

Symptom: Error: spawn ga4 ENOENT

Solution:

  1. Set GA4_BINARY_PATH environment variable
  2. Build binary: cd .. && make build
  3. Verify: ls -la /path/to/ga4

Authentication Errors

Symptom: Missing Google credentials

Solution:

  1. Set GOOGLE_APPLICATION_CREDENTIALS in MCP config
  2. Verify file exists: ls -la /path/to/credentials.json
  3. Check credentials have required OAuth scopes:
    • https://www.googleapis.com/auth/analytics.edit
    • https://www.googleapis.com/auth/analytics.readonly
    • https://www.googleapis.com/auth/webmasters

Quota Exceeded

Symptom: GSC quota exceeded

Solution:

  • GSC daily limit: 2,000 queries
  • Wait 24 hours for reset
  • Monitor quota.remaining in responses
  • Use dry_run: true for testing

Environment Variables

| Variable | Required | Description | |----------|----------|-------------| | GOOGLE_APPLICATION_CREDENTIALS | Yes | Path to service account JSON | | GOOGLE_CLOUD_PROJECT | Yes | GCP project ID | | GA4_BINARY_PATH | Yes | Path to ga4 binary | | GA4_DEFAULT_PROPERTY_ID | No | Default property ID (optional) | | GSC_DEFAULT_SITE | No | Default GSC site URL (optional) |


Additional Documentation


Support


License

MIT - See LICENSE for details


Version History

See CHANGELOG.md for complete version history and release notes.

Current Version: 2.2.0