| |

Build an MCP Server for Google Search Console: Give Claude Live SEO Data Access

Most SEO automation still works the same way it did in 2022: you write a Python script, schedule it with cron or an n8n trigger, and read the output in a Google Sheet. That model breaks down the moment you want an LLM to reason over your Search Console data on demand — “which of my pages lost the most clicks this month, and what do they have in common?” Copy-pasting a CSV into a chat window doesn’t scale, and hard-coding every question into a script defeats the point of having a flexible agent.

The Model Context Protocol (MCP) fixes this. Instead of bolting your data onto a one-off prompt, you expose Google Search Console as a set of tools an agent can call itself. Claude (or any MCP-compatible client) decides when to pull query data, which dates to compare, and how to slice dimensions — then reasons over the live result. This post walks through a working, minimal MCP server for GSC, the design decisions that matter for SEO specifically, and where this pattern pays off versus a plain script.

Why MCP changes the shape of SEO automation

A traditional pipeline is imperative: you decide the questions in advance and the code answers them. An MCP server is declarative: you describe the capabilities (search analytics, URL inspection, sitemap status) and let the agent compose them to answer questions you didn’t anticipate. That difference is the whole game.

Concretely, MCP is an open standard that defines how a client (Claude Desktop, Claude Code, or your own app) talks to a server that exposes three things: tools (functions the model can invoke), resources (read-only data the model can load), and prompts (reusable templates). For SEO, tools are what you care about. Each tool gets a name, a JSON schema for its arguments, and a description the model reads to decide when to call it. The protocol handles the transport; you just write Python functions.

The payoff is that one well-built GSC server replaces a folder full of single-purpose scripts. We covered the “many scripts” approach when comparing the GSC API against SEMrush and Ahrefs APIs for a custom rank tracker; MCP is the layer that turns those same API calls into something an agent can drive without you writing a new script every time.

The architecture, in one diagram’s worth of prose

Three moving parts. First, a Google service account (or OAuth credentials) with read access to your Search Console property — this is what authenticates the API calls. Second, the MCP server: a small Python process that wraps the Search Console API and registers each capability as a tool. Third, an MCP client — Claude Desktop or Claude Code — that connects to the server over stdio and surfaces those tools to the model.

When you ask “compare branded vs non-branded clicks for the last 28 days,” the client sends your message plus the tool schemas to the model. The model emits a tool call (query_search_analytics with the right date range and a query filter), your server executes it against the API, and the JSON result flows back into the model’s context. No CSV, no copy-paste, no pre-written query for that specific question.

Building the server with the Python SDK

The official mcp Python package ships a FastMCP helper that turns decorated functions into tools. Install the dependencies first:

pip install "mcp[cli]" google-api-python-client google-auth

Here is the core of a working server. It exposes two tools: one to list your verified properties, and one to run a Search Analytics query with the dimensions and filters that matter for day-to-day SEO work.

import os
from datetime import date, timedelta
from mcp.server.fastmcp import FastMCP
from google.oauth2 import service_account
from googleapiclient.discovery import build

SCOPES = ["https://www.googleapis.com/auth/webmasters.readonly"]
CREDS = service_account.Credentials.from_service_account_file(
    os.environ["GSC_SERVICE_ACCOUNT_JSON"], scopes=SCOPES
)
gsc = build("searchconsole", "v1", credentials=CREDS)

mcp = FastMCP("gsc-seo")

@mcp.tool()
def list_properties() -> list[str]:
    """List Search Console properties this account can read."""
    sites = gsc.sites().list().execute().get("siteEntry", [])
    return [s["siteUrl"] for s in sites
            if s["permissionLevel"] != "siteUnverifiedUser"]

@mcp.tool()
def query_search_analytics(
    site_url: str,
    days: int = 28,
    dimensions: list[str] | None = None,
    query_contains: str | None = None,
    row_limit: int = 250,
) -> list[dict]:
    """Run a Search Analytics query.
    dimensions: any of query, page, country, device, date.
    query_contains: optional substring filter on the search query.
    Returns rows with clicks, impressions, ctr, and position."""
    dimensions = dimensions or ["query"]
    body = {
        "startDate": str(date.today() - timedelta(days=days)),
        "endDate": str(date.today()),
        "dimensions": dimensions,
        "rowLimit": row_limit,
    }
    if query_contains:
        body["dimensionFilterGroups"] = [{
            "filters": [{
                "dimension": "query",
                "operator": "contains",
                "expression": query_contains,
            }]
        }]
    resp = gsc.searchanalytics().query(siteUrl=site_url, body=body).execute()
    return resp.get("rows", [])

if __name__ == "__main__":
    mcp.run()

That’s a complete server. The docstrings are not decoration — they are the only thing the model reads to decide when and how to call each tool, so write them like API documentation, not code comments. Vague descriptions are the single biggest reason agents misuse tools.

Registering it with the client

For Claude Desktop, add the server to your config file (claude_desktop_config.json), pointing at your Python interpreter and passing the service-account path as an environment variable:

{
  "mcpServers": {
    "gsc-seo": {
      "command": "python",
      "args": ["/abs/path/to/gsc_server.py"],
      "env": {
        "GSC_SERVICE_ACCOUNT_JSON": "/abs/path/to/service-account.json"
      }
    }
  }
}

Restart the client, and the two tools appear. Now “show me the 20 queries where I rank between position 5 and 15 with the most impressions” becomes a single natural-language request — the model calls query_search_analytics, gets the rows, and filters to the striking-distance band in its own reasoning. Those are exactly the keywords worth a content refresh, surfaced without a bespoke script.

The design decisions that actually matter for SEO

Return shaped data, not raw API dumps. The Search Console API nests metrics under keys arrays. Flatten them into {"query": ..., "clicks": ..., "position": ...} dicts before returning. Every token the model spends parsing awkward JSON is a token it can’t spend reasoning, and it raises the odds of a misread number.

Keep tools coarse but parameterized. Resist the urge to ship get_branded_clicks, get_mobile_ctr, and forty other narrow tools. One query_search_analytics with good parameters lets the model compose any slice. Too many tools bloats the schema the model has to read and makes it slower and less accurate at choosing.

Make date math explicit. SEO questions are almost always comparative — this period versus the last. Expose a clean days argument (and optionally a comparison tool) rather than forcing the model to construct date strings, which it will occasionally get wrong around month boundaries.

Cap row limits and paginate deliberately. A property with 50,000 queries will blow past the context window if you return everything. Default to a few hundred rows sorted by impressions, and add an explicit “fetch more” path only if you need it. This is the same discipline that keeps any LLM data pipeline affordable.

Where this beats a plain script — and where it doesn’t

MCP wins for exploratory, ad-hoc analysis: diagnosing a traffic drop, hunting striking-distance keywords, or comparing branded versus non-branded trends, where the next question depends on the last answer. It pairs naturally with agentic workflows — the same pattern we used in the autonomous technical-audit agent that files GitHub issues — because the agent can now reach live ranking data mid-task instead of working blind.

A plain scheduled script still wins for deterministic, repeated jobs: the weekly rank-tracking export, the nightly index-coverage check, anything where you want the exact same output every time with zero token cost. Don’t replace a working cron job with an LLM call just because you can. The right architecture is usually both — scripts for the scheduled backbone, an MCP server for the questions you can’t predict.

One more frontier worth flagging: once your agent can read GSC live, the natural next step is feeding it competitive and SERP data too. Tools like the GSC API cover your own property; pairing the server with a scraping layer such as Bright Data, Apify, or ScraperAPI lets the same agent reason over rankings you don’t own. That combination — first-party performance data plus live SERP context — is where agentic SEO starts to feel genuinely different from a dashboard.

Key takeaways

An MCP server turns Google Search Console from a data source you query into a capability your agent can drive. The build is small — under 60 lines for a useful first version — but the leverage is large: you stop writing a new script for every new question. Write tool descriptions like documentation, return flattened data, keep tools coarse and parameterized, and cap your row limits. Use MCP for exploration and scripts for the scheduled backbone, and you get the best of both.

If you build SEO automations, bookmark SEOAutomationClub and check back — we publish working code and real workflows twice a day, no fluff. For the data-feed side of agentic SEO, start with our breakdown of scraping backbones for SEO pipelines.

Frequently asked questions

Do I need OAuth, or is a service account enough for an MCP GSC server?

A service account is simpler for a server you run yourself: create it in Google Cloud, enable the Search Console API, and add the service account’s email as a full or restricted user on your Search Console property. OAuth is only necessary when you need to act on behalf of other users’ properties, such as in a multi-tenant SaaS tool.

Can I use this MCP server with tools other than Claude?

Yes. MCP is an open standard, so any MCP-compatible client can connect to the same server — Claude Desktop, Claude Code, and a growing list of third-party agents and IDEs. The server code does not change; only the client configuration differs.

Will exposing GSC to an agent run up a large API or token bill?

The Search Console API is free within generous quotas, so the cost is mostly LLM tokens. Keep it low by capping row limits, returning flattened data, and defaulting to sensible date ranges. For recurring deterministic reports, use a scheduled script instead of an agent call — reserve the MCP path for ad-hoc analysis where reasoning adds value.

Is an MCP server a replacement for n8n or Python pipelines?

No — it complements them. n8n and cron-driven Python scripts excel at scheduled, deterministic jobs that should produce identical output every run. An MCP server excels at open-ended questions where the agent composes tool calls on the fly. Most mature setups run both side by side.



Trusted resources & further reading

For deeper, primary-source detail on the techniques referenced above, see:


Daniel Reyes — Operator & Editor, SEO Donna. Daniel Reyes is the operator behind SEO Donna. He oversees the automation engine that researches and drafts each article, and personally reviews every piece for accuracy and usefulness before it’s published. He works with small-business owners who want SEO handled for them.

Researched and drafted with SEO Donna’s automation engine, then reviewed by a human operator before publishing.


Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *