Python自作MCPでGSC・GA4を自動化!ブログアクセス分析の手順

ブログを運営していると、Google Search Console(GSC)とGoogle Analytics 4(GA4)のデータを確認し、定期的なアクセス分析やリライト候補の選定を行う作業が発生します。しかし、ブラウザで各管理画面を行き来し、CSVを手動でダウンロードして表計算ソフトで集計するのは手間がかかります。

Model Context Protocol(MCP)を活用すれば、AIエージェントに自作の分析ツールを連携させることができます。AIにチャットで「アクセス分析して」と伝えるだけで、GSCとGA4の最新データを自動取得し、古いCSVファイルをクリーンアップした上で、アクセス数増加に向けた具体的な改善提案まで一貫して自律実行する仕組みを構築できます。

本記事では、Pythonを用いた独自のMCPサーバー構築から、サービスアカウントによるAPI認証、古いファイルの安全な自動削除、設定ファイルによるID自動読み込み、そしてAIによるアクセス分析ワークフローの実装手順までを詳しく解説します。

なお、2026年時点ではGoogle公式の google-analytics-mcp も公開されていますが、これはGA4専用ツールであり、Search Consoleのデータ取得には対応していません。また、APIの応答をチャットの会話上に直接返す仕様のため、数千〜数万件に及ぶ検索クエリやPVの生データをCSVとしてローカルに保存・蓄積する用途にも不向きです。本記事のように「GSCとGA4を1つの自作MCPに統合し、ローカルCSVへ安全に蓄積してAIに分析させる」構成にすることで、トークンを消費しすぎることなく実用的なアクセス分析が可能になります。

この記事で行うこと

この記事では、以下のステップに沿ってブログのアクセス分析自動化環境を構築します。

  1. Google Cloud Consoleで専用サービスアカウントを作成し、GSCとGA4に閲覧者権限を付与する
  2. Pythonの公式MCP SDKを用いてデータ取得サーバーを実装する
  3. CSV出力直前に対象フォルダ内の既存CSVファイルのみを自動クリーンアップするロジックを実装する
  4. config.jsonを用意してプロパティIDやサイトURLの指定を自動化する
  5. AntigravityやClaude DesktopなどのAIクライアントにMCPサーバーを登録する
  6. AIに「アクセス分析して」と指示し、期間ヒアリングから改善提案レポート出力までの動作を確認する

前提条件・対象読者

作業を進めるにあたり、以下の環境および事前知識を前提とします。

項目要件
対象読者ブログのアクセス分析やSEO改善を効率化したいエンジニア・ブロガー
開発環境OSWindows 11(macOSやLinuxでも同様に構築可能)
PythonバージョンPython 3.10以上(本記事ではPython 3.12を使用)
利用するAIクライアントAntigravity、Claude Desktop、CursorなどのMCP対応ツール
GoogleアカウントGoogle Search ConsoleおよびGA4の管理権限を持つアカウント

全体アーキテクチャと処理フロー

今回構築する自動化システムの全体像とデータ処理の流れは以下の通りです。

従来の運用では、GSCとGA4の各画面にログインして手動で期間を指定し、CSVを個別にエクスポートしてスプレッドシート等で結合・集計していました。 本システムを導入することで、指示から数十秒で最新データの取得・整頓・分析・施策提案までがチャット画面内で完結するようになります。

手順1:Google CloudでAPI有効化とサービスアカウント作成

外部のプログラムからSearch ConsoleとGA4のデータを安全に取得するために、Google Cloudのサービスアカウントキーを発行します。

既存のインフラ運用用アカウントを使い回すのではなく、最小権限の原則に従って本システム専用のサービスアカウントを新規作成することを推奨します。万が一キーが漏洩・不要になった際も、このアカウントのみを無効化できるため安全です。

1-1. APIの有効化

Google Cloud Consoleにログインし、プロジェクトを選択した上で「APIとサービス」から「ライブラリ」を開きます。以下の2つのAPIを検索し、それぞれ有効化します。

  • Google Search Console API
  • Google Analytics Data API

