Skip to main content
ai2026年2月22日5分で読めます

MCPサーバー:Google Analyticsを開発者にとって本当に役立つものにする

Model Context Protocolサーバーが、Google Analyticsをマーケティングダッシュボードから、会話形式でクエリできる開発者向けのデータソースに変える方法。

mcpanalyticsai
MCPサーバー:Google Analyticsを開発者にとって本当に役立つものにする

Google Analyticsは、開発者が我慢して使うマーケティングツールであり続けてきました。トラッキングを設定し、イベントを構成したら、あとはダッシュボードをクリックするのが好きな人に任せる、といった具合です。特定のページの直帰率、新機能のコンバージョンファネル、国別にセグメント化されたリアルタイムのアクティブユーザーなど、自分でデータが必要な場合、5つのネストされたメニューをクリックし、GA4のクエリビルダーと格闘し、質問をする方が答えるよりもなぜ手間がかかるのかと疑問に思うことになります。

Model Context Protocolはこれを完全に変えます。GA4のレポートAPIをMCPサーバーでラップすることで、Claudeや他のMCP互換AIアシスタントを通じて、会話形式でアナリティクスデータをクエリできます。ダッシュボードも、クエリビルダーも不要です。知りたいことを尋ねるだけです。

MCPとは何か、そしてなぜ重要なのか

Model Context Protocolは、Anthropicによって作成されたオープンスタンダードで、AIアシスタントが外部データソースやツールとどのように相互作用するかを定義します。AIのためのUSB-Cポートのようなものだと考えてください。つまり、互換性のあるアシスタントが、各ペアリングごとにカスタム統合コードなしで、互換性のあるデータソースに接続できるユニバーサルインターフェースです。

MCP以前は、AIアシスタントをアナリティクスデータに接続するには、専用のパイプラインを構築する必要がありました。データをCSVにエクスポートし、チャットに貼り付け、コンテキストウィンドウが十分に大きいことを願う、といった具合です。あるいは、カスタムAPI統合を構築し、プロンプトテンプレートを書き、認証を自分で処理する必要がありました。新しいデータソースごとに新しい統合が必要でした。

MCPはこれを3つの概念に標準化します。

  • リソース — サーバーが公開する構造化データ(GA4プロパティ、レポート、ディメンション、指標)
  • ツール — AIが呼び出すことができるアクション(レポートの実行、リアルタイムデータのクエリ、利用可能な指標のリスト表示)
  • プロンプト — サーバーが提供する再利用可能なプロンプトテンプレート(良好な結果を得るために事前に構造化された一般的なアナリティクスクエリ)

サーバーは認証、データ取得、応答フォーマットを処理します。AIアシスタントは自然言語理解と結果の解釈を処理します。どちらの側も、もう一方の実装の詳細を知る必要はありません。

なぜこれがアナリティクスに特に重要なのか

アナリティクスプラットフォームは情報が豊富ですが、インタラクションが貧弱です。GA4は非常に強力なデータを持っています。すべてのユーザーセッション、すべてのページビュー、すべてのイベント、製品全体のすべてのコンバージョンを追跡します。しかし、そのデータにアクセスするには、GA4の語彙(ディメンション、指標、セグメント、日付範囲、フィルター、比較)で考える必要があります。答えを得る前に、質問をGA4のクエリモデルに変換しなければなりません。

MCPサーバーはこれを逆転させます。あなたは平易な言葉で質問します。AIはそれを正しいAPI呼び出しに変換し、実行し、結果を解釈します。「質問がある」から「答えがある」までの摩擦は、数分から数秒に短縮されます。

GA4用MCPサーバーの構築

実装には、MCPサーバーフレームワーク、Google Analytics Data APIクライアント、そして自然言語の意図をAPI呼び出しにマッピングする接着ロジックの3つのレイヤーが含まれます。

プロジェクトのセットアップ

mkdir ga4-mcp-server
cd ga4-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk @google-analytics/data zod dotenv
npm install -D typescript @types/node

