45395 - シコウサクゴ -

Claude CodeとMCPサーバーでAI駆動のアクセス解析作ってみた

はじめに

本記事では、Claude Code(Anthropic社のCLIツール)をベースに、GA4(Google Analytics 4)とGSC(Google Search Console)のデータを直接操作できるカスタムMCPサーバーを構築し、アクセス解析とSEO対策を開発フローに統合する方法を紹介します。

こんな人が対象です

  • AI駆動開発に興味のあるエンジニア
  • アクセス解析データを活用した意思決定を行いたい開発のひと
  • Claude CodeやMCP(Model Context Protocol)の実践的な活用事例を知りたいひと

前提条件

環境

  • Mac M1(Apple Silicon)
  • Node.js >= 18
  • npm >= 10
  • Claude Code Maxプラン(MCPサーバー登録に必要)

インストール済みツール

  • TypeScript 5.9.x("target": "ES2022"、strict mode)
  • GA4設置済み(GA4プロパティIDが必要)
  • Google Search Console設置済み(サイト所有権確認済み)

主要パッケージバージョン

パッケージバージョン
@modelcontextprotocol/sdk^1.27.1
@google-analytics/data^5.2.1
googleapis^171.4.0
zod^4.3.6
typescript^5.9.3
tsup^8.5.1

この記事で得られる幸せポイント

  • MCPサーバーの設計・実装パターン
  • GA4/GSC APIの統合方法
  • Claude Codeとの連携による効率的なデータ分析ワークフロー
  • TypeScriptによる型安全なMCPサーバー実装
  • 自然言語(チャット形式)で複雑なアクセス解析できるようになる

MCPサーバーとは

Model Context Protocolの概要

MCP(Model Context Protocol)は、Anthropic社が策定したAIモデルと外部ツール・データソースを接続するための標準プロトコルです。Claude Codeは、MCPサーバーを通じて様々な外部システムと連携できます。

MCPサーバーの役割

MCPサーバーは以下の役割を担います:

  1. ツール登録: Claude Codeが使用できる機能(ツール)の定義
  2. データアクセス: 外部API(GA4、GSCなど)との通信
  3. 結果整形: API応答をClaude Codeが理解できる形式に変換

MCPサーバーのアーキテクチャ

1
┌─────────────────┐
2
│ Claude Code │ ← ユーザーの自然言語クエリ
3
└────────┬────────┘
4
│ MCP Protocol (stdio)
5
6
┌─────────────────┐
7
│ MCP Server │
8
│ ├ Tool Handler │ ← ツール実行ロジック
9
│ ├ API Client │ ← GA4/GSC API呼び出し
10
│ └ Auth Manager │ ← 認証管理
11
└────────┬────────┘
12
│ API Requests
13
14
┌─────────────────┐
15
│ GA4 / GSC API │
1 collapsed line
16
└─────────────────┘

GA4 Analyzer MCPサーバーの設計

プロジェクト構成

本記事で紹介するMCPサーバーは、クリーンアーキテクチャを意識した設計になっています。

1
src/
2
├── mcp/ # MCP関連コード
3
│ ├── server.ts # MCPサーバーエントリポイント
4
│ ├── config.ts # 設定管理
5
│ ├── tools/ # ツール定義
6
│ │ ├── analyze-comparison.ts # 比較分析ツール
7
│ │ ├── analyze-exploratory.ts # 探索的分析ツール
8
│ │ ├── check-config.ts # 設定確認ツール
9
│ │ └── validate-config.ts # 設定検証ツール
10
│ └── utils/
11
│ ├── validator.ts # 入力検証(Zod)
12
│ └── error-handler.ts
13
├── application/ # アプリケーション層
14
│ ├── query-orchestrator.ts
15
│ └── insight-generator.ts
6 collapsed lines
16
├── infrastructure/ # インフラ層
17
│ ├── ga4-client.ts # GA4 API クライアント
18
│ ├── gsc-client.ts # GSC API クライアント
19
│ └── auth-manager.ts # 認証管理
20
└── domain/ # ドメイン層
21
└── models/ # ドメインモデル

設計のポイント

1. レイヤー分離

  • MCP層: Claude Codeとのインターフェース
  • Application層: ビジネスロジック(分析オーケストレーション)
  • Infrastructure層: 外部API通信
  • Domain層: ドメインモデルとビジネスルール

2. 型安全性の確保

すべての入力はZodスキーマでバリデーション:

1
import { z } from "zod";
2
3
export const AnalyzeComparisonInputSchema = z.object({
4
query: z.string().min(1),
5
options: z.object({
6
verbose: z.boolean().default(false),
7
compareType: z.string().optional(),
8
propertyId: z.string().optional(),
9
}).default({ verbose: false }),
10
});
11
12
export type AnalyzeComparisonInput = z.infer<typeof AnalyzeComparisonInputSchema>;

3. エラーハンドリング

統一的なエラー変換機構を実装:

1
export function toMCPError(error: Error): MCPResponse<never> {
2
return {
3
success: false,
4
error: {
5
code: "INTERNAL_ERROR",
6
message: error.message,
7
},
8
};
9
}

実装の詳細

MCPサーバーの起動処理

bin/mcp-server.ts
1
import { startMCPServer } from "../src/mcp/server.js";
2
3
process.on("uncaughtException", (error) => {
4
console.error("[MCP Server] Uncaught exception:", error);
5
process.exit(1);
6
});
7
8
process.on("unhandledRejection", (reason, promise) => {
9
console.error("[MCP Server] Unhandled rejection at:", promise, "reason:", reason);
10
process.exit(1);
11
});
12
13
startMCPServer().catch((error: unknown) => {
14
console.error("[MCP Server] Failed to start:", error);
2 collapsed lines
15
process.exit(1);
16
});

MCPサーバー本体

src/mcp/server.ts
1
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
4
export async function startMCPServer(): Promise<void> {
5
const server = new McpServer(
6
{
7
name: "ga4-analyzer",
8
version: "0.1.0",
9
},
10
{
11
capabilities: {
12
tools: {},
13
},
14
},
32 collapsed lines
15
);
16
17
// ツール登録
18
const tools = registerTools();
19
20
for (const tool of tools) {
21
server.registerTool(
22
tool.name,
23
{
24
description: tool.description,
25
inputSchema: tool.inputSchema,
26
},
27
async (args) => {
28
const result = await tool.handler(args);
29
return {
30
content: [
31
{
32
type: "text",
33
text: JSON.stringify(result, null, 2),
34
},
35
],
36
};
37
},
38
);
39
}
40
41
const transport = new StdioServerTransport();
42
await server.connect(transport);
43
44
console.error(`[MCP Server] ga4-analyzer v0.1.0 started`);
45
console.error(`[MCP Server] Registered ${tools.length} tools: ${tools.map(t => t.name).join(", ")}`);
46
}

ツールの実装例:比較分析

src/mcp/tools/analyze-comparison.ts
1
async function handleAnalyzeComparison(
2
input: AnalyzeComparisonInput,
3
): Promise<MCPResponse<IntegratedAnalysisResult>> {
4
try {
5
// 環境変数から設定を読み込み
6
const envConfig = loadConfigFromEnv();
7
8
// オーケストレーターを作成
9
const orchestrator = new QueryOrchestrator();
10
11
// 比較分析を実行
12
const result = await orchestrator.execute(input.query, {
13
verbose: input.options.verbose,
14
compareType: input.options.compareType,
23 collapsed lines
15
propertyId: input.options.propertyId || envConfig.ga4PropertyId,
16
forceComparisonMode: true,
17
});
18
19
return {
20
success: true,
21
data: result,
22
};
23
} catch (error) {
24
return toMCPError(error as Error);
25
}
26
}
27
28
export function createAnalyzeComparisonTool(
29
inputSchema: typeof AnalyzeComparisonInputSchema,
30
): ToolDefinition<AnalyzeComparisonInput, IntegratedAnalysisResult> {
31
return {
32
name: "analyze_comparison",
33
description: "GA4データの比較分析を実行します。2つの期間を比較し、トラフィックソース、ページ、デバイス、イベント、検索キーワードの5軸で分析します。",
34
inputSchema,
35
handler: handleAnalyzeComparison,
36
};
37
}

Claude Codeとの連携設定

MCPサーバーの登録

Claude CodeにMCPサーバーを登録するには、.claude/plugins/repos/ga4-analyzer/.mcp.jsonを作成します:

1
{
2
"ga4-analyzer": {
3
"command": "node",
4
"args": [
5
"/Users/username/path/to/ga4-analyzer/dist/mcp-server.js"
6
],
7
"env": {
8
"GA4_PROPERTY_ID": "318772207",
9
"GA4_SERVICE_ACCOUNT_KEY": "/path/to/serviceaccount.json",
10
"GA4_ENABLE_GSC": "true",
11
"GA4_GSC_SITE_URL": "https://www.example.com",
12
"GA4_MAX_RETRIES": "3",
13
"GA4_REQUEST_TIMEOUT": "30000",
14
"GA4_DATA_LIMIT": "100"
15
}
2 collapsed lines
16
}
17
}