1-2. サービスアカウントの作成と鍵のダウンロード

  • 「IAMと管理」から「サービスアカウント」を開き、「サービスアカウントを作成」をクリックします。
  • サービスアカウント名に「my-blog-analytics-sa」など任意の名前を入力し、「作成して続行」を押します。
  • プロジェクトへのアクセス権付与(ロール)は、GCP内部のリソースを操作しないため何も指定せず「続行」をクリックして完了します。
  • 作成されたサービスアカウントをクリックし、「キー」タブを開きます。
  • 「鍵を追加」から「新しい鍵を作成」を選び、「JSON」形式を選択して作成すると、秘密鍵ファイルがダウンロードされます。
  • ファイル名を credentials.json に変更し、安全な作業フォルダに配置します。

1-3. Search ConsoleおよびGA4への閲覧権限付与

発行されたサービスアカウントのメールアドレス(例: my-blog-analytics-sa@your-project-id.iam.gserviceaccount.com)に対し、各ツールでアクセス権を付与します。

Search Console側の設定

  • Google Search Consoleを開き、ご自身のサイトを選択します。
  • 左メニュー最下部の「設定」から「ユーザーと権限」を開きます。
  • 「ユーザーを追加」をクリックし、サービスアカウントのメールアドレスを入力して権限を「閲覧者」に設定して追加します。

Google Analytics 4側の設定

  • Google アナリティクスを開き、左下の「管理(歯車アイコン)」を開きます。
  • プロパティ列の「プロパティのアクセス管理」をクリックします。
  • 右上の「+」ボタンから「ユーザーを追加」を選び、サービスアカウントのメールアドレスを入力して役割を「閲覧者」に設定して追加します。
  • 管理画面の「プロパティ設定」を開き、画面右上に表示されている数字の「プロパティID(例: 123456789)」を控えておきます。

手順2:PythonによるMCPサーバーの実装

サービスアカウント認証を行い、データを取得してCSV出力するMCPサーバープログラムを作成します。

フォルダ構成の例として、プロジェクト配下に以下のような構成を想定します。

analytics/
├── google-analytics/             # GA4 CSV保存先
├── search-console/              # GSC CSV保存先
└── pg/                          # プログラム配置先
    ├── analytics_mcp_server.py  # MCPサーバー本体
    ├── config.json              # サイト個別設定
    ├── credentials.json         # サービスアカウント鍵
    └── requirements.txt         # 依存ライブラリ

2-1. 依存ライブラリのインストール

仮想環境を作成し、必要なライブラリをインストールします。

mcp>=1.0.0
google-api-python-client>=2.100.0
google-auth>=2.20.0
google-analytics-data>=0.18.0
pandas>=2.0.0

PowerShellまたはターミナルで以下のコマンドを実行します。

python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install --upgrade pip
pip install -r requirements.txt

2-2. 設定ファイル(config.json)の作成

毎回チャット上でプロパティIDやサイトURLを入力する手間を省くため、同じディレクトリに設定ファイル config.json を作成します。 サイトURLやプロパティIDは、ご自身の環境の値に置き換えてください。

{
  "ga4_property_id": "123456789",
  "site_url": "https://example.com/"
}

2-3. MCPサーバー本体の実装(analytics_mcp_server.py)

以下の内容で analytics_mcp_server.py を作成します。MCP SDKのバージョン差分(v1系のFastMCPとv2系のMCPServer)を吸収する互換コードを組み込み、古いCSVの安全な自動削除ロジックも実装しています。

パス定義には相対パスを使用しているため、上記フォルダ構成のままであれば環境依存のパス変更なしでそのまま動作します。

import os
import sys
import json
from pathlib import Path
from typing import Optional
import pandas as pd

# MCP 1.x / 2.x 両対応
try:
    from mcp.server.mcpserver import MCPServer
except ImportError:
    from mcp.server.fastmcp import FastMCP as MCPServer

from google.oauth2 import service_account
from googleapiclient.discovery import build
from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.analytics.data_v1beta.types import (
    DateRange,
    Dimension,
    Metric,
    RunReportRequest,
)