@modelcontextprotocol/sdkパッケージはサーバーフレームワークを提供します。@google-analytics/dataは、GA4 Data API(Admin APIではなくレポートAPI)用のGoogleの公式クライアントライブラリです。zodはツールパラメータの入力検証を処理します。

Googleによる認証

GA4 API認証にはGoogle Cloudサービスアカウントを使用します。Google Cloud Consoleで作成し、JSONキーファイルをダウンロードして、GA4プロパティへの「閲覧者」アクセス権を付与します。

// src/analytics-client.ts
import { BetaAnalyticsDataClient } from "@google-analytics/data";

const analyticsClient = new BetaAnalyticsDataClient({
  keyFilename: process.env.GOOGLE_APPLICATION_CREDENTIALS,
});

const propertyId = process.env.GA4_PROPERTY_ID;

export async function runReport(
  dimensions: string[],
  metrics: string[],
  startDate: string,
  endDate: string,
  dimensionFilter?: Record<string, string>
) {
  const request: any = {
    property: `properties/${propertyId}`,
    dimensions: dimensions.map((d) => ({ name: d })),
    metrics: metrics.map((m) => ({ name: m })),
    dateRanges: [{ startDate, endDate }],
  };

  if (dimensionFilter) {
    const filterKey = Object.keys(dimensionFilter)[0];
    request.dimensionFilter = {
      filter: {
        fieldName: filterKey,
        stringFilter: {
          value: dimensionFilter[filterKey],
          matchType: "EXACT",
        },
      },
    };
  }

  const [response] = await analyticsClient.runReport(request);
  return formatReportResponse(response);
}

function formatReportResponse(response: any) {
  if (!response.rows || response.rows.length === 0) {
    return { data: [], summary: "No data found for the specified query." };
  }

  const headers = [
    ...(response.dimensionHeaders?.map((h: any) => h.name) ?? []),
    ...(response.metricHeaders?.map((h: any) => h.name) ?? []),
  ];

  const rows = response.rows.map((row: any) => {
    const values = [
      ...(row.dimensionValues?.map((v: any) => v.value) ?? []),
      ...(row.metricValues?.map((v: any) => v.value) ?? []),
    ];
    return Object.fromEntries(headers.map((h, i) => [h, values[i]]));
  });

  return {
    data: rows,
    rowCount: response.rowCount,
    summary: `Returned ${rows.length} rows.`,
  };
}

MCPサーバー

// src/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { runReport, getRealtimeData, getAvailableMetrics } from "./analytics-client";

const server = new McpServer({
  name: "ga4-analytics",
  version: "1.0.0",
});

// Tool: Run a custom report
server.tool(
  "run_report",
  "Run a Google Analytics 4 report with specified dimensions, metrics, and date range",
  {
    dimensions: z.array(z.string()).describe("GA4 dimensions like 'pagePath', 'country', 'deviceCategory'"),
    metrics: z.array(z.string()).describe("GA4 metrics like 'activeUsers', 'sessions', 'bounceRate'"),
    startDate: z.string().describe("Start date in YYYY-MM-DD format or relative like '7daysAgo'"),
    endDate: z.string().describe("End date in YYYY-MM-DD format or 'today'"),
    filter: z.record(z.string()).optional().describe("Optional dimension filter as key-value pair"),
  },
  async ({ dimensions, metrics, startDate, endDate, filter }) => {
    const result = await runReport(dimensions, metrics, startDate, endDate, filter);
    return {
      content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
    };
  }
);

// Tool: Get real-time active users
server.tool(
  "realtime_users",
  "Get current real-time active users, optionally segmented by a dimension",
  {
    dimension: z.string().optional().describe("Optional dimension to segment by, like 'country' or 'pagePath'"),
  },
  async ({ dimension }) => {
    const result = await getRealtimeData(dimension);
    return {
      content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
    };
  }
);

// Tool: List available metrics and dimensions
server.tool(
  "list_metrics",
  "List all available GA4 metrics and dimensions for the connected property",
  {},
  async () => {
    const result = await getAvailableMetrics();
    return {
      content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
    };
  }
);

