# Google Search Console MCP Server

An MCP server that gives Claude direct access to your Google Search Console data — search performance, URL indexation, sitemaps, and built-in SEO analysis tools including trending queries, cannibalization detection, traffic drop diagnostics, and automated query/page grouping.

## Prerequisites

- **Node.js** >= 18.0.0
- A C++ compiler (required by the `better-sqlite3` dependency):

| Platform | Install |
|----------|---------|
| **macOS** | `xcode-select --install` |
| **Windows** | [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) — select "Desktop development with C++" |
| **Linux (Debian/Ubuntu)** | `sudo apt-get install build-essential python3` |
| **Linux (Fedora/RHEL)** | `sudo dnf groupinstall "Development Tools" && sudo dnf install python3` |

## Quick Start

```bash
git clone https://github.com/serpfire/gsc-mcp-server.git
cd gsc-mcp-server
npm install
```

### 1. Create Google OAuth credentials

1. Go to [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
2. Create an OAuth 2.0 Client ID (type: **Desktop app**)
3. Enable the **Google Search Console API** in your project

### 2. Authenticate

```bash
GSC_OAUTH_CLIENT_ID=your-client-id \
GSC_OAUTH_CLIENT_SECRET=your-client-secret \
npm run setup
```

This opens your browser to sign in with Google. Tokens are stored at `~/.gsc-mcp/tokens.json` and auto-refresh. Run `setup` again anytime to switch accounts.

### 3. Add to Claude Code

```bash
claude mcp add --transport stdio gsc -s user \
  -e GSC_OAUTH_CLIENT_ID=your-client-id \
  -e GSC_OAUTH_CLIENT_SECRET=your-client-secret \
  -- node /absolute/path/to/gsc-mcp-server/src/index.js
```

Or manually add to `~/.claude.json`:

```json
{
  "mcpServers": {
    "gsc": {
      "type": "stdio",
      "command": "node",
      "args": ["/absolute/path/to/gsc-mcp-server/src/index.js"],
      "env": {
        "GSC_OAUTH_CLIENT_ID": "your-client-id",
        "GSC_OAUTH_CLIENT_SECRET": "your-client-secret"
      }
    }
  }
}
```

### Service Account Alternative

For automation or server-to-server use:

```json
{
  "env": {
    "GSC_AUTH_MODE": "service_account",
    "GSC_SERVICE_ACCOUNT_KEY_PATH": "/path/to/key.json"
  }
}
```

## Tools

### Core GSC Tools

| Tool | Description |
|------|-------------|
| `list_sites` | List all verified GSC properties |
| `get_site` | Get details for a specific site |
| `query_search_analytics` | Query clicks, impressions, CTR, position with dimensions/filters/pagination |
| `inspect_url` | Check URL index/crawl status (2,000/day limit) |
| `list_sitemaps` | List sitemaps for a site |
| `get_sitemap` | Get sitemap details |
| `submit_sitemap` | Submit a new sitemap |
| `delete_sitemap` | Remove a sitemap from GSC |
| `switch_account` | Re-authenticate with a different Google account (OAuth only) |

### Data Sync

| Tool | Description |
|------|-------------|
| `sync_search_data` | Fetch all performance data into local SQLite cache. Incremental by default. Required before analysis tools. |

### Analysis Tools

| Tool | Description |
|------|-------------|
| `find_low_hanging_fruit` | Find queries ranking 2-20 with high impressions but low CTR, scored by untapped potential |
| `detect_cannibalization` | Detect queries ranking on 2+ URLs using HHI scoring with action recommendations |
| `find_trending_queries` | Find queries with biggest growth/decline (period-over-period or year-over-year) |
| `find_trending_pages` | Find pages with biggest growth/decline with impact scoring |
| `analyze_traffic_drop` | Diagnose traffic drops across 5 views: site-level, GBP vs organic, groups, segments, brand vs non-brand |
| `get_group_performance` | Aggregated metrics broken down by named groups with share percentages |

### Group Management

| Tool | Description |
|------|-------------|
| `create_group` | Create a group with OR/AND/NOT regex rules for URL segments or query groups |
| `list_groups` | List all groups, optionally filtered by dimension |
| `get_group` | Get group details including rules |
| `update_group` | Update group description and/or rules |
| `delete_group` | Delete a group and its rules |
| `test_group` | Test a group against cached data — match count, rate, samples |

### Auto-Discovery

| Tool | Description |
|------|-------------|
| `discover_query_groups` | Auto-cluster queries using word overlap, n-gram similarity, and page co-occurrence |
| `promote_cluster_to_group` | Convert a discovered cluster into a named group |
| `auto_segment_pages` | Auto-create URL segment groups from page URL patterns |
| `auto_group_queries` | Auto-create query groups: brand, topic n-grams, intent modifiers |

## Resources

| URI | Description |
|-----|-------------|
| `sites://list` | All verified sites (auto-discovery) |

## Prompts

| Name | Description |
|------|-------------|
| `seo_performance_analysis` | Guided full SEO review (queries, pages, devices, trends) |
| `index_coverage_check` | Systematic URL indexation report |

## Example Queries

- "List my Search Console sites"
- "Show top 20 queries for sc-domain:example.com last 7 days"
- "Compare mobile vs desktop for https://example.com/"
- "Check if https://example.com/new-page is indexed"
- "Show sitemaps for sc-domain:example.com"
- "Sync data for sc-domain:example.com and find low-hanging fruit"
- "Detect keyword cannibalization on sc-domain:example.com"
- "Show trending queries for sc-domain:example.com last 28 days"
- "Auto-create query groups for sc-domain:example.com"

## Architecture

```
src/
├── index.js                          # Entry: auth -> client -> db -> server -> stdio
├── server.js                         # McpServer factory
├── auth/
│   ├── index.js                      # Auth factory (auto-detects mode from env)
│   ├── service-account.js            # Service account key file auth
│   ├── oauth.js                      # Browser flow + token persistence + auto-recovery
│   ├── token-store.js                # ~/.gsc-mcp/tokens.json
│   └── setup.js                      # Standalone setup script
├── gsc/                              # Pure GSC API wrappers (no MCP knowledge)
│   ├── client.js
│   ├── sites.js
│   ├── search-analytics.js
│   ├── sitemaps.js
│   └── url-inspection.js
├── db/
│   ├── connection.js                 # SQLite setup with WAL mode
│   └── schema.js                     # Migrations
├── analysis/                         # SEO analysis engine (uses local SQLite)
│   ├── sync.js                       # GSC API -> SQLite incremental sync
│   ├── low-hanging-fruit.js          # Opportunity scoring
│   ├── cannibalization.js            # HHI-based detection
│   ├── trending.js                   # PoP / YoY trending queries & pages
│   ├── traffic-drop.js              # Multi-view drop diagnostics
│   ├── group-performance.js          # Group-level aggregation
│   ├── auto-segment.js              # Automatic URL segmentation
│   ├── auto-group.js                # Automatic query grouping
│   ├── shared.js                     # Shared SQL helpers
│   ├── groups/
│   │   ├── manager.js                # CRUD for groups + rules
│   │   ├── compiler.js               # OR/AND/NOT rule compilation
│   │   └── matcher.js                # Regex matching engine
│   └── semantic/
│       ├── discover.js               # Cluster discovery orchestrator
│       ├── clustering.js             # Connected components algorithm
│       ├── similarity.js             # Multi-signal similarity scoring
│       └── labeler.js                # Cluster naming
├── tools/
│   ├── definitions.js                # Core GSC tools (declarative)
│   ├── analysis-definitions.js       # Analysis tools (declarative)
│   ├── middleware.js                  # Rate limiting + error handling wrapper
│   ├── auth.js                       # switch_account tool
│   └── index.js                      # Registration loop
├── resources.js                      # sites://list resource
├── prompts.js                        # SEO analysis + index check prompts
└── utils/
    ├── logger.js                     # stderr-only (stdout = MCP protocol)
    ├── errors.js                     # Google API error -> MCP error
    ├── constants.js                  # Scopes, rate limits, defaults
    ├── rate-limiter.js               # Sliding window
    └── open-url.js                   # Cross-platform browser opener
```

Two-layer separation: `gsc/` knows nothing about MCP. `tools/` knows nothing about Google's API. `analysis/` operates on local SQLite data. Each layer is independently testable.

## Environment Variables

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `GSC_AUTH_MODE` | No | Auto-detected | `oauth` or `service_account`. Auto-detected from which credentials are set. |
| `GSC_OAUTH_CLIENT_ID` | OAuth | — | OAuth client ID |
| `GSC_OAUTH_CLIENT_SECRET` | OAuth | — | OAuth client secret |
| `GSC_OAUTH_REFRESH_TOKEN` | No | — | Skip browser flow with a pre-existing token |
| `GSC_SERVICE_ACCOUNT_KEY_PATH` | Service acct | — | Path to JSON key file |
| `GSC_API_SCOPES` | No | `webmasters.readonly` | Override scopes (comma-separated) |
| `GSC_LOG_LEVEL` | No | `info` | `debug` / `info` / `warn` / `error` |
| `GSC_TOKEN_PATH` | No | `~/.gsc-mcp/tokens.json` | Custom token storage path |
| `GSC_DB_PATH` | No | `~/.gsc-mcp/data.db` | Custom SQLite database path |

## Debugging

Launch the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) for interactive testing:

```bash
npm run inspect
```

> **Windows note:** The inspect script uses bash. Run it from Git Bash, WSL, or replace with:
> ```bash
> npx @modelcontextprotocol/inspector node src/index.js
> ```