# 保存先パス定数(スクリプトから見た相対パスで定義)
SCRIPT_DIR = Path(__file__).resolve().parent
BASE_DIR = SCRIPT_DIR.parent
GA_DIR = BASE_DIR / "google-analytics"
GSC_DIR = BASE_DIR / "search-console"
DEFAULT_SITE_URL = "https://example.com/"

mcp = MCPServer("blog-analytics")

def load_config() -> dict:
    """config.json が存在すれば設定を読み込む"""
    config_path = SCRIPT_DIR / "config.json"
    if config_path.exists():
        try:
            with open(config_path, "r", encoding="utf-8") as f:
                return json.load(f)
        except Exception as e:
            print(f"警告: config.json の読み込みに失敗しました: {e}", file=sys.stderr)
    return {}

def get_credentials_path() -> Path:
    """サービスアカウントキー (credentials.json) のパスを取得する"""
    env_path = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS")
    if env_path and Path(env_path).exists():
        return Path(env_path)

    local_cred = SCRIPT_DIR / "credentials.json"
    if local_cred.exists():
        return local_cred

    raise FileNotFoundError(f"サービスアカウントキー (credentials.json) が見つかりません: {local_cred}")

def cleanup_csv_files(target_dir: Path) -> list[str]:
    """対象フォルダ内の既存CSVファイルのみを安全に削除する"""
    target_dir.mkdir(parents=True, exist_ok=True)
    deleted_files = []
    for file_path in target_dir.iterdir():
        if file_path.is_file() and file_path.suffix.lower() == ".csv":
            try:
                file_path.unlink()
                deleted_files.append(file_path.name)
            except Exception as e:
                print(f"警告: ファイル削除に失敗しました: {file_path.name}, {e}", file=sys.stderr)
    return deleted_files

def format_date_str(date_str: str) -> str:
    """YYYY-MM-DD 形式からハイフンを除去して YYYYMMDD に統一する"""
    return date_str.replace("-", "").replace("/", "").strip()

@mcp.tool()
def export_gsc_to_csv(
    start_date: str,
    end_date: str,
    site_url: Optional[str] = None
) -> str:
    """Google Search Console (GSC) から検索パフォーマンスを取得しCSV保存します。"""
    config = load_config()
    target_url = site_url or config.get("site_url") or DEFAULT_SITE_URL

    try:
        cred_path = get_credentials_path()
        deleted_files = cleanup_csv_files(GSC_DIR)
        
        scopes = ["https://www.googleapis.com/auth/webmasters.readonly"]
        credentials = service_account.Credentials.from_service_account_file(str(cred_path), scopes=scopes)
        service = build("searchconsole", "v1", credentials=credentials)

        all_rows = []
        start_row = 0
        row_limit = 25000

        while True:
            request_body = {
                "startDate": start_date,
                "endDate": end_date,
                "dimensions": ["query", "page"],
                "rowLimit": row_limit,
                "startRow": start_row,
            }
            response = service.searchanalytics().query(siteUrl=target_url, body=request_body).execute()
            rows = response.get("rows", [])
            if not rows:
                break

            for row in rows:
                keys = row.get("keys", ["", ""])
                all_rows.append({
                    "query": keys[0] if len(keys) > 0 else "",
                    "page": keys[1] if len(keys) > 1 else "",
                    "clicks": row.get("clicks", 0),
                    "impressions": row.get("impressions", 0),
                    "ctr": row.get("ctr", 0.0),
                    "position": row.get("position", 0.0)
                })

            start_row += len(rows)
            if len(rows) < row_limit:
                break

        df = pd.DataFrame(all_rows, columns=["query", "page", "clicks", "impressions", "ctr", "position"])
        filename = f"gsc_{format_date_str(start_date)}_{format_date_str(end_date)}.csv"
        output_path = GSC_DIR / filename
        df.to_csv(output_path, index=False, encoding="utf-8-sig")

        return f"【GSCデータ保存完了】\n・保存先: {output_path}\n・取得件数: {len(df):,} 件\n・旧ファイル削除: {len(deleted_files)} 件"
    except Exception as e:
        return f"【エラー】GSCデータ取得失敗: {str(e)}"

@mcp.tool()
def export_ga4_to_csv(
    start_date: str,
    end_date: str,
    property_id: Optional[str] = None
) -> str:
    """Google Analytics 4 (GA4) からPV・セッションを取得しCSV保存します。"""
    config = load_config()
    target_property_id = property_id or config.get("ga4_property_id")
    if not target_property_id:
        return "【エラー】GA4 プロパティIDが未設定です。config.json を確認してください。"

    try:
        cred_path = get_credentials_path()
        deleted_files = cleanup_csv_files(GA_DIR)
        client = BetaAnalyticsDataClient.from_service_account_file(str(cred_path))

        norm_property_id = str(target_property_id).strip()
        if not norm_property_id.startswith("properties/"):
            norm_property_id = f"properties/{norm_property_id}"

        all_rows = []
        offset = 0
        limit = 100000

        while True:
            request = RunReportRequest(
                property=norm_property_id,
                dimensions=[Dimension(name="pagePath")],
                metrics=[Metric(name="screenPageViews"), Metric(name="sessions")],
                date_ranges=[DateRange(start_date=start_date, end_date=end_date)],
                offset=offset,
                limit=limit,
            )
            response = client.run_report(request)
            if not response.rows:
                break

            for row in response.rows:
                all_rows.append({
                    "pagePath": row.dimension_values[0].value if row.dimension_values else "",
                    "screenPageViews": int(row.metric_values[0].value) if len(row.metric_values) > 0 else 0,
                    "sessions": int(row.metric_values[1].value) if len(row.metric_values) > 1 else 0,
                })

            offset += len(response.rows)
            if len(response.rows) < limit:
                break

        df = pd.DataFrame(all_rows, columns=["pagePath", "screenPageViews", "sessions"])
        filename = f"ga4_{format_date_str(start_date)}_{format_date_str(end_date)}.csv"
        output_path = GA_DIR / filename
        df.to_csv(output_path, index=False, encoding="utf-8-sig")

        return f"【GA4データ保存完了】\n・保存先: {output_path}\n・取得件数: {len(df):,} 件\n・旧ファイル削除: {len(deleted_files)} 件"
    except Exception as e:
        return f"【エラー】GA4データ取得失敗: {str(e)}"

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

Excelで開いた際に日本語文字化けが発生するのを防ぐため、CSV保存時は encoding=”utf-8-sig”(BOM付きUTF-8)を指定している点が実務上のポイントです。

手順3:AIクライアントへのMCPサーバー登録

作成したMCPサーバーをAIツールに登録します。Antigravityの場合は、グローバル設定ファイル(mcp_config.json)に以下の定義を追加します。パスはご自身の環境に合わせて変更してください。

{
  "mcpServers": {
    "blog-analytics": {
      "command": "C:\\path\\to\\analytics\\pg\\.venv\\Scripts\\python.exe",
      "args": [
        "C:\\path\\to\\analytics\\pg\\analytics_mcp_server.py"
      ]
    }
  }
}

仮想環境内の python.exe をフルパスで指定することで、必要なライブラリが揃った状態でMCPサーバーがバックグラウンド起動します。Claude Desktopの場合は claude_desktop_config.json に同様の形式で記述します。

手順4:AIエージェントに「アクセス分析ワークフロー」を組み込む

MCPツールを用意しただけでは、AIは「指示されたツールを呼ぶ」ことしかできません。「アクセス分析して」という一言で、データの取得から分析、レポート出力までを自律実行させるために、AIのプロンプトやスキル定義に以下のワークフローを記述します。

Antigravityの場合はスキルファイル(SKILL.md)に、Claude Desktopの場合はシステムプロンプトや設定の「カスタム指示」欄に、以下の手順を記述してください。

4-1. 期間のヒアリング(3択の提示)

ユーザーから「アクセス分析して」と指示された際、いきなりAPIを呼び出すのではなく、現在日付から計算した具体的な日付とともに以下の3択を提示させます。

  1. 直近3ヶ月(例: 2026-06-01 ~ 2026-08-31)
  2. 直近1ヶ月(例: 2026-08-01 ~ 2026-08-31)
  3. その他(任意の期間 YYYY-MM-DD ~ YYYY-MM-DD)