// Resource: Property metadata
server.resource(
  "property-info",
  "ga4://property/info",
  async (uri) => ({
    contents: [{
      uri: uri.href,
      mimeType: "application/json",
      text: JSON.stringify({
        propertyId: process.env.GA4_PROPERTY_ID,
        description: "Connected GA4 property for analytics queries",
      }),
    }],
  })
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);

これにより、AIアシスタントは3つのツールを利用できるようになります。任意のレポートの実行、リアルタイムデータの確認、利用可能な指標とディメンションの発見です。発見ツールは重要です。activeUsersなのかactive_usersなのかtotalUsersなのかを覚えていなくても、AIが正しいフィールド名を特定できるようになります。

リアルタイムデータエンドポイント

// Added to analytics-client.ts
export async function getRealtimeData(dimension?: string) {
  const request: any = {
    property: `properties/${propertyId}`,
    metrics: [{ name: "activeUsers" }],
  };

  if (dimension) {
    request.dimensions = [{ name: dimension }];
  }

  const [response] = await analyticsClient.runRealtimeReport(request);
  return formatReportResponse(response);
}

リアルタイムデータは最も価値の高い機能の1つです。ダッシュボードでは、リアルタイムセクションに移動し、読み込みを待ち、数字を読み取ります。MCPを通じて、「今サイトに何人いますか?」と入力すると、2秒で答えが得られます。

開発者が実際に実行したいリアルなクエリ

理論的な機能は退屈です。私が毎日実行している実際のクエリと、AIへの会話形式のリクエストとしてどのように見えるかを以下に示します。

トラフィック分析

「今週のセッション数上位10ページは何ですか?それぞれ先週と比較してどうですか?」

AIはこれを2つのrunReport呼び出し(今週用と先週用)に変換し、差分を計算して比較表を提示します。GA4のインターフェースでは、比較日付範囲の設定、正しいレポートの選択、ページディメンションの追加、セッションによるソート、10件への制限が必要です。MCPでは、1文で済みます。

「過去30日間のオーガニック検索からのトラフィックを、ランディングページ別に表示してください。」

これは、sessionDefaultChannelGroupをディメンションフィルター(「Organic Search」にフィルター)、landingPagePlusQueryStringをディメンション、sessionsengagedSessionsを指標とする単一のレポートになります。

コンバージョンデバッグ

「過去2週間の/pricingと/featuresの直帰率を比較してください。」

2つのフィルターされたレポート呼び出しで、結果が並べて比較されます。これは、会話形式で質問するのに30秒、GA4インターフェースで答えるのに3分かかる種類の質問です。

「サインアップイベントのコンバージョン率が最も高い国はどこですか?」

countryディメンション、sign_upイベントにフィルターされたeventCount指標、そしてコンバージョン率を計算するためのsessionsを含む単一のレポートです。AIは割り算を行い、直接パーセンテージを提示することもできます。

パフォーマンス監視

「今月のモバイルとデスクトップでのセッションあたりの平均エンゲージメント時間はどれくらいですか?」

deviceCategoryでセグメント化し、averageSessionDurationengagedSessionsを測定します。シンプルなクエリですが、GA4で正しいレポートに移動し、セグメントを構成する必要があります。

「今週1000回以上のビューがあるが、エンゲージメント率が30%未満のページはありますか?」

これは会話型アナリティクスが本当に輝くところです。AIはレポートを実行し、結果をプログラムでフィルターし、両方の基準に一致するページのみを返します。GA4では、スプレッドシートにエクスポートして手動でフィルターするか、カスタム探索を構築する必要があります。

ClaudeとAIアシスタントへの接続

Claude Desktopの設定

MCPサーバーを構築したら、Claude Desktopに接続するには設定ファイルを編集します。

{
  "mcpServers": {
    "ga4-analytics": {
      "command": "node",
      "args": ["/path/to/ga4-mcp-server/dist/server.js"],
      "env": {
        "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/service-account-key.json",
        "GA4_PROPERTY_ID": "123456789"
      }
    }
  }
}

macOSでは、これは~/Library/Application Support/Claude/claude_desktop_config.jsonに配置されます。Windowsでは、%APPDATA%\Claude\claude_desktop_config.jsonです。