メタデータファイル

.claude/plugins/repos/ga4-analyzer/.claude-plugin/metadata.json

1
{
2
"name": "ga4-analyzer",
3
"version": "0.1.0",
4
"description": "GA4 + Google Search Console integrated access analysis MCP server",
5
"author": "htada",
6
"type": "mcp"
7
}

ビルドとデプロイ

Terminal window
1
# 依存関係のインストール
2
npm install
3
4
# TypeScriptのビルド
5
npm run build
6
7
# MCPサーバーのテスト起動
8
npm run dev:mcp

実践的な使用例

1. 前日比アクセス増の要因分析

インプット(Claude Codeへの入力):

1
3/3のアクセス増の要因を教えて

MCPサーバーの動作:

  1. クエリを解析 → 対象日: 2026-03-03、比較日: 2026-03-02、分析タイプ: increase_factors
  2. GA4 APIから5軸(参照元・ページ・デバイス・イベント・検索キーワード)のデータを並列取得
  3. 前日比変化を計算し、有意な変化を特定

アウトプット(Claude Codeへの返却):

1
【対象日: 2026-03-03 vs 比較日: 2026-03-02】
2
3
総アクセス数: 1,200 セッション(前日比 +20.0%、+200 セッション)
4
5
【主な増加要因】
6
■ 参照元/メディア
7
1. google/organic: +200 セッション(+33.3%)★★★
8
9
■ ページ
10
1. /blog/typescript-tips: +85 セッション(+42.0%)★★★
11
2. /blog/astro-setup: +40 セッション(+18.0%)★★
12
13
【推察】
14
・Googleオーガニック検索の急増が主因。特定の記事への検索流入が増加した可能性。
15
・/blog/typescript-tips への流入が突出しており、SNSシェアまたは検索順位上昇が考えられる。

2. SEO効果の測定(GSC連携)

インプット:

1
過去30日間で検索順位が上昇したページを表示して

アウトプット:

1
【検索キーワード分析】期間: 2026-02-04 〜 2026-03-04
2
3
■ 検索順位が改善したページ TOP 5
4
1. /blog/typescript-tips
5
クリック数: 320(+85)、表示回数: 4,200(+1,100)
6
平均順位: 4.2 → 2.8(+1.4ポジション改善)★★★
7
8
2. /blog/astro-setup
9
クリック数: 180(+42)、表示回数: 2,800(+600)
10
平均順位: 8.5 → 6.1(+2.4ポジション改善)★★
11
12
3. /blog/firebase-deploy
13
クリック数: 95(+18)、表示回数: 1,500(+200)
14
平均順位: 12.3 → 10.8(+1.5ポジション改善)★
15
3 collapsed lines
16
【推察】
17
・/blog/typescript-tips は上位表示が安定してきており、CTRも改善傾向。
18
・技術系コンテンツ全般で評価が上昇中。内部リンク強化が効いている可能性。

3. 設定の確認

インプット:

1
GA4の設定を確認して

アウトプット:

1
{
2
"success": true,
3
"data": {
4
"ga4PropertyId": "318772207",
5
"enableGSC": true,
6
"gscSiteUrl": "https://45395.jp",
7
"maxRetries": 3,
8
"requestTimeout": 30000,
9
"dataLimit": 100,
10
"serviceAccountKeyPath": "/path/to/serviceaccount.json",
11
"status": {
12
"ga4": "connected",
13
"gsc": "connected"
14
}
15
}
1 collapsed line
16
}

