Skip to main content
Glama

mcp-server-google-analytics

by ruchernchong
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js"; import { BetaAnalyticsDataClient } from "@google-analytics/data"; // Validate environment variables function validateEnvironment(): void { const requiredEnvVars = [ "GOOGLE_CLIENT_EMAIL", "GOOGLE_PRIVATE_KEY", "GA_PROPERTY_ID", ]; const missingVars = requiredEnvVars.filter( (varName) => !process.env[varName], ); if (missingVars.length > 0) { throw new Error( `Missing required environment variables: ${missingVars.join(", ")}`, ); } } // Validate date format (YYYY-MM-DD) function validateDateFormat(date: string): boolean { const dateRegex = /^\d{4}-\d{2}-\d{2}$/; if (!dateRegex.test(date)) { return false; } const parsedDate = new Date(date); return parsedDate.toISOString().split("T")[0] === date; } // Validate date range function validateDateRange(startDate: string, endDate: string): void { if (!validateDateFormat(startDate)) { throw new McpError( ErrorCode.InvalidParams, `Invalid startDate format. Expected YYYY-MM-DD, got: ${startDate}`, ); } if (!validateDateFormat(endDate)) { throw new McpError( ErrorCode.InvalidParams, `Invalid endDate format. Expected YYYY-MM-DD, got: ${endDate}`, ); } if (new Date(startDate) > new Date(endDate)) { throw new McpError( ErrorCode.InvalidParams, "startDate cannot be after endDate", ); } } // Initialize environment validation validateEnvironment(); // Initialize the Google Analytics Data client const analyticsDataClient = new BetaAnalyticsDataClient({ credentials: { client_email: process.env.GOOGLE_CLIENT_EMAIL!, private_key: process.env.GOOGLE_PRIVATE_KEY!.replace(/\\n/g, "\n"), }, }); const propertyId = process.env.GA_PROPERTY_ID!; // Create the server const server = new Server( { name: "google-analytics-server", version: "1.0.0", }, { capabilities: { tools: {}, }, }, ); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "getPageViews", description: "Get page view metrics for a specific date range", inputSchema: { type: "object", properties: { startDate: { type: "string", description: "Start date in YYYY-MM-DD format", }, endDate: { type: "string", description: "End date in YYYY-MM-DD format", }, dimensions: { type: "array", items: { type: "string" }, description: "Dimensions to group by (e.g., page, country)", }, }, required: ["startDate", "endDate"], }, }, { name: "getActiveUsers", description: "Get active users metrics for a specific date range", inputSchema: { type: "object", properties: { startDate: { type: "string", description: "Start date in YYYY-MM-DD format", }, endDate: { type: "string", description: "End date in YYYY-MM-DD format", }, }, required: ["startDate", "endDate"], }, }, { name: "getEvents", description: "Get event metrics for a specific date range", inputSchema: { type: "object", properties: { startDate: { type: "string", description: "Start date in YYYY-MM-DD format", }, endDate: { type: "string", description: "End date in YYYY-MM-DD format", }, eventName: { type: "string", description: "Specific event name to filter by (optional)", }, }, required: ["startDate", "endDate"], }, }, { name: "getUserBehavior", description: "Get user behavior metrics like session duration and bounce rate", inputSchema: { type: "object", properties: { startDate: { type: "string", description: "Start date in YYYY-MM-DD format", }, endDate: { type: "string", description: "End date in YYYY-MM-DD format", }, }, required: ["startDate", "endDate"], }, }, ], }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "getPageViews": { const { startDate, endDate, dimensions = ["page"], } = args as { startDate: string; endDate: string; dimensions?: string[]; }; validateDateRange(startDate, endDate); const [response] = await analyticsDataClient.runReport({ property: `properties/${propertyId}`, dateRanges: [{ startDate, endDate }], dimensions: dimensions.map((dimension) => ({ name: dimension })), metrics: [{ name: "screenPageViews" }], }); return { content: [ { type: "text", text: JSON.stringify(response, null, 2), }, ], }; } case "getActiveUsers": { const { startDate, endDate } = args as { startDate: string; endDate: string; }; validateDateRange(startDate, endDate); const [response] = await analyticsDataClient.runReport({ property: `properties/${propertyId}`, dateRanges: [{ startDate, endDate }], metrics: [{ name: "activeUsers" }, { name: "newUsers" }], dimensions: [{ name: "date" }], }); return { content: [ { type: "text", text: JSON.stringify(response, null, 2), }, ], }; } case "getEvents": { const { startDate, endDate, eventName } = args as { startDate: string; endDate: string; eventName?: string; }; validateDateRange(startDate, endDate); const [response] = await analyticsDataClient.runReport({ property: `properties/${propertyId}`, dateRanges: [{ startDate, endDate }], dimensions: [{ name: "eventName" }, { name: "date" }], metrics: [{ name: "eventCount" }], ...(eventName && { dimensionFilter: { filter: { fieldName: "eventName", stringFilter: { value: eventName }, }, }, }), }); return { content: [ { type: "text", text: JSON.stringify(response, null, 2), }, ], }; } case "getUserBehavior": { const { startDate, endDate } = args as { startDate: string; endDate: string; }; validateDateRange(startDate, endDate); const [response] = await analyticsDataClient.runReport({ property: `properties/${propertyId}`, dateRanges: [{ startDate, endDate }], metrics: [ { name: "averageSessionDuration" }, { name: "bounceRate" }, { name: "sessionsPerUser" }, ], dimensions: [{ name: "date" }], }); return { content: [ { type: "text", text: JSON.stringify(response, null, 2), }, ], }; } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { if (error instanceof McpError) { throw error; } // Handle Google Analytics API errors if (error instanceof Error) { throw new McpError( ErrorCode.InternalError, `Google Analytics API error: ${error.message}`, ); } throw new McpError(ErrorCode.InternalError, "An unexpected error occurred"); } }); // Error handling for server startup process.on("uncaughtException", (error) => { console.error("Uncaught exception:", error); process.exit(1); }); process.on("unhandledRejection", (reason, promise) => { console.error("Unhandled rejection at:", promise, "reason:", reason); process.exit(1); }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Google Analytics MCP server running on stdio"); } main().catch((error) => { console.error("Failed to start server:", error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ruchernchong/mcp-server-google-analytics'

If you have feedback or need assistance with the MCP directory API, please join our Discord server