Claude Desktopを再起動すると、GA4ツールがツールメニューに表示されます。Claudeは会話中にそれらを直接呼び出すことができます。

Claude Codeの統合

主にターミナルで作業する開発者向けに、Claude CodeはMCPサーバーをネイティブにサポートしています。サーバーをプロジェクトの.mcp.jsonに追加します。

{
  "mcpServers": {
    "ga4-analytics": {
      "command": "node",
      "args": ["./mcp-servers/ga4/dist/server.js"],
      "env": {
        "GOOGLE_APPLICATION_CREDENTIALS": "./credentials/ga4-service-account.json",
        "GA4_PROPERTY_ID": "123456789"
      }
    }
  }
}

これで、エディターを離れることなくアナリティクスデータをクエリできます。パフォーマンスの回帰をデバッグしているときに、ブラウザーにコンテキストを切り替えることなく、「今週と先週の/checkoutの直帰率はどうですか?」と尋ねることができます。

その他のMCP互換クライアントとの使用

MCPが標準であることの美しさは、互換性のあるクライアントであればどれでも機能することです。Cursor、Windsurf、その他のMCPをサポートするエディターは、同じサーバーに接続できます。サーバーを一度構築すれば、どこでも機能します。

会話型アナリティクスの実例

私が定期的に使用する実際のワークフローを説明します。

朝のチェックイン

私はClaudeに「昨日のトラフィックの概要を教えてください。総セッション数、アクティブユーザー数、上位5ページ、エンゲージメント率が25%未満のページがあればそれも。」と尋ねることから一日を始めます。

AIは3つのツール呼び出しを行います。全体的な指標用、上位ページ用、低エンゲージメントページ用です。そして、次のような結果を返します。

昨日:2,341セッション、ユニークユーザー1,892人。上位ページは/home (580セッション)、/pricing (412)、/docs/getting-started (389)、/blog/mcp-guide (267)、/features (198)でした。エンゲージメント率が25%未満のページは2つありました:/legal/terms (12%)と/blog/old-post-2024 (22%)。

これはGA4インターフェースでは5〜10分かかります。MCPを通じては15秒です。

機能ローンチの監視

新機能をリリースするときは、人々がそれを見つけて使用しているかどうかを知りたいです。「ローンチ前の3日間とローンチ後の3日間の/features/new-dashboardへのトラフィックを、獲得チャネル別に比較してください。」

AIは比較を実行し、どのチャネルが発見を促進しているかを特定し(通常、ローンチ後の最初の数日間はソーシャルとリファラルがダイレクトとオーガニック検索に遅れをとります)、異常を強調表示します。

ユーザーの苦情のデバッグ

ユーザーが「サイトが遅い」と報告した場合、私は「過去7日間のページごとの平均ページ読み込み時間を、最も遅いものから順に教えてください。」と尋ねることができます。GA4にWeb Vitalsデータがある場合(測定プロトコルまたはgtagイベント経由)、MCPサーバーはブラウザーを開くことなくこれを表示できます。

週次レポート

「セッション数、新規ユーザー数、エンゲージメント率、コンバージョン率について、上位20ページそれぞれの週ごとの比較を生成してください。」

これにより、Slackメッセージやチームの更新に直接貼り付けられる整形されたテーブルが生成されます。スプレッドシートでの複雑な操作も、スクリーンショットのトリミングも不要です。

高度な機能の構築

キャッシュレイヤー

GA4のAPIにはレート制限があります(無料ティアの場合、通常プロパティあたり1分間に10リクエスト)。頻繁にクエリされる可能性のあるMCPサーバーの場合、キャッシュレイヤーを追加します。

import { createHash } from "crypto";

const cache = new Map<string, { data: any; expiry: number }>();

function getCacheKey(dimensions: string[], metrics: string[], startDate: string, endDate: string): string {
  const input = JSON.stringify({ dimensions, metrics, startDate, endDate });
  return createHash("md5").update(input).digest("hex");
}