確認できる内容:

  • GA4 プロパティIDと接続状態
  • GSC連携の有効/無効と対象サイトURL
  • データ取得上限(dataLimit
  • リトライ設定とタイムアウト値

技術的な工夫とベストプラクティス

1. 認証管理の抽象化

サービスアカウント認証をAuthenticationManagerで一元管理:

1
export class AuthenticationManager {
2
loadCredentials(config: {
3
serviceAccountKeyPath?: string;
4
useApplicationDefaultCredentials?: boolean;
5
}): GoogleAuth {
6
if (config.serviceAccountKeyPath) {
7
// サービスアカウントキーファイルから読み込み
8
const keyFile = JSON.parse(fs.readFileSync(config.serviceAccountKeyPath, 'utf-8'));
9
return new GoogleAuth({
10
credentials: keyFile,
11
scopes: [
12
'https://www.googleapis.com/auth/analytics.readonly',
13
'https://www.googleapis.com/auth/webmasters.readonly',
14
],
15
});
11 collapsed lines
16
} else {
17
// Application Default Credentialsを使用
18
return new GoogleAuth({
19
scopes: [
20
'https://www.googleapis.com/auth/analytics.readonly',
21
'https://www.googleapis.com/auth/webmasters.readonly',
22
],
23
});
24
}
25
}
26
}

2. リトライ機構の実装

API呼び出しの信頼性を高めるため、自動リトライを実装:

1
async function executeWithRetry<T>(
2
fn: () => Promise<T>,
3
maxRetries: number = 3,
4
timeout: number = 30000,
5
): Promise<T> {
6
let lastError: Error;
7
8
for (let attempt = 0; attempt < maxRetries; attempt++) {
9
try {
10
return await Promise.race([
11
fn(),
12
new Promise<never>((_, reject) =>
13
setTimeout(() => reject(new Error('Timeout')), timeout)
14
),
15
]);
10 collapsed lines
16
} catch (error) {
17
lastError = error as Error;
18
if (attempt < maxRetries - 1) {
19
await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1)));
20
}
21
}
22
}
23
24
throw lastError!;
25
}

3. 環境変数による柔軟な設定

MCP経由の環境変数とローカル設定ファイルを適切にマージ:

1
export function loadConfigFromEnv(): Partial<Configuration> {
2
return {
3
ga4PropertyId: process.env.GA4_PROPERTY_ID || '',
4
serviceAccountKeyPath: process.env.GA4_SERVICE_ACCOUNT_KEY,
5
enableGSC: process.env.GA4_ENABLE_GSC === 'true',
6
gscSiteUrl: process.env.GA4_GSC_SITE_URL,
7
maxRetries: parseInt(process.env.GA4_MAX_RETRIES || '3', 10),
8
requestTimeout: parseInt(process.env.GA4_REQUEST_TIMEOUT || '30000', 10),
9
dataLimit: parseInt(process.env.GA4_DATA_LIMIT || '100', 10),
10
};
11
}

4. TypeScriptの型安全性

ジェネリクスを活用した型安全なツール定義:

1
export interface ToolDefinition<TInput, TOutput> {
2
name: string;
3
description: string;
4
inputSchema: z.ZodSchema<TInput>;
5
handler: (input: TInput) => Promise<MCPResponse<TOutput>>;
6
}
7
8
export interface MCPResponse<T> {
9
success: boolean;
10
data?: T;
11
error?: {
12
code: string;
13
message: string;
14
};
15
}

パフォーマンスとスケーラビリティ

データキャッシング戦略

頻繁にアクセスされるデータはメモリキャッシュを活用:

1
class DataCache {
2
private cache: Map<string, { data: any; timestamp: number }> = new Map();
3
private ttl: number = 5 * 60 * 1000; // 5分
4
5
get(key: string): any | null {
6
const entry = this.cache.get(key);
7
if (!entry) return null;
8
9
if (Date.now() - entry.timestamp > this.ttl) {
10
this.cache.delete(key);
11
return null;
12
}
13
14
return entry.data;
15
}
8 collapsed lines
16
17
set(key: string, data: any): void {
18
this.cache.set(key, {
19
data,
20
timestamp: Date.now(),
21
});
22
}
23
}

バッチ処理の最適化

複数のAPIリクエストを並列実行:

1
async function fetchMultipleDimensions(
2
ga4Client: GA4ApiClient,
3
dimensions: string[],
4
dateRange: DateRange,
5
): Promise<Map<string, DimensionData>> {
6
const results = await Promise.all(
7
dimensions.map(dim =>
8
ga4Client.runReport({
9
dimensions: [{ name: dim }],
10
metrics: [{ name: 'activeUsers' }],
11
dateRanges: [dateRange],
12
})
13
)
14
);
15
2 collapsed lines
16
return new Map(dimensions.map((dim, i) => [dim, results[i]]));
17
}

セキュリティ考慮事項

1. 認証情報の保護

  • サービスアカウントキーは環境変数またはセキュアストレージに保存
  • キーファイルは.gitignoreに追加
  • 最小権限の原則(ReadOnlyスコープのみ)

2. 入力バリデーション

すべてのユーザー入力をZodスキーマで検証:

1
export const QueryInputSchema = z.object({
2
query: z.string().min(1).max(500),
3
options: z.object({
4
dateRange: z.object({
5
start: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
6
end: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
7
}).optional(),
8
}).optional(),
9
});

3. レート制限

API呼び出しを制限してコスト管理:

1
class RateLimiter {
2
private requests: number[] = [];
3
private limit: number = 10;
4
private window: number = 60000; // 1分
5
6
async checkLimit(): Promise<void> {
7
const now = Date.now();
8
this.requests = this.requests.filter(t => now - t < this.window);
9
10
if (this.requests.length >= this.limit) {
11
const oldestRequest = this.requests[0];
12
const waitTime = this.window - (now - oldestRequest);
13
await new Promise(resolve => setTimeout(resolve, waitTime));
14
}
15
3 collapsed lines
16
this.requests.push(now);
17
}
18
}

デバッグとトラブルシューティング

ログ出力の最適化

MCPサーバーではstderrを使用してログ出力:

1
// stdout: MCPプロトコル通信用
2
// stderr: デバッグログ用
3
4
console.error(`[MCP Server] Tool executed: ${toolName}`);
5
console.error(`[MCP Server] Input: ${JSON.stringify(input)}`);
6
console.error(`[MCP Server] Result: ${JSON.stringify(result)}`);

よくある問題と解決策

問題1: 認証エラー

1
Error: Unable to authenticate with GA4 API

解決策:

  • サービスアカウントキーのパスを確認
  • サービスアカウントにGA4プロパティへのアクセス権限があるか確認
  • スコープが正しいか確認(analytics.readonly

問題2: タイムアウト

1
Error: Request timeout after 30000ms

解決策:

  • GA4_REQUEST_TIMEOUT環境変数を増やす
  • データ取得期間を短縮
  • GA4_DATA_LIMITを減らす

問題3: MCPサーバーが起動しない

1
Error: Cannot find module './dist/mcp-server.js'

解決策:

Terminal window
1
# ビルドを実行
2
npm run build
3
4
# ビルド成果物を確認
5
ls -la dist/

今後の拡張可能性

1. リアルタイム分析

GA4 Realtime APIを統合してリアルタイムトラフィック監視を実現:

1
export class RealtimeAnalyzer {
2
async getCurrentVisitors(propertyId: string): Promise<number> {
3
const response = await ga4Client.runRealtimeReport({
4
property: `properties/${propertyId}`,
5
metrics: [{ name: 'activeUsers' }],
6
});
7
8
return response.rows?.[0]?.metricValues?.[0]?.value || 0;
9
}
10
}

2. アラート機能

異常検知とSlack通知:

1
export class AlertSystem {
2
async checkAnomalies(metrics: Metrics): Promise<void> {
3
if (metrics.activeUsers < metrics.baseline * 0.5) {
4
await this.sendSlackNotification({
5
message: 'Traffic dropped by 50%!',
6
severity: 'high',
7
});
8
}
9
}
10
}

3. カスタムダッシュボード

分析結果をMarkdownやHTMLで出力:

1
export class ReportGenerator {
2
generateMarkdownReport(analysis: AnalysisResult): string {
3
return `
4
# アクセス解析レポート
5
6
## 期間比較
7
8
| 指標 | 前期 | 今期 | 変化率 |
9
| ---------- | -------------------------- | ------------------------- | ----------------------- |
10
| ユーザー数 | ${analysis.previous.users} | ${analysis.current.users} | ${analysis.changeRate}% |
11
12
## トップページ
13
14
${analysis.topPages.map((page, i) => `${i + 1}. ${page.path} (${page.views} views)`).join('\n')}
15
`;
2 collapsed lines
16
}
17
}

主要なポイント

  1. MCPプロトコル: AIモデルと外部ツールを標準化された方法で接続
  2. クリーンアーキテクチャ: レイヤー分離による保守性の向上
  3. 型安全性: TypeScript + Zodによる堅牢な実装
  4. 実践的な統合: 自然言語でアクセス解析を実行できるワークフロー

AI駆動開発の未来

MCPサーバーを活用することで、従来は手動で行っていたデータ分析やレポート作成を、自然言語でClaude Codeに依頼できるようになります。

  • 開発速度の向上: ダッシュボードを開かずにデータ分析
  • 意思決定の高速化: 必要な情報を即座に取得
  • SEO最適化の自動化: 検索順位の継続的なモニタリング

次のステップ

本記事のコードをベースに、以下のようなカスタマイズが可能です:

  • 他の分析ツール(PageSpeed Insight、 BigQuery MCPなど)との統合
  • カスタムメトリクスの定義と追跡
  • 定期的な自動レポート生成
  • A/Bテスト結果の自動分析

MCPサーバーのエコシステムは急速に成長しており、今後さらに多くの統合が可能になるでしょう。


リソース

Article title:Claude CodeとMCPサーバーでAI駆動のアクセス解析作ってみた
Article author:45395
Release time:2025-03-05