4-2. 4大観点によるデータ分析ロジック

期間の回答を受け取ったAIは、MCP経由でGSCとGA4のCSVを取得した後、ファイルを直接読み込んで以下の4つの観点で集計を行います。

  1. CTR改善候補:検索順位は1〜10位と高いが表示回数の割にクリックされていない記事
  2. 順位アップ候補:4〜20位に位置しており、少しの追記で1〜3位を狙える記事
  3. 主力記事からの導線強化:GA4でPV上位の記事から、回遊を促す内部リンクの配置先
  4. 新規記事ネタ候補:検索需要はあるが、専用記事がなく取りこぼしているクエリ

4-3. 改善提案レポートの出力

分析後、AIは以下のような実践的なレポートをチャット画面に出力します。

# アクセス分析およびアクセス数増加のための改善提案

## 1. データ取得サマリー
- 対象期間: 2026-06-01 ~ 2026-08-31
- GSCデータ: gsc_20260601_20260831.csv (18,990 件)
- GA4データ: ga4_20260601_20260831.csv (289 件)
- サイト全体概況: 総PV数 46,139回、総クリック数 7,071回

## 2. 【即効性重視】CTR改善・タイトル見直し候補
- 対象記事: https://example.com/git/git-pull/
  - 該当クエリ: git pull(表示 4,406回 / クリック 8回 / 順位 1.9位)
  - 課題: 順位1.9位に対してCTRが0.18%と低迷。読者メリットが伝わっていない。
  - 改善タイトル案: 【Git】git pullの使い方と仕組みを図解!fetchとの違いも解説

## 3. 【SEO底上げ】順位アップ・コンテンツ強化リライト候補
- 対象記事: https://example.com/windows/windows11-new-features/
  - 狙うべきクエリ: windows11 26h2(表示 3,659回 / 順位 6.5位)
  - 追記案: リリース時期とハードウェア要件、前バージョンとの比較表を追加

タイトル案を提示する際は、検索結果で途切れない「32〜42文字」を厳守させ、重要キーワードを先頭に配置するルールをAIに与えておくことで、そのままリライトに使える高品質な提案が得られます。

良くなった点と注意点

本システムを導入して実際に運用した結果、以下の効果と注意点が明確になりました。

良くなった点(メリット)

  • 毎月のデータ収集・集計にかかっていた30分以上の作業が、指示から数十秒で完了する
  • 過去の古いCSVが自動で削除されるため、フォルダ内が常に最新のデータ1組に保たれる
  • 「順位は高いのに読まれていない記事」がデータから機械的に抽出され、客観的なリライト計画が立てられる

注意点・制限事項

  • サービスアカウントキー(credentials.json)は機密情報のため、Git管理から除外(.gitignoreに追加)することを徹底する
  • GSC APIは1日あたりのクエリ上限、GA4 APIはトークン消費制限があるため、過度な連続呼び出しは避ける

まとめ

自作MCPサーバーを構築することで、AIエージェントを単なるチャット相手から「自社システムやAPIを自律的に操作するアシスタント」へと進化させることができます。

特にGoogle Search ConsoleやGoogle Analytics 4といったマーケティングデータとの連携は、ブログやWebサイトのアクセス向上における施策立案を大幅にスピードアップします。API連携とファイルクリーンアップの基盤を一度整えておけば、毎月のサイト改善が格段にスムーズになります。

次に読むおすすめ記事

【完全版】エンジニアのための生成AI・AIエージェント活用ガイド
2023年に始まった生成AIの爆発的な普及から数年が経過し、エンジニアやITビジネスパーソンにおけるAIの活用フェーズは**「単なるチャットでの質問(Q&A)」から「自律型AIエージェントによる業務・開発の自動化」へと完全に移行**しました…
Pythonの「FastMCP」で自作MCPサーバーを爆速開発・導入する手順
近年、AIエージェントにローカル環境のファイルを操作させたり、社内の独自システムと連携させたりするニーズが急速に高まっています。それを実現する業界標準プロトコルとして注目されているのが、Anthropic社が提唱した「Model Conte…

参考情報