export async function cachedRunReport(
  dimensions: string[],
  metrics: string[],
  startDate: string,
  endDate: string
) {
  const key = getCacheKey(dimensions, metrics, startDate, endDate);
  const cached = cache.get(key);

  if (cached && cached.expiry > Date.now()) {
    return cached.data;
  }

  const result = await runReport(dimensions, metrics, startDate, endDate);

  // Cache for 5 minutes (real-time queries get shorter TTL)
  const ttl = startDate === "today" ? 60_000 : 300_000;
  cache.set(key, { data: result, expiry: Date.now() + ttl });

  return result;
}

リアルタイムクエリは1分のTTL(Time To Live)を持ちます。履歴クエリは5分です。これにより、AIが連続して複数の関連クエリを行う会話セッション中に、レート制限を十分に回避できます。

マルチプロパティサポート

複数の製品やクライアントを管理している場合、GA4プロパティ間の切り替えをサポートするようにサーバーを拡張します。

server.tool(
  "switch_property",
  "Switch to a different GA4 property",
  {
    propertyId: z.string().describe("The GA4 property ID to switch to"),
  },
  async ({ propertyId }) => {
    // Validate access
    const properties = await listAccessibleProperties();
    if (!properties.includes(propertyId)) {
      return {
        content: [{ type: "text", text: `No access to property ${propertyId}` }],
      };
    }

    currentPropertyId = propertyId;
    return {
      content: [{ type: "text", text: `Switched to property ${propertyId}` }],
    };
  }
);

これで、何も再設定することなく「ステージングプロパティに切り替えて、昨日のトラフィックを表示してください」と言うことができます。

セキュリティに関する考慮事項

アナリティクスデータにアクセスするMCPサーバーを実行すると、意図的な処理が必要な複数のセキュリティ上の懸念が生じます。

認証情報の管理

サービスアカウントのキーファイルは最も機密性の高い部分です。バージョン管理にコミットしないでください。環境変数またはシークレットマネージャーを使用してください。MCPサーバーをローカルで実行している場合(Claude Desktopの一般的なケース)、キーファイルをプロジェクトディレクトリの外に、制限されたファイル権限で保存してください。

chmod 600 /path/to/service-account-key.json

チーム環境では、キーファイルの代わりにGoogle CloudのWorkload Identity Federationの使用を検討してください。これにより、ランタイムIDに紐付けられた短命のトークンを使用することで、静的な認証情報が完全に排除されます。

最小権限の原則

サービスアカウントはGA4への読み取り専用アクセス権を持つべきです。具体的には、GA4プロパティレベルで「閲覧者」ロールを付与し、「編集者」または「管理者」ロールは付与しないでください。MCPサーバーがアナリティクス設定を変更したり、オーディエンスを作成したり、測定設定を変更したりする必要は決してありません。

Google Cloudプロジェクトレベルでは、analyticsdata.readonlyスコープのみを付与してください。これにより、キーが侵害された場合でも、サービスアカウントが他のGoogle Cloudサービスにアクセスするのを防ぎます。

データ公開範囲

MCPサーバーがアクセスできるデータについて意図的に考えてください。GA4にはPII(個人識別情報)が含まれる可能性があります。ユーザーID、クライアントID、IP由来の地理位置情報などです。MCPサーバーは、サポートしたいクエリに不要なフィールドを削除またはマスクする必要があります。

const BLOCKED_DIMENSIONS = ["userID", "clientId"];

function validateDimensions(dimensions: string[]) {
  const blocked = dimensions.filter((d) => BLOCKED_DIMENSIONS.includes(d));
  if (blocked.length > 0) {
    throw new Error(`Blocked dimensions for privacy: ${blocked.join(", ")}`);
  }
}

これは、他のチームメンバーがMCPサーバーとやり取りできる場合に特に重要です。個々のユーザーデータを公開する可能性のあるカジュアルなクエリを防ぎたいものです。

トランスポートセキュリティ

MCPは現在、Claude Desktopで使用される場合、stdio(標準入出力)を介して実行されます。これは、通信がローカルプロセス境界内で発生することを意味します。ネットワークへの露出はありません。ただし、MCPサーバーをHTTP経由でデプロイする場合(SSEトランスポートを使用)、TLS、認証トークン、レート制限が必要です。他のAPIエンドポイントと同様に扱ってください。

監査ログ

MCPサーバーが処理するすべてのクエリをログに記録してください。セキュリティのためだけでなく、説明責任のためです。「誰がいつアナリティクスデータをクエリしたか」という質問があったときに、答えられるようにすべきです。

function logQuery(tool: string, params: Record<string, unknown>) {
  console.log(JSON.stringify({
    timestamp: new Date().toISOString(),
    tool,
    params,
    propertyId: currentPropertyId,
  }));
}

トレードオフと制限事項

うまくいくこと

会話型アナリティクスは、開発者にとってGA4の学習曲線を排除します。GA4のディメンション名や指標名、レポートタイプ、フィルター構文を知る必要はありません。AIが翻訳を処理します。一度だけ尋ねて二度と繰り返さないようなアドホックな質問の場合、これはダッシュボードよりも劇的に高速です。

アナリティクスと開発コンテキストを組み合わせることは強力です。Claude Codeでデバッグ中にアナリティクスデータを同時に確認できると、フィードバックループが短縮されます。コード、エラーログ、ユーザーへの影響を1か所で確認できます。

うまくいかないこと

反復的な洗練が必要な複雑な探索は、GA4の探索インターフェースの方がまだ高速です。複数のステージ、カスタムセグメント、比較グループを含むファネル分析を構築している場合、ビジュアルビルダーは会話型インターフェースでは匹敵しない即時フィードバックを提供します。

自動更新されるリアルタイムダッシュボードは、MCPの現在のモデルの範囲外です。MCPはリクエスト-レスポンス型です。あなたが尋ね、それが答えます。リアルタイムユーザーを表示するライブ更新ダッシュボードには、GA4のリアルタイムビューやカスタムGrafanaパネルのような専用ツールの方が優れています。

データ量は制約です。GA4 Data APIは、1リクエストあたり最大100,000行を返します。毎日数百万のイベントがあるプロパティの場合、この制限に達しないように、ディメンションと日付範囲を具体的に指定する必要があるかもしれません。APIには大規模なデータセットに対するサンプリング動作もあり、高カーディナリティのクエリでは結果が正確ではなく推定される場合があります。

MCP vs. 直接API統合

アナリティクスデータが必要な製品機能(SaaSの管理ダッシュボードなど)を構築している場合は、GA4 APIを直接使用してください。MCPは人間が関与するクエリ用であり、プログラムによるデータパイプライン用ではありません。実行するクエリが正確にわかっている場合、自然言語翻訳とAI解釈のオーバーヘッドは不要です。

MCPが輝くのは、質問が非構造化である場合、探しているものが正確にわからない場合、または一度限りの質問に対して適切な統合を構築する労力が正当化されない場合です。これは、スクリプトを書く代わりにREPLを開くアナリティクス版です。

はじめに

これを自分で試したい場合、最小限のパスは次のとおりです。

  1. Google Cloudプロジェクトを作成し、Google Analytics Data APIを有効にします。
  2. GA4プロパティへの閲覧者アクセス権を持つサービスアカウントを作成します。
  3. サービスアカウントのキーJSONをダウンロードします。
  4. 上記で説明したように、MCPサーバーをクローンまたは構築します。
  5. Claude DesktopまたはClaude Codeの設定にサーバーを追加します。
  6. 質問を開始します。

GA4プロパティが既にある場合、全体のセットアップには約30分かかります。その見返りはすぐに得られます。アナリティクスの質問に対する答えを5分ではなく5秒で初めて得たとき、ダッシュボードに戻りたいとは思わないでしょう。

アナリティクスデータは、ほとんどの開発チームで最も活用されていない資産の1つです。価値がないからではなく、アクセスが常に不便で、カジュアルなクエリを妨げてきたからです。MCPはその摩擦を取り除きます。データは同じです。質問も同じです。変わるのは、答えを得るまでの時間です。

DU

Danil Ulmashev

Full Stack Developer

一緒にお仕事しませんか?